打印
[其他ST产品]

stm32之ADC应用实例(单通道、多通道、基于DMA)

[复制链接]
547|29
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
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(下图是芯片固件库的截图)

使用特权

评论回复
板凳
逢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 | 只看该作者
那么就有:

使用特权

评论回复
5
逢dududu必shu|  楼主 | 2022-1-27 23:00 | 只看该作者
16个外部通道:简单的说就是芯片上有16个引脚是可以接到模拟电压上进行电压值检测的。16个通道不是独立的分配给3个转换器(ADC1、ADC2、ADC3)使用,有些通道是被多个转换器共用的。首先看看16个通道在固件库的宏定义(写代码要看的):

使用特权

评论回复
6
逢dududu必shu|  楼主 | 2022-1-27 23:03 | 只看该作者

使用特权

评论回复
7
逢dududu必shu|  楼主 | 2022-1-27 23:13 | 只看该作者
到这里大家可能会有疑问,每个通道到底对应哪个引脚呢?下面先给出部分引脚图:

使用特权

评论回复
8
逢dududu必shu|  楼主 | 2022-1-27 23:20 | 只看该作者

使用特权

评论回复
9
逢dududu必shu|  楼主 | 2022-1-27 23:20 | 只看该作者
16个通道的引脚都在上面的图中,拿其中的一个进行说明:

使用特权

评论回复
10
逢dududu必shu|  楼主 | 2022-1-27 23:22 | 只看该作者
ADC123_IN10:字母“ADC”不用多说,“123”代表它被3个(ADC1、ADC2、ADC3)转换器共用的引脚,“10”对应刚才那张宏定义图里面的ADC_Channel_10,这样就能找到每个通道对应的引脚了。

使用特权

评论回复
11
逢dududu必shu|  楼主 | 2022-1-27 23:27 | 只看该作者
2个内部通道:一个是内部温度传感器,一个是内部参考电压。

使用特权

评论回复
12
逢dududu必shu|  楼主 | 2022-1-27 23:28 | 只看该作者
在某个项目中要用到芯片里面的AD转换器,那么要怎么写应用代码?(以下是代码讲解)

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

初始化(设置用的哪个引脚、单通道、还是多通道同时转换、是否使用DMA等配置)?
怎么让转换器进行一次数据获取?

使用特权

评论回复
13
逢dududu必shu|  楼主 | 2022-1-27 23:29 | 只看该作者
以下分别讲述三种不同方式(单通道、多通道、基于DMA的多通道采集)的ADC应用实例:
/*单通道的ADC采集*/
void  Adc_Config(void)
{        
    /*定义两个初始化要用的结构体,下面给每个结构体成员赋值*/
        ADC_InitTypeDef ADC_InitStructure;
        GPIO_InitTypeDef GPIO_InitStructure;
       
        /*
          使能GPIOA和ADC1通道时钟
          注意:除了RCC_APB2PeriphClockCmd还有RCC_APB1PeriphClockCmd,那么该如何选择?
      APB2:高速时钟,最高72MHz,主要负责AD输入,I/O,串口1,高级定时器TIM
      APB1:低速时钟,最高36MHz,主要负责DA输出,串口2、3、4、5,普通定时器TIM,USB,IIC,CAN,SPI
          */
        RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA |RCC_APB2Periph_ADC1, ENABLE );          
    RCC_ADCCLKConfig(RCC_PCLK2_Div6);  //72M/6=12, ADC的采样时钟最快14MHz  
      
    /*配置输入电压所用的PA0引脚*/         
        GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;
        GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN; //GPIO_Mode_AIN:模拟输入(还有其他什么模式?请看下面的附录图1)
        GPIO_Init(GPIOA, &GPIO_InitStructure);       

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

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

    /*下面4步按流程走,走完就行*/
        ADC_ResetCalibration(ADC1);        //重置指定的ADC的校准寄存器
        while(ADC_GetResetCalibrationStatus(ADC1)); //等待上一步操作完成
        ADC_StartCalibration(ADC1);        //开始指定ADC的校准状态       
        while(ADC_GetCalibrationStatus(ADC1));//等待上一步操作按成               
}       

使用特权

评论回复
14
逢dududu必shu|  楼主 | 2022-1-27 23:31 | 只看该作者
初始化完成之后,在主函数中:

void main(void)
{       
  float ADC_ConvertedValue;
  float ADC_ConvertedValueLocal;

        Adc_Config();
        while(1)
        {
          ADC_SoftwareStartConvCmd(ADC1, ENABLE);                //启动转换       
          while(!ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC ));  //等待转换完成
          
          ADC_ConvertedValue=ADC_GetConversionValue(ADC1); //获取转换结果*ADC_ConvertedValue*       
          ADC_ConvertedValueLocal=(float)ADC_ConvertedValue*(3.3/4096);   //计算出实际电压值*ADC_ConvertedValueLocal*
          
          //这里适当加上一些延迟
          //最好连续转换几次 取平均值  这里就省略写了 点到为止
        }
}

使用特权

评论回复
15
逢dududu必shu|  楼主 | 2022-1-27 23:32 | 只看该作者
附录图1-GPIO_Mode值:

使用特权

评论回复
16
逢dududu必shu|  楼主 | 2022-1-27 23:33 | 只看该作者
附录图2-ADC_Mode值:

使用特权

评论回复
17
逢dududu必shu|  楼主 | 2022-1-27 23:37 | 只看该作者
附录图3-ADC_ExternalTrigConv值:

使用特权

评论回复
18
逢dududu必shu|  楼主 | 2022-1-27 23:47 | 只看该作者
附录图4-ADC_SampleTime值:

使用特权

评论回复
19
逢dududu必shu|  楼主 | 2022-1-27 23:56 | 只看该作者
对于一些刚接触stm32的人来说,看了上面的代码可能还会有很多疑问。

使用特权

评论回复
20
逢dududu必shu|  楼主 | 2022-1-27 23:57 | 只看该作者
为什么要使能时钟?时钟到底设置多少才合适?
对于ADC_GetConversionValue(ADC1)这个函数参数并没有指定那个通道,如果多个通道同时使用CAN1转换器转换时怎么获取每个通道的值?

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

60

主题

442

帖子

0

粉丝