×
单片机 > 单片机程序设计 > 详情

STM32 ADC转换(DMA)

发布时间:2020-08-26 发布时间:
|
上次博客已经讲了如何实现ADC转换。这次我使用DMA来帮助ADC的转换。用DMA的话,可以实现多路ADC通道同时转化了。
下面就讲讲怎么借助DMA实现DAC的多路转换。还是基于我自己的规范工程。
1、工程的修改
1)由于要使用ADC功能,必须使用到库文件stm32f10x_adc.c,所以将是stm32f10x_adc.c文件添加到STM32F10x_StdPeriod_Driver工程组中。
2)这次需要使用DMA功能,所以还要讲stm32f10x_dma.c文件添加到STM32F10x_StdPeriod_Driver工程组中.
3)打开stm32f10x_conf.h文件,将原先屏蔽的:#include "stm32f10x_adc.h" 与“#include stm32f10x_dma.h”语句的屏蔽去掉。
4)新建ADConvert.c与ADConvert.h两个文件分别保存到BSP文件夹下的src与inc两个文件中。并将ADConvert.c文件添加到BSP工程组中。
 
2、ADConvert.c与ADConvert.h两个文件的编写。
首先当然是ADC对应的引脚的初始化了。我依旧选择ADC1的通道0与通道1:,他们对应的引脚分别为:PA0与PA1。引脚初始化代码如下:

/*************************************************************
Function : ADConvert_GPIO_Init
Description: ADC的引脚配置
Input : none
return : none
*************************************************************/
static void ADConvert_GPIO_Init(void)
{
GPIO_InitTypeDef GPIO_InitStructure;

RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);

GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1; //ADC通道0与通道1对应的引脚
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN; //模拟输入引脚
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
}

接下去ADC的代码:

/*************************************************************
Function : ADConvert_ADC1_Init
Description: ADC1初始化
Input : none
return : none
*************************************************************/
static void ADConvert_ADC1_Init(void)
{
ADC_InitTypeDef ADC_InitStructure;

RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE);

ADC_DeInit(ADC1);//将外设 ADC1 的全部寄存器重设为缺省值
ADC_InitStructure.ADC_Mode = ADC_Mode_Independent;//ADC工作模式:ADC1和ADC2工作在独立模式
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数据右对齐
ADC_InitStructure.ADC_NbrOfChannel = 2;//顺序进行规则转换的ADC通道的数目
ADC_Init(ADC1, &ADC_InitStructure);//根据ADC_InitStruct中指定的参数初始化外设ADCx的寄存器

//ADC1,ADC通道x,规则采样顺序值为y,采样时间为239.5周期
ADC_RegularChannelConfig(ADC1, ADC_Channel_0, 1, ADC_SampleTime_239Cycles5);
ADC_RegularChannelConfig(ADC1, ADC_Channel_1, 2, ADC_SampleTime_239Cycles5);

ADC_Cmd(ADC1, ENABLE);//打开ADC1
ADC_DMACmd(ADC1, ENABLE);//打开ADC1的DMA支持
ADC_ResetCalibration(ADC1);//开启复位校准
while(ADC_GetResetCalibrationStatus(ADC1));//等待复位校准结束
ADC_StartCalibration(ADC1);//开始AD校准
while(ADC_GetCalibrationStatus(ADC1));//等待校准结束
ADC_SoftwareStartConvCmd(ADC1, ENABLE);//连续转换开始,ADC通过DMA方式不断的更新RAM区
}

增没有DMA的代码比起来,ADC的配置多了一句:ADC_DMACmd(ADC1, ENABLE);这句话打开ADC的DMA的支持。在配置完ADC后,要开启ADC的复位校准、AD校准,还要记得打开软件起始信号:ADC_SoftwareStartConvCmd(ADC1, ENABLE)。
接下去还要配置DMA,看以查看手册可知负责ADC1的DMA是DMA1的通道1,故我们需要配置它,代码如下:

