ADC (Analog to Digital Converter),模/数转换器。主要用于模拟信号需要以数字形式处理、存储或传输。STM32 在 片 上 集 成 的 ADC 外 设 非 常 强 大 。 在 STM32F103xC 、STM32F103xD 和 STM32F103xE 增强型产品,内嵌 3 个 12 位 的 ADC每个ADC 的结果可以左对齐或右对齐方式存储在 16 位数据寄存器中。模拟看门狗特性允许应用程序检测输入电压是否超出用户定义的高/低阀值。ADC 共用多达 21 个外部通道,可以实现单次或多次扫描转换。
ADC的主要技术指标
对于 ADC 来说,我们需要关注的就是它的分辨率、转换速度、ADC 类型、参考电压范围。
分辨率
12 位分辨率。不能直接测量负电压,所以没有符号位,即其最小量化单位 LSB = Vref+ / 212 。
转换时间
转换时间是可编程的。采样一次至少要用 14 个 ADC 时钟周期,而 ADC 的时钟频率最高为 14MHz,也就是说,它的采样时间最短为 1us。足以胜任中、低频数字示波器的采样工作。
ADC 类型
ADC 的类型决定了它性能的极限,STM32 是逐次比较型 ADC。
参考电压范围
参考电压负极是要接地的,即 V REF- = 0V。而参考电压正极的范围为 2.4V≦V REF+ ≦3.6V,所以 STM32 的 ADC 是不能直接测量负电压的,而且其输入的电压信号的范围为:V REF- ≦ V IN ≦ V REF+ 。当需要测量负电压或测量的电压信号超出范围时,要先经过运算电路进行平移或利用电阻分压。
ADC 工作过程分析
以 ADC 的规则通道转换来进行过程分析。所有的器件都是围绕中间的模拟至数字转换器部分(下面简称 ADC 部件)展开的。它的左端为 V REF+ 、V REF- 等 ADC 参考电压,ADCx_IN0~ADCx_IN15 为 ADC 的输入信号通道,即某些 GPIO 引脚。输入信号经过这些通道被送到 ADC 部件,ADC 部件需要受到触发信号才开始进行转换,如 EXTI 外部触发、定时器触发,也可以使用软件触发。ADC 部件接收到触发信号之后,在 ADCCLK 时钟的驱动下对输入通道的信号进行采样,并进行模数转换,其中 ADCCLK 是来自 ADC 预分频器的。
ADC 部件转换后的数值被保存到一个 16 位的规则通道数据寄存器(或注入通道数据寄存器)之中,我们可以通过 CPU 指令或 DMA 把它读取到内存(变量)。模数转换之后,可以触发 DMA 请求,或者触发 ADC 的转换结束事件。如果配置了模拟看门狗,并且采集得的电压大于阈值,会触发看门狗中断。
ADC 采集数据实例
- 采用DMA模式
使用 ADC 时常常需要不间断采集大量的数据,在一般的器件中会使用中断进行处理,但使用中断的效率还是不够高。在 STM32 中,使用 ADC 时往往采用 DMA 传输的方式,由 DMA 把 ADC 外设转换得的数据传输到 SRAM,再进行处理,甚至直接把 ADC 的数据转移到串口发送给上位机。
(1).DMA配置
void AdcDmaInit(void)
{
DMA_InitTypeDef DMA_InitStructure;
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);
DMA_DeInit(DMA1_Channel1);
DMA_InitStructure.DMA_PeripheralBaseAddr = ADC1_DR_Address;
DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)&ADCConvertedValue;
DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;
DMA_InitStructure.DMA_BufferSize = 1;
DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Disable;
DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord;
DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord;
DMA_InitStructure.DMA_Mode = DMA_Mode_Circular;
DMA_InitStructure.DMA_Priority = DMA_Priority_High;
DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;
DMA_Init(DMA1_Channel1, &DMA_InitStructure);
DMA_Cmd(DMA1_Channel1, ENABLE);
}
ADC 的 DMA 配置部分跟串口 DMA 配置部分很类似,它的 DMA 整体上被配置为:使用 DMA1 的通道 1,数据从 ADC 外设的数据寄存器(ADC1_DR_Address)转移到内存(ADC_ConvertedValue 变量),内存、外设地址都固定,每次传输的数据大小为半字(16 位),使用 DMA 循环传输模式。其中 ADC1 外设的 DMA 请求通道为 DMA1 的通道 1,初始化时要注意。DMA 传输的外设地址 ADC1_DR_Address 是一个自定义的宏: #define ADC1_DR_Address ((u32)0x40012400+0x4c),ADC_DR 数据寄存器保存了 ADC 转换后的数值,以它作为 DMA 的传输源地址。它的地址是由 ADC1 外设的基地址(0x4001 2400) 加上 ADC 数据寄存器(ADC_DR)的地址偏移 (0x4c)计算得到的。
(2).ADC配置
ADC 模式的配置,主要为对 ADC 的初始化结构体进行赋值。
a.ADC_Mode
STM32 具有多个 ADC,而不同的 ADC 又是共用通道的,当两个 ADC 采集同一个通道的先后顺序、时间间隔不同,就演变出了各种各样的模式,如同步注入模式、同步规则模式等 10 种,根据应用要求选择适合的模式以适应采集数据的要求。只需要使用一个 ADC时,将Mode赋值为 ADC_Mode_Independent (独立模式)即可。
b.ADC_ScanConvMode
当有多个通道需要采集信号时,可以把 ADC 配置为按一定的顺序来对各个通道进行扫描转换,即轮流采集各通道的值。若采集多个通道,必须开启此模式。本实验只采集一个通道的信号,所以 DISABLE(禁止)使用扫描转换模式。
c. ADC_ContinuousConvMode
连续转换模式,此模式与单次转换模式相反,单次转换模式 ADC 只采集一次数据就停止转换。而连续转换模式则在上一次 ADC 转换完成后,立即开启下一次转换。本实验需要循环采集电压值,所以 ENABLE(使能)连续转换模式。
d. ADC_ExternalTrigConv
ADC 需要在接收到触发信号才开始进行模数转换,这些触发信号可以是外部中断触发(EXTI 线)、定时器触发。这两个为外部触发信号,如果不使用外部触发信号可以使用软件控制触发。本实验中使用软件控制触发所以该成员被赋值为 ADC_ExternalTrigConv_None(软件触发)。
e. ADC_DataAlign
数据对齐方式。ADC 转换后的数值是被保存到数据寄存器(ADC_DR)的0~15 位或 16~32 位,数据宽度为 16 位,而 ADC 转换精度为 12 位。把 12 位的数据保存到 16 位的区域,就涉及左对齐和右对齐的问题。这里的左、右对齐跟 word 文档中的文本左、右对齐是一样的意思。左对齐即 ADC 转换的数值最高位 D12 与存储区域的最高位 Bit 15 对齐,存储区域的低 4 位无意义。右对齐则相反,ADC 转换的数值最低位 D0 保存在存储区域的最低位 Bit 0 ,高 4 位无意义。本实验中 ADC 的转换值最后被保存在一个 16 位的变量之中,选择ADC_DataAlign_Right (右对齐) 会比较方便。
f. ADC_NbrOfChannel
这个成员保存了要进行 ADC 数据转换的通道数,可以为 1~16 个。本实验中只需要采集 PC1 这个通道,所以把成员赋值为 1 就可以了。
配置完结构体后,就可以调用外设初始化函数进行初始化了,ADC 的初始化使用 ADC_Init()函数,初始化完成后别忘记调用 ADC_Cmd() 函数来使能 ADC外设,用 ADC_DMACmd() 函数来使能 ADC 的 DMA 接口。在本实验中初始化ADC1。
void AdcInit(void)
{
ADC_InitTypeDef ADC_InitStructure;
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1 | RCC_APB2Periph_GPIOC, ENABLE);
RCC_ADCCLKConfig(RCC_PCLK2_Div2);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;
GPIO_Init(GPIOC, &GPIO_InitStructure);
ADC_InitStructure.ADC_Mode = ADC_Mode_Independent;
ADC_InitStructure.ADC_ScanConvMode = ENABLE;
ADC_InitStructure.ADC_ContinuousConvMode = ENABLE;
ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;
ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;
ADC_InitStructure.ADC_NbrOfChannel = 1;
ADC_Init(ADC1, &ADC_InitStructure);
ADC_RegularChannelConfig(ADC1, ADC_Channel_14, 1, ADC_SampleTime_55Cycles5);
AdcDmaInit();
ADC_DMACmd(ADC1, ENABLE);
ADC_Cmd(ADC1, ENABLE);
ADC_ResetCalibration(ADC1);
while(ADC_GetResetCalibrationStatus(ADC1));
ADC_StartCalibration(ADC1);
while(ADC_GetCalibrationStatus(ADC1));
ADC_SoftwareStartConvCmd(ADC1, ENABLE);
}
以上是DMA模式采集ADC的部分代码,我已上传至网盘,链接:https://pan.baidu.com/s/1FhmhlwnpLCSUlfwAFkNAYQ&shfl=sharepset 提取码:pu8d
还有非DMA模式采集ADC的工程,方法与DMA模式大同小异,同样附上链接:https://pan.baidu.com/s/1CpkgFA4eaCDx63cpYS6bKg&shfl=sharepset 提取码:vwa9 。
很多人做完AD实验后对ADC采集到的数据表示误差太大?或者说想要增加采值的精确度,这个时候,我们应该想到之前学到的单片机滤波算法,根据不同的采集情况的误差影响来选定合适的滤波算法,能大大的减小误差给数值的精确度带来影响,滤波算法详情请见:https://blog.csdn.net/huolu0602/article/details/102488612。