嵌入式程序员我爱编程

STM32多通道AD采样DMA传输的实现

2016-10-30  本文已影响781人  桂慧要努力当个攻城师

在嵌入式产品中有时候需要实现对外部的模拟量进行采样处理和记录,而这就需要使用到ADC功能,将外部的模拟量转换成数字量。而在复杂的嵌入式产品中,往往需要使用多路AD采样,例如在智能家居产品,电池电量检测,热敏温度传感器,烟雾传感器,气敏传感器等都是可以使用ADC来实现采样的。在本文章,将会介绍如何通过意法的STM32 MCU实现用DMA完成多通道的AD采样功能。

什么叫ADC

ADC即模拟数字转换器(英语:Analog-to-digital converter)是用于将模拟形式的连续信号转换为数字形式的离散信号的一类设备。一个模拟数字转换器可以提供信号用于测量。与之相对的设备成为数字模拟转换器。

影响AD采样的因素有哪些

分辨率指的是ADC的位数,例如STM32F103MCU的内部ADC的分辨率是12位,那么它所采样的结果就在0-4096之间。

根据基准电压和参考电压的不同,其值也是不同的,例如在基准电压为3.3V,参考电压最低为0V,最高为3.3V,采样分辨率位12位的嵌入式系统中,则ADC的最小量程单位则为:3.3V/4096 = 0.00080566。

在无负电压的嵌入式系统中,量程范围0-基准电压。

电源质量直接影响了AD采样的正确性和稳定性,如果条件满足,建议使用线性稳压源,若是使用开关电源的话,需要在VDDA模拟电源输入和参考电压输入接一个线性稳压管,同时要注意减小PCB板布局走线中结电容对采样电路的影响。

STM32F103 ADC主要特性

DMA简介

直接存储器存取用来提供在外设和存储器之间或者存储器和存储器之间的高速数据传输。无须CPU任何干预,通过DMA数据可以快速地移动。这就节省了CPU的资源来做其他操作。

STM32F103 DMA主要特性

如何实现多通道AD采样的DMA传输

软件流程.JPG

ADC功能引脚配置

void ADC_GPIO_Config(void)
{
    GPIO_InitTypeDef  GPIO_InitStructure;
    
    /* Enable DMA clock */
    RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);
    
    /* Enable ADC1 and GPIOC clock */
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1 | RCC_APB2Periph_GPIOA | RCC_APB2Periph_GPIOB, ENABLE);
    
    //配置模拟通道输入引脚
    GPIO_InitStructure.GPIO_Pin = ADC1_PIN;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;//配置引脚为模拟输入模式
    GPIO_Init(ADC1_GPIOX, &GPIO_InitStructure);             // 输入时不用设置速率
    
    //配置模拟通道输入引脚
    GPIO_InitStructure.GPIO_Pin = ADC2_PIN;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;//配置引脚为模拟输入模式
    GPIO_Init(ADC2_GPIOX, &GPIO_InitStructure);             // 输入时不用设置速率
    
    //配置模拟通道输入引脚
    GPIO_InitStructure.GPIO_Pin = ADC3_PIN;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;//配置引脚为模拟输入模式
    GPIO_Init(ADC3_GPIOX, &GPIO_InitStructure);             // 输入时不用设置速率

    GPIO_InitStructure.GPIO_Pin = ADC4_PIN;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;
    GPIO_Init(ADC4_GPIOX,&GPIO_InitStructure);
    
}

配置多通道ADC功能

void ADC_Multichannel_Config(void)
{
    ADC_InitTypeDef  ADC_InitStructure; 
    
    ADC_DeInit(ADC1);  //将外设 ADC1 的全部寄存器重设为缺省值
    
    /* ADC1 configuration ------------------------------------------------------*/ 
    
    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 = M; //顺序进行规则转换的ADC通道的数目
    ADC_Init(ADC1, &ADC_InitStructure); //根据ADC_InitStruct中指定的参数初始化外设ADCx的寄存器
    
    /* ADC1 regular channel11 configuration */ 
    //设置指定ADC的规则组通道,设置它们的转化顺序和采样时间
    //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_RegularChannelConfig(ADC1, ADC_Channel_4, 3, ADC_SampleTime_239Cycles5 );
    ADC_RegularChannelConfig(ADC1, ADC_Channel_8, 4, ADC_SampleTime_239Cycles5);
    // 开启ADC的DMA支持(要实现DMA功能,还需独立配置DMA通道等参数)     
    ADC_DMACmd(ADC1, ENABLE); 
    /* Enable ADC1 */    
    ADC_Cmd(ADC1, ENABLE);    //使能指定的ADC1
     /* Enable ADC1 reset calibaration register */      
    ADC_ResetCalibration(ADC1);  //复位指定的ADC1的校准寄存器 
    
    /* Enable ADC1 reset calibaration register */      
    ADC_ResetCalibration(ADC1);  //复位指定的ADC1的校准寄存器 
    /* Start ADC1 calibaration */
    ADC_StartCalibration(ADC1);  //开始指定ADC1的校准状态 
    /* Check the end of ADC1 calibration */
    while(ADC_GetCalibrationStatus(ADC1)); 
    //获取指定ADC1的校准程序,设置状态则等待
}

配置DMA通道,使能ADC转换结果从外设到内存

void DMA_Configuration(void) 
{
    /* ADC1  DMA1 Channel Config */  
    DMA_InitTypeDef DMA_InitStructure;
    RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE); //使能DMA传输
    DMA_DeInit(DMA1_Channel1);   //将DMA的通道1寄存器重设为缺省值
    DMA_InitStructure.DMA_PeripheralBaseAddr =  (u32)&ADC1->DR;  //DMA外设ADC基地址
    DMA_InitStructure.DMA_MemoryBaseAddr = (u32)&ADC_Value;  //DMA内存基地址
    DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;  //内存作为数据传输的目的地
    DMA_InitStructure.DMA_BufferSize = N*M;  //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通道
}

开始启动ADC转换功能

void ADC_Start(void)
{
    ADC_GPIO_Config();
    ADC_Multichannel_Config();
    DMA_Configuration();
    ADC_SoftwareStartConvCmd(ADC1, ENABLE); //开始ADC转换
    DMA_Cmd(DMA1_Channel1, ENABLE);  //启动DMA通道
    
}

使用冒泡排序法,对各通道的连续五次采样结果取平均值

u16 ADC1_AveragValue(u16 ADC_Value[N][M])
{
    u16 ADC1_Value[N];
    u8 i = 0;
    u8 j = 0;
    u16 temp = 0;
    u16 ADC1_Av = 0;
    for (i = 0;i < N;i++)
    {
              ADC1_Value[i] = ADC_Value[i][0];
    }
    /*冒泡排序*/
    for(i=0; i<N-1; i++)
    {
//内循环选择要进行比较的数
        for(j=0; j<N-1-i; j++)
        {
             if(ADC1_Value[j]>ADC1_Value[j+1])
             {
                    temp=ADC1_Value[j];
                    ADC1_Value[j]=ADC1_Value[j+1];
                    ADC1_Value[j+1]=temp;
             }
        }
    }
    /*去掉最大值和最小值*/
    for (i = 0; i<N-2;i++)
    {
        ADC1_Av += ADC1_Value[i+1];
        
    }
    /*取平均值*/
    ADC1_Av = ADC1_Av/4;
    return ADC1_Av;
    
}

转换采样结果

(float)ADC1_AveragValue(ADC_Value)/4096*3.3)
上一篇 下一篇

猜你喜欢

热点阅读