[其他ST产品] stm32之ADC应用实例(单通道、多通道、基于DMA)

[复制链接]
 楼主| 逢dududu必shu 发表于 2022-1-27 22:49 | 显示全部楼层 |阅读模式
AD, DM, ST, dc
  • 硬件:STM32F103VCT6
  • 开发工具:Keil uVision4
  • 下载调试工具:ARM仿真器

网上资料很多,这里做一个详细的整合。(也不是很详细,但很通俗)。
所用的芯片内嵌3个12位的模拟/数字转换器(ADC),每个ADC共用多达16个外部通道,2个内部通道。


 楼主| 逢dududu必shu 发表于 2022-1-27 22:53 | 显示全部楼层
3个:代表ADC1、ADC2、ADC3(下图是芯片固件库的截图)
1786761f2b1e8315ad.png
 楼主| 逢dududu必shu 发表于 2022-1-27 22:56 | 显示全部楼层
12位:也叫ADC分辨率、采样精度。先来看看二进制的12位可表示0-4095个数,也就是说转换器通过采集转换所得到的最大值是4095,如:“111111111111”=4095,那么我们怎么通过转换器转换出来的值得到实际的电压值呢?如果我们要转换的电压范围是0v-3.3v的话,转换器就会把0v-3.3v平均分成4096份。设转换器所得到的值为x,所求电压值为y。
 楼主| 逢dududu必shu 发表于 2022-1-27 22:59 | 显示全部楼层
那么就有: 2481561f2b34ba2b39.png
 楼主| 逢dududu必shu 发表于 2022-1-27 23:00 | 显示全部楼层
16个外部通道:简单的说就是芯片上有16个引脚是可以接到模拟电压上进行电压值检测的。16个通道不是独立的分配给3个转换器(ADC1、ADC2、ADC3)使用,有些通道是被多个转换器共用的。首先看看16个通道在固件库的宏定义(写代码要看的):
 楼主| 逢dududu必shu 发表于 2022-1-27 23:03 | 显示全部楼层
 楼主| 逢dududu必shu 发表于 2022-1-27 23:13 | 显示全部楼层
到这里大家可能会有疑问,每个通道到底对应哪个引脚呢?下面先给出部分引脚图:
 楼主| 逢dududu必shu 发表于 2022-1-27 23:20 | 显示全部楼层
 楼主| 逢dududu必shu 发表于 2022-1-27 23:20 | 显示全部楼层
16个通道的引脚都在上面的图中,拿其中的一个进行说明:
 楼主| 逢dududu必shu 发表于 2022-1-27 23:22 | 显示全部楼层
ADC123_IN10:字母“ADC”不用多说,“123”代表它被3个(ADC1、ADC2、ADC3)转换器共用的引脚,“10”对应刚才那张宏定义图里面的ADC_Channel_10,这样就能找到每个通道对应的引脚了。
 楼主| 逢dududu必shu 发表于 2022-1-27 23:27 | 显示全部楼层
2个内部通道:一个是内部温度传感器,一个是内部参考电压。
 楼主| 逢dududu必shu 发表于 2022-1-27 23:28 | 显示全部楼层
在某个项目中要用到芯片里面的AD转换器,那么要怎么写应用代码?(以下是代码讲解)

芯片固件的库函数为我们提供了很多封装好的函数,只要运用它提供的函数接口就可以了,宏观上来讲就搞懂两个事情就行了:

初始化(设置用的哪个引脚、单通道、还是多通道同时转换、是否使用DMA等配置)?
怎么让转换器进行一次数据获取?
 楼主| 逢dududu必shu 发表于 2022-1-27 23:29 | 显示全部楼层
