基于STM32F103C8T6四路AD采集数据显示在oled屏上DMA传输方式(附百度网盘下载链接)

本文采用四路AD采集光照强度、烟雾浓度、一氧化碳、空气质量等四个物理量,并采用中位值平均滤波(防脉冲干扰平均滤波法)算法对偶然出现的脉冲性干扰,消除由其引起的采样值偏差。

ADC在另外一篇基于STM32F103C8T6四路AD采集数据显示在oled屏上非DMA传输方式已经介绍过啦,在这里就不再介绍了,主要说明DMA传输方式。

  1. DMA的作用

DMA,直接存储器存取,是一种可以大大减轻CPU工作量的存取方式。当CPU需要处理由ADC外设采集回来的数据时,CPU首先要把数据从ADC外设的寄存器读取到内存中(变量),然后进行计算处理,这是一般的方法。但是在转移数据的过程中会占用CPU的资源,而我们希望CPU更多地用于数据计算或者响应中断,因此,DMA正是为CPU分担数据处理工作,CPU可以在DMA转移数据时同时进行数据处理或响应中断。

void ADC1_Init(void)
{
    GPIO_InitTypeDef GPIO_InitStruct;
    ADC_InitTypeDef ADC_InitStruct;
    DMA_InitTypeDef  DMA_InitStructure;
    
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA|RCC_APB2Periph_ADC1,ENABLE);
    RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);//DMA1时钟使能 
    
    RCC_ADCCLKConfig(RCC_PCLK2_Div6);
    GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AIN;//模拟输入
    GPIO_InitStruct.GPIO_Pin = GPIO_Pin_4|GPIO_Pin_5|GPIO_Pin_6|GPIO_Pin_7;
    GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(GPIOA, &GPIO_InitStruct);
    
    ADC_RegularChannelConfig(ADC1, ADC_Channel_4, 1, ADC_SampleTime_55Cycles5);
    ADC_RegularChannelConfig(ADC1, ADC_Channel_5, 2, ADC_SampleTime_55Cycles5);
    ADC_RegularChannelConfig(ADC1, ADC_Channel_6, 3, ADC_SampleTime_55Cycles5);
    ADC_RegularChannelConfig(ADC1, ADC_Channel_7, 4, ADC_SampleTime_55Cycles5);
    
    ADC_InitStruct.ADC_Mode = ADC_Mode_Independent;//独立工作模式
    ADC_InitStruct.ADC_ScanConvMode = ENABLE;  //扫描模式    
    ADC_InitStruct.ADC_ContinuousConvMode =ENABLE;//连续转换
    //不使用外部触发,使用内部软件触发
    ADC_InitStruct.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;
    ADC_InitStruct.ADC_DataAlign = ADC_DataAlign_Right;//右对齐    
    ADC_InitStruct.ADC_NbrOfChannel =4;//需要扫描的通道数
    ADC_Init(ADC1, &ADC_InitStruct);//ADC初始化
    
  DMA_InitStructure.DMA_PeripheralBaseAddr = ADC1_DR_Address;
  DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
  DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord;
    DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)ADC_Value;//存储器地址
    DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;//存储器增量模式
    
  DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord;
    DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;//外设到储存器(数据传输方向)
    DMA_InitStructure.DMA_BufferSize = ADC_Value_Size;//数据传输量 
    DMA_InitStructure.DMA_Mode = DMA_Mode_Circular;// 循环扫描
    DMA_InitStructure.DMA_Priority = DMA_Priority_Medium;//中等优先级
    DMA_InitStructure.DMA_M2M = DMA_M2M_Disable; 
    DMA_Init(DMA1_Channel1, &DMA_InitStructure);
    
    DMA_Cmd(DMA1_Channel1,ENABLE);
    
    ADC_DMACmd(ADC1,ENABLE);

    ADC_Cmd(ADC1, ENABLE);
    
    ADC_ResetCalibration(ADC1);//开始复位校准
    while(ADC_GetResetCalibrationStatus(ADC1) == SET);//获取ADC重置校准寄存器的状态
    ADC_StartCalibration(ADC1);//开始指定ADC的校准状态
    while(ADC_GetCalibrationStatus(ADC1));//等待AD校准结束
  ADC_SoftwareStartConvCmd(ADC1, ENABLE);
}
  1. ADC参数

ADC_DataAlign:用来设置 ADC 数据对齐方式是左对齐还是右对齐,ADC转换后的数据被保存到数据寄存器中(ADC_DR)的0~15位或16~32位,数据宽度为16位,ADC转换精度12位。左对齐则是把ADC转换后的数值最高位D12与存储区域的最高位Bit15对齐,存储区域的低4位无意义。右对齐则是ADC转换的数值最低位D0保存在存储区域的最低位Bit0,高四位无意义

ADC_Mode:ADC的模式非常多,包括独立模式,注入同步模式等等,而不同的ADC是共用通道的,当两个ADC采集同一个通道的先后顺序、时间间隔不同时,就会有多种模式。我们只使用了一个ADC,所以选择独立模式,参数为 ADC_Mode_Independent。

ADC_ScanConvMode:用来设置是否开启扫描模式,当有多个通道需要采集信号时,可以把ADC配置为按一定的顺序轮流采集各通道的值。如果采集多个通道,必须开启此模式。这里我们是采集多个通道的信号,这里我们选择开启 : ENABLE

