工程代码可见Github<传送门>
1个留待补充的程序:
避免干扰,尝试用开关总中断的方法
一、输入捕获测量原理
之前我们做的是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上,随后再开启下一次捕获。