在我们的电子设计中,经常需要对外部的模拟量进行采集,如一些传感器的输出量,电位器的旋转量等等,特别是电压电流的采集可以说是家常便饭,这些都离不开MCU最常用的外设,ADC,它可以将模拟量转换为数字量,量化后给MCU进行处理,稍微复杂一点的产品,往往需要多路模拟量采集,今天晓宇姐姐结合实际案例,跟大家一起分享一个我经常用的方案之一,通过定时器自动触发多路ADC进行电压电流的采集,并通过DMA传送数据到内存,在需要的时候,去内存读取数据并进行处理即可,小伙伴们,搞起来吧!

本次我们采集3路AD信号,一路电压,一路电流,还有一个电位器,方便观察数据。

硬件电路分析

图1:AD多路采集

1、首先来看电压采集跟电位器(模拟传感器信号)的采集,24V的电源电压需要分压,这两个电压都足够高,所以直接送到MCU的AD引脚即可,这里记得要并一个100nF的电容,作用是存储电荷的,ADC在快速充放电的时候,这个电容可以起到补给的作用,另外也有滤波的作用,所以这个一定要加。

2、电流的采集,这里用了一个经典的差分放大电路,放大原理就不细说了,大概就是经过运放的虚短,虚断等特性,这里方便计算,一般取R4+R5=R8+R9,R6=R10,最终的传递函数位Vout=(CURR_I - GND)* R6/(R4+R5),这里的放大倍数为10倍。

一般情况下,在将运算放大器的输入端连接到放大器,使用“反相”或“非反相”输入端放大单个输入信号,而另一个输入端接地,也是可以的,只是只能放大一个电平,这里用差分电路展示,上图的反向输入可以接其它电压,有时候我们需要放大的电压两端电势没有一个接地的,比如我们在母线电压输入端串联一个小电阻,分别将电阻两边的电压送到差分放大器,就可以实现母线电流的采集了。

软件分析

这里以STM32F051来举例说明,STM32F051包含一个分辨率为12位的ADC模块,所以采样精度能达到Vref/4096,同时具有19个ADC通道,其中16个外部采样通道和3个内部信号源。

我们一般需要配置引脚,分辨率,数据对齐,触发方式,采样方式,扫描方式等等,这里有一个规则通道跟注入通道之分,注入就是可以插队的意思,有一些时序精度要求很高的场合会用,一般场合用规则通道即可。

关于通道组,这里有一个点需要注意的是,一个通道组转换完才会进入中断,并不是单个通道,又因为MCU内部只有一个ADC_DR,所以有部分同学在开始配置多通道后发现采集的数据都不对,其实我们这样记就行了,如果是只采样一个通道,分单次转换模式跟连续转换模式(重复启动ADC),如果是规则多通道的采集,我们必须要使用扫描模式,而且,这里一定要开启DMA功能,DMA会在每个通道转换完之后,自动的把结果传到内存中。

图2:DMA简易示图

关于DMA,大家应该都有过了解了,DMA控制器依赖于处理器内核,但DMA不影响总线传输,因为DMA控制器总是在系统总线空闲的时候使用总线。该总线实现处理器和DMA控制器之间最优化设计,使两者之间的冲突降到最低,因此传输性能得到提高。如上图所示,我们配置完DMA之后,每次数据采集完毕,DMA会自动的帮MCU把数据运送到我们指定的内存空间,这个搬运不依赖于CPU时钟,所以也算是实现了并行操作,相比在主程序中开启采集,我们的MCU可以有更多的时间去计算运行别的东西。

关于触发,我们可以选择手动触发一次ADC采集,也可以通过定时器的中断去触发这里强调一点,如果只是利用定时器的更新事件去触发ADC,我们也没必要开启更新中断,定时器会源源不断的产生更新事件。如果选择开启,可以在中断中执行一些操作,例如通过某些参数变化情况去改变AD采集的间隔时间。

我们采集3个通道的值,分别是通道4(VOL_AD)、通道11(CURR_O)、通道14(POT_AD)

下面上代码:

1、ADC IO 配置,配置为模拟输入

