单片机-STM32部分:11、ADC

发布于:2025-05-11 ⋅ 阅读:(23) ⋅ 点赞:(0)

飞书文档https://x509p6c8to.feishu.cn/wiki/OclUwlkifiRKR2k6iLbczn5tn8g

STM32的ADC是一种逐次逼近型模拟数字转换器。

是用于将模拟形式的连续信号转换为数字形式的离散信号的一类设备。

逐次逼近型ADC的原理图下:

STM32f103系列有3ADC,精度为12位,每个ADC最多有16个外部通道。

其中ADC1有两个内部通道,温度传感器和通道ADC1_IN16相连接,内部参照电压VREFINT和ADC1_IN17相连接。

其中ADC1和ADC2都有16个外部通道,ADC3一般有8个外部通道,各通道的A/D转换可以单次、连续、扫描或间断执行,ADC转换的结果可以左对齐或右对齐储存在16位数据寄存器中。ADC的输入时钟不得超过14MHz,其时钟频率由PCLK2分频产生。

通道

ADC1

ADC2

ADC3

通道0

IN0

PA0

PA0

PA0

通道1

IN1

PA1

PA1

PA1

通道2

IN2

PA2

PA2

PA2

通道3

IN3

PA3

PA3

PA3

通道4

IN4

PA4

PA4

通道5

IN5

PA5

PA5

通道6

IN6

PA6

PA6

通道7

IN7

PA7

PA7

通道8

IN8

PB0

PB0

通道9

IN9

PB1

PB1

通道10

IN10

PC0

PC0

PC0

通道11

IN11

PC1

PC1

PC1

通道12

IN12

PC2

PC2

PC2

通道13

IN13

PC3

PC3

PC3

通道14

IN14

PC4

PC4

通道15

IN15

PC5

PC5

内部通道16

IN16

连接内部温度传感器

内部通道17

IN17

连接内部VRF

ADC框图说明

电压输入范围

ADC所能测量的电压范围就是VREF- ≤ VIN ≤ VREF+,原理图中把 VSSA 和 VREF-接地,把 VREF+和 VDDA 接 3V3,得到ADC 的输入电压范围为: 0~3.3V。

ADC输入通道

ADCx_INT0-ADCx_INT15 对应三个ADC的16个外部通道,此外,还有两个内部通道:温度检测或者内部电压检测,选择对应通道之后,便会选择对应GPIO引脚。

规则通道,注入通道

我们看到,在选择了ADC的相关通道引脚之后,在模拟至数字转换器中有两个通道,规则通道注入通道,
规则通道至多16个,注入通道至多4个,一般使用规则通道。

规则通道就是普通的通道,注入通道就是“插队”通道,是优先级高的通道,如果规则通道遇到注入通道插队,就必须先执行完注入通道的数据转换,再进行规则通道的数据转换,这点类似中断优先级的概念。

假如你在家里的院子内放了5个温度探头,室内放了3个温度探头;你需要时刻监视室外温度即可,但偶尔你想看看室内的温度;因此你可以使用规则通道组循环扫描室外的5个探头并显示AD转换结果,当你想看室内温度时,通过一个按钮启动注入转换组(3个室内探头)并暂时显示室内温度,当你放开这个按钮后,系统又会回到规则通道组继续检测室外温度。

ADC时钟

ADC预分频器的ADCCLK是ADC模块的时钟来源。通常,由时钟控制器提供的ADCCLK时钟和PCLK2(APB2时钟)同步。RCC控制器为ADC时钟提供一个专用的可编程预分频器,分频因子由RCC_CFGR的ADCPRE[1:0]配置,可配置2/4/6/8分频,但是为了保证ADC转换结果的准确性,ADC的输入时钟不得超过14MHz

外部触发转换

支持多种外部事件触发,比如定时器捕捉、EXTI线,同时ADC3的触发源与ADC1/2的触发源有所不同,上图已经给出。

DMA

