为了能够使用数字电路处理模拟信号,必须将模拟信号转换成相应的数字信号,方能送入数字系统(例如微型计算机)进行处理,而这一种从模拟信号到数字信号的转换称为模—数转换,或简称为A/D(Analog to Digital)转换[1]。当作为传感器的电路系统产出模拟信号时,AD模块无疑是直观显示传感器功能状态的最佳选择。
STM32F4xx系列的AD最高可配置12位的分辨率,同时可在其规则通道转换期间可产生 DMA 请求来减轻CPU负担。
STM32的AD采集主要区别在于使用的AD数量和AD使用的通道数量,多AD的采集优势在于可以增加传感器的扫描频率,获得更加多的AD采集数据,如在AD通道数量比较紧缺而且对数据量没那么多的情况下,单AD多通道采集无疑是比较好的方法。本次主要讨论的就是单AD多通道采集数据的方法。由于规则通道组只有一个数据寄存器,因此,对于多个规则通道的转换,使用 DMA 非常有帮助。这样可以避免丢失在下一次写入之前还未被读出的 ADC_DR 寄存器中的数据[2]。
二、AD初始化的方式
STM32F429中提供了3个AD可供使用,其中DMA2是直接对ADC提供服务的,DMA2的请求映射分别对应的是:ADC1:数据流0通道0和数据流4通道0,ADC2:数据流2通道1和数据流3通道1,ADC3:数据流0通道2和数据流1通道2,详情可参考CPU手册。
首先需要初始化的是ADC所引出的GPIO引脚,没啥需要注意的,只要没有搞错引脚就行,配置方法可见上一篇(STM32)GPIO库函数使用一览 。
第二步时配置所需要使用的DMA,配置的时候需要注意的几项:1.ADC的外设基址,负责获取ADC的数据;2.读取数据的地址,从这里可以读出DMA的ADC数据,即是设置一个与通道数相应匹配的大小的数组;3.搞清楚DMA的请求映射。
最后就是配置ADC,STM32F4中的AD初始化库函数:ADC_CommonInit()和ADC_Init()配置的分别是通用类型变量和ADC的初始化
typedef struct
{
uint32_t ADC_Mode;
uint32_t ADC_Prescaler;
uint32_t ADC_DMAAccessMode;
uint32_t ADC_TwoSamplingDelay;
}ADC_CommonInitTypeDef;
ADC_CommonInit()中包含的是ADC模式选择,分频,DMA模式配置和采样延迟。
1.ADC模式主要针对于ADC数量的选择,独立模式可选ADC_Mode_Independent。
2.分频可选ADC_Prescaler_Div2/Div4/Div6/Div8。
3.DMA模式,是针对于多ADC中配置的,独立模式选择ADC_DMAAccessMode_Disabled。
4.采样延迟可配置5-20的任意值,ADC_TwoSamplingDelay_10Cycles。
typedef struct
{
uint32_t ADC_Resolution;
FunctionalState ADC_ScanConvMode;
FunctionalState ADC_ContinuousConvMode;
uint32_t ADC_ExternalTrigConvEdge;
uint32_t ADC_ExternalTrigConv;
uint32_t ADC_DataAlign;
uint8_t ADC_NbrOfConversion;
}ADC_InitTypeDef;
ADC_Init()中包含的是ADC分辨率,是否扫描,是否连续转换,触发极性和触发选择,数据对齐和通道数目。
1.分辨率可从12/10/8/6中做选择,ADC_Resolution_12/10/8/6b.
2.在多通道时需要对扫描转换使能,单通道的不需要。
3.连续转换指的是在各个通道读值完毕后继续读值。
4.外部触发的条件有很多,需要根据外设来具体分析
5.数据对齐有两种,左对齐和右对齐,一般是选择右对齐。
6.通道数目为对应配置的通道数。
三、AD的配置功能实现
ADC_RegularChannelConfig(ADC1, ADC_Channel_10, 1, ADC_SampleTime_56Cycles);
ADC_RegularChannelConfig(ADC1, ADC_Channel_12, 2, ADC_SampleTime_56Cycles);
ADC_RegularChannelConfig(ADC1, ADC_Channel_13, 3, ADC_SampleTime_56Cycles);
ADC_RegularChannelConfig();设置ADC通道的转换顺序及时间。
ADC_DMARequestAfterLastTransferCmd(ADC1,ENABLE); 该函数仅在单ADC模式中使用,功能是在ADC最后传输后DMA启用或禁用请求。
ADC_DMACmd(ADC1, ENABLE);ADC是否使用DMA的请求。
ADC_Cmd(ADC1, ENABLE);使用ADC的请求。
在初始化后,接上ADC的功能请求函数即可完成对ADC功能的配置。
此时ADC的数据全部由DMA中配置的读取地址中获取,就是当时所写的数组中获取。
功能示例中表现的是单AD多通道的例子:
#include "bsp.h"
#include
static void ADC_Configuration(void);
uint16_t ADC_Value[7]={0};//AD数据
uint16_t local[7]={0};
int main()
{
while(1){
local[0]=(float)(ADC_Value[0]*3.3/4096);
local[1]=(float)(ADC_Value[1]*3.3/4096);
local[2]=(float)(ADC_Value[2]*3.3/4096);
printf("channel 1 value = %fV\r\n",local[0]);
printf("channel 2 value = %fV\r\n",local[1]);
printf("channel 3 value = %fV\r\n",local[2]);
}
}
/*独立模式多通道 电压采集*/
static void ADC_Configuration(void)
{
GPIO_InitTypeDef GPIO_InitStructure_C;
GPIO_InitTypeDef GPIO_InitStructure_A;
ADC_InitTypeDef ADC_InitStructure;
ADC_CommonInitTypeDef ADC_CommonInitStructure;
/* 使能 GPIOC clock */
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOC, ENABLE);
GPIO_InitStructure_C.GPIO_Pin = GPIO_Pin_0|GPIO_Pin_2|GPIO_Pin_3;
GPIO_InitStructure_C.GPIO_Mode = GPIO_Mode_AIN;
GPIO_InitStructure_C.GPIO_PuPd = GPIO_PuPd_NOPULL; //不上拉不下拉
GPIO_Init(GPIOC, &GPIO_InitStructure_C);
/* 使能 GPIOA clock */
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE);
GPIO_InitStructure_A.GPIO_Pin = GPIO_Pin_3|GPIO_Pin_4|GPIO_Pin_5|GPIO_Pin_6;
GPIO_InitStructure_A.GPIO_Mode = GPIO_Mode_AIN;
GPIO_InitStructure_A.GPIO_PuPd = GPIO_PuPd_NOPULL; //不上拉不下拉
GPIO_Init(GPIOA, &GPIO_InitStructure_A);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE);
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_DMA2, ENABLE);
/* DMA Init 结构体初始化 DMA2 Stream 0 channel 0 配置用于ADC1*/
DMA_InitTypeDef DMA_InitStructure;
DMA_DeInit(DMA2_Stream0);
DMA_InitStructure.DMA_Channel = DMA_Channel_0; //选择DMA通道
DMA_InitStructure.DMA_PeripheralBaseAddr =(uint32_t)(0x40012000+0x4c); //外设基址->ADC
DMA_InitStructure.DMA_Memory0BaseAddr = (uint32_t)ADC_Value; //储存器地址->读取数据地址
DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralToMemory; //数据传输方向为外设到存储器
DMA_InitStructure.DMA_BufferSize = 7; //缓冲区大小,即一次传输的数据项
DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable; //外部寄存器只有一个,地址不用递增
DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable; //存储器地址递增
DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord; //外设数据大小为一个字符,四个字节(可改HalfWord为半个字,两个字节)
DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord; //存储器数据大小与外设一致
DMA_InitStructure.DMA_Mode = DMA_Mode_Circular; //循环传输模式
DMA_InitStructure.DMA_Priority = DMA_Priority_High; //DMA优先级最高
DMA_InitStructure.DMA_FIFOMode = DMA_FIFOMode_Disable; //DMA直连模式
DMA_InitStructure.DMA_FIFOThreshold = DMA_FIFOThreshold_HalfFull; //FIFO大小,FIFO模式禁止,不用配置
DMA_InitStructure.DMA_MemoryBurst = DMA_MemoryBurst_Single; //存储器突发单次传输
DMA_InitStructure.DMA_PeripheralBurst = DMA_PeripheralBurst_Single; //外设突发单次传输
DMA_Init(DMA2_Stream0, &DMA_InitStructure);
DMA_Cmd(DMA2_Stream0, ENABLE); //使能DMA数据流
/* DMA Init 结构体初始化 DMA2 Stream 0 channel 0 配置用于ADC1*/
RCC_APB2PeriphResetCmd(RCC_APB2Periph_ADC1,ENABLE); //ADC1复位
RCC_APB2PeriphResetCmd(RCC_APB2Periph_ADC1,DISABLE); //复位结束
/* ADC Common结构体参数初始化*/
ADC_CommonInitStructure.ADC_Mode = ADC_Mode_Independent; //独立模式
ADC_CommonInitStructure.ADC_Prescaler = ADC_Prescaler_Div4; //时钟分频
ADC_CommonInitStructure.ADC_DMAAccessMode = ADC_DMAAccessMode_Disabled; //禁止DMA直接访问模式
ADC_CommonInitStructure.ADC_TwoSamplingDelay = ADC_TwoSamplingDelay_10Cycles; //采样时间间隔
ADC_CommonInit(&ADC_CommonInitStructure);
/*ADC Init结构体参数初始化*/
ADC_InitStructure.ADC_Resolution = ADC_Resolution_12b; //分辨率
ADC_InitStructure.ADC_ScanConvMode = ENABLE; //多通道采集,开启扫描模式
ADC_InitStructure.ADC_ContinuousConvMode = ENABLE; //连续转换
ADC_InitStructure.ADC_ExternalTrigConvEdge = ADC_ExternalTrigConvEdge_None; //禁止外部边沿触发
ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right; //右对齐
ADC_InitStructure.ADC_NbrOfConversion = 7; //转换通道数
ADC_Init(ADC1, &ADC_InitStructure);
/* 配置ADC1 的通道转换顺序和采样时间周期*/
ADC_RegularChannelConfig(ADC1, ADC_Channel_10, 1, ADC_SampleTime_56Cycles); //引脚PC0
ADC_RegularChannelConfig(ADC1, ADC_Channel_12, 2, ADC_SampleTime_56Cycles); //引脚PC2
ADC_RegularChannelConfig(ADC1, ADC_Channel_13, 3, ADC_SampleTime_56Cycles); //引脚PC3
ADC_RegularChannelConfig(ADC1, ADC_Channel_3, 4, ADC_SampleTime_56Cycles); //引脚PA3
ADC_RegularChannelConfig(ADC1, ADC_Channel_4, 5, ADC_SampleTime_56Cycles); //引脚PA4
ADC_RegularChannelConfig(ADC1, ADC_Channel_5, 6, ADC_SampleTime_56Cycles); //引脚PA5
ADC_RegularChannelConfig(ADC1, ADC_Channel_6, 7, ADC_SampleTime_56Cycles); //引脚PA6
ADC_DMARequestAfterLastTransferCmd(ADC1,ENABLE); //使能DMA请求
ADC_DMACmd(ADC1, ENABLE); //使能ADC1 DMA
ADC_Cmd(ADC1, ENABLE); //使能ADC1
ADC_SoftwareStartConv(ADC1); //开启软件使能
}
实现后,可以打印出3个通道的电压值,电压的计算主要与参考电压VREF有关;计算公式为:
电压=(VREF*扫描值)/2^12
[1]阎石.数字电子技术基础(第五版)[M].高等教育出版社,2006.
[2]STM32F407, 429参考手册.