前言:
下面的以通用定时器为例,当然高级定时器具有通用定时器的全部功能
ICP1S:上面经过分频后的信号;这里的捕获指的是产生一个捕获事件。
一:输入捕获功能
1:简历
IC(Input Capture)输入捕获
输入捕获模式下,当通道输入引脚出现指定电平跳变时,当前CNT的值将被锁存到CCR中,可用于测量PWM波形的频率、占空比、脉冲间隔、电平持续时间等参数
每个高级定时器和通用定时器都拥有4个输入捕获通道
可配置为PWMI模式,同时测量频率和占空比
可配合主从触发模式,实现硬件全自动测量
2:频率测量
对于STM32测频率而言,它也是只能测量数字信号的
频率 : 是单位时间(1s)内周期性过程重复、循环或振动的次数
测频法 : 一般情况下T为1s,符合频率的定义,-----适合测量高频信号
测周法原理 : 先对信号的周期T进行测量,然后根据f=1/T而得到信号的频率。测量2个上升沿的时间, 没有一个精度无穷大的秒表来测量时间,所以使用这个方法来测力量(见 : 3:测周法)-----适合测量高频信号
当N越大时, 正负1的误差对我们的影响就越小
当: 待测频率<中界频率----使用测周法
待测频率>中界频率----使用测频法
3:HAL库测量原理
与上面的方法不同的是我们测量的是:一个上升沿和一个下降沿。
那我们知道捕获一个完整脉冲频率,就是需要捕获一个上升沿和一个下降沿。我们这里测的是高电平,使用先捕获上升沿在捕获下降沿。
(STM32F1C8T6微控制器中的通用定时器(TIM2、TIM3、TIM4和TIM5)都是16位的。这些定时器可以执行多种功能,包括基本的定时功能、输入捕获、输出比较以及PWM信号生成。)
当捕获到上升沿时,清空计数值,使其从0开始计数,之后分成两种情况:
**情况1:**比较常规,在捕获到一个下降沿的时候,在一个计数周期内完成采集(没有产生溢出),此时下降沿所得到的计数值就为整个脉冲周期所计数次数。没有溢出计数器计的个数为:CCRX2
情况2:比较特殊就是上升沿和下降沿之间的间隔时间太长了,需要计数溢出好几次才捕获到下降沿,此时需要加上溢出次数的用时才是整个脉冲的周期。溢出计数器计的个数为:N*(ARR+1)+CCRX2
(ARR+1)=====>溢出一次的计数器计数的个数;*N溢出N次计数器计数的个数
上面是的计数器计数的个数,实际上也就是高电平的个数
计数频率计算:Ft/(PSC+1) Ft===>定时器频率(我们的为72MHZ)
周期(计一个数的时间):1/频率 或者 (PSC+1)/Ft
4:主从触发模式
主模式 : 可以将定时器内部的信号,映射到TRGO引脚 , 用于触发别的外设,所以这部分叫做主模式
从模式 : 就是接收其他外设或者自身外设的一些信号, 用于控制自身定时器的运行,也就是被别的信号控制
触发源选择 : 就是选择从模式的触发信号源的, 从模式的一部分, 触发源选择,选择指定的一个信号,得到TRGI,TRGI去触发从模式,从模式可以在从模式列表里,选择一项操作来自动执行
5:结构
输入捕获模式基本结构
PWMI模式的结构
:HAL配置
//使能输入捕获中断--(CNT==ARR溢出产生的中断)
HAL_TIM_IC_Start_IT(&HandleTIM4CH1,TIM_CHANNEL_1);
//使能定时器中断 开启输入捕获的事件中断
__HAL_TIM_ENABLE_IT(&HandleTIM4CH1,TIM_IT_UPDATE);
输入捕获的分频系数:设置多少,当来的信号符合我们捕获触发方式选择的时候就会产生一个捕获事件。
eg:选择上升沿触发,分频系数选择1:当每来一个上升沿信号的时候,就会触发我们的捕获信号。
选择2,就是2个上升沿触发一次捕获事件。选择下降沿触发,那么就是下降沿触发捕获事件。
二:代码
A:测量高电平的时间
按键:一段接高电平,一段接IO口 (这个样子按下去才是高电平)
1MHZ--精度
计数频率计算:Ft/(PSC+1) 72MHZ/72=1MHZ 对应的事件单位为1us(微秒),也就是我们计一个数需要1us
频率(f)是单位时间内周期性事件发生的次数。
频率与时间(T)的关系可以用以下的数学公式表示:
f = 1/T
这个公式告诉我们,频率是时间的倒数。也就是说,如果我们知道一个事件的频率,我们可以求出它发生的周期时间。因此,要找出1MHZ对应的时间单位,我们只需对1MHZ求倒数即可。
计算结果为:1MHZ对应的周期时间是 1e-06 秒。
所以,1MHZ对应的时间单位是 微秒(us)。
uint8_t g_timxchy_cap_sta = 0; /* 输入捕获状态 */
uint16_t g_timxchy_cap_val = 0; /* 输入捕获值 */
【5:0】11 1111=63 使用8为最多可以溢出63次,每溢出一次就给输入捕获状态加1
/* 输入捕获状态(g_timxchy_cap_sta)
* [7] :0,没有成功的捕获;1,成功捕获到一次.
* [6] :0,还没捕获到高电平;1,已经捕获到高电平了.
* [5:0]:捕获高电平后溢出的次数,最多溢出63次,所以最长捕获值 = 63*65536 + 65535 = 4194303
* 按1us的计数频率,最长溢出时间为:4194303 us, 约4.19秒
函数熟悉
__HAL_TIM_DISABLE(&HandleTIM4CH1); /* 关闭定时器4 */__HAL_TIM_SET_COUNTER(&HandleTIM4CH1, 0); /* 定时器4计数器清零 */TIM_RESET_CAPTUREPOLARITY(&HandleTIM4CH1, TIM_CHANNEL_1); /* 一定要先清除原来的设置!! */TIM_SET_CAPTUREPOLARITY(&HandleTIM4CH1, TIM_CHANNEL_1, TIM_ICPOLARITY_FALLING); /* 定时器4通道1设置为下降沿捕获 */__HAL_TIM_ENABLE(&HandleTIM4CH1); /* 使能定时器4 */
//读取计数器的值
__HAL_TIM_GET_COUNTER(&HandleTIM4CH1);
注意事项:
TIM_ICinit.ICSelection=TIM_ICSELECTION_DIRECTTI; /* 映射到TI1上 */
上面的代码是映射到TI1上,不是选择通道1;任何通道都ok的
这个会不会产生2个中断,一个是CNT=ARR产生的中断;一个是捕获事件产生的中断(上升沿或者下降沿捕获)
1:CNT=ARR
使能输入捕获中断--(CNT==ARR溢出产生的中断)
HAL_TIM_IC_Start_IT(&HandleTIM4CH1,TIM_CHANNEL_1);
回调函数
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
2:一个是捕获事件产生的中断(上升沿或者下降沿捕获)
使能定时器中断 开启输入捕获的事件中断
__HAL_TIM_ENABLE_IT(&HandleTIM4CH1,TIM_IT_UPDATE);
回调函数
void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *htim)
HAL bug
我们在使用 TIM_RESET_CAPTUREPOLARITY(&HandleTIM4CH1, TIM_CHANNEL_1); 清除原来的配置(切换捕获极性)的时候,HAL些的有一点小问题。
1:我们从官网下载的HAL都是只能读不能修改的,我们首先把他的权限获得,在txt文件中点击属性,然后去掉只读的。
有问题的代码(官网下载的)
#define TIM_RESET_CAPTUREPOLARITY(__HANDLE__, __CHANNEL__) \(((__CHANNEL__) == TIM_CHANNEL_1) ? ((__HANDLE__)->Instance->CCER &= ~(TIM_CCER_CC1P | TIM_CCER_CC1NP))) :\((__CHANNEL__) == TIM_CHANNEL_2) ? ((__HANDLE__)->Instance->CCER &= ~(TIM_CCER_CC2P | TIM_CCER_CC2NP)) :\((__CHANNEL__) == TIM_CHANNEL_3) ? ((__HANDLE__)->Instance->CCER &= ~(TIM_CCER_CC3P)) :\((__HANDLE__)->Instance->CCER &= ~(TIM_CCER_CC4P)))
需要改为:
#define TIM_RESET_CAPTUREPOLARITY(__HANDLE__, __CHANNEL__) \(((__CHANNEL__) == TIM_CHANNEL_1) ? ((__HANDLE__)->Instance->CCER &= ~(TIM_CCER_CC1P | TIM_CCER_CC1NP)) :\((__CHANNEL__) == TIM_CHANNEL_2) ? ((__HANDLE__)->Instance->CCER &= ~(TIM_CCER_CC2P | TIM_CCER_CC2NP)) :\((__CHANNEL__) == TIM_CHANNEL_3) ? ((__HANDLE__)->Instance->CCER &= ~(TIM_CCER_CC3P)) :\((__HANDLE__)->Instance->CCER &= ~(TIM_CCER_CC4P)))
完整代码:
//输入捕获功能
TIM_HandleTypeDef HandleTIM4CH1;void IC_TIM4_CH1_init(uint16_t arr,uint16_t psc)
{TIM_IC_InitTypeDef TIM_ICinit={0};HandleTIM4CH1.Instance=TIM4; //基地址HandleTIM4CH1.Init.Period=arr;HandleTIM4CH1.Init.Prescaler=psc;HandleTIM4CH1.Init.CounterMode=TIM_COUNTERMODE_UP; //向上计数TIM_ICinit.ICFilter=0; //滤波器TIM_ICinit.ICPolarity=TIM_ICPOLARITY_RISING; //极性选择上升沿触发TIM_ICinit.ICPrescaler=TIM_ICPSC_DIV1; //分频1,就是每个上升沿我们都计一次数TIM_ICinit.ICSelection=TIM_ICSELECTION_DIRECTTI; /* 映射到TI1上 */HAL_TIM_IC_Init(&HandleTIM4CH1);/*配置输入映射通道*/HAL_TIM_IC_ConfigChannel(&HandleTIM4CH1, &TIM_ICinit, TIM_CHANNEL_1);//使能输入捕获中断--(CNT==ARR溢出产生的中断)HAL_TIM_IC_Start_IT(&HandleTIM4CH1,TIM_CHANNEL_1);//使能定时器中断 开启输入捕获的事件中断__HAL_TIM_ENABLE_IT(&HandleTIM4CH1,TIM_IT_UPDATE);}void HAL_TIM_IC_MspInit(TIM_HandleTypeDef *htim)
{if(htim->Instance==TIM4){__HAL_RCC_GPIOB_CLK_ENABLE() ;__HAL_RCC_TIM4_CLK_ENABLE();HAL_NVIC_SetPriority(TIM4_IRQn, 1, 3); /* 抢占1,子优先级3 */HAL_NVIC_EnableIRQ(TIM4_IRQn); /* 开启ITMx中断 */GPIO_InitTypeDef GPIO_InitType;GPIO_InitType.Mode=GPIO_MODE_AF_PP; /*复用推完输出*/GPIO_InitType.Pin=GPIO_PIN_6;GPIO_InitType.Pull=GPIO_PULLUP; //下拉 GPIO_InitType.Speed=GPIO_SPEED_FREQ_HIGH;HAL_GPIO_Init(GPIOB,&GPIO_InitType); }
}/* 输入捕获状态(g_timxchy_cap_sta)* [7] :0,没有成功的捕获;1,成功捕获到一次.* [6] :0,还没捕获到高电平;1,已经捕获到高电平了.* [5:0]:捕获高电平后溢出的次数,最多溢出63次,所以最长捕获值 = 63*65536 + 65535 = 4194303* 注意:为了通用,我们默认ARR和CCRy都是16位寄存器,对于32位的定时器(如:TIM5),也只按16位使用* 按1us的计数频率,最长溢出时间为:4194303 us, 约4.19秒** (说明一下:正常32位定时器来说,1us计数器加1,溢出时间:4294秒)*/
uint8_t g_timxchy_cap_sta = 0; /* 输入捕获状态 */
uint16_t g_timxchy_cap_val = 0; /* 输入捕获值 *//* 定时器5中断服务函数 */
void TIM4_IRQHandler(void)
{HAL_TIM_IRQHandler(&HandleTIM4CH1); /* 定时器HAL库共用处理函数 */}/* 定时器输入捕获中断处理回调函数,捕获事件产生的中断 */
void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *htim)
{if (htim->Instance == TIM4){if ((g_timxchy_cap_sta & 0X80) == 0) /* 还没有成功捕获 */{ //捕获到高电平了,进入这个里面就是捕获到了下降沿if (g_timxchy_cap_sta & 0X40) /* 捕获到一个下降沿 */{g_timxchy_cap_sta |= 0X80; /* 标记成功捕获到一次高电平脉宽 */g_timxchy_cap_val = HAL_TIM_ReadCapturedValue(&HandleTIM4CH1, TIM_CHANNEL_1); /* 获取当前的捕获值 */TIM_RESET_CAPTUREPOLARITY(&HandleTIM4CH1, TIM_CHANNEL_1); /* 一定要先清除原来的设置 */TIM_SET_CAPTUREPOLARITY(&HandleTIM4CH1, TIM_CHANNEL_1, TIM_ICPOLARITY_RISING); /* 配置TIM4通道1上升沿捕获 */}//没有捕获到高电平else /* 还未开始,第一次捕获上升沿 */{ //捕获高电平的2个任务:清空计数值,使他从0开始计数g_timxchy_cap_sta = 0; /* 清空 */g_timxchy_cap_val = 0;g_timxchy_cap_sta |= 0X40; /* 标记捕获到了上升沿 */__HAL_TIM_DISABLE(&HandleTIM4CH1); /* 关闭定时器4 */__HAL_TIM_SET_COUNTER(&HandleTIM4CH1, 0); /* 定时器4计数器清零 */TIM_RESET_CAPTUREPOLARITY(&HandleTIM4CH1, TIM_CHANNEL_1); /* 一定要先清除原来的设置!! */TIM_SET_CAPTUREPOLARITY(&HandleTIM4CH1, TIM_CHANNEL_1, TIM_ICPOLARITY_FALLING); /* 定时器4通道1设置为下降沿捕获 */__HAL_TIM_ENABLE(&HandleTIM4CH1); /* 使能定时器4 */}}}
}/* 定时器更新中断回调函数 CNT==ARR产生的中断函数*/
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{if (htim->Instance == TIM4){if ((g_timxchy_cap_sta & 0X80) == 0) /* 还未成功捕获 */{if (g_timxchy_cap_sta & 0X40) /* 已经捕获到高电平了 */{if ((g_timxchy_cap_sta & 0X3F) == 0X3F) /* 高电平太长了 */{TIM_RESET_CAPTUREPOLARITY(&HandleTIM4CH1, TIM_CHANNEL_1); /* 一定要先清除原来的设置 */TIM_SET_CAPTUREPOLARITY(&HandleTIM4CH1, TIM_CHANNEL_1, TIM_ICPOLARITY_RISING);/* 配置TIM4通道1上升沿捕获 */g_timxchy_cap_sta |= 0X80; /* 标记成功捕获了一次 */g_timxchy_cap_val = 0XFFFF;}else /* 累计定时器溢出次数 */{g_timxchy_cap_sta++;}}}}
}#include "stm32f1xx_hal.h"
#include "rcc.h"
#include "led.h"
#include "delay.h"
#include "OLED.h"
#include "wwdg.h"
#include "key.h"
#include "IC.h"
#include "pwm.h"int main(void)
{uint32_t temp = 0;HAL_Init(); /* 初始化HAL库 */sys_stm32_clock_init(RCC_PLL_MUL9); /* 设置时钟, 72Mhz */delay_init(72); /* 延时初始化 */LED_Init(); /* LED初始化 */LED_Exit_Init();OLED_Init();//IC_TIM2_CH2_init(65536-1,72-1);//TIM3_CH2_init(72-1,1000-1); //频率:72000000/7200/1000=10s=10 000msTIM3_CH2_init(7200-1,10000-1); //频率:72000000/7200/1000=10s=10 000msOLED_ShowString(1, 1, "Freq:00000Hz");OLED_ShowString(2, 1, "Duty:00%");IC_TIM4_CH1_init(0xFFFF,72-1);while (1){if (g_timxchy_cap_sta & 0X80) /* 成功捕获到了一次高电平 */{temp = g_timxchy_cap_sta & 0X3F;temp *= 65536; /* 溢出时间总和 */temp += g_timxchy_cap_val; /* 得到总的高电平时间 */g_timxchy_cap_sta = 0; /* 开启下一次捕获*/}OLED_ShowNum(1, 6,temp,8);}
}
不使用一个定时器? 输出比较使用TMI3(输出pwm), 输入捕获使用TIM4(测频率)
4个输入捕获和输出比较通道(CH),共用4个CCR寄存器, 它们的CH1到CH4,4个通道的引脚,也是共用的. 对于同一个定时器,输入捕获和输出比较只能使用其中一个,不能同时使用