STM32F103采用DMA方式多路ADC采样

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输入电压

#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资源。

  • 7
    点赞
  • 41
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

章鱼哥嵌入式开发

坚持不易,你们的鼓励是我的动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值