基于原子STM32F103RC(STM32Fmini)/STM32F407ZG(STM32F4探索者)的多通道ADC采集,通过MDA传输采集数据。
在学习STM32的时候,发现无论是STM32F103RC还是STM32F407ZG都只有ADC转换的例程和UART DMA的例程。所以写了一份DMA传输ADC的程序,希望对大家有帮助。
程序:使用了STM32F407ZG(STM32F4探索者)的开发板,通过ADC去采集PA5,PA6,PA7引脚的电平。然后由DMA传输,在串口上显示出采集的值。
PS:程序在STM32F1mini上也通用自己对照着改一下IO口和一些通道配置就好。
首先当然是配置我们的IO口:
void My_DAC_IO_Init(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE);
//初始化ADC1通道5.6.7
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5 | GPIO_Pin_6 | GPIO_Pin_7; //PA5 通道5 PA6 通道6 PA7 通道7
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN; //模拟输入模式
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL ; //不带上下拉
GPIO_Init(GPIOA, &GPIO_InitStructure);
}
查阅手册可以看到PA5对应的是ADC的IN5,PA6对应的是ADC的IN6,PA7对应的是ADC的IN7。
这样就配置好了通道。通道也选择的是相近的这方便与DMA传输过程中的地址增加。
接下来配置ADC:
void My_Adc_Init(void)
{
ADC_CommonInitTypeDef ADC_CommonInitStructure;
ADC_InitTypeDef ADC_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE);
ADC_CommonInitStructure.ADC_DMAAccessMode = ADC_DMAAccessMode_Disabled; //禁止DMA直接访问
ADC_CommonInitStructure.ADC_Mode = ADC_Mode_Independent; //ADC独立模式
ADC_CommonInitStructure.ADC_Prescaler = ADC_Prescaler_Div4; //4分频
ADC_CommonInitStructure.ADC_TwoSamplingDelay = ADC_TwoSamplingDelay_20Cycles; //采样时间间隔
ADC_CommonInit(&ADC_CommonInitStructure);
ADC_InitStructure.ADC_ContinuousConvMode = ENABLE; //连续转换使能,因为这里是转换三个通道
ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right; //ADC的对齐模式
ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConvEdge_None; //禁止外部触发
ADC_InitStructure.ADC_ExternalTrigConvEdge = ADC_ExternalTrigConv_T1_CC1; //因为禁止了外部这边随便选择什么
ADC_InitStructure.ADC_NbrOfConversion = 3; //3个通道转换
ADC_InitStructure.ADC_Resolution = ADC_Resolution_12b; //12位ADC
ADC_InitStructure.ADC_ScanConvMode = ENABLE; //多通道采集开启
ADC_Init(ADC1, &ADC_InitStructure);
ADC_RegularChannelConfig(ADC1, ADC_Channel_5, 1, ADC_SampleTime_3Cycles); //配置ADC通道转换顺序
ADC_RegularChannelConfig(ADC1, ADC_Channel_6, 2, ADC_SampleTime_3Cycles);
ADC_RegularChannelConfig(ADC1, ADC_Channel_7, 3, ADC_SampleTime_3Cycles);
ADC_DMARequestAfterLastTransferCmd(ADC1, ENABLE); //使能DMA请求
ADC_DMACmd(ADC1, ENABLE); //使能ADC-MDA
ADC_Cmd(ADC1, ENABLE); //使能ADC
ADC_SoftwareStartConv(ADC1); //开启软件转换
}
ADC的配置不会的话可以去看原子提供的ADC视频里面有详细的讲解每个配置功能是干嘛的,这边需要注意的是多了一下两个个函数:
1.
ADC_DMARequestAfterLastTransferCmd(ADC1, ENABLE); //使能DMA请求
这个函数使能了ADC的MDA请求。
2.
ADC_DMACmd(ADC1, ENABLE);
这个函数使能了ADC的DMA模式。
然后就需要去配置我们的DMA了,
#ifndef __DMA_H
#define __DMA_H
#include "sys.h"
#define RHEOSTAT_NOFCHANEL 3
// ADC DR寄存器的宏定义 ADC转换后的值就存放在这个寄存器里面
#define RHEOSTAT_ADC_DR_ADDR ((u32)ADC1+0x4c)
extern __IO uint16_t ADC_ConvertedValue[RHEOSTAT_NOFCHANEL];
void My_DMA_Init(void);
#endif
这是.h文件,一些定义在.h里面定义
#include "dma.h"
__IO uint16_t ADC_ConvertedValue[RHEOSTAT_NOFCHANEL]={0};
void My_DMA_Init(void)
{
DMA_InitTypeDef DMA_InitStructure;
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_DMA2, ENABLE);
DMA_InitStructure.DMA_BufferSize = RHEOSTAT_NOFCHANEL; //数据大小 = 3
DMA_InitStructure.DMA_Channel = DMA_Channel_0; //DMA通道0
DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralToMemory; //DMA传输方向从外设到寄存器
DMA_InitStructure.DMA_FIFOMode = DMA_FIFOMode_Disable; //不用FIFO用直接模式
DMA_InitStructure.DMA_FIFOThreshold = DMA_FIFOThreshold_HalfFull; //半字 = 两个字节
DMA_InitStructure.DMA_Memory0BaseAddr = (u32)ADC_ConvertedValue; //寄存器的基地址
DMA_InitStructure.DMA_MemoryBurst = DMA_MemoryBurst_Single; //FIFO禁用不需要配置
DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord; //半字 = 两个字节
DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable; //寄存器地址增加使能。
DMA_InitStructure.DMA_Mode = DMA_Mode_Circular; //DMA循环模式
DMA_InitStructure.DMA_PeripheralBaseAddr = RHEOSTAT_ADC_DR_ADDR; //外设基地址
DMA_InitStructure.DMA_PeripheralBurst = DMA_PeripheralBurst_Single; //FIFO禁用不需要配置
DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord; //半字 = 两个字节
DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable; //外设地址不需要变化
DMA_InitStructure.DMA_Priority = DMA_Priority_High; //传输通道为高
DMA_Init(DMA2_Stream0, &DMA_InitStructure);
DMA_Cmd(DMA2_Stream0, ENABLE); //使能DMA
}
在这里我们需要注意的一下几点:
1.数据是从ADC传到ADC_ConvertedValue这个数组,也就是从外设(ADC)传输到寄存器(数组存放地址)
2.外设地址:为 ((u32)ADC1+0x4c) 其中ADC1是ADC的基地址,0x4c是ADC_DR寄存器的偏移量,因为ADC_DR寄存器是存放转换结果的,所以我们需要通过DMA传输出去的就是这块寄存的里面的数据。
3.寄存器地址:(u32)ADC_ConvertedValue 这个是自己定义的一个数组用来存放我们DMA传输出来的ADC转换后的数据。
4.因为我们采集的三个通道,然后把数据存放在数组中,那么每个通道就对应一个数组的一个元素。所以数组的地址需要递增,也就是寄存器的地址需要增加,但是外设的地址不需要变化,因为每次传输的外设地址都是这个ADC_DR寄存器,这个寄存器的地址是不变的。
备注:如果有DMA通道配置不懂的也可以去看看原子的视频,有详细的讲解。
这样配置完后我们的底层程序就差不多完成。然后在主函数中进行编写应用程序。
float ADC_ConvertedValueLocal[RHEOSTAT_NOFCHANEL]={0};
int main(void)
{
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);//设置系统中断优先级分组2
delay_init(168); //延时初始化
uart_init(115200); //初始化串口波特率 = 115200
My_DAC_IO_Init();
My_DMA_Init();
My_Adc_Init();
while(1)
{
ADC_ConvertedValueLocal[0] =(float) ADC_ConvertedValue[0]/4096*(float)3.3;
ADC_ConvertedValueLocal[1] =(float) ADC_ConvertedValue[1]/4096*(float)3.3;
ADC_ConvertedValueLocal[2] =(float) ADC_ConvertedValue[2]/4096*(float)3.3;
printf("\r\n CH1_C3 value = %f V \r\n",ADC_ConvertedValueLocal[0]);
printf("\r\n CH2_PA4 value = %f V \r\n",ADC_ConvertedValueLocal[1]);
printf("\r\n CH3_PA6 value = %f V \r\n",ADC_ConvertedValueLocal[2]);
printf("\r\n\r\n");
}
}
这样通过串口工具我们就可以读取到我们的采集结果了。
———-附带程序的链接链接:https://pan.baidu.com/s/1TQZzicrVJ5f86FBPI96KYg 密码:du89
上一篇: PCF8591芯片以及AD学习