同时ADC还支持DMA触发,规则和注入通道转换结束后会产生DMA请求,用于将转换好的数据传输到内存。

注意,只有ADC1ADC3可以产生DMA请求

中断

可以配置产生中断,在ADC转换结束时,读取ADC数据

开发板中的ADC电路说明

开发板使用PA0做ADC采集实验,PA0外部接了一个滑动电阻器,R92为保护电阻,当我们滑动电阻器时,PA0上的电压会在0-3.3V变化,而PA0是绑定到ADC的通道0上的,所以我们可以采集ADC1通道0的值,来计算出PA0的电压。

STM32CUBEMX ADC参数说明

下面,我们使用STM32CubeMX配置ADC1的0通道,采集数据。

1、ADCs_Common_Settings  ADC通用设置

模式设置

Mode  ADC_Mode_Independent 这里设置为独立模式

如果不需要ADC同步或者只是用了一个ADC的时候,应该设成独立模式。

如果需要多个ADC同时使用时,根据采样的需求,可设置双重ADC同步模式,两个ADC采集一个或多个通道,可以提高采样率。

独立模式,常用
<1> Independent mode                      ADC1 和ADC2 工作在独立模式
双重模式,少用,多ADC时使用
<2> Dual regular simultaneous + injected simultaneous mode ADC1和ADC2工作在同步规则模式和同步注入模式
<3> regular regular simultaneous + alternate trigger mode  ADC1和ADC2工作在同步规则模式和交替触发模式
<4> Dual injected simultaneous mode only           ADC1和ADC2工作在同步注入模式
<5> Dual regular simultaneous mode only            ADC1和ADC2工作在同步规则模式
<6> Dual interleaved mode only                 ADC1和ADC2工作在交叉模式
<7> Dual alternate trigger mode only             ADC1和ADC2工作在交替触发模式

Data Alignment (数据对齐方式): 右对齐/左对齐

因为ADC得到的数据是12位精度的,但是数据存储在 16 位数据寄存器中,所以ADC的存储结果可以分为左对齐或右对齐方式(12位)

Scan Conversion Mode( 扫描模式 ) DISABLE

是否开启扫描模式(针对多通道的设置),如果只是用了一个通道的话,DISABLE就可以了(也只能DISABLE),如果使用了多个通道的话,会自动设置为ENABLE。

注意,这里设置多通道后,需要修改Number of Conversion为对应通道数量才生效,否则不能设置为EBABLE

Continuous Conversion Mode(连续转换模式) ENABLE

是否开启连续转换模式,设置为ENABLE,即连续转换。如果设置为DISABLE,则是单次转换。

ADC单通道转换:
“扫描模式关闭,单次转换模式”:只进行一次转换,不过可以持续使能ADC达到不断采集的的。
“扫描模式关闭,连续转换模式”:使能一次ADC后,能够连续转换。

ADC多通道转换:
“扫描模式开启,单次转换模式”:每个通道转换完一次后结束转换,需要通过持续使能ADC不断进行多通道连续采集。
“扫描模式开启,连续转换模式”:每个通道转换完之后还能继续循环转换,不需要反复使能ADC。

Discontinuous Conversion Mode(间断模式) :DISABLE

因为我们只用到了1个ADC,所以这个直接不使能即可,只能在扫描模式为EBABLE时才能设置。

间断模式可以把多个通道的ADC转换分组,间断采集。

比方,我们用到某ADC模块的CH1/CH2/CH3/CH4/CH5五个通道,将它们分成3组,使用定时器触发ADC。第一次触发时,进行CH1/CH2两个通道的AD转换,第二次触发时进行CH3/CH4两个通道的AD转换,第三次触发时,完成CH5通道的AD转换。 第四次触发时进行跟第一次触发一样的转换,这样循环下去。

2ADC Regular Conversion Mode ADC常规转换模式

Enable Regular Conversions (启用常规转换模式)  ENABLE

