基于ARM开发板从零开始学习STM32 05-DMA模式采集ADC数据实例(附非DMA模式数据采集方法)

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接: https://blog.csdn.net/huolu0602/article/details/102639019

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  采集数据实例

  1. 采用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

猜你喜欢

转载自blog.csdn.net/huolu0602/article/details/102639019