首页 » 智能 » 晓宇姐姐带你软硬结合感想沾染下ADC DMA采集多路电压电流的最佳姿势_通道_准时器

晓宇姐姐带你软硬结合感想沾染下ADC DMA采集多路电压电流的最佳姿势_通道_准时器

落叶飘零 2025-01-02 00:08:33 0

扫一扫用手机浏览

文章目录 [+]

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

本次我们采集3路AD旗子暗记,一起电压,一起电流,还有一个电位器,方便不雅观察数据。

晓宇姐姐带你软硬结合感想沾染下ADC DMA采集多路电压电流的最佳姿势_通道_准时器 晓宇姐姐带你软硬结合感想沾染下ADC DMA采集多路电压电流的最佳姿势_通道_准时器 智能

硬件电路剖析

图1:AD多路采集

晓宇姐姐带你软硬结合感想沾染下ADC DMA采集多路电压电流的最佳姿势_通道_准时器 晓宇姐姐带你软硬结合感想沾染下ADC DMA采集多路电压电流的最佳姿势_通道_准时器 智能
(图片来自网络侵删)

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.voltage3.351.7)/(40964.7) current = Flag.current / 4965; //Flag.motor_current((3.3/4096)/10)/0.4 pot = Flag.pot / 1241; //Flag.pot3.3/4096 //数据处理 //... }}

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

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

相关文章

C语言在PWM控制中的应用与探索

随着科技的不断发展,PWM(脉冲宽度调制)技术在工业、家用电器、通信等领域得到了广泛的应用。PWM技术通过调整脉冲信号的占空比,实...

智能 2025-01-05 阅读0 评论0

C语言在分布式系统中的应用与方法

随着互联网的飞速发展,分布式系统已经成为现代计算机技术的重要组成部分。C语言作为一种历史悠久、性能优异的编程语言,在分布式系统的开...

智能 2025-01-05 阅读0 评论0

C语言在多项式计算中的应用与讨论

多项式在数学、物理学、计算机科学等领域具有广泛的应用。在计算机科学中,多项式计算是基础且重要的一环。C语言作为一种广泛应用于系统软...

智能 2025-01-05 阅读0 评论0