【STM32】ADC|多通道ADC采集

本次实现的是ADC实现数字信号与模拟信号的转化,数字信号时不连续的,模拟信号是连续的。

1.ADC转化的原理

模拟-数字转换技术使用的是逐次逼近法,使用二分比较的方法来确定电压值

当单片机对应的参考电压为3.3v时,0~ 3.3v(模拟信号)对应0~4095(数字信号),假如我们知道一个数字信号的值为x,那么他对应的模拟信号

x/4095=y/3.3

y就是x对应的模拟信号

假如要确定0.8v对应的数字信号的值

他会将0.8v电压保存在电容中,然后先和3.3v的一半对应的1.65v(2048)做比较,如果小于1.65v的话,12位最高位就是0,因为1000 0000 0000 对应的就是2048

接着就是和1.65的一半比较,0.825v(1024)>0.8v,所以12位中倒数第二高位就是0,因为0.825(1024)对应二进制就是0100 0000 0000

接着0.8v和0.825的一半比较->0.4125v(512),0.8v>0.4125v,所以第三位为1,因为512对应的二进制为0010 00000000

按这个方法依次确定0.8v对应的12位的二进制数,然后这个二进制对应的十进制就是0.8v对应的数字信号的值

所以对应的过程是1.启动ADC 2.采样&转换 3.获取&计算,针对STM32F103C8T6芯片有10个外部通道,和两个内部通道(内部温度传感器,内部参考电压)进行ADC的转化,ADC1和ADC2,两个转化结构,每个转化结构都有一个注入组和一个规则组,我们现在只讲讲规则组,我们要对一个通道的电压值进行采集的时候,我们需要将这个通道注册进这个通道中,当启动ADC,然后采样转化的电压值就会放在规则通道数据寄存器(12位二进制值)中,等待获取

2.实操

单片机有一个电位器,通过旋转电位器,使得PA5的输出电压发送变化,我们将其输出到串口上去

对应的PA5刚好是ADC1的通道5

c 复制代码
  char message[50]="";
  float v=0.0;
  int value=0;
  /* USER CODE END 2 */

  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while (1)
  { 
	  HAL_ADC_Start(&hadc1);//开启ADC1
	  HAL_ADC_PollForConversion(&hadc1,HAL_MAX_DELAY);//等待转换
	  value=HAL_ADC_GetValue(&hadc1);//从寄存器中获取电压值
	  v=(value/4095.0)*3.3;//转模拟信号
	  sprintf(message,"v:%.2f,value:%d",v,value);
	  HAL_UART_Transmit(&huart2,message,strlen(message),HAL_MAX_DELAY);
	  HAL_Delay(500);
   /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */
  }

但是发现一个问题,就是电位器扭到头

最大为3.25v,达不到3.3v,这是因为手册里面说

c 复制代码
  char message[50]="";
  float v=0.0;
  int value=0;
  HAL_ADCEx_Calibration_Start(&hadc1);//校准
  /* USER CODE END 2 */

  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while (1)
  {
	  HAL_ADC_Start(&hadc1);//开启ADC1
	  HAL_ADC_PollForConversion(&hadc1,HAL_MAX_DELAY);//等待转换
	  value=HAL_ADC_GetValue(&hadc1);//从寄存器中获取电压值
	  v=(value/4095.0)*3.3;//转模拟信号
	  sprintf(message,"v:%.2f,value:%d",v,value);
	  HAL_UART_Transmit(&huart2,(uint8_t*)message,strlen(message),HAL_MAX_DELAY);
	  HAL_Delay(500);




    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */
  }

其实还有一种是循环转换,只需要开启一次ADC即可

c 复制代码
  char message[50]="";
  float v=0.0;
  int value=0;
  HAL_ADCEx_Calibration_Start(&hadc1);//校准
  HAL_ADC_Start(&hadc1);//开启ADC1
  HAL_ADC_PollForConversion(&hadc1,HAL_MAX_DELAY);//等待转换
  /* USER CODE END 2 */

  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while (1)
  {
	
	  value=HAL_ADC_GetValue(&hadc1);//从寄存器中获取电压值
	  v=(value/4095.0)*3.3;//转模拟信号
	  sprintf(message,"v:%.2f,value:%d",v,value);
	  HAL_UART_Transmit(&huart2,(uint8_t*)message,strlen(message),HAL_MAX_DELAY);
	  HAL_Delay(500);




    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */
  }

