工程代码可见Github<传送门>
之前我们做的是PWM波的输出,现在再来看PWM的输入,即测量PWM波频率和占空比的测量。
理解一副重要的示意图:
图中紫色部分为我们输入的方波信号,首先我们刚开始时要把捕获通道设置为上升沿触发,假如我们捕获到了一个上升沿,也就是到达了A点,那么我们的定时器就开始计数,随后我们还要吧捕获通道设置成为下降沿触发。当我们捕获了一个下降沿时,也就是到达了B点,那么我们获取一个计数值,定时器还得继续计数,同时重新把捕获通道设置为上升沿触发。直到再次捕获到了一个上升沿,那么就代表已经到达C点了,我们再获取一次计数值。
那么我们通过获取的这两个计数值,就很容易的算出频率和占空比。<参考eavane大神博客>
频率 = 1000000 / 第二次捕获值(72分频的前提)
占空比 = 第一次捕获值 / 第二次捕获值 * 100
知道了原理,再来看看具体实现吧。
main.c
/******************************************************************************* * 文件名:main.c * 描 述: * 作 者:CLAY * 版本号:v1.0.0 * 日 期: 2019年1月26日 * 备 注:TIM3_CH1-PA6, TIM3_CH2-PA7配置为输入捕获, * 测量TIM4的CH1-PB6和PB7的频率以及占空比 ******************************************************************************* */ #include "config.h" #include "led.h" #include "key.h" #include "timer.h" #include "beep.h" #include "lcd.h" #include "stdio.h" #include "usart.h" #include "i2c.h" #include "eeprom.h" #include "PWMMode_Advance.h" #include "PWM_Compare.h" #include "PWM_Capture.h" u8 string[20]; int main(void) { STM3210B_LCD_Init(); LCD_Clear(Blue); LCD_SetBackColor(Blue); LCD_SetTextColor(White); LEDInit(); KeyInit(); BeepInit(); TIM2Init(2000, 72);//定时2ms TIM1_PWMInit(2000, 40, 80);//频率2K CH2占空比40% CH3占空比80% TIM4_PWMCompare(2000, 1000, 40, 80);//CH1占空比2K 40% CH2 1K占空比80% TIM3_CaptureInit(); while(1) { KeyDriver(); if(TIM3_CH1_CAPTURE_MODE == 3) { sprintf((char*)string,"ch1_fre:%d ",1000000 / TIM3_CH1_CAPTURE_HL); LCD_DisplayStringLine(Line3, string); sprintf((char*)string,"ch1_duty:%d ",TIM3_CH1_CAPTURE_H * 100/TIM3_CH1_CAPTURE_HL); LCD_DisplayStringLine(Line4, string); TIM3_CH1_CAPTURE_MODE = 0; } if(TIM3_CH2_CAPTURE_MODE == 3) { sprintf((char*)string,"ch2_fre:%d ",1000000 / TIM3_CH2_CAPTURE_HL); LCD_DisplayStringLine(Line7, string); sprintf((char*)string,"ch2_duty:%d ",TIM3_CH2_CAPTURE_H * 100/TIM3_CH2_CAPTURE_HL); LCD_DisplayStringLine(Line8, string); TIM3_CH2_CAPTURE_MODE = 0; } } } void KeyAction(int code) { if(code == 1)//按下B1,切换灯状态,蜂鸣器鸣叫0.1s { GPIOC->ODR ^= (1<<8);//PC8不断取反 GPIOD->ODR |= (1<<2);//PD2置1,使能573锁存器 GPIOD->ODR &= ~(1<<2);//PD2清0,关闭573锁存器 Beep(100); } else if(code == 2) { Beep(-1); } else if(code == 3) { Beep(0); } else if(code == 4) { } }
PWM_Capture.c
#include "PWM_Capture.h" u8 TIM3_CH1_CAPTURE_MODE = 0; u32 TIM3_CH1_CAPTURE_H = 0, TIM3_CH1_CAPTURE_HL = 0; u8 TIM3_CH2_CAPTURE_MODE = 0; u32 TIM3_CH2_CAPTURE_H = 0, TIM3_CH2_CAPTURE_HL = 0; u8 CAPTURE_MODE; void TIM3_IOInit(void) { GPIO_InitTypeDef GPIO_InitStructure; //***注意这里是APB2*** RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); //使能 GPIOA时钟 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6|GPIO_Pin_7; //TIM3通道1-PA6 TIM3通道2-PA7 GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;//***输入捕获使用IO模式为上拉输入或者浮空输入 // GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;//输入就所谓输出速度了,这句可省略。 GPIO_Init(GPIOA, &GPIO_InitStructure); //初始化 GPIO } void NVIC_TIM3Enable(void) { NVIC_InitTypeDef NVIC_initstructure; NVIC_initstructure.NVIC_IRQChannel = TIM3_IRQn; //选择TIM4中断通道 NVIC_initstructure.NVIC_IRQChannelCmd = ENABLE; //使能中断通道 NVIC_initstructure.NVIC_IRQChannelPreemptionPriority = 0; //设定抢占优先级为0 NVIC_initstructure.NVIC_IRQChannelSubPriority = 0; //设定响应优先级为0 NVIC_Init(&NVIC_initstructure); } void TIM3Init(void) { TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure; //***注意这里是APB1*** RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE); //使能定时器3时钟 TIM_TimeBaseStructure.TIM_Period = 0xFFFF; //***设置为0xFFFF TIM_TimeBaseStructure.TIM_Prescaler = 72-1; //设置预分频值 TIM_TimeBaseStructure.TIM_ClockDivision = 0; //设置时钟分割:TDTS = Tck_tim TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; //TIM 向上计数模式 TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStructure); //初始化 TIM3 } void TIM3_CaptureInit(void) { TIM_ICInitTypeDef TIM_ICInitStructure; TIM3_IOInit();//TIM3通道1-PA6 TIM3通道2-PA7配置 TIM3Init();//TIM3定时器配置 NVIC_TIM3Enable();//中断向量配置 TIM_ICInitStructure.TIM_Channel = TIM_Channel_1;//选择通道 TIM_ICInitStructure.TIM_ICPolarity = TIM_ICPolarity_Rising;//上升沿触发 TIM_ICInitStructure.TIM_ICSelection = TIM_ICSelection_DirectTI;//必须选择指向TI寄存器 TIM_ICInitStructure.TIM_ICPrescaler = TIM_ICPSC_DIV1;//不分频 TIM_ICInitStructure.TIM_ICFilter = 0;//不滤波 TIM_ICInit(TIM3, &TIM_ICInitStructure); TIM_ICInitStructure.TIM_Channel = TIM_Channel_2; TIM_ICInitStructure.TIM_ICPolarity = TIM_ICPolarity_Rising; TIM_ICInitStructure.TIM_ICSelection = TIM_ICSelection_DirectTI; TIM_ICInitStructure.TIM_ICPrescaler = TIM_ICPSC_DIV1; TIM_ICInitStructure.TIM_ICFilter = 0; TIM_ICInit(TIM3, &TIM_ICInitStructure); TIM_ITConfig(TIM3, TIM_IT_CC1|TIM_IT_CC2, ENABLE); TIM_Cmd(TIM3, ENABLE); }
PWM_Capture.h
#ifndef _PWM_CAPTURE_H #define _PWM_CAPTURE_H #include "config.h" void TIM3_CaptureInit(void); extern u8 CAPTURE_MODE; extern u8 TIM3_CH1_CAPTURE_MODE; extern u32 TIM3_CH1_CAPTURE_H, TIM3_CH1_CAPTURE_HL; extern u8 TIM3_CH2_CAPTURE_MODE; extern u32 TIM3_CH2_CAPTURE_H, TIM3_CH2_CAPTURE_HL; #endif
stm32f10x_it.c
void TIM2_IRQHandler(void) { static u16 tmr500ms = 0; if(TIM_GetITStatus(TIM2, TIM_FLAG_Update)) { TIM_ClearITPendingBit(TIM2, TIM_FLAG_Update); tmr500ms++; KeyScan(); BeepScan(2);//2ms扫描 if(tmr500ms >= 250) { tmr500ms = 0; CAPTURE_MODE ^= 1; } } } void TIM3_IRQHandler(void) { if(TIM_GetITStatus(TIM3, TIM_IT_CC1) == 1) { TIM_ClearITPendingBit(TIM3, TIM_IT_CC1); if(CAPTURE_MODE) { switch(TIM3_CH1_CAPTURE_MODE) { case 0: TIM3_CH1_CAPTURE_H = 0;// TIM3_CH1_CAPTURE_HL = 0; TIM3_CH1_CAPTURE_MODE = 1; TIM_SetCounter(TIM3, 0);//计数值清0 TIM_OC1PolarityConfig(TIM3, TIM_ICPolarity_Falling);//设置为下降沿触发 break; case 1: TIM3_CH1_CAPTURE_H = TIM_GetCounter(TIM3);//第一次获取计数值 TIM3_CH1_CAPTURE_MODE = 2; TIM_OC1PolarityConfig(TIM3, TIM_ICPolarity_Rising);//设置为上升沿触发 break; case 2: TIM3_CH1_CAPTURE_HL = TIM_GetCounter(TIM3);//第二次获取计数值 TIM3_CH1_CAPTURE_MODE = 3; TIM_OC1PolarityConfig( TIM3,TIM_ICPolarity_Rising);//设置为上升沿触发 break; default: break; } } else { TIM3_CH1_CAPTURE_MODE = 0; } } if(TIM_GetITStatus(TIM3, TIM_IT_CC2) == 1) { TIM_ClearITPendingBit(TIM3, TIM_IT_CC2); if(!CAPTURE_MODE) { switch(TIM3_CH2_CAPTURE_MODE) { case 0: TIM3_CH2_CAPTURE_H = 0; TIM3_CH2_CAPTURE_HL = 0; TIM3_CH2_CAPTURE_MODE = 1; TIM_SetCounter(TIM3, 0); TIM_OC2PolarityConfig( TIM3,TIM_ICPolarity_Falling); break; case 1: TIM3_CH2_CAPTURE_H = TIM_GetCounter(TIM3); TIM3_CH2_CAPTURE_MODE = 2; TIM_OC2PolarityConfig( TIM3,TIM_ICPolarity_Rising); break; case 2: TIM3_CH2_CAPTURE_HL = TIM_GetCounter(TIM3); TIM3_CH2_CAPTURE_MODE = 3; TIM_OC2PolarityConfig( TIM3,TIM_ICPolarity_Rising); break; default: break; } } else { TIM3_CH2_CAPTURE_MODE = 0; } } }
1、实验结果
2、由于我是直接用之前的工程,之前TIM3做了通用定时器的PWM模式输出实验,而现在要配置为输入捕获,两个同时存在会出现如下错误:
所以要删除pwmmode
相关文件,包括以下几个:
3、程序中的变量释义
TIM3_CH1_CAPTURE_MODE
用来记录当前捕获在哪个位置
TIM3_CH1_CAPTURE_H
用来记录第一次捕获的时间
TIM3_CH1_CAPTURE_HL
用来记录第二次捕获的时间
4、CAPTURE_MODE
的优秀所在
两个通道肯定不能同时进行捕获,首先两者同时捕获,同时记录这个无所谓,关键是它们还都涉及计算。计算最怕被中断打断,这样可能导致结果不正确。这里目前有两种解决方案,一种是分时复用,就像上面程序中写的那样,另外一种解决办法就像我们51操作步进电机计算节拍或者就像控制DHT11时序一样,开关总中断,这个方法稍后再做调试,会一并挂上来。
5、中断函数里总的处理思想
当TIM3_CH1_CAPTURE_MODE=0时,如果发生了中断,那么就代表我们捕获到了上升沿,当前在上面简图的A点,我们把定时器计数值清0,让定时器重新计数,同时还要把触发方式设置为下降沿触发。
当TIM3_CH1_CAPTURE_MODE=1时候,发生了中断,那么就代表我们捕获到了下降沿,当前的位置在上面简图的B点,我们获取一次定时器的计数值,同时还得把我们触发方式设置为上升沿触发。
当TIM3_CH1_CAPTURE_MODE=2时候,发生中断,那么就代表我们一个周期已经捕获完毕了,当前位置在上面简图的C点,我们再获取一次定时器的计数值,随后就可以根据我们两次获取的计数值,算出我们方波的频率和周期。
当TIM3_CH1_CAPTURE_MODE=3时,就意味着我们可以开始计算周期和频率,当我们处理好数据后,我们就可以把TIM3_CH1_CAPTURE_MODE清0,准备开始下一次捕获。
然后在主函数中,进行TIM3_CH1_CAPTURE_MODE状态判断,进而计算出占空比和频率的值,然后显示到LCD上,随后再开启下一次捕获。