STM32HAL库使用HAL_GetTick()和形参Timeout实现超时退出
Timeout
在使用STM32CubeMX生成的HAL库模板编程时,我们会发现部分用于轮询模式的函数会有一个入口参数Timeout, 注释为超时时间.
例如串口轮询模式的发送接收函数的最后一个参数
HAL_StatusTypeDef HAL_UART_Transmit(UART_HandleTypeDef *huart, const uint8_t *pData, uint16_t Size, uint32_t Timeout);
HAL_StatusTypeDef HAL_UART_Receive(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size, uint32_t Timeout);
Timeout参数定义了在执行如串口接收、SPI传输等函数时,等待操作完成的最长时间。如果在这个时间内操作没有完成, 函数就会停止等待并返回一个HAL_StatusTypeDef类型的状态值HAL_TIMEOUT,表明操作因超时而未完成, 实现跳出本句代码继续执行后续代码。
通过分析程序我们发现函数是通过这句代码 (HAL_GetTick() - Tickstart) > Timeout) 来实现函数内处理超时跳出,防止系统瘫痪。
HAL_GetTick()
HAL_GetTick()函数是STM32 HAL库中的一个重要函数,用于获取系统启动以来的毫秒级计数, 该函数基于SysTick中断实现,每当SysTick定时器定时中断时,全局变量uwTick会自增1,表示当前系统时间增加了1ms。因此,通过读取uwTick的值,HAL_GetTick()能够返回自系统启动以来经过的毫秒数。
__weak uint32_t HAL_GetTick(void)
{
return uwTick;//返回自系统启动以来经过的毫秒
}
写到这里是笔者产生了一个疑问, 我们在图形化配置CubeMX时STM32的NVIC中SysTick的抢占优先级默认为15, 也就是默认配置优先级为最低, 如此的话我们在进入到一些外设中断的时候SysTick中断还能精准的计时吗?
经过探索笔者查询到HAL_GetTick()的机制: HAL_GetTick()函数是基于SysTick定时器实现的,用于获取系统启动以来的毫秒级计数。这一机制主要依赖于SysTick定时器的持续计数,而非其中断优先级。尽管SysTick中断的优先级最低,但SysTick定时器本身是一个24位的倒计数定时器,当计到0时会自动重装载定时初值并继续计数。因此,HAL_GetTick()函数能够保持精准计时,不受中断优先级的影响。
超时跳出的原理
通过分析函数HAL_UART_Transmit (), 我们发现函数开始会定义一个变量tickstart,这个变量就是用来存储函数刚开始执行时系统的毫秒数
uint32_t tickstart = 0U;
tickstart = HAL_GetTick();//接收当前的系统毫秒数
在HAL_UART_Transmit中又调用了函数UART_WaitOnFlagUntilTimeout,我们在这个函数中会看到以下代码:
if (((HAL_GetTick() - Tickstart) > Timeout) || (Timeout == 0U))
{
return HAL_TIMEOUT;
}
HAL_GetTick()的返回值为当前时刻系统的毫秒数,
而Tickstart又是HAL_UART_Transmit传递到UART_WaitOnFlagUntilTimeout形参, 里面存储的是HAL_UART_Transmit开始执行时系统毫秒数.
也就是说如果 我们在函数入口输入Timeout的参数为1000,也就是1000毫秒,则当HAL_GetTick() - Tickstart > 1000时就会跳出函数, 然后返回一个HAL_TIMEOUT。
此外我们发现如果Timeout给定的参数如果是0, 会直接超时. 这就导致函数不会执行任何指令而直接退出并返回HAL_TIMEOUT。
超时函数实现
通过模仿HAL库函数的超时机制, 我们也可以实现自己的超时函数, 下面是一个例子;
HAL_StatusTypeDef prototype(uint32_t Timeout)//prototype为函数名
{
uint32_t tickstart = 0U;
tickstart = HAL_GetTick();//接收当前的系统毫秒数
//可以将超时跳出函数放在while或者for循环中, 用于需要重复执行多次的操作, 在每次操作时进行超时的判断
while
{
/*需要执行的操作*/
if(((HAL_GetTick() - Tickstart) > Timeout) || (Timeout == 0U))
return HAL_TIMEOUT;
}
}
for(判断)
{
/*需要执行的操作*/
if(((HAL_GetTick() - Tickstart) > Timeout) || (Timeout == 0U))
return HAL_TIMEOUT;
}