前言
ADC在STM32系列单片机的使用中占用着很大的比例,常见的案例是通过ADC单次转换电压值,这种方式的缺陷在于转换效率不高。一般的单片机带有ADC1和ADC2两个ADC转换,单次转换需要执行一定的程序,想得到结果需要耗费一些时间在赋值,调用中断上面。在此基础上,为了提高转换的效率,借用单片机内部自带的DMA传输单元,可以直接越过CPU指令,将数据传送到我们所定义的寄存单元内部,这样我们需要查看检测的电压数据时,只需要直接访问存储数组即可。
一、软件设计思路
整体的软件设计思路分为两个大的环节:初始化ADC和开启高速DMA数据传输。在本次实验中,选用ADC1作为转换单元,这是比较基础的只用到一个ADC的转换方式,在ADC的使用之中,可以以ADC1和ADC2相互配合的方式进行交替采样,其优点在于两次采样中间只需要间隔极短的时间,比单次采样要快很多,在后续会进行更新,此处只用到一个ADC单元。
ADC初始化需要设置好转换的时间,转换的IO口以及转换的触发方式。而高速DMA也需要设置对应的触发方式,这里是通过ADC进行触发。在DMA传输的过程中,需要设定好DMA传输的方式以及地址,下面就针对具体的代码进行设计。
二、代码
代码如下:开头用define定义了ADC1_DR_Address,这是ADC存放检测数据的地址,DMA单元可以通过此地址访问数据,并将数据存储到我们定义的数组内部。那么后面定义了全局变量ADC_ConvertedValue,我们检测到的所有数据要存放到这个数组内部,可以看到,目前设置的ADC存放数据总共有四个,对应四个检测通道,可以根据自己的实际需求,去进行设计。具体数组的存放大小计算公式是:设置的检测通道*每个通道想要求平均的个数。比如四个通道,十个想要求平均的数据,则数组大小就设置为40,那么通过软件设置,数组[0]对应的就是第一个通道第一个数据,数组[1]对应的是第二个通道第一个数据,数组[4]对应的就是第一个通道第二个数据,依此类推。
在实际的ADC初始化中,调用ADC,DMA,GPIO三个InitTypeDef,这些都是实现定义好的,但是记得在FWlib库中调用dma和adc的官方例程库。前面初始化了DMA时钟,ADC1时钟,以及我们所定义的采样引脚,在这里定义的ADC采样引脚为PA1-PA4,可以自行修改。那么在GPIO口初始化了以后,就开始对DMA进行配置。
DMA配置过程中需要对几个参数进行注意,在这里重点说明一下:
DMA_PeripheralBaseAddr:可以理解为获取数据的地址,我们需要获取ADC的数据,则将ADC存放数据的地址放到此处。
DMA_MemoryBaseAddr:存放数据的地址,这里的地址单元由我们定义的数组决定,只需要将定义数组赋值过去即可。
DMA_BufferSize:缓冲区长度,取决于你的数组长度,有多少个数据就填多少
DMA_PeripheralDataSize:与下方内存的DataSize类似,都设置为了half word,那么half word其实等同于16位,2个byte,根据自己需求决定
其它的就正常配置就行,DMA配置完毕以后,就要对ADC进行设置。ADC设置的话是要与DMA相互配合,故初始化ADC的工作模式为独立工作,之前提到了ADC可以互相配合也可以单独工作。在只使用一个ADC的情况下,只需要设置为独立工作模式即可。需要注意的是,如果需要连续不断地采集ADC数据,就要设置ADC工作在扫描和连续转换模式中。用于转换的通道也可以进行设置,具体看通道的个数,用到了多少通道就设置参数为多少。通道配置中根据芯片本身的资源,可以从channel0-channel15(以STM32F103ZET6)为例,1,2,3,4等数代表的是转换的次序,主要是方便查看你的数据位置,避免出错。转换精度此处我们设置为239.5,这个得根据你自己的需求来,设置的精度越高转换时间越长,精度自然越好,反之亦然,这里折中设置。最后再对ADC进行一些使能即可。
adc.c
#include "adc.h"
#define ADC1_DR_Address ((u32)0x40012400+0x4c)
__IO uint16_t ADC_ConvertedValue[4];//ADC存放数据地址
void Init_adc(void)
{
ADC_InitTypeDef ADC_InitStructure;
DMA_InitTypeDef DMA_InitStructure;
GPIO_InitTypeDef GPIO_InitStructure;
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);//启动DMA时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE);//启动ADC1时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);//采样脚设置
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1 | GPIO_Pin_2 | GPIO_Pin_3 | GPIO_Pin_4;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;
GPIO_Init(GPIOA, &GPIO_InitStructure);
//DMA配置
DMA_DeInit(DMA1_Channel1);//DMA1通道1配置
DMA_InitStructure.DMA_PeripheralBaseAddr = ADC1_DR_Address;//外设地址
DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)ADC_ConvertedValue;//内存地址
DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;//dma传输方向单向
DMA_InitStructure.DMA_BufferSize = 4;//设置DMA在传输时缓冲区的长度
DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;//设置DMA的外设递增模式,一个外设
DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;//设置DMA的内存递增模式
DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord;//外设数据字长
DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord; //内存数据字长
DMA_InitStructure.DMA_Mode = DMA_Mode_Circular; //设置DMA的传输模式:连续不断的循环模式
DMA_InitStructure.DMA_Priority = DMA_Priority_High;//设置DMA的优先级别
DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;//设置DMA的2个memory中的变量互相访问
DMA_Init(DMA1_Channel1, &DMA_InitStructure);
DMA_Cmd(DMA1_Channel1, ENABLE);//使能通道1
//ADC配置
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 = 4;//用于转换的通道数
ADC_Init(ADC1, &ADC_InitStructure);
//规则模式通道配置
ADC_RegularChannelConfig(ADC1, ADC_Channel_1 , 1, ADC_SampleTime_239Cycles5);
ADC_RegularChannelConfig(ADC1, ADC_Channel_2 , 2, ADC_SampleTime_239Cycles5);
ADC_RegularChannelConfig(ADC1, ADC_Channel_3 , 3, ADC_SampleTime_239Cycles5);
ADC_RegularChannelConfig(ADC1, ADC_Channel_4 , 4, ADC_SampleTime_239Cycles5);
ADC_DMACmd(ADC1, ENABLE);//使能ADC1的DMA
ADC_Cmd(ADC1, ENABLE);//使能ADC1
ADC_ResetCalibration(ADC1);//使能ADC1复位校准寄存器
while(ADC_GetResetCalibrationStatus(ADC1));//检查校准寄存器是否复位完毕
ADC_StartCalibration(ADC1);//开始校准
while(ADC_GetCalibrationStatus(ADC1));//检测是否校准完毕
ADC_SoftwareStartConvCmd(ADC1, ENABLE);//开启ADC1的软件转换
}
adc.h
#ifndef __ADC_H
#define __ADC_H
#include "stm32f10x.h"
void Init_adc(void);
#endif /* __ADC_H */
需要调用参数只需要在定时器里面进行计数求平均即可,当初始化ADC以后,我们所定义的数组里面就会源源不断的出现数据,如前文提到的,第一个数据对应的就是第一个转换通道的,那么根据这个关系,就可以对采集到的数据进行平均化处理。文章来源:https://www.toymoban.com/news/detail-665252.html
总结
此代码还有很多可以优化的地方,比如利用到交替采样。如果用到了F4的单片机,还可以利用F4的DSP库进行一些函数的计算,比如均方根算法,进行交流电压交流电流的采样。文章来源地址https://www.toymoban.com/news/detail-665252.html
到了这里,关于基于STM32F103C8T6的高速DMA传输多通道ADC数据的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!