GPIO_InitTypeDef GPIO_InitStructure;
DMA_InitTypeDef DMA_InitStructure;
ADC_InitTypeDef ADC_InitStructure;
NVIC_InitTypeDef NVIC_InitStructure;
​
/*-------ADC GPIO配置---------*/
GPIO_InitStructure.GPIO_Pin  = CURR_O | POT_AD;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AN; //模拟输入
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;
GPIO_Init(GPIOC, &GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = VOL_AD;
GPIO_Init(GPIOA, &GPIO_InitStructure);

2、DMA配置,配置3个通道,所以内存中定义一个结构体存储DMA搬运过来的值

adc_sample_t adc_data;
​
DMA_InitTypeDef DMA_InitStructure;
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1,ENABLE);  //DMA时钟开启
​
/*-------DMA配置AD采集---------*/
DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)&ADC1->DR;//外设基地,DMA搬运数据的地址
DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)&adc_data;//内存基地址,DMA搬运数据放到内存的地址
DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;//外设到内存,源是外设
DMA_InitStructure.DMA_BufferSize = 3;//3个通道
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循环模式
DMA_InitStructure.DMA_Priority = DMA_Priority_Medium;//DMA优先级为中
DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;//内存到内存失能
DMA_Init(DMA1_Channel1,&DMA_InitStructure);
​
/*--------------DMA中断配置-----------------*/
NVIC_InitStructure.NVIC_IRQChannel = DMA1_Channel1_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPriority = 1;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
DMA_ClearFlag(DMA1_FLAG_TC1);//清除传送完成中断标志
DMA_ITConfig(DMA1_Channel1,DMA_IT_TC,DISABLE);//中断先不打开
DMA_Cmd(DMA1_Channel1,ENABLE);

3、ADC配置,配置各项参数

/*-------ADC配置,用于采样电流,电压,电位器---------*/
ADC_JitterCmd(ADC1,ADC_JitterOff_PCLKDiv4,ENABLE);//移除时钟为PCLKDiv4时在触发到启动转换延迟中产生的抖动
RCC_ADCCLKConfig(RCC_ADCCLK_PCLK_Div4);//ADC时钟为PLCK的4分频。也就是12MHz
ADC_InitStructure.ADC_Resolution = ADC_Resolution_12b;//ADC的位数。这里选择12位
ADC_InitStructure.ADC_ContinuousConvMode = DISABLE;//连续转换模式禁能
ADC_InitStructure.ADC_ExternalTrigConvEdge = ADC_ExternalTrigConvEdge_Falling;//触发沿为下降沿触发
ADC_InitStructure.ADC_ExternalTrigConv =  ADC_ExternalTrigConv_T1_CC4;//ADC的触发源为定时器1的第四通道
ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;//数据对齐为右对齐
ADC_InitStructure.ADC_ScanDirection = ADC_ScanDirection_Upward;//通道的扫描方向,由小到大扫描
ADC_Init(ADC1, &ADC_InitStructure);
/*-------ADC通道及采样时间配置---------*/
ADC_ChannelConfig(ADC1,Vbus_VOLTAGE_CHANNEL, ADC_SampleTime_7_5Cycles);
ADC_ChannelConfig(ADC1,Bridge_CURRENT_CHANNEL, ADC_SampleTime_7_5Cycles);
ADC_ChannelConfig(ADC1,ELE_GUN_CHANNEL, ADC_SampleTime_7_5Cycles);
/*-------使用ADC前需要先校准---------*/
ADC_GetCalibrationFactor(ADC1);
ADC_Cmd(ADC1, ENABLE);
/*-------------等待ADC准备好--------------*/
while(!ADC_GetFlagStatus(ADC1, ADC_FLAG_ADRDY));
/*-------------使能ADC的DMA传输功能--------------*/
ADC_DMACmd(ADC1,ENABLE);
/*-------------ADC的DMA模式配置--------------*/
ADC_DMARequestModeConfig(ADC1,ADC_DMAMode_Circular);
ADC_StartOfConversion(ADC1);//开始转换

