STM32F103采用DMA方式多路ADC采样
前言
stm32采用DMA方式进行ADC采样可以高效的进行数据采集,不用cpu实时参与,以节省单片机资源,让单片机可以在同一时间里干更多事,STM32F103 ADC为12位ADC的,是一种逐次逼近型模拟数字转换器,它有多达18个通道,可测量16个外部和2个内部信号源。各通道的A/D转换可以单次、连续、扫描或间断模式执行。
stm32 ADC供电要求:2.4V到3.6V ,ADC输入电压范围为:VREF- ≤ VIN ≤ VREF+
规则通道转换期间有DMA请求产生。
一、头文件 adc.h
#ifndef __ADC_H
#define __ADC_H
#include "sys.h"
#define FIRL_N 16
#define AD_NUM 8
/*******************************************************************************/
/* ADC 8路输入 */
/*******************************************************************************/
#define ADC_CH0_RCC_APB2Periph_GPIO RCC_APB2Periph_GPIOA
#define ADC_CH0_GPIO GPIOA //PA0
#define ADC_CH0_PIN GPIO_Pin_0
#define ADC_CH0_CHNL ADC_Channel_0
#define ADC_CH1_RCC_APB2Periph_GPIO RCC_APB2Periph_GPIOA
#define ADC_CH1_GPIO GPIOA //PA1
#define ADC_CH1_PIN GPIO_Pin_1
#define ADC_CH1_CHNL ADC_Channel_1
#define ADC_CH2_RCC_APB2Periph_GPIO RCC_APB2Periph_GPIOA
#define ADC_CH2_GPIO GPIOA //PA2
#define ADC_CH2_PIN GPIO_Pin_2
#define ADC_CH2_CHNL ADC_Channel_2
#define ADC_CH3_RCC_APB2Periph_GPIO RCC_APB2Periph_GPIOA
#define ADC_CH3_GPIO GPIOA //PA3
#define ADC_CH3_PIN
#define ADC_CH3_CHNL ADC_Channel_3
#define ADC_CH4_RCC_APB2Periph_GPIO RCC_APB2Periph_GPIOA
#define ADC_CH4_GPIO GPIOA //PA4
#define ADC_CH4_PIN GPIO_Pin_4
#define ADC_CH4_CHNL ADC_Channel_4
#define ADC_CH5_RCC_APB2Periph_GPIO RCC_APB2Periph_GPIOA
#define ADC_CH5_GPIO GPIOA //PA5
#define ADC_CH5_PIN GPIO_Pin_5
#define ADC_CH5_CHNL ADC_Channel_5
#define ADC_CH6_RCC_APB2Periph_GPIO RCC_APB2Periph_GPIOA
#define ADC_CH6_GPIO GPIOA //PA6
#define ADC_CH6_PIN GPIO_Pin_6
#define ADC_CH6_CHNL ADC_Channel_6
#define ADC_CH7_RCC_APB2Periph_GPIO RCC_APB2Periph_GPIOA
#define ADC_CH7_GPIO GPIOA //PA7
#define ADC_CH7_PIN GPIO_Pin_7
#define ADC_CH7_CHNL ADC_Channel_7
二、初始化配置
1.ADC GPIO配置
/*******************************************************************************/
/* Function 'ADC1_GPIO_Configuration': ADC1_GPIO配置 */
/*******************************************************************************/
void ADC1_GPIO_Configuration(void) //ADC1_GPIO配置
{
//内部温度对应 16通道,无引脚,只需开启adc时钟即可。
//内部参考电压,对应17 通道。无引脚。只需开启时钟
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); //使能PA端口时钟
GPIO_InitStructure.GPIO_Pin = ADC_CH0_PIN;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;
GPIO_Init(ADC_CH0_GPIO, &GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = ADC_CH1_PIN;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;
GPIO_Init(ADC_CH1_GPIO, &GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = ADC_CH2_PIN;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;
GPIO_Init(ADC_CH2_GPIO, &GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = ADC_CH3_PIN;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;
GPIO_Init(ADC_CH3_GPIO, &GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = ADC_CH4_PIN;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;
GPIO_Init(ADC_CH4_GPIO, &GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = ADC_CH5_PIN;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;
GPIO_Init(ADC_CH5_GPIO, &GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = ADC_CH6_PIN;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;
GPIO_Init(ADC_CH6_GPIO, &GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = ADC_CH7_PIN;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;
GPIO_Init(ADC_CH7_GPIO, &GPIO_InitStructure);
}
2.开启ADC和DMA时钟
/*******************************************************************************/
/* Function 'ADC1_DMA_RCC_Configuration': 外设ADC,DMA时钟开启 */
/*******************************************************************************/
void ADC1_DMA_RCC_Configuration(void) //
{
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1
| ADC_CH0_RCC_APB2Periph_GPIO
| ADC_CH1_RCC_APB2Periph_GPIO
| ADC_CH2_RCC_APB2Periph_GPIO
| ADC_CH3_RCC_APB2Periph_GPIO
| ADC_CH4_RCC_APB2Periph_GPIO
| ADC_CH5_RCC_APB2Periph_GPIO
| ADC_CH6_RCC_APB2Periph_GPIO
| ADC_CH7_RCC_APB2Periph_GPIO , ENABLE);
}
3.多路ADC DMA采样配置
__IO uint16_t ADValFilter[AD_NUM];
/*******************************************************************************/
/* Function 'ADC1_DMA_Init': ADC1 DMA 配置 */
/*******************************************************************************/
#define ADC1_DR_Address ((uint32_t)0x4001244C) //ADC_DR(ADC规则数据寄存器),偏移量=0x4c ADC1(0x40012400-0x400127ff)
//so ADC1_DR_Address=0x40012400+0x4c
u16 ADValue[FIRL_N][AD_NUM];
void ADC1_DMA_Init(void)
{
ADC_InitTypeDef ADC_InitStructure;
DMA_InitTypeDef DMA_InitStructure;
DMA_DeInit(DMA1_Channel1); //选择DMA的通道1
//设定从ADC外设的数据寄存器(ADC1_DR_Address)转移到内存(ADCConcertedValue)
//每次传输大小16位,使用DMA循环传输模式
DMA_InitStructure.DMA_PeripheralBaseAddr = ADC1_DR_Address;
DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)ADValue; //数据缓冲区的地址
DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC; //外设为数据源
DMA_InitStructure.DMA_BufferSize = FIRL_N*AD_NUM; //数据缓冲区,大小2半字
DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable; // 外设地址固定
DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable; //内存地址增加,多组adc时,使能,数据传输时,内存增加
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_InitStructure.DMA_M2M = DMA_M2M_Disable;
DMA_Init(DMA1_Channel1, &DMA_InitStructure);
DMA_Cmd(DMA1_Channel1, ENABLE);
/* ADC1 初始化*/
ADC_InitStructure.ADC_Mode = ADC_Mode_Independent; //ADC独立模式 相对于双重模式
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_NUM; //转换组的通道数目
ADC_Init(ADC1, &ADC_InitStructure);
RCC_ADCCLKConfig(RCC_PCLK2_Div8);
/* ADC1 regular channel11 configuration */
//配置ADC1的通道11为55.5个采样周期
//默认组,adc1 ,通道11,排序为1,55.5周期
//ADC1,ch17,序号1,55.5.。。
ADC_RegularChannelConfig(ADC1, ADC_CH0_CHNL,1, ADC_SampleTime_239Cycles5);
ADC_RegularChannelConfig(ADC1, ADC_CH1_CHNL,2, ADC_SampleTime_239Cycles5);
ADC_RegularChannelConfig(ADC1, ADC_CH2_CHNL,3, ADC_SampleTime_239Cycles5);
ADC_RegularChannelConfig(ADC1, ADC_CH3_CHNL,4, ADC_SampleTime_239Cycles5);
ADC_RegularChannelConfig(ADC1, ADC_CH4_CHNL,5, ADC_SampleTime_239Cycles5);
ADC_RegularChannelConfig(ADC1, ADC_CH5_CHNL,6, ADC_SampleTime_239Cycles5);
ADC_RegularChannelConfig(ADC1, ADC_CH6_CHNL,7, ADC_SampleTime_239Cycles5);
ADC_RegularChannelConfig(ADC1, ADC_CH7_CHNL,8, ADC_SampleTime_239Cycles5);
// ADC_RegularChannelConfig(ADC1, ADC_Channel_17,10, ADC_SampleTime_239Cycles5);//内部参考电压
// ADC_TempSensorVrefintCmd(ENABLE);// 使能芯片内部温度传感器,若不需要可以注释掉
ADC_DMACmd(ADC1, ENABLE);//使能ADC_DMA
ADC_Cmd(ADC1, ENABLE); //使能ADC
ADC_ResetCalibration(ADC1); //使能ADC1的复位校准寄存器
while(ADC_GetResetCalibrationStatus(ADC1)); //等待校准完成
ADC_StartCalibration(ADC1); //使能ADC1的开始校准寄存器
while(ADC_GetCalibrationStatus(ADC1)); //等待完成
ADC_SoftwareStartConvCmd(ADC1, ENABLE); //使用软件触发,由于没有采用外部触发
}
三、软件滤波
采用了16次平均值滤波,除去最小值
void Get_ADConv(u16* value)
{
u8 i,chnl;
for (i=0;i<AD_NUM;i++)
{
ADValFilter[i]=0;
}
for(chnl=0; chnl<AD_NUM; chnl++)
{
for(i=0;i<FIRL_N;i++)
{
ADValFilter[chnl]+=ADValue[i][chnl];
}
ADValFilter[chnl]>>=4;
if(ADValFilter[chnl]<20)ADValFilter[chnl] = 0;
// ADValFilter[chnl] -= 3;
*value++ = ADValFilter[chnl];
}
}
四、主函数调用
1.初始化函数配置
/*******************************************************************************/
/* Function 'App_MutiAdcCfg': 对ADC进行初始化配置,并实现DMA方式 */
/*******************************************************************************/
void App_MutiAdcCfg(void)
{
ADC1_GPIO_Configuration();
ADC1_DMA_RCC_Configuration();
ADC1_DMA_Init();
}
2.main函数调用
主程序将采样出来的多路通道ADC采样值 04096转换成010000 ,硬件上对应0~10v输入电压文章来源:https://www.toymoban.com/news/detail-462590.html
#define AD_SCALE 2.441f
__IO float f_adval[8];
int main(void)
{
u8 i,ad_chnl = 0;
u16 ADvolt[AD_NUM];
uint16_t nCRC = 0;
uint16_t SendIndex = 0;
delay_init(); //延时函数初始化
uart_init(BaudRate); //串口初始化为9600
App_MutiAdcCfg(); //ADC初始化
while(1)
{
Get_ADConv(ADvolt);
for (ad_chnl=0; ad_chnl<AD_NUM; ad_chnl++)
{
f_adval[ad_chnl] = AD_SCALE * ADvolt[ad_chnl];
sHoldRegValue[ad_chnl] = (u16)f_adval[ad_chnl];
}
nSendBuff[0] = 0x01;
nSendBuff[1] = 0x03;
nSendBuff[2] = 0x10;
for (i=0; i<8; i++)
{
nSendBuff[3+i*2] = sHoldRegValue[i]>>8;
nSendBuff[4+i*2] = sHoldRegValue[i]&0xff;
}
SendIndex = 19;
nCRC = CRC16(nSendBuff,SendIndex);
nSendBuff[SendIndex++] = (uint8_t)((nCRC>>8)&0x00ff);
nSendBuff[SendIndex++] = (uint8_t)(nCRC&0x00ff);
UART_Write(USART1,nSendBuff,19);
delay_ms(500);
}
总结
项目应用中不建议adc用查询方式进行读取,用DMA结合adc采样,然后用定时器控制采样频率,不建议用延时的方式,由定时器触发ADC采样,这样采样的频率可控,且定时器触发不会占用任何CPU资源。文章来源地址https://www.toymoban.com/news/detail-462590.html
到了这里,关于STM32F103采用DMA方式多路ADC采样的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!