一、实验目的
用TIM5的通道1(PA0)来做输入捕获,捕获PA0上高电平的脉宽(用KEY_UP按键输入高电平),通过串口打印高电平脉宽时间。
二、输入捕获简介
三、代码及分析
timer.c
#include "timer.h"
TIM_HandleTypeDef TIM5_Handler; // 定时器5句柄
// arr 自动重装载值(TIM5、TIM2是32位的)
void TIM5_CH1_Cap_Init(u32 arr, u16 psc)
{
TIM_IC_InitTypeDef TIM5_CH1Config;
TIM5_Handler.Instance = TIM5; //通用定时器5
TIM5_Handler.Init.Prescaler = psc; //设置分频系数
TIM5_Handler.Init.CounterMode = TIM_COUNTERMODE_UP; //设置向上计数
TIM5_Handler.Init.Period= arr; //自动重装载值
TIM5_Handler.Init.ClockDivision=TIM_CLOCKDIVISION_DIV1; //时钟分频因子
HAL_TIM_IC_Init(&TIM5_Handler);
// 以上为初始化TIM5时基参数
// 设置TIM5输入捕获参数
TIM5_CH1Config.ICPolarity = TIM_ICPOLARITY_RISING;//上升沿捕获
TIM5_CH1Config.ICSelection = TIM_ICSELECTION_DIRECTTI;//映射到TI1上
TIM5_CH1Config.ICPrescaler = TIM_ICPSC_DIV1; //配置输入分频,不分频
TIM5_CH1Config.ICFilter = 0; //配置输入滤波器,不滤波
HAL_TIM_IC_ConfigChannel(&TIM5_Handler,&TIM5_CH1Config,TIM_CHANNEL_1);
HAL_TIM_IC_Start_IT(&TIM5_Handler, TIM_CHANNEL_1); //开启TIM5的捕获通道1,并且开启捕获中断
__HAL_TIM_ENABLE_IT(&TIM5_Handler, TIM_IT_UPDATE);//使能更新中断
}
//定时器5底层驱动,时钟使能,引脚配置。
//此函数会被HAL_TIM_IC_Init()调用
void HAL_TIM_IC_MspInit(TIM_HandleTypeDef *htim)
{
GPIO_InitTypeDef GPIO_Initure;
if(htim->Instance==TIM5)
{
__HAL_RCC_TIM5_CLK_ENABLE(); //使能TIM5时钟
__HAL_RCC_GPIOA_CLK_ENABLE(); //开启GPIOA时钟
GPIO_Initure.Pin=GPIO_PIN_0; //PA0
GPIO_Initure.Mode=GPIO_MODE_AF_PP; //复用推挽输出
GPIO_Initure.Pull=GPIO_PULLDOWN; //下拉
GPIO_Initure.Speed=GPIO_SPEED_HIGH; //高速
GPIO_Initure.Alternate=GPIO_AF2_TIM5; //PA0复用为TIM5_CH1
HAL_GPIO_Init(GPIOA,&GPIO_Initure); //初始化PA0
HAL_NVIC_SetPriority(TIM5_IRQn,2,0); //设置中断优先级,抢占优先级2,子优先级0
HAL_NVIC_EnableIRQ(TIM5_IRQn); //开启ITM5中断
}
}
//捕获状态
//[7]:0,没有成功捕获;1,成功捕获一次
//[6]: 0,还没有捕获到低电平;1,已经捕获到低电平
//[5:0] : 捕获低电平后溢出的次数(对于32位的计数器来说,1us计数器加1,溢出时间:4294s)
u8 TIM5CH1_CAPTURE_STA = 0; // 输入捕获状态
u32 TIM5CH1_CAPTURE_VAL; //输入捕获值(TIM5/TIM2是32位)
void TIM5_IRQHandler (void)
{
HAL_TIM_IRQHandler (&TIM5_Handler);//定时器公用处理函数
}
//定时器更新中断(计数溢出)中断处理回调函数, 该函数在HAL_TIM_IRQHandler中会被调用
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)//更新中断(溢出)发生时执行
{
if((TIM5CH1_CAPTURE_STA&0X80)==0)//还未成功捕获
{
if(TIM5CH1_CAPTURE_STA&0X40)//已经捕获到高电平了
{
if((TIM5CH1_CAPTURE_STA&0X3F)==0X3F)//高电平太长了
{
TIM5CH1_CAPTURE_STA|=0X80; //标记成功捕获了一次
TIM5CH1_CAPTURE_VAL=0XFFFFFFFF;
}else TIM5CH1_CAPTURE_STA++;
}
}
}
//定时器输入捕获中断处理回调函数,该函数在HAL_TIM_IRQHandler中会被调用
void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *htim)//捕获中断发生时执行
{
if((TIM5CH1_CAPTURE_STA&0X80)==0)//还未成功捕获
{
if(TIM5CH1_CAPTURE_STA&0X40) //捕获到一个下降沿
{
TIM5CH1_CAPTURE_STA|=0X80; //标记成功捕获到一次高电平脉宽
TIM5CH1_CAPTURE_VAL=HAL_TIM_ReadCapturedValue(&TIM5_Handler,TIM_CHANNEL_1);//获取当前的捕获值.
TIM_RESET_CAPTUREPOLARITY(&TIM5_Handler,TIM_CHANNEL_1); //一定要先清除原来的设置!!
TIM_SET_CAPTUREPOLARITY(&TIM5_Handler,TIM_CHANNEL_1,TIM_ICPOLARITY_RISING);//配置TIM5通道1上升沿捕获
}else //还未开始,第一次捕获上升沿
{
TIM5CH1_CAPTURE_STA=0; //清空
TIM5CH1_CAPTURE_VAL=0;
TIM5CH1_CAPTURE_STA|=0X40; //标记捕获到了上升沿
__HAL_TIM_DISABLE(&TIM5_Handler); //关闭定时器5
__HAL_TIM_SET_COUNTER(&TIM5_Handler,0);
TIM_RESET_CAPTUREPOLARITY(&TIM5_Handler,TIM_CHANNEL_1); //一定要先清除原来的设置!!
TIM_SET_CAPTUREPOLARITY(&TIM5_Handler,TIM_CHANNEL_1,TIM_ICPOLARITY_FALLING);//定时器5通道1设置为下降沿捕获
__HAL_TIM_ENABLE(&TIM5_Handler);//使能定时器5
}
}
}
main.c
extern u8 TIM5CH1_CAPTURE_STA; //输入捕获状态
extern u32 TIM5CH1_CAPTURE_VAL; //输入捕获值
int main(void)
{
long long temp=0;
Cache_Enable(); //打开L1-Cache
HAL_Init(); //初始化HAL库
Stm32_Clock_Init(432,25,2,9); //设置时钟,216Mhz
delay_init(216); //延时初始化
uart_init(115200); //串口初始化
LED_Init(); //初始化LED
TIM5_CH1_Cap_Init(0XFFFFFFFF,108-1); //以1MHZ的频率计数
while(1)
{
if(TIM5CH1_CAPTURE_STA&0X80) //成功捕获到了一次高电平
{
temp=TIM5CH1_CAPTURE_STA&0X3F;
temp*=0XFFFFFFFF; //溢出时间总和
temp+=TIM5CH1_CAPTURE_VAL; //得到总的高电平时间
printf("HIGH:%lld us\r\n",temp);//打印总的高点平时间
TIM5CH1_CAPTURE_STA=0; //开启下一次捕获
}
}
}
(1)自己定义一个 TIM5_CH1_Cap_Init(u32 arr, u16 psc)函数,调用函数HAL_TIM_IC_Init(&TIM5_Handler)进行时基部分的初始化。再调用HAL_TIM_IC_ConfigChannel(&TIM5_Handler,&TIM5_CH1Config,TIM_CHANNEL_1)设置TIM5输入捕获参数初始化。HAL_TIM_IC_Start_IT(&TIM5_Handler, TIM_CHANNEL_1); //开启TIM5的捕获通道1,并且开启捕获中断__HAL_TIM_ENABLE_IT(&TIM5_Handler, TIM_IT_UPDATE);//使能更新中断
(2)编写 HAL_TIM_IC_MspInit(TIM_HandleTypeDef *htim)函数,此函数会被HAL_TIM_IC_Init()调用。进行定时器5底层驱动,时钟使能,引脚配置。并开启TIM5中断,设置中断优先级。
(3)编写中断服务函数TIM5_IRQHandler (void),此函数内部又会调用 HAL_TIM_IRQHandler (&TIM5_Handler);//定时器公用处理函数
(4)此函数HAL_TIM_IRQHandler (&TIM5_Handler) 内部会对中断类型进行判断,会调用HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *htim)//定时器输入捕获中断处理回调函数和HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)//定时器更新中断(计数溢出)中断处理回调函数。而真正的逻辑会在这两个中断回调函数中编写。
在定时器更新中断(计数溢出)中断处理回调函数中:
(TIM5CH1_CAPTURE_STA&0X80)==0 为真,表示位[7] 位0,没有成功捕获;
TIM5CH1_CAPTURE_STA&0X40为真,则表示位[6] 位1,已经捕获到高电平;
(TIM5CH1_CAPTURE_STA&0X3F)==0X3F为真,则说明高电平太长了,定时器一直在溢出。然后TIM5CH1_CAPTURE_STA|=0X80; 标记成功捕获了一次。把输入捕获的值设为TIM5CH1_CAPTURE_VAL=0XFFFFFFFF;
在定时器输入捕获中断处理回调函数中:
若还未开始,第一次捕获到上升沿,首先把状态标识为和输入捕获的值清零。
TIM5CH1_CAPTURE_STA=0;
TIM5CH1_CAPTURE_VAL=0;
标记捕获了上升沿:TIM5CH1_CAPTURE_STA|=0X40;
此时,关闭定时器5:__HAL_TIM_DISABLE(&TIM5_Handler);
计数器的值设为0: __HAL_TIM_SET_COUNTER(&TIM5_Handler,0);
一定要先清除原来的设置: TIM_RESET_CAPTUREPOLARITY(&TIM5_Handler,TIM_CHANNEL_1);
定时器5通道1设置为下降沿捕获: TIM_SET_CAPTUREPOLARITY(&TIM5_Handler,TIM_CHANNEL_1,TIM_ICPOLARITY_FALLING);
使能定时器5 : __HAL_TIM_ENABLE(&TIM5_Handler);
若 (TIM5CH1_CAPTURE_STA&0X40) 为真,则标记捕获到一个下降沿 。
TIM5CH1_CAPTURE_STA|=0X80; 标记成功捕获到一次高电平脉宽
TIM5CH1_CAPTURE_VAL=HAL_TIM_ReadCapturedValue(&TIM5_Handler,TIM_CHANNEL_1);//获取当前的捕获值
TIM_RESET_CAPTUREPOLARITY(&TIM5_Handler,TIM_CHANNEL_1); //一定要先清除原来的设置!!
TIM_SET_CAPTUREPOLARITY(&TIM5_Handler,TIM_CHANNEL_1,TIM_ICPOLARITY_RISING);//配置TIM5通道1上升沿捕获
在主函数中:
if(TIM5CH1_CAPTURE_STA&0X80) //成功捕获到了一次高电平
{
temp=TIM5CH1_CAPTURE_STA&0X3F;
temp*=0XFFFFFFFF; //溢出时间总和
temp+=TIM5CH1_CAPTURE_VAL; //得到总的高电平时间
printf("HIGH:%lld us\r\n",temp); //打印总的高点平时间
TIM5CH1_CAPTURE_STA=0; //开启下一次捕获
}