页面加载中,请稍候
来源:芯片之家发布时间:2021-10-251826浏览
询问 AI
作者:晓宇,排版:晓宇
本次我们采集3路AD信号,一路电压,一路电流,还有一个电位器,方便观察数据。硬件电路分析
这里以STM32F051来举例说明,STM32F051包含一个分辨率为12位的ADC模块,所以采样精度能达到Vref/4096,同时具有19个ADC通道,其中16个外部采样通道和3个内部信号源。
我们一般需要配置引脚,分辨率,数据对齐,触发方式,采样方式,扫描方式等等,这里有一个规则通道跟注入通道之分,注入就是可以插队的意思,有一些时序精度要求很高的场合会用,一般场合用规则通道即可。

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

GPIO_InitTypeDefGPIO_InitStructure;DMA_InitTypeDefDMA_InitStructure;ADC_InitTypeDefADC_InitStructure; NVIC_InitTypeDefNVIC_InitStructure;2、DMA配置,配置3个通道,所以内存中定义一个结构体存储DMA搬运过来的值
*-------ADCGPIO配置---------*/GPIO_InitStructure.GPIO_Pin = CURR_O | POT_AD;GPIO_InitStructure.GPIO_ModeGPIO_Mode_AN; //模拟输入GPIO_InitStructure.GPIO_PuPdGPIO_PuPd_NOPULL;GPIO_Init(GPIOC, &GPIO_InitStructure); GPIO_InitStructure.GPIO_PinVOL_AD;GPIO_Init(GPIOA, &GPIO_InitStructure);
adc_sample_tadc_data;
DMA_InitTypeDefDMA_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_PeripheralDataSizeDMA_PeripheralDataSize_HalfWord;//外设数据16位DMA_InitStructure.DMA_MemoryDataSizeDMA_MemoryDataSize_HalfWord;//内存数据16位DMA_InitStructure.DMA_ModeDMA_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_IRQChannelDMA1_Channel1_IRQn;NVIC_InitStructure.NVIC_IRQChannelPriority1;NVIC_InitStructure.NVIC_IRQChannelCmdENABLE;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分频。也就是12MHzADC_InitStructure.ADC_Resolution = ADC_Resolution_12b;//ADC的位数。这里选择12位ADC_InitStructure.ADC_ContinuousConvModeDISABLE;//连续转换模式禁能ADC_InitStructure.ADC_ExternalTrigConvEdgeADC_ExternalTrigConvEdge_Falling;//触发沿为下降沿触发 ADC_InitStructure.ADC_ExternalTrigConvADC_ExternalTrigConv_T1_CC4;//ADC的触发源为定时器1的第四通道ADC_InitStructure.ADC_DataAlignADC_DataAlign_Right;//数据对齐为右对齐ADC_InitStructure.ADC_ScanDirectionADC_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_InitTypeDefGPIO_InitStructure;TIM_TimeBaseInitTypeDefTIM_TimeBaseStructure;TIM_OCInitTypeDefTIM_OCInitStructure;NVIC_InitTypeDefNVIC_InitStructure;*------------定时器时钟开启---------------------*/RCC_APB2PeriphClockCmd(RCC_APB2Periph_TIM1, ENABLE); *-------PWMGPIO配置---------*/GPIO_InitStructure.GPIO_Pin = GPIO_Pin_11;GPIO_InitStructure.GPIO_ModeGPIO_Mode_AF;GPIO_InitStructure.GPIO_OTypeGPIO_OType_PP;GPIO_InitStructure.GPIO_SpeedGPIO_Speed_50MHz;GPIO_InitStructure.GPIO_PuPdGPIO_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) - 1TIM_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_OCPolarityTIM_OCPolarity_High;//有效电平为高 TIM_OCInitStructure.TIM_OCNPolarity = TIM_OCNPolarity_High;//互补通道有效电平为高 TIM_OCInitStructure.TIM_OCIdleState = TIM_OCIdleState_Set;//空闲时输出高 TIM_OCInitStructure.TIM_OCNIdleState = TIM_OCNIdleState_Set;//互补通道空闲时输出高5、DMA中断,AD采集完一组数据,进入DMA中断
*---------初始化触发AD采样的时间---------*/ TIM_OCInitStructure.TIM_Pulse100; //占空比TIM_OC4Init(TIM1, &TIM_OCInitStructure);*------------通道4触发中断使能---------------*/ TIM_ITConfig(TIM1,TIM_IT_CC4,ENABLE); NVIC_InitStructure.NVIC_IRQChannelTIM1_CC_IRQn; NVIC_InitStructure.NVIC_IRQChannelPriority2; NVIC_InitStructure.NVIC_IRQChannelCmdENABLE; NVIC_Init(&NVIC_InitStructure);
*--------初始化先关闭定时器------------*/ TIM_Cmd(TIM1,DISABLE);*--------使能PWM输出------------*/TIM_CtrlPWMOutputs(TIM1,ENABLE);
voidDMA1_Channel1_IRQHandler(void){uint32_tadc_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、定时器中断,可以不加,这里展示一下
voidTIM1_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_H8、主程序中,全部初始化并且开启后,只需要从内存中读取三个值就可以了,想要更改采样的间隔时间就更改定时器1中通道4的占空比值。
#includestm32f0xx.h"
*-----------ADC宏定义---------------*/#defineADC_POARTC GPIOC#defineCURR_O GPIO_Pin_1 #definePOT_AD GPIO_Pin_4
#defineADC_POARTA GPIOA#defineVOL_AD GPIO_Pin_4
#defineCURR_O_CHANNEL ADC_Channel_11#define POT_CHANNEL ADC_Channel_14#defineVOL_CHANNEL ADC_Channel_4
*---------结构体定义-----------*/typedefstruct{ uint16_t vol; uint16_tcurr_o;uint16_tpot; }adc_sample_t;
externadc_sample_tadc_data;voidADC_DMA_Init(void);
#endif* __ADC_H */
** main.c** Created on: 20171229 * @Author: * @version V1.0.0 * ,%%%%%%%%,* ,%%/\%%%%/\%%* ,%%%\c''''J/%%%* %. %%%%/ o o \%%%* `%%. %%%% |%%%* `%% `%%%%(__Y__)%%'* // ;%%%%`\-/%%%'* (( / `%%%%%%%'* \\ .' |* \\ / \ | |* \\/ ) | |* \ /_ | |__* (____________))))))) ¹¥³Çʨ**/float voltage, current;intmain(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 数据处理 ...}}到这里,我们在配置好之后,基本上就不用管了,需要的时候就读取内存中数据的值就可以了,一切基本都是自动完成的,我们的主程序可以干更重要的事情。关于数据,这里只是展示一下,计算出的都是单次采集一组的值,大家可以根据自己的实际场景进行一些算法处理,如平均值采样法、递推平均值采样法等等,这里由于篇幅问题这里就不一一展开说了,关于ADC其实还有很多学问,下次再跟大家分享!
新闻来源:芯片之家,文中所述为作者独立观点,不代表icspec立场。更多精彩资讯请下载icspec App。如对本稿件有异议,请联系微信客服specltkj。
暂无评论哦,快来评论一下吧!
3 天前

1 天前

2026-07-06