定时器可以使能位PWM模式,当然也可以使能为PWM波的输入捕获!按键可以输入脉冲,正好对应的是定时器TIM5的通道1,输入捕获模式可以用来测量脉冲宽度或者测量频率。
一句话概括
**通过检测定时器某个通道上的边沿信号,在边沿信号发生跳变(比如上升沿/下降沿)的时候,将当前定时器的值(TIMx_CNT)存放到对应的捕获/比较寄存器(TIMx_CCRx)里面,完成一次捕获。**同时还可以配置捕获时是否触发中断/DMA 等。
目的:
用 TIM5 的通道 1(PA0)来做输入捕获,捕获 PA0 上高电平的脉宽(用 WK_UP 按键输入高电平),通过串口打印高电平脉宽时间
算法流程
我们用到 TIM5_CH1 来捕获高电平脉宽,也就是要先设置输入捕获为上升沿检测,记录发生上升沿的时候 TIM5_CNT 的值。然后配置捕获信号为下降沿捕获,当下降沿到来时,发生捕获,并记录此时的 TIM5_CNT 值。这样,前后两次 TIM5_CNT 之差,就是高电平的脉宽,同时 TIM5 的计数周期已知,从而可以计算出高电平脉宽的准确持续时间。
捕获/比较模式寄存器 1:TIMx_CCMR1
TIMx_CCMR1寄存器可以配置通道1和2,低八位[7:0]用于捕获/比较通道 1 的控制。
0-1:位控制输入输出的方向及输入脚的选择,我们配置值为01,为输入模式,IC1映射到TI1上,由下图可知,当然还有一个选择就是IC2映射到TI1上。平时就默认IC1映射到TI1上。
我们用到的是IC1
2-3:输入捕获预分频器
值00:不分频,每次边沿都触发捕获
值01:每两个边沿出现触发捕获
值10:每四个边沿出现触发捕获
值11:每八个边沿出现触发捕获
我们用到的是:00
4-7:输入捕获滤波器
0000:无虑波,每次采样触发一次都会产生一个输出的跳变
0001:每两次采样触发一次都会产生一个输出的跳变
等等;
我们用到的是:0000
捕获/比较使能寄存器:TIMx_CCER
我们要用到这个寄存器的最低 2 位,CC1E 和 CC1P 位。
所以,要使能输入捕获,必须设置 CC1E=0,而 CC1P 则根据自己的需要来配置。
初始化寄存器流程:
(1):使能GPIOA,定时器5时钟
(2):初始化定时器,得出溢出时间,GPIO及中断,
(3):初始化定时器为输入捕获模式,映射通道1到IC1
(4):开启更新中断和捕获中断,编写中断服务函数
如果捕获的是高电平信号的脉宽,那第一次捕获是上升沿,第二次捕获时下降沿,必须在捕获上升沿之后,设置捕获边沿为下降沿,同时,如果脉宽比较长,那么定时器就会溢出,对溢出必须做处理。这两件事,我们都在中断里面做,所以必须开启捕获中断和更新中断。
(5):开启定时器
输入捕获头文件timepwm.h
#ifndef TIMEPWM_H
#define TIMEPWM_H
#include "sys.h"
void timepwm_init(u16 arr,u16 prer);
#endif
输入捕获源文件timepwm.c
#include "timepwm.h"
#include "sys.h"
#include "delay.h"
#include "led.h"
#include "usart.h"
void timepwm_init(u16 arr,u16 prer)
{
GPIO_InitTypeDef GPIO_InitStructure;
TIM_TimeBaseInitTypeDef TIM_InitStructure;
TIM_ICInitTypeDef TIM_ICInitStructure;
NVIC_InitTypeDef NVIC_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM5, ENABLE);
GPIO_InitStructure.GPIO_Mode=GPIO_Mode_IPD;
GPIO_InitStructure.GPIO_Pin=GPIO_Pin_0;
GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;
GPIO_Init(GPIOA,&GPIO_InitStructure);
GPIO_ResetBits(GPIOA,GPIO_Pin_0);
TIM_InitStructure.TIM_ClockDivision=TIM_CKD_DIV1;
TIM_InitStructure.TIM_CounterMode=TIM_CounterMode_Up;
TIM_InitStructure.TIM_Period=arr;
TIM_InitStructure.TIM_Prescaler=prer;
TIM_TimeBaseInit(TIM5, &TIM_InitStructure);
TIM_ICInitStructure.TIM_Channel=TIM_Channel_1;
TIM_ICInitStructure.TIM_ICFilter=0x00;
TIM_ICInitStructure.TIM_ICPolarity=TIM_ICPolarity_Rising;
TIM_ICInitStructure.TIM_ICPrescaler= TIM_ICPSC_DIV1;
TIM_ICInitStructure.TIM_ICSelection= TIM_ICSelection_DirectTI;
TIM_ICInit(TIM5, &TIM_ICInitStructure);
NVIC_InitStructure.NVIC_IRQChannel= TIM5_IRQn;
NVIC_InitStructure.NVIC_IRQChannelCmd=ENABLE;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=2;
NVIC_InitStructure.NVIC_IRQChannelSubPriority=0;
NVIC_Init(&NVIC_InitStructure);
TIM_ITConfig( TIM5,TIM_IT_Update|TIM_IT_CC1,ENABLE);
TIM_Cmd(TIM5,ENABLE );
}
unsigned char status;
short int value;
void TIM5_IRQHandler(void)
{
if((status&0x80)==0)
{
if(TIM_GetITStatus(TIM5, TIM_IT_Update) != RESET)
{
if(status&0x40)
{
if((status&0x3f)==0x3f)
{
status|=0x80;
value=0xffff;
}
else
status++;
}
}
if(TIM_GetITStatus(TIM5, TIM_IT_CC1) != 0)
{
if(status&0x40)
{
status|=0x80;
value=TIM_GetCapture1(TIM5);
TIM_OC1PolarityConfig(TIM5,TIM_ICPolarity_Rising);
}
else
{
status=0;
value=0;
TIM_SetCounter(TIM5,0);
status|=0x40;
TIM_OC1PolarityConfig(TIM5,TIM_ICPolarity_Falling);
}
}
}
TIM_ClearITPendingBit(TIM5, TIM_IT_CC1|TIM_IT_Update);
}
主函数main.c
#include "delay.h"
#include "sys.h"
#include "usart.h"
#include "timer.h"
#include "stdio.h"
#include "timepwm.h"
extern unsigned char status;
extern short int value;
**#if 1//添加printf输出到串口
#pragma import(__use_no_semihosting)
struct __FILE
{
int handle;
};
FILE __stdout;
_sys_exit(int x)
{
x = x;
}
int fputc(int ch, FILE *f)
{
while(USART_GetFlagStatus(USART1,USART_FLAG_TC)==RESET);
USART_SendData(USART1,(uint8_t)ch);
return ch;
}
#endif**
int main(void)
{
u32 temp;
delay_init();
LED_Init();
usart_init(115200);
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
timepwm_init(0xFFFF,71);
while(1)
{
delay_ms(10);
if(status&0x80)
{
temp=(status&0x3f);
temp*=65536;
temp+=value;
printf("temp:%d",temp);
status=0;
}
}
}
这段引入 printf 函数支持的代码在 usart.h 头文件的最上方,这段代码加入之后便可以通过printf 函数向串口发送我们需要的内容,方便开发过程中查看代码执行情况以及一些变量值。这段代码不需要修改,引入到 usart.h 即可。
计算
总时间计算:
首先我们设置定时器的频率为72MHz/prer:72M/72=1MHz,周期为1/f=1us,定时器的位数为16位,最大溢出时计数2的16次方为65535,次数为65536,溢出时间为65536us,根据当前计数值,加上溢出次数的总和,可算出总时间值;
中断函数思路
定义一个八位的状态变量,定义一个十六位的定时器变量存储定时器值
更新中断和捕获中断都是定时器中断,我们要设置优先级及中断分组,一旦进入中断函数,有可能是捕获中断也可能是更新中断,所以进入中断函数先判断中断类型。
void TIM5_IRQHandler(void)//**程序文字化,进入中断函数**
{
if(脉冲接收完成)
{
if(如果是更新中断)
{
if(bit6为1)//也就是说接收到了上升沿
{
if(溢出次数达到上限0x3f)
{
bit7标志位赋值为1;
计数值赋值为最大0xffff;
}
else
次数加1;
}
}
if(如果是捕获中断)
{
if(判断bit6为1)//这次来的应该是下降沿
{
记下当前定时器值;
bit7赋值为1,表示捕获结束;
改变为下次上升沿捕获;
}
else//这次来的是上升沿,第一次捕获
{
定时器置零,方便后面时间计算;
十六位变量值清零;
bit6赋值为1;
改变捕极性为下降沿;
}
}
}
清除中断标志位;
}
希望我的理解可以为读者解决问题,也请大家有什么好的方法或者算法可以在留言下方给出,谢谢,相互学习!