晓宇姐姐带你软硬结合,感受下ADC DMA采集多路电压电流的最佳姿势
芯片之家
共 10023字,需浏览 21分钟
·
2021-10-27 01:07
硬件电路分析
图1:AD多路采集
软件分析
这里以STM32F051来举例说明,STM32F051包含一个分辨率为12位的ADC模块,所以采样精度能达到Vref/4096,同时具有19个ADC通道,其中16个外部采样通道和3个内部信号源。
我们一般需要配置引脚,分辨率,数据对齐,触发方式,采样方式,扫描方式等等,这里有一个规则通道跟注入通道之分,注入就是可以插队的意思,有一些时序精度要求很高的场合会用,一般场合用规则通道即可。
关于通道组,这里有一个点需要注意的是,一个通道组转换完才会进入中断,并不是单个通道,又因为MCU内部只有一个ADC_DR,所以有部分同学在开始配置多通道后发现采集的数据都不对,其实我们这样记就行了,如果是只采样一个通道,分单次转换模式跟连续转换模式(重复启动ADC),如果是规则多通道的采集,我们必须要使用扫描模式,而且,这里一定要开启DMA功能,DMA会在每个通道转换完之后,自动的把结果传到内存中。
图2:DMA简易示图
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);
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);
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);
}
void TIM1_CC_IRQHandler(void)
{
if(TIM_GetITStatus(TIM1, TIM_IT_CC4) != RESET)
{
TIM_ClearITPendingBit(TIM1, TIM_IT_CC4);
TIM1->CCR4 = 500; //这里可以更改ADC的采集间隔
}
}
/*-----------ADC宏定义---------------*/
/*---------结构体定义-----------*/
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);
/*
* 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.4
pot = Flag.pot / 1241; //Flag.pot*3.3/4096
//数据处理
//...
}
}
关于电子软硬件的学习,希望大家Enjoy!码字不易,喜欢点赞转发,您的支持就是我继续创作的最佳动力!
灰常实用的一键开关机电路,各位大佬进来mark一下?
老宇哥手把手教你分析过压保护电路设计,你GET到精髓了吗?
硬件产品研发,除了电子元器件成本,还有什么成本?
真实案例,现场的MOS管大面积烧毁,百思不得其姐,求大神们分析原因
评论