4、定时器配置,这里只开启通道4的下降沿触发ADC执行一次采集,想要更改采集的时间间隔更改通道4的占空比TIM1->CCR4即可。

/*------------------结构体变量---------------------*/
GPIO_InitTypeDef GPIO_InitStructure;
TIM_TimeBaseInitTypeDef  TIM_TimeBaseStructure;
TIM_OCInitTypeDef  TIM_OCInitStructure;
NVIC_InitTypeDef NVIC_InitStructure;
/*------------定时器时钟开启---------------------*/
RCC_APB2PeriphClockCmd(RCC_APB2Periph_TIM1, ENABLE);
/*-------PWM GPIO配置---------*/
GPIO_InitStructure.GPIO_Pin   = GPIO_Pin_11;
GPIO_InitStructure.GPIO_Mode  = GPIO_Mode_AF;
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_PuPd  = GPIO_PuPd_NOPULL;
GPIO_Init(GPIOA, &GPIO_InitStructure);
/*---------PWM复用引脚---------*/
GPIO_PinAFConfig(GPIOA, GPIO_PinSource11, GPIO_AF_2);
/*-------PWM时基配置---------*/
TIM_TimeBaseStructure.TIM_Prescaler= 0;
TIM_TimeBaseStructure.TIM_CounterMode= TIM_CounterMode_Up;
TIM_TimeBaseStructure.TIM_Period= 3199;//频率为15K,TIM1_Period = (SystemCoreClock / Frequnecy) - 1
TIM_TimeBaseStructure.TIM_ClockDivision= 0;
TIM_TimeBaseStructure.TIM_RepetitionCounter= 0;
TIM_TimeBaseInit(TIM1, &TIM_TimeBaseStructure);
/*-------PWM配置---------*/
TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;//PWM模式一
TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;//输出使能,可以在通道4引脚看到占空比波形
TIM_OCInitStructure.TIM_OutputNState = TIM_OutputNState_Disable;//互补通道输出禁能
TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;//有效电平为高
TIM_OCInitStructure.TIM_OCNPolarity = TIM_OCNPolarity_High;//互补通道有效电平为高
TIM_OCInitStructure.TIM_OCIdleState = TIM_OCIdleState_Set;//空闲时输出高
TIM_OCInitStructure.TIM_OCNIdleState = TIM_OCNIdleState_Set;//互补通道空闲时输出高
​
/*---------初始化触发AD采样的时间---------*/
TIM_OCInitStructure.TIM_Pulse = 100; //占空比
TIM_OC4Init(TIM1, &TIM_OCInitStructure);
/*------------通道4触发中断使能---------------*/
TIM_ITConfig(TIM1,TIM_IT_CC4,ENABLE);
NVIC_InitStructure.NVIC_IRQChannel = TIM1_CC_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPriority = 2;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
​
/*--------初始化先关闭定时器------------*/
TIM_Cmd(TIM1,DISABLE);
/*--------使能PWM输出------------*/
TIM_CtrlPWMOutputs(TIM1,ENABLE); 

5、DMA中断,AD采集完一组数据,进入DMA中断

void DMA1_Channel1_IRQHandler(void)
{uint32_t adc_value;adc_value= adc_data.vol;Flag.voltage = adc_value;//电压值adc_value= adc_data.curr_o;Flag.current = adc_value;//电流值adc_value= adc_data.pot;Flag.pot = adc_value;//电位器值DMA_ClearFlag(DMA1_FLAG_TC1);
}

6、定时器中断,可以不加,这里展示一下

