一、定时器功能之高精度定时(微秒级)
我们常用的延时函数中无论是HAL_Delay还是vTaskDelay()函数都是毫秒级的定时,我们可以借助定时器实现一个微秒级更高精度的延时函数。这个定时器不会影响FreeRtos的任务切换
这里就是用定时器的计数功能来实现定时
前面我们设置了Tim2的分频值为72/(71+1) = 1MHz,也就是1000000Hz,那么频率就是1/1000000 = 1us,也就是1us(1微秒)计数一次,前面我们设置的ARR自动重载的值为999,也就是999+1 = 1000us会重新加载CNT的值为0
1、原理:
t_old原计数值 与 t_now当下计数值之间不可以超过一个溢出周期的前提下。
因为原计数与当下计数值之间在不存在延时操作的情况下,不可能出现超过一个溢出周期的。
上面就是两种延时周期的运算方式
配置TIM2时钟,用它的基本计数功能就可以,不需要去管通道,因为通道是做PWM或者输入捕获的,我们这里只需要它最基本的定时计数,前面的章节我们用TIM2还做了PWM,占用了通道,但是这不影响基本计数定时功能
//实现更高精度的延时函数(微秒级)
void tim2_u_delay(uint32_t us)
{HAL_TIM_Base_Start(&htim2);//启用定时器基本计数功能。//1.获取ARR自动装载寄存器中的值:uint32_t load_ARR = __HAL_TIM_GET_AUTORELOAD(&htim2);//2.获取一个起始检测点的计数值:uint32_t t_start_CNT = __HAL_TIM_GET_COUNTER(&htim2);//3.检测周期:uint32_t t_current_CNT = 0;uint32_t ticks = 0;while (true){/* 检测 */t_current_CNT = __HAL_TIM_GET_COUNTER(&htim2);if(t_current_CNT >= t_start_CNT){ticks += (t_current_CNT - t_start_CNT);}else{ticks += (load_ARR) - t_start_CNT + 1 + t_current_CNT;}//更新起始点start:t_start_CNT = t_current_CNT;if(ticks >= us){break;}}}
二、输入捕获
1、输入捕获原理
当定时器启动输入捕获模式,当外部信号出现变化时,此时会记录计数器CNT的值,并保存到CCR1,此时会发生中断,可以在中断回调函数中读取出CCR1此时的值,也就是读取出CNT计数器的值,这个时间和ARR的值相比看一下占用周期的比例,就可以完整的得出外部信号的波形
举例使用:监测脉冲宽度,也就是记录脉冲的时间
这里将一个信号分两次接入chn1和chn2,chn1捕获上升沿,chn2捕获下降沿,这样分别捕获的时间会再CCR1和CCR2中,上面的计算公式中的分辨率就是计数器CNT的频率,这里假设PSC出来的是1MHZ,也就是1秒1000000次,设置ARR为1000,CNT只是记录前面的PSC频率的计数,此时1MHZ,1/1000000 = 1us,也就是1us记录一次,这里的分辨率就是1us
通道1和通道2是一对,通道3和通道4是一对,这样一对中,一个通道监测上升沿,一个通道监测下降沿,就可以判断一个高电平的时长
2、典型应用
1、 脉宽测量:通过捕获信号的上升沿和下降沿,可以测量信号的高电平或低电平持续时间。
3、周期测量:通过两次上升沿或下降沿之间的时间差,可以测量信号的周期。
4、频率测量:通过周期测量,可以计算出信号的频率。
3、输入捕获HAL库接口函数API:
1.启动输入捕获:HAL_TIM_IC_Start
HAL_StatusTypeDef HAL_TIM_IC_Start(TIM_HandleTypeDef *htim, uint32_t Channel);
HAL_StatusTypeDef HAL_TIM_IC_Start_IT(TIM_HandleTypeDef *htim, uint32_t Channel);
1功能:启动定时器通道的输入捕获功能:
2功能:启动定时器通道的输入捕获功能并且开启捕获中断。
参数:
htim:即要启动的定时器的句柄。
Channel:启动输入捕获的通道是哪个。返回值:HAL_OK: 操作成功。HAL_ERROR: 操作失败。HAL_BUSY: 定时器资源忙
2.停止输入捕获:HAL_TIM_IC_Stop
HAL_StatusTypeDef HAL_TIM_IC_Stop(TIM_HandleTypeDef *htim, uint32_t Channel);
HAL_StatusTypeDef HAL_TIM_IC_Stop_IT(TIM_HandleTypeDef *htim, uint32_t Channel);
1.功能:停止定时器通道的输入捕获功能:
2功能:停止定时器通道的输入捕获功能并且关闭捕获中断。
参数:
htim:即要停止的定时器的句柄。
Channel:停止输入捕获的通道是哪个。
返回值:HAL_OK: 操作成功。HAL_ERROR: 操作失败。HAL_BUSY: 定时器资源忙
3.输入捕获配置通道:HAL_TIM_IC_ConfigChannel,相应的输入参数也可直接在CubMX直接配置。
HAL_StatusTypeDef HAL_TIM_IC_ConfigChannel(TIM_HandleTypeDef *htim, TIM_IC_InitTypeDef *sConfig, uint32_t Channel);
功能:配置输入捕获通道
参数:
htim: 指向 TIM_HandleTypeDef 结构体的指针,该结构体包含了定时器的配置信息。
sConfig: 指向 TIM_IC_InitTypeDef 结构体的指针,该结构体用于配置输入捕获通道的参数。
Channel: 要配置的定时器通道。可以是 TIM_CHANNEL_1, TIM_CHANNEL_2, TIM_CHANNEL_3, 或 TIM_CHANNEL_4。TIM_IC_InitTypeDef 结构体用于设置输入捕获通道的参数,主要包括:
ICPolarity: 输入捕获极性。可以是 TIM_ICPOLARITY_RISING(上升沿)或 TIM_ICPOLARITY_FALLING(下降沿),或者 TIM_ICPOLARITY_BOTHEDGE(两边缘)。
ICSelection: 输入捕获选择。可以是 TIM_ICSELECTION_DIRECTTI(直接输入)或 TIM_ICSELECTION_INDIRECTTI(间接输入)。
ICPrescaler: 输入捕获预分频器。可以是 TIM_ICPSC_DIV1(无分频),TIM_ICPSC_DIV2(除2),TIM_ICPSC_DIV4(除4),或 TIM_ICPSC_DIV8(除8)。
ICFilter: 输入捕获滤波器值,用于过滤输入信号中的噪声。
返回值HAL_OK: 配置成功。HAL_ERROR: 配置失败。
4.当捕获成功之后的中断回调函数,直接重写这个函数就可以:
__weak void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *htim)
//此函数是捕获之后的中断回调函数,是一个弱函数。
//当捕获发生后,可以重新定义相应的逻辑。
//注意:此中断回调函数,需要要执行HAL_TIM_IC_Start_IT时才会被调用。
5、超声波测距驱动(输入捕获实例)
原理:启动超声波就是给Trig引脚通10us的高电平,然后超声波左侧就会发出,这个超声波的频率是40KHZ,发出8个周期,也就是0.2ms,超声波发出之后,Echo引脚就会自动拉高电平,当超声波回来以后,也就是右侧的接收超声波的8个周期完成,echo就会把电平拉低
实际就是超声波发出后,echo拉高电平,超声波接收完成,echo拉低电平,此时只需要监测echo处于高电平的时间就可以计算出距离
距离=声速(340m/s)*传播时间 / 2
echo可以维持最大38ms,也就是超出了量程,飞了
通道1选择上升沿直接监测,通道2选择下降沿间接监测,就可以获取出Echo处于高电平的周期
1、选择通道捕获模式
通道1选择输入直接捕获模式,通道2选择输入间接捕获模式
2、选择边沿监测模式
通道1选择上升沿监测,通道2选择下降沿监测
3、选择分频系数
4、整体
Input Filter (4 bits value)是输入滤波,设置的最大值15
配置PD15为输出模式,接Trig
PD14是TIM4的chnl3,接Echo
前面说了这个超声波测距模块的Echo维持高电平的时间最大为38ms,所以这里就要设置一个周期要大于38ms,这里设置了分频为72/(71+1) = 1MHz,也就是1/1000000 = 1us,也就是1us的时候CNT计数一次,38ms = 38000us,所以只要ARR大于38000就可以了,这里我设置了65535,完全可以包含38ms