1
完善资料让更多小伙伴认识你,还能领取20积分哦, 立即完善>
STM32F407规则采样和注入采样混合使用和数据处理
1.ADC 1.1相关IO口、DMA宏定义(adc.h) 1.2ADC IO 口的配置(adc.c) 1.3DMA配置(adc.c) 1.3.1DMA中断配置(adc.c) 1.4ADC配置(adc.c) 1.4.1ADC规则采样与注入采样配置(adc.c) 2.TIM配置(adc.c) 3.滤波函数(adc.c) 4.中断服务函数(stm32f4xx_it.c) 5.在while循环中运行VALUE_PRINTF函数(main.c) 代码基于野火的ADC采样例程进一步修改而来。 代码最后实现功能: 1.软件触发规则采样,(ADC1两个规则通道,ADC2两个规则通道)DMA中断进行数据处理; 2.TIM1-CH4上升沿外部触发注入采样(ADC1一个注入通道,ADC2一个注入通道); 3.对采集的数据进行简单的均值滤波; 4.串口打印采集电压数据。 代码的注释以及说明都标注出来了! 1.ADC 1.1相关IO口、DMA宏定义(adc.h) /*=====================通道1 IO======================*/ // PB0 通过调帽接电位器 // ADC IO宏定义 #define RHEOSTAT_ADC_GPIO_PORT1 GPIOB #define RHEOSTAT_ADC_GPIO_PIN1 GPIO_Pin_0 #define RHEOSTAT_ADC_GPIO_CLK1 RCC_AHB1Periph_GPIOB #define RHEOSTAT_ADC_CHANNEL1 ADC_Channel_8 /*=====================通道2 IO ======================*/ // PA6 悬空,可用杜邦线接3V3或者GND来实验 // ADC IO宏定义 #define RHEOSTAT_ADC_GPIO_PORT2 GPIOB #define RHEOSTAT_ADC_GPIO_PIN2 GPIO_Pin_1 #define RHEOSTAT_ADC_GPIO_CLK2 RCC_AHB1Periph_GPIOB #define RHEOSTAT_ADC_CHANNEL2 ADC_Channel_9 /*=====================注入通道 IO ======================*/ // PA6 悬空,可用杜邦线接3V3或者GND来实验 // ADC IO宏定义 #define RHEOSTAT_ADC_GPIO_PORT3 GPIOA #define RHEOSTAT_ADC_GPIO_PIN3 GPIO_Pin_6 #define RHEOSTAT_ADC_GPIO_CLK3 RCC_AHB1Periph_GPIOA #define RHEOSTAT_ADC_CHANNEL3 ADC_Channel_6 /*=====================注入通道 IO ======================*/ // PA1 悬空,可用杜邦线接3V3或者GND来实验 // ADC IO宏定义 #define RHEOSTAT_ADC_GPIO_PORT4 GPIOA #define RHEOSTAT_ADC_GPIO_PIN4 GPIO_Pin_1 #define RHEOSTAT_ADC_GPIO_CLK4 RCC_AHB1Periph_GPIOA #define RHEOSTAT_ADC_CHANNEL4 ADC_Channel_1 // ADC 序号宏定义 #define RHEOSTAT_ADC1 ADC1 #define RHEOSTAT_ADC1_CLK RCC_APB2Periph_ADC1 // PB1 通过调帽接光敏电阻 // ADC IO宏定义 #define RHEOSTAT_ADC2_GPIO_PORT1 GPIOA #define RHEOSTAT_ADC2_GPIO_PIN1 GPIO_Pin_3 #define RHEOSTAT_ADC2_GPIO_CLK1 RCC_AHB1Periph_GPIOA #define RHEOSTAT_ADC2_CHANNEL1 ADC_Channel_3 /*=====================ADC2 通道2 IO ======================*/ // PA1 悬空,可用杜邦线接3V3或者GND来实验 // ADC IO宏定义 #define RHEOSTAT_ADC2_GPIO_PORT2 GPIOA #define RHEOSTAT_ADC2_GPIO_PIN2 GPIO_Pin_4 #define RHEOSTAT_ADC2_GPIO_CLK2 RCC_AHB1Periph_GPIOA #define RHEOSTAT_ADC2_CHANNEL2 ADC_Channel_4 #define RHEOSTAT_ADC2 ADC2 #define RHEOSTAT_ADC2_CLK RCC_APB2Periph_ADC2 // ADC CDR寄存器宏定义,ADC转换后的数字值则存放在这里 #define RHEOSTAT_ADC_CDR_ADDR ((uint32_t)0x40012308) // ADC DMA 通道宏定义,这里我们使用DMA传输 #define RHEOSTAT_ADC_DMA_CLK RCC_AHB1Periph_DMA2 #define RHEOSTAT_ADC_DMA_CHANNEL DMA_Channel_0 #define RHEOSTAT_ADC_DMA_STREAM DMA2_Stream0 // 这个N是为了滤波使用的 #define N 20 // ADC 中断宏定义 #define Rheostat_ADC_IRQ ADC_IRQn #define Rheostat_ADC_INT_FUNCTION ADC_IRQHandler void Rheostat_Init(void); void ADC1_External_T1_CC4_Init(void); //这个是为了触发ADC注入采样 void VALUE_PRINTF(void); //用串口打印函数转换结果 void FILTER(void); //简单的平均值滤波 u16 GetVolt(u16 advalue); #endif /* __BSP_ADC_H */ 1.2ADC IO 口的配置(adc.c) static void Rheostat_ADC_GPIO_Config(void) { GPIO_InitTypeDef GPIO_InitStructure; // 使能 GPIO 时钟 RCC_AHB1PeriphClockCmd(RHEOSTAT_ADC_GPIO_CLK1, ENABLE); RCC_AHB1PeriphClockCmd(RHEOSTAT_ADC_GPIO_CLK2, ENABLE); RCC_AHB1PeriphClockCmd(RHEOSTAT_ADC_GPIO_CLK3, ENABLE); RCC_AHB1PeriphClockCmd(RHEOSTAT_ADC_GPIO_CLK4, ENABLE); RCC_AHB1PeriphClockCmd(RHEOSTAT_ADC2_GPIO_CLK1, ENABLE); RCC_AHB1PeriphClockCmd(RHEOSTAT_ADC2_GPIO_CLK2, ENABLE); // 配置 IO GPIO_InitStructure.GPIO_Pin = RHEOSTAT_ADC_GPIO_PIN1; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN; GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL ; //不上拉不下拉 GPIO_Init(RHEOSTAT_ADC_GPIO_PORT1, &GPIO_InitStructure); // 配置 IO GPIO_InitStructure.GPIO_Pin = RHEOSTAT_ADC_GPIO_PIN2; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN; GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL ; //不上拉不下拉 GPIO_Init(RHEOSTAT_ADC_GPIO_PORT2, &GPIO_InitStructure); // 配置 IO GPIO_InitStructure.GPIO_Pin = RHEOSTAT_ADC_GPIO_PIN3; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN; GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL ; //不上拉不下拉 GPIO_Init(RHEOSTAT_ADC_GPIO_PORT3, &GPIO_InitStructure); // 配置 IO GPIO_InitStructure.GPIO_Pin = RHEOSTAT_ADC_GPIO_PIN4; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN; GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL ; //不上拉不下拉 GPIO_Init(RHEOSTAT_ADC_GPIO_PORT4, &GPIO_InitStructure); // 配置 IO GPIO_InitStructure.GPIO_Pin = RHEOSTAT_ADC2_GPIO_PIN1; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN; GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL ; //不上拉不下拉 GPIO_Init(RHEOSTAT_ADC2_GPIO_PORT1, &GPIO_InitStructure); // 配置 IO GPIO_InitStructure.GPIO_Pin = RHEOSTAT_ADC2_GPIO_PIN2; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN; GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL ; //不上拉不下拉 GPIO_Init(RHEOSTAT_ADC2_GPIO_PORT2, &GPIO_InitStructure); } 1.3DMA配置(adc.c) 定义需要用到的变量: __IO uint16_t ADC_ConvertedValue[4]; float ADC_ConvertedValueLocal[6]; vu16 AD_Value[20][4]; //AD采样值 vu16 After_filter[4]; //AD滤波后 static void Rheostat_ADC_Mode_Config(void) { DMA_InitTypeDef DMA_InitStructure // ------------------DMA Init 结构体参数 初始化-------------------------- // ADC1使用DMA2,数据流0,通道0,这个是手册固定死的 // 开启DMA时钟 RCC_AHB1PeriphClockCmd(RHEOSTAT_ADC_DMA_CLK, ENABLE); // 外设基址为:ADC 数据寄存器地址, DMA_InitStructure.DMA_PeripheralBaseAddr = RHEOSTAT_ADC_CDR_ADDR; // 存储器地址,实际上就是一个内部SRAM的变量 DMA_InitStructure.DMA_Memory0BaseAddr = (vu32)AD_Value; // 数据传输方向为外设到存储器 DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralToMemory; /******************************************************** 缓冲区大小为,指一次传输的数据量,下边这个80是4*20等于80,不是要进行平均值滤波嘛, 规则通道每个通道采集20个值,80采集完进中断。 Q:为什么规则采样和注入采样一个6个通道,而这里只有4*20 = 80个数据,而不是120个呢? A:我个人理解:DMA存储的是规则通道的数据,注入组的数据有专门的数据寄存器进行存储。 *********************************************************/ DMA_InitStructure.DMA_BufferSize = 80; // 外设寄存器只有一个,地址不用递增 DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable; // 存储器地址固定 DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable; // // 外设数据大小为半字,即两个字节 DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord; // 存储器数据大小也为半字,跟外设数据大小相同 DMA_InitStructure.DMA_MemoryDataSize = DMA_PeripheralDataSize_HalfWord; // 循环传输模式,循环模式开启,buffer写满后,自动回到初始地址开始传输 DMA_InitStructure.DMA_Mode = DMA_Mode_Circular; // DMA 传输通道优先级为高,当使用一个DMA通道时,优先级设置不影响 DMA_InitStructure.DMA_Priority = DMA_Priority_High; // 禁止DMA FIFO ,使用直连模式 DMA_InitStructure.DMA_FIFOMode = DMA_FIFOMode_Disable; // FIFO 大小,FIFO模式禁止时,这个不用配置 DMA_InitStructure.DMA_FIFOThreshold = DMA_FIFOThreshold_HalfFull; DMA_InitStructure.DMA_MemoryBurst = DMA_MemoryBurst_Single; DMA_InitStructure.DMA_PeripheralBurst = DMA_PeripheralBurst_Single; // 选择 DMA 通道,通道存在于流中 DMA_InitStructure.DMA_Channel = RHEOSTAT_ADC_DMA_CHANNEL; //初始化DMA流,流相当于一个大的管道,管道里面有很多通道 DMA_Init(RHEOSTAT_ADC_DMA_STREAM, &DMA_InitStructure); //开启DMA中断 DMA_ITConfig(RHEOSTAT_ADC_DMA_STREAM,DMA_IT_TC,ENABLE); // 使能DMA流 DMA_Cmd(RHEOSTAT_ADC_DMA_STREAM, ENABLE); 1.4ADC配置(adc.c) // 先定义这两个ADC结构体 ADC_InitTypeDef ADC_InitStructure; ADC_CommonInitTypeDef ADC_CommonInitStructure; // 开启ADC时钟 RCC_APB2PeriphClockCmd(RHEOSTAT_ADC1_CLK , ENABLE); RCC_APB2PeriphClockCmd(RHEOSTAT_ADC2_CLK , ENABLE); // -------------------ADC Common 结构体 参数 初始化------------------------ // ADC双通道规则-注入模式 ADC_CommonInitStructure.ADC_Mode = ADC_DualMode_RegSimult_InjecSimult; // 时钟为fpclk x分频 ADC_CommonInitStructure.ADC_Prescaler = ADC_Prescaler_Div4; // 禁止DMA直接访问模式 ADC_CommonInitStructure.ADC_DMAAccessMode = ADC_DMAAccessMode_1; // 采样时间间隔 ADC_CommonInitStructure.ADC_TwoSamplingDelay = ADC_TwoSamplingDelay_5Cycles; ADC_CommonInit(&ADC_CommonInitStructure); // -------------------ADC Init 结构体 参数 初始化-------------------------- ADC_StructInit(&ADC_InitStructure); // ADC 分辨率 ADC_InitStructure.ADC_Resolution = ADC_Resolution_12b; // 扫描模式,多通道采集才需要 ADC_InitStructure.ADC_ScanConvMode = ENABLE; // 连续转换 ADC_InitStructure.ADC_ContinuousConvMode = ENABLE; //禁止外部边沿触发 ADC_InitStructure.ADC_ExternalTrigConvEdge = ADC_ExternalTrigConvEdge_None; //外部触发通道,本例子使用软件触发,此值随便赋值即可 ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_T1_CC1; //数据右对齐 ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right; /********************************************************* 转换通道2个,配置要求:ADC1和ADC2分别两路规则采样,软件触发。 这里我的理解是: 这里的通道数是主ADC(ADC1)的规则转换通道数,因为选择的是双ADC规则-注入模式, 所以从ADC(ADC2)的两个通道就不用往这里加上去。 ************************************************************/ ADC_InitStructure.ADC_NbrOfConversion = 2; |
|
|
|
1.4.1ADC规则采样与注入采样配置(adc.c)
//--------------------------------------------------------------------------- ADC_Init(RHEOSTAT_ADC1, &ADC_InitStructure); // 配置 ADC1 通道转换顺序,采样时间为3个时钟周期 ADC_RegularChannelConfig(RHEOSTAT_ADC1, RHEOSTAT_ADC_CHANNEL1, 1, ADC_SampleTime_3Cycles); ADC_RegularChannelConfig(RHEOSTAT_ADC1, RHEOSTAT_ADC_CHANNEL2, 2, ADC_SampleTime_3Cycles); //--------------------------------------------------------------------------- ADC_Init(RHEOSTAT_ADC2, &ADC_InitStructure); // 配置 ADC2 通道转换顺序,采样时间为3个时钟周期 ADC_RegularChannelConfig(RHEOSTAT_ADC2, RHEOSTAT_ADC2_CHANNEL1, 1, ADC_SampleTime_3Cycles); ADC_RegularChannelConfig(RHEOSTAT_ADC2, RHEOSTAT_ADC2_CHANNEL2, 2, ADC_SampleTime_3Cycles); //--------------------------------------------------------------------------- //配置注入采样的通道转换顺序,ADC1只使用一个注入通道 ADC_InjectedSequencerLengthConfig(RHEOSTAT_ADC1,1); ADC_InjectedChannelConfig(RHEOSTAT_ADC1,RHEOSTAT_ADC_CHANNEL3,1,ADC_SampleTime_112Cycles); //配置注入采样的通道转换顺序,ADC2也只使用一个注入通道 ADC_InjectedSequencerLengthConfig(RHEOSTAT_ADC2,1); ADC_InjectedChannelConfig(RHEOSTAT_ADC2,RHEOSTAT_ADC_CHANNEL4,1,ADC_SampleTime_112Cycles); //配置注入采样的触发方式,TIM1-CH4的脉冲上升沿触发 ADC_ExternalTrigInjectedConvConfig(RHEOSTAT_ADC1,ADC_ExternalTrigInjecConv_T1_CC4); ADC_ExternalTrigInjectedConvEdgeConfig(RHEOSTAT_ADC1,ADC_ExternalTrigInjecConvEdge_Rising); //这个照理说不用配置,因为我ADC的模式选的是双通道-规则-注入模式,但是我没有去掉这两行语句进行实验, ADC_ExternalTrigInjectedConvConfig(RHEOSTAT_ADC2,ADC_ExternalTrigInjecConv_T1_CC4); ADC_ExternalTrigInjectedConvEdgeConfig(RHEOSTAT_ADC2,ADC_ExternalTrigInjecConvEdge_Rising); // 使能DMA请求 after last transfer (multi-ADC mode) ADC_MultiModeDMARequestAfterLastTransferCmd(ENABLE); //ADC_DMARequestAfterLastTransferCmd(RHEOSTAT_ADC1,ENABLE); // 使能ADC DMA ADC_DMACmd(RHEOSTAT_ADC1, ENABLE); ADC_DMACmd(RHEOSTAT_ADC2, ENABLE); // 使能ADC ADC_Cmd(RHEOSTAT_ADC1, ENABLE); ADC_Cmd(RHEOSTAT_ADC2, ENABLE); //开始adc转换,软件触发 ADC_SoftwareStartConv(RHEOSTAT_ADC1); ADC_SoftwareStartConv(RHEOSTAT_ADC2); } 2.TIM配置(adc.c) void ADC1_External_T1_CC4_Init(void) { TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure; TIM_OCInitTypeDef TIM_OCInitStructure; RCC_APB2PeriphClockCmd(RCC_APB2Periph_TIM1, ENABLE); /* Time Base configuration */ TIM_TimeBaseStructInit(&TIM_TimeBaseStructure); TIM_TimeBaseStructure.TIM_Period = 99; TIM_TimeBaseStructure.TIM_Prescaler = 1679; TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1; TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; TIM_TimeBaseInit(TIM1, &TIM_TimeBaseStructure); TIM_SelectOutputTrigger(TIM1,TIM_TRGOSource_Update); //选择TRGO触发源为计时器更新时间 /* TIM1 channel1 configuration in PWM mode */ TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1; TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; TIM_OCInitStructure.TIM_Pulse =10; TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_Low; TIM_OC4Init(TIM1, &TIM_OCInitStructure); // TIM_ITConfig(TIM1, TIM_IT_CC4, ENABLE); // CCR4的中断,这个通过设置CCR4的pulse来控制产生中断相当于PWM-ON的位置 TIM_CtrlPWMOutputs(TIM1, ENABLE); TIM_Cmd(TIM1, ENABLE); } //主函数初始化Rheostat_Init()就可以 void Rheostat_Init(void) { Rheostat_ADC_GPIO_Config(); Rheostat_ADC_Mode_Config(); ADC1_External_T1_CC4_Init(); } 3.滤波函数(adc.c) /*---------------------------------均值滤波-----------------------------*/ void FILTER(void) { int sum = 0; u8 count,i; for(i=0; i《4; i++) { for(count=0; count《N; count++) { sum += AD_Value[count][i]; } After_filter[i] = sum / N; // ADC_ConvertedValueLocal[i] =(float)(After_filter[i] *3.3/4096); sum = 0; } } 4.中断服务函数(stm32f4xx_it.c) extern __IO uint16_t ADC_ConvertedValue[4]; //开始全部初始化为0 extern float ADC_ConvertedValueLocal[7]; void DMA2_Stream0_IRQHandler(void) { if(DMA_GetITStatus(DMA2_Stream0,DMA_IT_TCIF0) != RESET) { // 读取ADC的转换值 FILTER(); } DMA_ClearITPendingBit(DMA2_Stream0,DMA_IT_TCIF0); } 5.在while循环中运行VALUE_PRINTF函数(main.c) static void Delay(__IO uint32_t nCount) //简单的延时函数 { for(; nCount != 0; nCount--); } void VALUE_PRINTF(void) { __IO uint16_t ADC_InjectValue[2]={0}; //定义数组 /******************存放注入转换数据*****************************/ ADC_InjectValue[0] = ADC_GetInjectedConversionValue(RHEOSTAT_ADC1,ADC_InjectedChannel_1); ADC_InjectValue[1] = ADC_GetInjectedConversionValue(RHEOSTAT_ADC2,ADC_InjectedChannel_1); ADC_ConvertedValueLocal[0] =(float)(After_filter[0] *3.3/4096); ADC_ConvertedValueLocal[1] =(float)(After_filter[1] *3.3/4096); ADC_ConvertedValueLocal[2] =(float)(After_filter[2] *3.3/4096); ADC_ConvertedValueLocal[3] =(float)(After_filter[3] *3.3/4096); ADC_ConvertedValueLocal[4] =(float)((uint16_t)ADC_InjectValue[0]*3.3/4096); ADC_ConvertedValueLocal[5] =(float)((uint16_t)ADC_InjectValue[1]*3.3/4096); printf(“rn The current PB0 value = %f V rn”,ADC_ConvertedValueLocal[0]); printf(“rn The current PA3 value = %f V rn”,ADC_ConvertedValueLocal[1]); printf(“rn The current PB1 value = %f V rn”,ADC_ConvertedValueLocal[2]); printf(“rn The current PA4 value = %f V rn”,ADC_ConvertedValueLocal[3]); printf(“rn The current PA6 value = %f V rn”,ADC_ConvertedValueLocal[4]); printf(“rn The current PA1 value = %f V rn”,ADC_ConvertedValueLocal[5]); printf(“rnrn”); Delay(0xffffff); } 如有错误,还望大家不吝赐教! |
|
|
|
只有小组成员才能发言,加入小组>>
调试STM32H750的FMC总线读写PSRAM遇到的问题求解?
1771 浏览 1 评论
X-NUCLEO-IHM08M1板文档中输出电流为15Arms,15Arms是怎么得出来的呢?
1619 浏览 1 评论
1070 浏览 2 评论
STM32F030F4 HSI时钟温度测试过不去是怎么回事?
724 浏览 2 评论
ST25R3916能否对ISO15693的标签芯片进行分区域写密码?
1673 浏览 2 评论
1936浏览 9评论
STM32仿真器是选择ST-LINK还是选择J-LINK?各有什么优势啊?
729浏览 4评论
STM32F0_TIM2输出pwm2后OLED变暗或者系统重启是怎么回事?
569浏览 3评论
594浏览 3评论
stm32cubemx生成mdk-arm v4项目文件无法打开是什么原因导致的?
552浏览 3评论
小黑屋| 手机版| Archiver| 德赢Vwin官网 ( 湘ICP备2023018690号 )
GMT+8, 2024-12-22 16:43 , Processed in 0.944543 second(s), Total 77, Slave 61 queries .
Powered by 德赢Vwin官网 网
© 2015 bbs.elecfans.com
关注我们的微信
下载发烧友APP
德赢Vwin官网 观察
版权所有 © 湖南华秋数字科技有限公司
德赢Vwin官网 (电路图) 湘公网安备 43011202000918 号 电信与信息服务业务经营许可证:合字B2-20210191 工商网监 湘ICP备2023018690号