void TIM1_CC_IRQHandler(void)
{if(TIM_GetITStatus(TIM1, TIM_IT_CC4) != RESET){TIM_ClearITPendingBit(TIM1, TIM_IT_CC4);TIM1->CCR4 = 500; //这里可以更改ADC的采集间隔}
}

7、头文件

#ifndef __ADC_H
#define  __ADC_H
​
#include "stm32f0xx.h"
​
/*-----------ADC宏定义---------------*/
#define ADC_POARTC      GPIOC
#define CURR_O         GPIO_Pin_1
#define POT_AD         GPIO_Pin_4
​
#define ADC_POARTA      GPIOA
#define VOL_AD          GPIO_Pin_4
​
#define CURR_O_CHANNEL ADC_Channel_11
#define POT_CHANNEL    ADC_Channel_14
#define VOL_CHANNEL    ADC_Channel_4
​
/*---------结构体定义-----------*/
typedef struct
{uint16_t vol; uint16_t curr_o;uint16_t pot;
}adc_sample_t;
​
extern adc_sample_t adc_data;
void ADC_DMA_Init(void);
​
#endif /* __ADC_H */

8、主程序中,全部初始化并且开启后,只需要从内存中读取三个值就可以了,想要更改采样的间隔时间就更改定时器1中通道4的占空比值。

/** main.c**      Created on: 20171229  *      @Author: *      @version V1.0.0 *                        ,%%%%%%%%,*                      ,%%/\%%%%/\%%*                     ,%%%\c''''J/%%%*           %.        %%%%/ o  o \%%%*           `%%.      %%%%       |%%%*            `%%      `%%%%(__Y__)%%'*            //        ;%%%%`\-/%%%'*            ((      /   `%%%%%%%'*             \\     .'           |*              \\   /        \  | |*               \\/          ) | |*                \          /_ | |__*                (____________))))))) ¹¥³Çʨ**/float voltage, current;int main(void)
{   //一系列初始化后//..... //DMA中断使能DMA_ITConfig(DMA1_Channel1,DMA_IT_TC,ENABLE);   //定时器1使能TIM_Cmd(TIM1,ENABLE);printf("HELLO ADC\r\n");while (1){voltage = Flag.voltage / 112.84f;  // (Flag.voltage*3.3*51.7)/(4096*4.7)current = Flag.current / 4965;  //Flag.motor_current*((3.3/4096)/10)/0.4pot = Flag.pot / 1241; //Flag.pot*3.3/4096//数据处理//...}
}

到这里,我们在配置好之后,基本上就不用管了,需要的时候就读取内存中数据的值就可以了,一切基本都是自动完成的,我们的主程序可以干更重要的事情。关于数据,这里只是展示一下,计算出的都是单次采集一组的值,大家可以根据自己的实际场景进行一些算法处理,如平均值采样法、递推平均值采样法等等,这里由于篇幅问题这里就不一一展开说了,关于ADC其实还有很多学问,下次再跟大家分享!

关于电子软硬件的学习,希望大家Enjoy!码字不易,喜欢点赞转发,您的支持就是我继续创作的最佳动力!

芯片之家

芯片之家提供超过45万个Symbol和封装库免费下载,支持多种EDA格式。网站另有大量参考设计和文档资源下载,电路图和源代码资料齐全,欢迎学习交流!

晓宇姐姐带你软硬结合,感受下ADC DMA采集多路电压电流相关推荐

  1. 晓宇姐姐带你逛遍AWE2021,15万平米的展馆到底发布了哪些黑科技?

    作者:晓宇,排版:晓宇 微信公众号:芯片之家(ID:chiphome-dy) 中国家电及消费电子博览会(Appliance&electronics World Expo,简称AWE)由中国家用 ...

  2. 晓宇哥新年力作!AI离线语音开关控制器

    作者:晓宇,整理:晓宇 微信公众号:芯片之家(ID:chiphome-dy) 曾经很长一段时间,晓宇哥在这个青春而骚动的城市,舍不得在这个安静的夜晚睡去,睡前总是习惯看看公众号,看看头条,知乎等等,尽 ...

  3. Stratifyd汪晓宇:当公司目标都指向客户,团队管理其实很简单 | 舵舟

    姓名:汪晓宇 身份:Stratifyd(斯图飞腾)创始人 出生日期:1983年 融资轮次及估值:B轮 团队规模:100-150 行业: 大数据&人工智能 导语: 舵舟,专注于给成长性企业提供组 ...

  4. 汪晓宇:如何破除中美大数据产业差距

    文章讲的是汪晓宇:如何破除中美大数据产业差距,目前,大数据正掀起一场革命,"数据就是生产力"这个说法也正从"预言"变成现实. 无论是哪一个行业,都再也没有办法躲 ...

  5. Talk | 清华大学陈晓宇苏黎世联邦理工黄嘉伟 :基于实际应用的强化学习

    本期为TechBeat人工智能社区第455期线上Talk! 北京时间11月17日(周四)20:00,清华大学交叉信息研究院在读博士生--陈晓宇与苏黎世联邦理工大学计算机科学在读博士生--黄嘉伟的Tal ...

  6. 老宇哥带你玩转 ESP32:04 串口玩起来是真方便

    今天我们来玩儿串口. 概述 ESP32 芯片有3 个 UART 接口,UART0,UART1,UART2,支持异步通信和 IrDA,通信速度最高可达 5Mbps,3 个接口可以被 DMA 或 CPU ...

  7. 深读5G发展的趋势后带给我的感受

    深读5G发展的趋势带给我的感受 重要合作 现在无人驾驶市场愈演愈烈,5G在期中发挥的作用也是至关重要的,无人驾驶汽车前端与后端的服务器需要5G带来的数据传输速度和低延时.5G如今面临最大的问题在于基站 ...

  8. Stratifyd创始人汪晓宇:打造AI数据分析生态 | 亿欧专访

    人工智能发展至今已历经三次浪潮,身处第三次浪潮的今天,人们已经感知到AI对于生活方方面面的影响. 智能家居.智能机器人.指纹识别.人脸识别等等,都是AI在日常生活中的应用体现.人工智能正以不可逆转的迅 ...

  9. 喜报 | 40 under 40 杰出人才榜,Stratifyd 创始人兼CEO汪晓宇博士登榜

    3 月 29 日,Charlotte Business Journal 2019 年度 40 名 40 岁以下杰出人才榜单(CBJ 40 under 40)公布,Stratifyd 创始人兼 CEO ...

最新文章

  1. Bullsh*t,System. currentTimeMillis大胆用起来,我说的!
  2. 掌握 ASP.NET 之路:自定义实体类简介
  3. 了解 Windows Azure 存储的可伸缩性、可用性、持久性和计费
  4. css 百分比 怎么固定正方形_你未必知道的49个CSS知识点
  5. 这些黑科技让百姓安心、安全过年
  6. JEECG-P3首个开源插件诞生!CMS网站插件 Jeecg-p3-biz-cms1.0版本发布!
  7. 图像模糊处理(信息学奥赛一本通-T1128)
  8. 快速从入门到精通!mysql字符串截取前两位
  9. 在WebBrowser中通过模拟键盘鼠标操控网页中的文件上传控件
  10. 磁盘管理 之 parted命令添加swap,文件系统
  11. 设计模式 笔记 享元模式 Flyweight
  12. 【Android】局域网屏幕共享与反向控制功能的实现
  13. 发票专用驱动sjz_增值税发票选择确认平台使用手册
  14. 游戏测试永乐大典——测试成就感
  15. 淘宝首页中meta标签的作用整理
  16. 音频特征----频谱图
  17. Excel下拉列表式的动态图表,你会制作吗?
  18. 好用的Chrome插件大全网站:插件小屋
  19. latex怎样让题号顶格_笔记本-latex写作问题集锦
  20. 如何更改 Win7 网络连接显示名称

热门文章

  1. WeChall CTF Writeup(七)
  2. PHP接口interface详解
  3. 一图说明APQP的过程和五大质量管理工具的关系
  4. 小区IPTV 养老院IPTV电视系统解决方案
  5. 卷不动了?300 秒快速了解 Java 9 - 16 新特性,助你脱离内卷
  6. idea配置 Tomcat Deployment添加时没有Artifact的完美解决方式!较全面
  7. 简述数学建模的过程_数学建模入门知识分享(一):什么是数模
  8. 电脑手机模拟器模拟手机浏览器,在线浏览手机网站
  9. ResNet50模型识别二维化的心电信号——以MIT-BIH心律失常数据库为例
  10. @Pointcut 配置用法浅析