ADC_ContinuousConvMode: 用来设置是否开启连续转换模式,因为是连续转换,所以 我们选择开启连续转换模式 : ENABLE。

ADC_ExternalTrigConv:用来设置启动规则转换组转换的外部事件 ,ADC在收到触发信号后才开始进行模数转换

ADC_NbrOfChannel:保存要进行ADC数据转换的通道数,可以为1 ~ 16个。这里我们是多次采集4个通道,所以值为 4即可

  1. DMA参数

DMA_PeripheralBaseAddr:用来设置 DMA 传输的外设基地址 ,因为我们是ADC1传输,这里设置为(uint32_t)&ADC1->DR.

DMA_PeripheralInc:设置传输数据的时候外设地址是不变还是递增。如果设置 为递增,那么下一次传输的时候地址加 1,这里因为我们是一直往固定外设地址&ADC1->DR 发 送数据,所以地址不递增,值为 DMA_PeripheralInc_Disable DMA_PeripheralDataSize:用来设置外设的数据长度是为字节传输(8bits),半 字传输 (16bits) 还 是 字 传 输 (32bits) , 这 里 我 们 是 半字传 输 , 所 以 值设置为DMA_PeripheralDataSize_HalfWord。

DMA_MemoryBaseAddr:内存基地址,也就是我们存放 DMA 传输数据的内存地址。这里我们定义一个数组来存储DMA传输的数据。

DMA_MemoryInc: 设置传输数据时候内存地址是否递增 。这里我们是用数组来存储数据,毫无疑问内存地址是需要递增的,所以值为 DMA_MemoryInc_Enable。 DMA_MemoryDataSize:是用来设置内存的数据长度,同样设置为半字传输

DMA_DIR:设置数据传输方向,是从外设读取数据到内存还是从内存读取数 据发送到外设,也就是外设是源目地还是目的地,这里我们设置为外设读取数据到内存, 所以外设自然就是源目地了。 DMA_BufferSize:设置一次传输数据量的大小 DMA_Mode:用来设置 DMA 模式是否循环采集,也就是说,比如我们要从中采 集 1个字节发送到串口,如果设置为重复采集,那么它会在1个字节采集完成之后继续从内 存的第一个地址采集,如此循环 。

DMA_Priority:是设置 DMA 通道的优先级,有低,中,高,超高三种模式,如果要开启多个通道 , DMA_Priority_Medium,这个值就非常有意义

DMA_M2M:设 置 是 否 是 存 储 器 到 存 储 器 模 式 传 输 。我们是外设到存储器,所以我们选 择 DMA_M2M_Disable

  1. 中位值平均滤波(防脉冲干扰平均滤波法)

方法:“中位值滤波法"+“算术平均滤波法”

连续采样N个数据,去掉一个最大值和一个最小值,然后计算N-2个数据的算术平均值,N值的选取:3~14

优点:融合了两种滤波的优点。对于偶然出现的脉冲性干扰,可消除有其引起的采样值偏差。对周期干扰有良好的抑制作用,平滑度高,适于高频振荡的系统。

缺点:测量速度慢。

这里采用了一个二维数组来分别存储四个AD通道的采样值,然后分别进行计算,具体代码如下:

void middleAverageFilter(void)    //中位值平均滤波(防脉冲干扰平均滤波法)
{
    u16 i,j,k,g;
    u16 temp;
      u16 Sum_Value[ADC_Value_Size] = {0};
    u16 value_buf[ADC_Value_Size][ADC_Value_Number] = {0};
        
        for(g = 0 ; g < ADC_Value_Size ; g++)
        {
            for(i = 0; i < ADC_Value_Number; i++)
            {
                    value_buf[g][i] = ADC_Value[g];

            }
        }

    /*从小到大冒泡排序*/    
        for(g = 0 ; g < ADC_Value_Size ; g++)
        {
            for(j = 0; j < ADC_Value_Number-1; j++)
            {
                    for(k = 0; k < ADC_Value_Number-j-1; k++)
                    {
                            if(value_buf[k] > value_buf[k+1])
                            {
                                    temp = value_buf[g][k];
                                    value_buf[g][k] = value_buf[g][k+1];
                                    value_buf[g][k+1] = temp;
                                
                            }
                    }
            }
      }
        for(g = 0 ; g < ADC_Value_Size ; g++)
        {
            for(i = 1; i < ADC_Value_Number-1; i++)
            {
                    Sum_Value[g] += value_buf[g][i];
            } 
      }
    Light_value =4096 - Sum_Value[0]/(ADC_Value_Number-2);
        m7_value=Sum_Value[1]/(ADC_Value_Number-2);
        m135_value =4096 - Sum_Value[2]/(ADC_Value_Number-2);
        m2_value = Sum_Value[3]/(ADC_Value_Number-2);            
}
  1. 传感器

本实验用到了 MQ-2 烟雾传感器、MQ-7一氧化碳传感器、MQ-135空气质量传感器和光敏电阻传感器。传感器这里就不再介绍啦,大家有传感器就应该有资料的,直接连在对应的引脚就可以用啦。

  1. 实验现象

  1. 下载链接

链接:https://pan.baidu.com/s/1mSeRNDlMI7LjZT-7dlyOQQ

提取码:1zi4

物联沃分享整理
物联沃-IOTWORD物联网 » 基于STM32F103C8T6四路AD采集数据显示在oled屏上DMA传输方式(附百度网盘下载链接)

发表评论