需要开启常规转换模式后,才可以进行下方配置

Number OF Conversion(转换通道数)    1
用到几个通道就设置为几,如果设置多个通道会自动使能扫描模式

Extenal Trigger Conversion Source (外部触发转换源)

设定ADC的触发方式
Regular Conversion launched by software 规则的软件触发 调用函数触发即可
Timer X Capture Compare X event 外部引脚触发,
Timer X Trigger Out event 定时器通道输出触发 需要设置相应的定时器设置

Rank转换顺序

多个通道时会有多个Rank,可以设定每个通道的转换顺序,采样时间

补充:采样时间越长,越准确,这里存在一个竞争冒险的关系。

ADC总转换时间如下计算:
Tconv = 采样时间+12.5个周期
例如: 当ADCCLK=14MHz(最大),采样时间为1.5周期(最快)
TCONV = 1.5 + 12.5 = 14周期 = 1μs
因此,ADC的最小采样时间1us

注入通道设置

也就是注入通道的设置,和转换通道没啥太大区别。注入通道一般使用外部中断、定时器中断作为触发源。

Injected Conversion Mode
Injected Discontinuous Mode 注入通道连续转换
Auto Injected Mode 常规通道转换1次后注入自动转换

WahchDog

Enable Analog WatchDog Mode(使能模拟看门狗中断)

例如,我们想在芯片突然掉电时,保存一些必要数据。这时候我们就可以设置模拟看门狗中断,例如检测通道0的边界电压低于1500或者3500时,触发中断,这时候我们可以在ADC中断中做数据保存操作,存储必要数据。


 

ADC轮询采样

设置完成后,如果提示时钟设置错误,需要手动修改降低频率

同时,为了打印ADC值,我们可以添加串口打印函数


  main.c
 
  /* USER CODE BEGIN Includes */
  #include <stdio.h>
  /* USER CODE END Includes */

   /* USER CODE BEGIN 2 */
   HAL_ADCEx_Calibration_Start(&hadc1); //ADC校准
   HAL_ADC_Start(&hadc1);     //启动ADC转换,设置为连续采样后,只需开启一次
  /* USER CODE END 2 */

  while (1)
  {
    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */
    uint16_t ADC_Value;
    HAL_ADC_PollForConversion(&hadc1,50);//查询函数,查询转换完成标志位。每次采样,CUP在这里都要
                                       //等待采样完成才能进行下一步,超时时间可设置大于采样时间

    ADC_Value = HAL_ADC_GetValue(&hadc1);   //获取AD值
    printf("ADC1 Reading : %d \r\n",ADC_Value);
    //12位ADC,2的12次方,是4096,分辨率为3.3/4096
    printf("PA1 True Voltage value : %.4f \r\n",ADC_Value*3.3f/4096);
    
     HAL_Delay(1000);
     //HAL_ADC_Stop(&hadc1);
  }
  /* USER CODE END 3 */
 
 
/* USER CODE BEGIN 4 */
int fputc(int ch, FILE *f)
{
    HAL_UART_Transmit(&huart1 , (uint8_t *)&ch, 1, 0xFFFF);
    return ch;
}
/* USER CODE END 4 */

实验结果

参考飞书文档

关于ADC校准

HAL_ADCEx_Calibration_Start(&hadc1); //ADC校准
这个为什么要用 ADC 校准?
这个就像芯片生产过程制造中的差异化一样,ADC部分也存在一些差异化(虽然很小),其参考电压、偏置电压、增益等参数可能存在一些不确定性和漂移,这些参数的变化会导致 ADC 的测量结果产生误差。
为了提高测量精度,消除 ADC 的偏移误差和增益误差,所以在使用 ADC 采样的时候都需要进行 ADC 校准。

ADC转换结束中断

因为使用上方的轮询方式效率太低太慢了,这时候我们可以开启ADC转换结束中断,可以提升采样效率。


