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

在嵌入式产品中有时候需要实现对外部的模拟量进行采样处理和记录,而这就需要使用到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主要特性

  • 12-位分辨率
  • 转换结束,注入转换结束和发生模拟看门狗事件时产生中断
  • 单次和连续转换模式
  • 从通道0到通道n的自动扫描模式
  • 自校准
  • 带内嵌数据一致的数据对齐
  • 通道之间采样间隔可编程
  • 规则转换和注入转换均有外部触发选项
  • 间断模式
  • 双重模式(带2个或以上ADC的器件)
  • ADC转换时间:─ STM32F103xx 增强型产品: ADC 时钟为 56MHz 时为 1μs(ADC 时钟为 72MHz 为 1.17μs)─ STM32F101xx 基本型产品: ADC 时钟为 28MHz 时为 1μs(ADC 时钟为 36MHz 为 1.55μs)─ STM32F102xxUSB 型产品: ADC 时钟为 48MHz 时为 1.2μs
  • ADC供电要求: 2.4V到3.6V
  • ADC输入范围: VREF- ≤ VIN ≤ VREF+● 规则通道转换期间有DMA请求产生。

DMA简介

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

STM32F103 DMA主要特性

  • 12个 独立的可配置的通道(请求)DMA1有7个通道, DMA2有5个通道
  • 每个通道都直接连接专用的硬件DMA请求,每个通道都同样支持软件触发。这些功能通过软件来配置。
  • 在七个请求间的优先权可以通过软件编程设置(共有四级:很高、高、中等和低),假如在相等优先权时由硬件决定(请求0优先于请求1,依此类推) 。
  • 独立的源和目标数据区的传输宽度(字节、半字、全字),模拟打包和拆包的过程。源和目标地址必须按数据传输宽度对齐。
  • 支持循环的缓冲器管理
  • 每个通道都有3个事件标志(DMA 半传输, DMA传输完成和DMA传输出错),这3个事件标志逻辑或成为一个单独的中断请求。
  • 存储器和存储器间的传输
  • 外设和存储器,存储器和外设的传输
  • 闪存、 SRAM、外设的SRAM、 APB1 APB2和AHB外设均可作为访问的源和目标。
  • 可编程的数据传输数目:最大为65536

如何实现多通道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)
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,311评论 6 481
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,339评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 152,671评论 0 342
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,252评论 1 279
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,253评论 5 371
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,031评论 1 285
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,340评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,973评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,466评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,937评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,039评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,701评论 4 323
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,254评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,259评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,485评论 1 262
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,497评论 2 354
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,786评论 2 345

推荐阅读更多精彩内容