#define ADC1_DR_Address 0x4001244C //ADC1的数据寄存器地址
u16 AD_Value[2];

/*************************************************************
Function : ADConvert_DMA_Init
Description: DMA初始化
Input : none
return : none
*************************************************************/
static void ADConvert_DMA_Init(void)
{
DMA_InitTypeDef DMA_InitStructure;

RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);//使能DMA时钟

DMA_DeInit(DMA1_Channel1);//将DMA的通道1寄存器重设为缺省值
DMA_InitStructure.DMA_PeripheralBaseAddr = ADC1_DR_Address;//DMA外设ADC基地址 (u32)&ADC1->DR也可以
DMA_InitStructure.DMA_MemoryBaseAddr = (u32)&AD_Value;//DMA内存基地址
DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;//内存作为数据传输的目的地
DMA_InitStructure.DMA_BufferSize = 2;//DMA通道的DMA缓存的大小
DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;//外设地址寄存器不变
DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;//内存地址寄存器递增
DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord; //数据宽度为16位
DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord;//数据宽度为16位
DMA_InitStructure.DMA_Mode = DMA_Mode_Circular;//工作在循环缓存模式
DMA_InitStructure.DMA_Priority = DMA_Priority_High;//DMA通道 x拥有高优先级
DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;//DMA通道x没有设置为内存到内存传输
DMA_Init(DMA1_Channel1, &DMA_InitStructure);//根据DMA_InitStruct中指定的参数初始化DMA的通道
DMA_Cmd(DMA1_Channel1, ENABLE);//启动DMA通道
}

首先要说的DMA对应的ADC1的基地址为ADC1_DR_Address,宏定义它为0x4001244C,可以查看手册可知,其实他指向的是ADC1的数据寄存器,所以可以用&ADC1->DR来替代它。还要定义两个成员数为2的AD_Value[2]的数组,分别保存ADC1通道0与通道1采样转换所得到的值。但是如果想要测量地更加准准确可以多采样几次,这样的话需要定义一个更大的数组:u16 AD_Value[50][2]; 让2个通道各采样50次保存到这个数组中,然后每个通道中在对着50次求平均值。
接下去需要编写一个总函数:ADConvert_Init(),将DAC配置相关的代码都包含进去,代码如下:

/*************************************************************
Function : ADConvert_Init
Description: ADC初始化
Input : none
return : none
*************************************************************/
void ADConvert_Init(void)
{
ADConvert_GPIO_Init();
ADConvert_ADC1_Init();
ADConvert_DMA_Init();
}

下面的ADConvert.h的代码,非常简单,仅仅声明了可能会其他文件调用的:ADConvert_Init():

#ifndef __ADCONVERT_H__
#define __ADCONVERT_H__
#include "stm32f10x.h"

void ADConvert_Init(void);

#endif

3、main函数的编写
在main.c中,需要声明下刚在ADConvert.c中定义的变量AD_Value,然后在while(1)中每隔1s就转换后的值打印出来。代码如下:

extern u16 AD_Value[2];
/*************************************************************
Function : main
Description: main入口
Input : none
return : none
*************************************************************/
int main(void)
{
BSP_Init();
ADConvert_Init();
PRINTF("\nmain() is running!\r\n");
while(1)
{
LED1_Toggle();
printf("ADC1_IN0:AD=%d volt=%1.1fV\r\n", AD_Value[0], 3.3*AD_Value[0]/4096);
printf("ADC1_IN1:AD=%d volt=%1.1fV\r\n", AD_Value[1], 3.3*AD_Value[1]/4096);
Delay_ms(1000);
}

}

4、测试
我们直接将ADC1的两路通道:通道1与通道2 对应的引脚:PA0与PA1 短接,然后接到电位器可变电阻端,然后转动电位器改变电阻。在串口调试软件中可以观察对应的AD值与计算出来的电压值,如图所示:


『本文转载自网络,版权归原作者所有,如有侵权请联系删除』

热门文章 更多
MSP430F5529 上手小例程2