STM32 ADC与DMA在电流检测中的应用(第一部分:ADC与DMA的配置)
文章目录
前言
最近做的项目需要检测电压和电流,所以学习了一下ADC和DMA。此文章以检测电流为例,从ADC、DMA、的配置,数据的采集和数据的处理几个方面介绍以STM32F103为控制器的完整开发过程。
一、ADC基本介绍
1、ADC是什么
ADC是Analog-to-DigitalConverter的缩写。指模/数转换器或者模拟/数字转换器。是指将连续变量的模拟信号转换为离散的数字信号的器件。典型的模拟数字转换器将模拟信号转换为表示一定比例电压值的数字信号。
2、ADC的供电和基准电压
ADC供电电源由VDDA和VSSA提供,ADC 的参考电压都是通过 Vref+ 引脚提供的并作为ADC转换器的基准电压。ADC所能测量的电压范围就是VREF-≤ VIN ≤ VREF+,把VSSA 和VREF-接地,把VREF+和VDDA 接3V3,得到ADC的输入电压范围为:0~3.3V。对于100脚以下的芯片,STM32没有把VREF引脚引出来,所以,我们只能把基准电压芯片连接到VDDA引脚。注意,STM32单片机上面有好多电源引脚,其中有若干VDD引脚,只有一个 VDDA 引脚,VDDA 引脚就是模拟供电引脚。不过,需要注意,VDDA的电压不是随便定义的。例如,STM32F051系列单片机就规定,VDDA必须要大于或者等于VDD才可以正常工作,所以这时候,最好是给单片机3.0V供电,再给VDDA采用一个3.3V的基准电压芯片供电。
3、ADC通道
STM32F103带3个ADC控制器,一共支持23个通道,包括21个外部和2个内部信号源;但是每个ADC控制器最多只可以有18个通道,包括16个外部和2个内部信号源
二、DMA的基本介绍
DMA,全称Direct Memory Access,即直接存储器访问。DMA传输将数据从一个地址空间复制到另一个地址空间,提供在外设和存储器之间或者存储器和存储器之间的高速数据传输,而不用经过CPU。对于STM32F10X系列芯片有2个DMA控制器 两个DMA控制器,DMA1有7个通道,DMA2有5个通道。
DMA读取ADC数据过程:
1、DMA传输时外设对DMA控制器发出请求。
2、DMA控制器收到请求,触发DMA工作。
3、DMA控制器从AHB外设获取ADC采集的数据,存储到DMA通道中
4、DMA控制器的DMA总线与总线矩阵协调,使用AHB把外设ADC采集的数据经由DMA通道存放到SRAM中,这个数据的传输过程中,完全不需要内核的参与,也就是不需要CPU的参与,
三、ADC和DMA的配置
1、配置GPIO端口
开启ADC和ADC I/O端口的时钟,设置ADC检测电流信号引脚为模拟输入模式。
示例:
void Bsp_ADCGpio_Config()
{
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA ,ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1,ENABLE);//开启ADC1时钟
RCC_ADCCLKConfig(RCC_PCLK2_Div6);// 这条语句的用于降低 ADC 采集速度,
//其作用实际上就是对 ADC 时钟进行 6 分频,提升 AD 采集的精确度。
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0|GPIO_Pin_1;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;
GPIO_Init(GPIOA, &GPIO_InitStructure);
}
2、配置DMA_InitTypeDef 结构体
DMA_InitTypeDef 定义于文件“stm32f10x_dma.h”:
typedef struct
{
u32 DMA_PeripheralBaseAddr;
u32 DMA_MemoryBaseAddr;
u32 DMA_DIR;
u32 DMA_BufferSize;
u32 DMA_PeripheralInc;
u32 DMA_MemoryInc;
u32 DMA_PeripheralDataSize;
u32 DMA_MemoryDataSize;
u32 DMA_Mode;
u32 DMA_Priority;
u32 DMA_M2M;
} DMA_InitTypeDef;
示例:
DMA_InitStructure.DMA_PeripheralBaseAddr = (u32)(&(ADC1->DR));
DMA_InitStructure.DMA_MemoryBaseAddr = (u32)&ADC_ConvertedValue;
DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;
DMA_InitStructure.DMA_BufferSize = TIMES*AD_CHANNEL;
DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;
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_Low;
DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;
DMA_PeripheralBaseAddr:
该参数用以定义 DMA 外设基地址,ADC_DR寄存器用于存放规则转换的数据。所以在此应用中此参数为:(u32)(&(ADC1->DR))
DMA_MemoryBaseAddr:
该参数用以定义 DMA 内存基地址
DMA_DIR:
规定外设是作为数据传输的目的地还是来源(数据传输方向)
DMA_DIR_PeripheralSRC :外设作为数据传输的来源
DMA_DIR_PeripheralDST :外设作为数据传输的目的地
DMA_BufferSize:
用以定义指定 DMA 通道的 DMA 缓存的大小,单位为数据单位。根据传输方向,数据单位等于结构中参数 DMA_PeripheralDataSize 或者参数 DMA_MemoryDataSize 的值。
DMA_PeripheralInc:
用来设定外设地址寄存器递增与否。
DMA_PeripheralInc_Enable: 外设地址寄存器递增
DMA_PeripheralInc_Disable: 外设地址寄存器不变
DMA_MemoryInc:
用来设定内存地址寄存器递增与否。
DMA_PeripheralInc_Enable: 内存地址寄存器递增
DMA_PeripheralInc_Disable: 内存地址寄存器不变
DMA_PeripheralDataSize:
设定了外设数据宽度。该参数的取值范围如下。
DMA_PeripheralDataSize_Byte 数据宽度为 8 位
DMA_PeripheralDataSize_HalfWord 数据宽度为 16 位
DMA_PeripheralDataSize_Word 数据宽度为 32 位
DMA_MemoryDataSize:
设定了存储器数据宽度。该参数的取值范围如下。
DMA_MemoryDataSize_Byte 数据宽度为 8 位
DMA_MemoryDataSize_HalfWord 数据宽度为 16 位
DMA_MemoryDataSize_Word 数据宽度为 32 位
DMA_Mode:
设置了 DMA 的工作模式。该参数的取值范围如下。
DMA_Mode_Circular 工作在循环缓存模式,接收完一次数据后,CNDTR不清0,可继续接收下一次的数据。
DMA_Mode_Normal 工作在正常缓存模式,接收完一次数据后,CNDTR自动清0,需要先关闭DMA,重置CNDTR,然后再开启DMA。
DMA_Priority:
设定 DMA 通道 x 的软件优先级。该参数的取值范围如下。
DMA_Priority_VeryHigh DMA 通道 x 拥有非常高优先级
DMA_Priority_High DMA 通道 x 拥有高优先级
DMA_Priority_Medium DMA 通道 x 拥有中优先级
DMA_Priority_Low DMA 通道 x 拥有低优先级
DMA_M2M:
使能 DMA 通道的内存到内存传输。该参数的取值范围如下。
DMA_M2M_Enable DMA 通道 x 设置为内存到内存传输
DMA_M2M_Disable DMA 通道 x 没有设置为内存到内存传输
3、void DMA_DeInit(DMA_Channel_TypDef*DMAy_Channelx)
功能:将DMAyChannelx寄存器的初始化为其默认值
注释:RCC_ResetCmd中对DMA无定义,因此采用的直接操纵DMA寄存器的方式
示例:
DMA_DeInit(DMA1_Channel1);
4、void DMA_Init(DMA_Channel_TypeDef* DMAy_Channelx, DMA_InitTypeDef* DMA_InitStruct)
功能:根据DMA_InitStruct中的指定参数初始化DMAy通道x
注释:结构体成员DMA_BufferSize的值大小等于数据需要传输的次数
示例:
DMA_Init(DMA1_Channel1, &DMA_InitStructure);
5、void DMA_Cmd(DMA_Channel_TypeDef* DMAy_Channelx, FunctionalState NewState)
功能:使能或者失能DMA外设
示例:
DMA_Cmd(DMA1_Channel1, ENABLE);
6、配置ADC结构体
ADC_DataAlign:
转换结果数据对齐模式,可选右对齐或者左对齐。一般我们选择右对齐模式。
ADC_DATAALIGN_RIGHT :右对齐
ADC_DATAALIGN_LEFT:左对齐
ScanConvMode:
配置是否使用扫描。如果是单通道AD转换使用DISABLE,如果是多通道AD转换使用ENABLE。
Enable: 使能
Disable: 失能
ADC_ContinuousConvMode:
配置是启动自动连续转换还是单次转换。使用ENABLE配置为使能自动连续转换;使用DISABLE配置为单次转换,转换一次后停止需要手动控制才重新启动转换。
Enable: 使能
Disable: 失能
NbrOfConversion:
转换通道数目(规则序列中有几个转换)。
ADC_ExternalTrigConv:
外部触发事件选择,列举了很多外部触发条件,可根据项目需求配置触发来源。实际上,我们一般使用软件自动触发。
ADC_EXTERNALTRIGCONV_T1_CC1 选择定时器1的捕获比较1作为转换外部触发
ADC_EXTERNALTRIGCONV_T1_CC2 选择定时器1的捕获比较2作为转换外部触发
ADC_EXTERNALTRIGCONV_T2_CC2 选择定时器2的捕获比较2作为转换外部触发
ADC_EXTERNALTRIGCONV_T3_TRGO 选择定时器3的TRGO作为转换外部触发
ADC_EXTERNALTRIGCONV_T4_CC4 选择定时器4的捕获比较4作为转换外部触发
ADC_EXTERNALTRIGCONV_EXT_IT11 选择外部中断线11事件作为转换外部触发
ADC_EXTERNALTRIGCONV_T2_CC3 选择定时器2的捕获比较3作为转换外部触发
ADC_EXTERNALTRIGCONV_T3_CC1 选择定时器3的捕获比较1作为转换外部触发
ADC_EXTERNALTRIGCONV_T5_CC1 选择定时器5的捕获比较1作为转换外部触发
ADC_EXTERNALTRIGCONV_T5_CC3 选择定时器5的捕获比较3作为转换外部触发
ADC_EXTERNALTRIGCONV_T8_CC1 选择定时器8的捕获比较1作为转换外部触发
ADC_EXTERNALTRIGCONV_T1_CC3 选择定时器1的捕获比较3作为转换外部触发
ADC_EXTERNALTRIGCONV_T8_TRGO 选择定时器8的TRGO作为转换外部触发
ADC_SOFTWARE_START 选择软件触发
ADC_Mode:
ADC工作模式选择,有独立模式、双重模式以及三重模式。这里设置为独立模式:ADC_Mode_Independent,在这个模式下,双ADC不能同步,每个ADC接口独立工作。所以如果不需要ADC同步或者只是用了一个ADC的时候,就应该设成独立模式了。
ADC_Mode_Independent :独立模式
ADC_Mode_RegInjecSimult :混合的同步规则+注入同步模式
ADC_Mode_RegSimult_AlterTrig:混合的同步规则+交替触发模式
ADC_Mode_InjecSimult_FastInterl:混合同步注入+快速交替模式
ADC_Mode_InjecSimult_SlowInterl:混合同步注入+慢速交替模式
ADC_Mode_InjecSimult:注入同步模式
ADC_Mode_RegSimult :规则同步模式
ADC_Mode_FastInterl:快速交替模式
ADC_Mode_SlowInterl:慢速交替模式
ADC_Mode_AlterTrig :交替触发模式
示例:
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 = AD_CHANNEL;
7、void ADC_Init(ADC_TypeDef* ADCx, ADC_InitTypeDef* ADC_InitStruct)
初始化函数:配置ADC模式、扫描模式、单次连续模式、外部触发方式、对齐方式、规则序列长度。
示例:
ADC_Init(ADC1, &ADC_InitStructure);
8、void ADC_Cmd(ADC_TypeDef* ADCx, FunctionalState NewState)
使能函数:配置ADC使能。
示例:
ADC_Cmd(ADC1, ENABLE);
9、void ADC_SoftwareStartConvCmd(ADC_TypeDef* ADCx, FunctionalState NewState);
软件转换函数:ADC使能软件转换(在ADC_Init函数中,外部触发方式选择none)。
示例:
ADC_SoftwareStartConvCmd(ADC1, ENABLE);
10、void ADC_RegularChannelConfig(ADC_TypeDef* ADCx, uint8_t ADC_Channel, uint8_t Rank, uint8_t ADC_SampleTime);
通道配置函数:配置某个ADC控制器的某个通道以某种采样率置于规则组的某一位(对应函数的四个参数:ADC控制器名、ADC通道名、规则组的第n个、采样率)。
示例:
ADC_RegularChannelConfig(ADC1, ADC_Channel_0, 1, ADC_SampleTime_55Cycles5);
11、void ADC_ResetCalibration(ADC_TypeDef* ADCx)
功能:复位选中的ADC校准寄存器
注释:如果正在进行转换时设置RSTCAL,清除校准寄存器需要额外的周期
示例:
ADC_ResetCalibration(ADC1);
12、void ADC_DMACmd(ADC_TypeDef* ADCx, FunctionalState NewState)
功能:使能或者失能指定的ADC外设的DMA请求
注释:只有ADC1和ADC3能产生DMA请求
示例:
ADC_DMACmd(ADC1, ENABLE);
13、void ADC_StartCalibration(ADC_TypeDef* ADCx)
功能:启动选定的ADC校准过程
示例:
ADC_StartCalibration(ADC1);
14、FlagStatus ADC_GetResetCalibrationStatus(ADC_TypeDef* ADCx)
功能:获取选定的ADC复位校准寄存器状态
注释:选定的ADC复位校准寄存器是否初始化成功
示例:
while(ADC_GetResetCalibrationStatus(ADC1));
15、FlagStatus ADC_GetCalibrationStatus(ADC_TypeDef* ADCx)
功能:获取选定的ADC校准状态
注释:检查校准是否完成
示例:
while(ADC_GetCalibrationStatus(ADC1));
DMA与ADC整体配置代码示例:
void Bsp_ADCDMA_Init()
{
DMA_InitTypeDef DMA_InitStructure;
ADC_InitTypeDef ADC_InitStructure;
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);//开启DMA1的时钟
DMA_DeInit(DMA1_Channel1);//复位DMA1通道1
DMA_InitStructure.DMA_PeripheralBaseAddr = (u32)(&(ADC1->DR));
DMA_InitStructure.DMA_MemoryBaseAddr = (u32)&ADC_ConvertedValue;
DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;
DMA_InitStructure.DMA_BufferSize = TIMES*AD_CHANNEL;
DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;
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_Low;
DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;
DMA_Init(DMA1_Channel1, &DMA_InitStructure);
DMA_Cmd(DMA1_Channel1, ENABLE);
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 = AD_CHANNEL;
ADC_Init(ADC1, &ADC_InitStructure);
ADC_TempSensorVrefintCmd(ENABLE);
ADC_RegularChannelConfig(ADC1, ADC_Channel_0, 1, ADC_SampleTime_55Cycles5);
ADC_RegularChannelConfig(ADC1, ADC_Channel_1, 2, ADC_SampleTime_55Cycles5);
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);
}
下一章将介绍电流检测的电路和数据处理的方法