void ADC1_2_IRQHandler(void)
{
  /* USER CODE BEGIN ADC1_2_IRQn 0 */

  /* USER CODE END ADC1_2_IRQn 0 */
  HAL_ADC_IRQHandler(&hadc1);
  /* USER CODE BEGIN ADC1_2_IRQn 1 */

  /* USER CODE END ADC1_2_IRQn 1 */
}

void HAL_ADC_IRQHandler(ADC_HandleTypeDef* hadc){
    xxx
    HAL_ADC_ConvCpltCallback(hadc);
    xxx
}

/**
  * @brief  Conversion complete callback in non blocking mode
  * @param  hadc: ADC handle
  * @retval None
  */
__weak void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef* hadc)
{
  /* Prevent unused argument(s) compilation warning */
  UNUSED(hadc);
  /* NOTE : This function should not be modified. When the callback is needed,
            function HAL_ADC_ConvCpltCallback must be implemented in the user file.
   */
}

如果使能了ADC转换结束中断,可以这样写:

main.c

uint16_t ADC_Value;

/* USER CODE BEGIN 2 */
HAL_ADCEx_Calibration_Start(&hadc1);

while (1)
{
  /* USER CODE BEGIN 3 */
  HAL_ADC_Start_IT(&hadc1); //每秒转换一次,是否可以反正while外,只初始化一次
  HAL_Delay(1000);
}
/* USER CODE END 3 */

/* USER CODE BEGIN 4 */
void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef* hadc)    //ADC转换完成回调
{
    HAL_ADC_Stop_IT(&hadc1);        //关闭ADC
    ADC_Value=HAL_ADC_GetValue(&hadc1);  //获取ADC转换的值
    printf("ADC1 Reading : %d \r\n",ADC_Value);
    printf("%.4f V\r\n",(ADC_Value*3.3/4096));     //串口打印电压信息
}


ADC DMA采样

如果采样频率太高,而且多通道同时采样,为了减少对CPU的占用,我们可以使用DMA采样的方式,这样可以大大提高采样效率。

注意DMA采样一般只用在多通道采样的场景,单通道时,直接用ADC中断采样即可。

  • Mode 模式:独立采集
  • Scan Conversion Mode 扫描模式 :ENABLE
  • Continuous Conversion Mode 连续转换模式:ENABLE
  • 两路ADC,分别是通道0和通道1

采样周期选择:采样周期越短,ADC 转换数据输出周期就越短但数据精度也越低,采样周期越长,ADC 转换数据输出周期就越长同时数据精度越高。
要注意:转换周期太小的时候,会导致DMA触发的中断干扰CPU导致无法正常工作。

  • 配置下DMA模式为Circular,既循环更新数据,否则默认的Normal模式触发后只执行 一次,
  • 配置自增地址为Memory方式,并选择word或half word,这里选择word,是因为我程序里定义uint32_t 的数组来存储多路ADC数据的,占用四个字节既选择word

 main.c
  
   /* USER CODE BEGIN 0 */
  uint32_t ADC1_Value_DMA[2];
  
   /* USER CODE BEGIN 2 */
   HAL_ADCEx_Calibration_Start(&hadc1);
   HAL_ADC_Start_DMA(&hadc1, ADC1_Value_DMA, 2);
 
 
  while (1)
  {
    /* USER CODE BEGIN 3 */
    float Channel_0 = (float)(ADC1_Value_DMA[0]&0xFFF)*3.3/4096;
    float Channel_1 = (float)(ADC1_Value_DMA[1]&0xFFF)*3.3/4096;
    printf("ADC1 Channel_6 Count: %d,   voltage: %f \r\n",ADC1_Value_DMA[0],Channel_0);
    printf("ADC1 Channel_7 Count: %d,   voltage:  %f \r\n",ADC1_Value_DMA[1], Channel_1);
  }
  /* USER CODE END 3 */

记得把DMA放到ADC前面初始化哦


网站公告

今日签到

点亮在社区的每一天
去签到