发布时间:2022-12-01 09:00
学习定时器捕获,手撕正点原子的代码,自己重写定时器中断服务函数,记录一哈心得
注:本笔记针对对定时器捕获有一定了解的人或许能看懂,因为比较懒,对很多相关的基础知识没有阐述,推荐一篇其他大牛写的文章,写的很好很好,我主要最开始看不懂正点原子写的中断服务函数,然后自己写了一个,有相同问题的小伙伴可以看看,希望能给你带来帮助
https://blog.csdn.net/qq_38410730/article/details/80011330
输入捕获模式可以用来测量脉冲宽度或者测量频率。STM32的定时器,除了TIM6、TIM7,其他的定时器都有输入捕获的功能。下面以一个简单的脉冲输入为例,简单地讲述一下输入捕获用于测量脉冲宽度的工作原理:
先设置输入捕获为上升沿检测,记录发生上升沿时TIMx_CNT的值。然后配置捕获信号为下降沿捕获,当下降沿到来的时候发生捕获,并记录此时的TIMx_CNT的值。这样,前后两次TIMx_CNT的值之差就是高电平的脉宽。同时根据TIM的计数频率,我们就能知道高电平脉宽的准确时间。
定时器计时时间计算
溢出时间= ((重装载值+1)*(分频值+1))/APB1时钟频率;
使用定时器之前都必须开启定时器时钟,基本定时器属于 APB1总线外设。APB1总线外设时钟=72M。
比如我们把定时器设置自动重装载寄存器 arr 的值为4999,设置时钟预分频器寄存器psc的值为7199,则驱动计数器的时钟:
CK_CNT = APB1Periph/ (7199+1)=72M/7200=10K,则计数器计数一次的时间等于:1/CK_CNT=0.0001s=0.1ms=100us,
当计数器从0计数到4999时,产生一次中断,则中断一次的时间为:100usX5000=0.0001sX5000=0.5s=500ms也就是半秒钟。
定时器捕获的思路还是很简单的,下面我们直接上代码
#include "sys.h"
#include "delay.h"
#include "stdio.h"
#include "usart.h"
/*****************
注意事项:使用printf函数要包含stdio.h头文件
还有串口的初始化,printf函数默认的输出为串口
******************/
u8 catchhigh=0;//高电平捕获标志位,0为没有高电平,1为有高电平
u8 temp=0;//定时器溢出次数
u16 value=0;//捕获结束时定时器的值
u8 successful=0;//捕获完成标志位
void tim5init()
{
GPIO_InitTypeDef GPIO_InitStruct;
TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStruct;
TIM_ICInitTypeDef TIM_ICInitStruct;
NVIC_InitTypeDef NVIC_InitStruct;
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM5,ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
GPIO_InitStruct.GPIO_Mode=GPIO_Mode_IPD;//下拉输入,因为按键接的是3.3V的高电平
GPIO_InitStruct.GPIO_Pin=GPIO_Pin_0;
GPIO_InitStruct.GPIO_Speed=GPIO_Speed_50MHz;
GPIO_Init(GPIOA,&GPIO_InitStruct);
TIM_TimeBaseInitStruct.TIM_ClockDivision=TIM_CKD_DIV1;
TIM_TimeBaseInitStruct.TIM_Period=0xFFFF;
TIM_TimeBaseInitStruct.TIM_CounterMode=TIM_CounterMode_Up;
TIM_TimeBaseInitStruct.TIM_Prescaler=71;
TIM_TimeBaseInit(TIM5,&TIM_TimeBaseInitStruct);
TIM_ICInitStruct.TIM_Channel=TIM_Channel_1;
TIM_ICInitStruct.TIM_ICFilter=0x00;
TIM_ICInitStruct.TIM_ICPolarity=TIM_ICPolarity_Rising;
TIM_ICInitStruct.TIM_ICPrescaler=TIM_ICPSC_DIV1;
TIM_ICInitStruct.TIM_ICSelection=TIM_ICSelection_DirectTI;
TIM_ICInit(TIM5,&TIM_ICInitStruct);
NVIC_InitStruct.NVIC_IRQChannel=TIM5_IRQn;
NVIC_InitStruct.NVIC_IRQChannelCmd=ENABLE;
NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority=2;
NVIC_InitStruct.NVIC_IRQChannelSubPriority=2;
NVIC_Init(&NVIC_InitStruct);
TIM_ITConfig(TIM5,TIM_IT_Update|TIM_IT_CC1,ENABLE);
TIM_Cmd(TIM5, ENABLE);
}
int main()
{
u32 sendvalue=0;//发送的值
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
tim5init();
delay_init();
uart_init(115200);
while(1)
{
delay_ms(10);//
if(successful==1)
{
sendvalue=65536*temp+value;//
printf("low:%d us\r\n",sendvalue);
successful=0;//清除捕获成功标志位
}
}
}
void TIM5_IRQHandler()
{
if((TIM_GetITStatus(TIM5, TIM_IT_CC1)==SET)&&(catchhigh==0))//捕获到上升沿,捕获开始
{
value=0;//清零
temp=0;
TIM_SetCounter(TIM5,0);//定时器清零
catchhigh=1;//修改标志位
TIM_OC1PolarityConfig(TIM5,TIM_ICPolarity_Falling);//修改触发条件,捕获下降沿
}
else if((TIM_GetITStatus(TIM5, TIM_IT_CC1)==SET)&&(catchhigh==1))//捕获到下降沿,捕获结束
{
value=TIM_GetCapture1(TIM5);//获取定时器的值
TIM_OC1PolarityConfig(TIM5,TIM_ICPolarity_Rising);//修改中断触发方式
successful=1;//标记捕获成功
catchhigh=0;//标记捕获高电平为0
}
if((TIM_GetITStatus(TIM5,TIM_IT_Update)==SET)&&(catchhigh==1))//产生溢出
{
temp++;//溢出次数加一
}
TIM_ClearITPendingBit(TIM5,TIM_IT_Update|TIM_IT_CC1);//清除中断标志位
}
小白一枚,能看到这里的前辈,有什么问题,欢迎指出,让我知道自己的不足之处,学习进步。