以下分别讲述三种不同方式(单通道、多通道、基于DMA的多通道采集)的ADC应用实例:
  1. /*单通道的ADC采集*/
  2. void  Adc_Config(void)
  3. {        
  4.     /*定义两个初始化要用的结构体,下面给每个结构体成员赋值*/
  5.         ADC_InitTypeDef ADC_InitStructure;
  6.         GPIO_InitTypeDef GPIO_InitStructure;
  7.        
  8.         /*
  9.           使能GPIOA和ADC1通道时钟
  10.           注意:除了RCC_APB2PeriphClockCmd还有RCC_APB1PeriphClockCmd,那么该如何选择?
  11.       APB2:高速时钟,最高72MHz,主要负责AD输入,I/O,串口1,高级定时器TIM
  12.       APB1:低速时钟,最高36MHz,主要负责DA输出,串口2、3、4、5,普通定时器TIM,USB,IIC,CAN,SPI
  13.           */
  14.         RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA |RCC_APB2Periph_ADC1, ENABLE );          
  15.     RCC_ADCCLKConfig(RCC_PCLK2_Div6);  //72M/6=12, ADC的采样时钟最快14MHz  
  16.       
  17.     /*配置输入电压所用的PA0引脚*/         
  18.         GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;
  19.         GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN; //GPIO_Mode_AIN:模拟输入(还有其他什么模式?请看下面的附录图1)
  20.         GPIO_Init(GPIOA, &GPIO_InitStructure);       

  21.        
  22.         ADC_DeInit(ADC1); //复位,将ADC1相关的寄存器设为默认值
  23.         ADC_InitStructure.ADC_Mode = ADC_Mode_Independent;        //工作模式:ADC1和ADC2独立工作模式  (还有其他什么模式?请看下面的附录图2)
  24.         ADC_InitStructure.ADC_ScanConvMode = DISABLE;        //数模转换工作:扫描(多通道)模式=ENABLE、单次(单通道)模式=DISABLE
  25.         ADC_InitStructure.ADC_ContinuousConvMode = DISABLE;//数模转换工作:连续=ENABLE、单次=DISABLE
  26.         ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;        //ADC转换由软件触发启动 (还有其他什么模式?请看下面的附录图3)
  27.         ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;        //ADC数据右对齐   除了右就是左:ADC_DataAlign_Left
  28.         ADC_InitStructure.ADC_NbrOfChannel = 1;        //顺序进行规则转换的ADC通道的数目   范围是1-16
  29.         ADC_Init(ADC1, &ADC_InitStructure);        //根据ADC_InitStruct中指定的参数初始化外设ADC1的寄存器

  30.     /*为啥要设置下面这一步?
  31.      细心的你可以发现上面初始化了一个引脚通道,初始化了一个ADC转换器,但ADC转换器并不知道你用的是哪个引脚吧?
  32.      这一步目的是:设置指定ADC的规则组通道(引脚),设置它们的转化顺序和采样时间
  33.      函数原型:void ADC_RegularChannelConfig(ADC_TypeDef* ADCx, u8 ADC_Channel, u8 Rank, u8 ADC_SampleTime)
  34.      参数1 ADCx:x可以是1或者2来选择ADC外设ADC1或ADC2
  35.      参数2 ADC_Channel:被设置的ADC通道  范围ADC_Channel_0~ADC_Channel_17
  36.      参数3 Rank:规则组采样顺序。取值范围1到16。
  37.      ADC_SampleTime:指定ADC通道的采样时间值  (取值范围?请看下面的附录图4)
  38.     */       
  39.          ADC_RegularChannelConfig(ADC1, ADC_Channel_0, 1, ADC_SampleTime_239Cycles5 );                 
  40.                               
  41.         ADC_Cmd(ADC1, ENABLE);        //使能指定的ADC  注意:函数ADC_Cmd只能在其他ADC设置函数之后被调用

  42.     /*下面4步按流程走,走完就行*/
  43.         ADC_ResetCalibration(ADC1);        //重置指定的ADC的校准寄存器
  44.         while(ADC_GetResetCalibrationStatus(ADC1)); //等待上一步操作完成
  45.         ADC_StartCalibration(ADC1);        //开始指定ADC的校准状态       
  46.         while(ADC_GetCalibrationStatus(ADC1));//等待上一步操作按成               
  47. }       
复制代码
 楼主| 逢dududu必shu 发表于 2022-1-27 23:31 | 显示全部楼层
初始化完成之后,在主函数中:

  1. void main(void)
  2. {       
  3.   float ADC_ConvertedValue;
  4.   float ADC_ConvertedValueLocal;

  5.         Adc_Config();
  6.         while(1)
  7.         {
  8.           ADC_SoftwareStartConvCmd(ADC1, ENABLE);                //启动转换       
  9.           while(!ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC ));  //等待转换完成
  10.           
  11.           ADC_ConvertedValue=ADC_GetConversionValue(ADC1); //获取转换结果*ADC_ConvertedValue*       
  12.           ADC_ConvertedValueLocal=(float)ADC_ConvertedValue*(3.3/4096);   //计算出实际电压值*ADC_ConvertedValueLocal*
  13.           
  14.           //这里适当加上一些延迟
  15.           //最好连续转换几次 取平均值  这里就省略写了 点到为止
  16.         }
  17. }
复制代码
 楼主| 逢dududu必shu 发表于 2022-1-27 23:32 | 显示全部楼层
附录图1-GPIO_Mode值:

7963961f2bb182ce55.png
 楼主| 逢dududu必shu 发表于 2022-1-27 23:33 | 显示全部楼层
附录图2-ADC_Mode值:
8606461f2bb4fbc671.png
 楼主| 逢dududu必shu 发表于 2022-1-27 23:37 | 显示全部楼层
附录图3-ADC_ExternalTrigConv值:

739561f2bc3f7d2f0.png
 楼主| 逢dududu必shu 发表于 2022-1-27 23:47 | 显示全部楼层
附录图4-ADC_SampleTime值:
104661f2bea477937.png
 楼主| 逢dududu必shu 发表于 2022-1-27 23:56 | 显示全部楼层
对于一些刚接触stm32的人来说,看了上面的代码可能还会有很多疑问。
 楼主| 逢dududu必shu 发表于 2022-1-27 23:57 | 显示全部楼层
为什么要使能时钟?时钟到底设置多少才合适?
对于ADC_GetConversionValue(ADC1)这个函数参数并没有指定那个通道,如果多个通道同时使用CAN1转换器转换时怎么获取每个通道的值?
您需要登录后才可以回帖 登录 | 注册

本版积分规则

69

主题

493

帖子

2

粉丝
快速回复 在线客服 返回列表 返回顶部