STM32微控制器的模数转换功能ADC
ADC简介
ADC(Analog-Digital Converter)模拟-数字转换器,ADC可以将引脚上连续变化的模拟电压转换为内存中存储的数字变量,建立模拟电路到数字电路的桥梁。
STM32—ADC
简介
整体结构图:
GPIO端口选择:
通过选取外部GPIO口,作为模拟信号量的输入,根据STM32不同型号引脚来选取ADC模拟输入引脚。(PS:以STM32F103R8T6为例,有ADC1,ADC2,并且有0~9十个ADC输入引脚复用在下图对应GPIO引脚,不同型号会有所不同,具体可以查看具体型号的引脚定义表)
通道选择:
STM32有16个多路通道。可以把转换组织成两组:规则组(最多16)和注入组(最多4)。在任意多个通道上以任意顺序进 行的一系列转换构成成组转换。(PS:可以如下顺序完成转换:通道3、通道8、通道2、通道 2、通道0、通道2、通道2、通道15。)
● 规则组由多达16个转换组成。规则通道和它们的转换顺序在ADC_SQRx寄存器中选择。规 则组中转换的总数应写入ADC_SQR1寄存器的L[3:0]位中。
● 注入组由多达4个转换组成。注入通道和它们的转换顺序在ADC_JSQR寄存器中选择。注入 组里的转换总数目应写入ADC_JSQR寄存器的L[1:0]位中。
如果ADC_SQRx或ADC_JSQR寄存器在转换期间被更改,当前的转换被清除,一个新的启动脉冲将发送到ADC以转换新选择的组
通道寄存器:
注入通道数据寄存器一共四个(4*16),对应注入通道每一个注入通道对应一个数据寄存器
规则通道数据寄存器就一个(1*16)。(这里需要注意当选择多规则通道时,需要DMA配合转运数据寄存器的值,如果转移不及时数据将会覆盖。
DMA触发转换:
根据规则组与注入组的不同对应的触发源不同(具体触发源如下图,大部分的触发源是多定时器,这些触发源有硬件支持可以不需要进入中断(需要根据触发源配置不同定时器的更新事件),也可以选择定时器到达时选择进入中断进行ADC转换),ADC经常需要每隔一段时间来触发转换,这样我们可以根据对应通道的定时器触发源,设置定时时间来申请中断触发ADC转换。除了这些触发源我们也可以选择软件触发ADC,需要我们在程序当中调用触发转换的代码即可。
DMA完成标志位:
根据不同的通道有着不同的标志位,规则组对应的标志位是EOC,通道组对应的标注位是JEOC,当对应的标志位为1,表示转换完成。
ADC转换频率:
ADC转换频率,来自RCC,通过ADC预分频器分频最大可以达到14MHZ
模拟看门狗:
通过模拟开门狗对ADC转换结果进行监听(通过让ADC转换结果即数据寄存器的值与模拟看门狗的阈值对比),如果不在阈值内就会产生看门狗相应中断标志位触发NVIC中断可以在中断进行相应的处理。
ADC基本结构图:
ADC转换模式:
1、单次转换,非扫描模式:只有一个通道,触发一次转换一次。
2、单次转换,扫描模式:只有一个通道,触发一次连续转换(数据需要及时取出否则容易被覆盖,可以使用DMA转移数据)。
3、多次转换,非扫描模式:有多个通道,触发一次每个通道转换一次(PS:这里是多通道所以需要DMA协调转运数据,否则数据容易覆盖)。
4、多次转换,扫描模式:有多个通道,触发一次每个通道转换一次,然后轮询每个通道转换一次(PS:这里是多通道所以需要DMA协调转运数据,否则数据容易覆盖)。
ADC校准:
ADC有一个内置自校准模式。校准可大幅减小因内部电容器组的变化而造成的准精度误差。在校准期间,在每个电容器上都会计算出一个误差修正码(数字值),这个码用于消除在随后的转换 中每个电容器上产生的误差。
通过设置ADC_CR2寄存器的CAL位启动校准。一旦校准结束,CAL位被硬件复位,可以开始正 常转换。
建议在上电时执行一次ADC校准。校准阶段结束后,校准码储存在ADC_DR中。
注意:
1 建议在每次上电后执行一次校准。
2 启动校准前,ADC必须处于关电状态(ADON=’0’)超过至少两个ADC时钟周期。
ADC实验
实验工具:
软件:win10、keil
硬件:STM32F103C8T6、OLED(4引脚)、电位器、杜邦线、串口烧录工具、螺丝刀。
实验现象:
通过螺丝刀调节电位器,产生不同模拟信号量通过ADC1通道0输入STM32F03C8T6,STM32通过ADC转换将模拟型号转换成数据信号通过OLED显示。
实验代码书写:
开启时钟:
这里我们需要使用ADC1,与GPIOA_Pin_0,使用我们需要开始ADC1与GPIOA的时钟。
/*开启时钟*/
RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE); //开启ADC1的时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); //开启GPIOA的时钟
配置ADC采集时钟:
配置ADC采集时钟,来自RCC系统时钟(系统时钟72MHz)通过ADC分配器分频系数可以选择RCC_PCLK2_Div2: ADC clock = PCLK2/2、RCC_PCLK2_Div4: ADC clock = PCLK2/4、RCC_PCLK2_Div6: ADC clock = PCLK2/6、 RCC_PCLK2_Div8: ADC clock = PCLK2/8。(PS:ADC采集时钟最大可以达到14MHz,所以只能选择6分频与8分频,达不到最大的采用频率)
/*设置ADC时钟*/
RCC_ADCCLKConfig(RCC_PCLK2_Div6); //选择时钟6分频,ADCCLK = 72MHz / 6 = 12MHz
GPIO初始化:
因为我们需要配置GPIO口为ADC模拟信号输入,所以我们需要将对应的端口配置为模拟输入的模式。
/*GPIO初始化*/
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure); //将PA0引脚初始化为模拟输入
通道组选择配置:
这里我们选择规则组通道,根据自己的要求可以选择ADC的端口(ADC1~ADC3)、输入频道(ADC的输入口具体看GPIO口,这里我使用的是GPIOA_Pin_0,对应ADC_Channel_0)、放入的通道序号(1~16一共16个通道)、以及优先级(安装自己的需求)。(PS:这里我们选择的是规则组通道,我们也可以选择注入组通道,使用ADC_InjectedChannelConfig(),可以根据自己的选择进行配置,这里通道只能选择1~4四个通道,其他同注入组相同)
/*规则组通道配置*/
ADC_RegularChannelConfig(ADC1, ADC_Channel_0, 1, ADC_SampleTime_55Cycles5); //规则组序列1的位置,配置为通道0
ADC初始化:
通过一个结构体来配置模式、数据对齐方式、触发方式,是否连续转换、是否扫描模式、通道个数。
/*ADC初始化*/
ADC_InitTypeDef ADC_InitStructure; //定义结构体变量
ADC_InitStructure.ADC_Mode = ADC_Mode_Independent; //模式,选择独立模式,即单独使用ADC1
ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right; //数据对齐,选择右对齐
ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None; //外部触发,使用软件触发,不需要外部触发
ADC_InitStructure.ADC_ContinuousConvMode = DISABLE; //连续转换,失能,每转换一次规则组序列后停止
ADC_InitStructure.ADC_ScanConvMode = DISABLE; //扫描模式,失能,只转换规则组的序列1这一个位置
ADC_InitStructure.ADC_NbrOfChannel = 1; //通道数,为1,仅在扫描模式下,才需要指定大于1的数,在非扫描模式下,只能是1
ADC_Init(ADC1, &ADC_InitStructure); //将结构体变量交给ADC_Init,配置ADC1
ADC使能:
使用ADC之前需要使能ADC。
/*ADC使能*/
ADC_Cmd(ADC1, ENABLE); //使能ADC1,ADC开始运行
ADC校准:
开始使用ADC转换之前需要ADC校准,防止误差,具体步骤固定。
/*ADC校准*/
ADC_ResetCalibration(ADC1); //固定流程,内部有电路会自动执行校准
while (ADC_GetResetCalibrationStatus(ADC1) == SET);
ADC_StartCalibration(ADC1);
while (ADC_GetCalibrationStatus(ADC1) == SET);
ADC软件触发函数:
通过ADC_SoftwareStartConvCmd(ADC1, ENABLE)软件触发ADC转换,通过while (ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC) == RESET)等待EOC标志位(EOC为1时表示转换完成,0没有转换完成),如果选择的是注入通道应该判断JEOC标志位。
uint16_t AD_GetValue(void)
{
ADC_SoftwareStartConvCmd(ADC1, ENABLE); //软件触发AD转换一次
while (ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC) == RESET); //等待EOC标志位,即等待AD转换结束
return ADC_GetConversionValue(ADC1); //读数据寄存器,得到AD转换的结果
}
工程文件分享:
http://链接:https://pan.baidu.com/s/1r5dbjqI26ntK15U8znWE-A 提取码:xqct
作者:朱朱的小任