3.ADC多路采集

在前面说过,如果要获取对应通道的电压值,就需要将这个通道注册进规则组中,然后数据准备就绪, HAL_ADC_PollForConversion函数不断的检测ADC状态寄存器中转换结束标志位(EOC)是否为1,如果是,转换完成,将转换的值放入规则通道数据寄存器中,然后调用HAL_ADC_GetValue读取规则通道数据寄存器,然后对应的ADC状态寄存器中的转换结束标志位置0.

在多采集中需要将多个通道打开实现转换,会按照规则组注册顺序,依次将对应通道的电压值放入规则通道数据寄存器,然后写到内存中。

我们可以使用DMA来对规则通道数据寄存器的值搬运到内存,当搬运完成,就会触发DMA完成中断,进行处理转换后的值

本次实现热敏电阻,电位器,单片机内部温度,内部参考电压的ADC转换,并通过串口将其发送


PA4,PA5 ADC在ADC1的通道4,通道5上,以及内部温度通道,内部参考电压通道

注意将四个通道的多久采集值调到四个通道转换最大值,确保数据都已经被转换

c 复制代码
uint16_t data[4];
char message[50]="";
void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef* hadc)
{

  if(hadc==&hadc1)
  {
	  sprintf(message,"%d %d %d %d",data[0],data[1],data[2],data[3]);
      HAL_UART_Transmit(&huart2, (uint8_t*)message,sizeof(message),HAL_MAX_DELAY);

  }

}
int main(void)
{

  HAL_Init();



 

  HAL_ADCEx_Calibration_Start(&hadc1);//校准

  while (1)
  {

	  HAL_ADC_Start_DMA(&hadc1,(uint32_t*)data,4);//开启ADC,DMA完成搬运调用DMA完成中断中断,在中断中发送转换值到串口
	  HAL_Delay(500);
    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */
  }
  /* USER CODE END 3 */
}

依次是电位器,热敏电阻,内部温度,内部参考电压未转化为模拟电压

除此之外我们可以使用循环转换,和DMA的循环搬运就可以实现开启一次ADC就可以一直将转换后的四个值发送到串口,因为会一直转换,不用知道什么时候转换完成,我们将发送到串口放到while中

c 复制代码
uint16_t data[4];
char message[50]="";

int main(void)
{

  HAL_Init();



 

  HAL_ADCEx_Calibration_Start(&hadc1);//校准
 HAL_ADC_Start_DMA(&hadc1,(uint32_t*)data,4);//开启ADC,DMA完成搬运调用DMA完成中断中断,在中断中发送转换值到串口
  while (1)
  {

	  
	  sprintf(message,"%d %d %d %d",data[0],data[1],data[2],data[3]);
      HAL_UART_Transmit(&huart2, (uint8_t*)message,sizeof(message),HAL_MAX_DELAY);
	  HAL_Delay(500);
    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */
  }
  /* USER CODE END 3 */
}
相关推荐
得单片机的运19 小时前
STM32的以太网的搭建
stm32·单片机·嵌入式硬件·物联网·以太网·iot·w5500
酷飞飞19 小时前
RTC和看门狗基于GD32F407VE的天空星的配置
stm32·单片机·嵌入式硬件·mcu
qq_4017004120 小时前
STM32的HardFault错误处理技巧
stm32
WD1372980155721 小时前
WD5030A,24V降5V,15A 大电流,应用于手机、平板、笔记本充电器
stm32·单片机·嵌入式硬件·智能手机·汽车·电脑·51单片机
日更嵌入式的打工仔21 小时前
GPIO 中断通用配置指南
stm32·单片机·嵌入式硬件
平凡灵感码头21 小时前
基于 STM32 的智能门锁系统,系统界面设计
stm32·单片机·嵌入式硬件
Truffle7电子1 天前
STM32理论 —— 存储、中断
stm32·嵌入式硬件·嵌入式·存储·中断
报错小能手1 天前
linux学习笔记(32)网络编程——UDP
单片机·嵌入式硬件
XiangrongZ1 天前
江协科技STM32课程笔记(四)—定时器TIM(输入捕获)
笔记·科技·stm32
xyx-3v1 天前
SPI四种工作模式
stm32·单片机·嵌入式硬件·学习