修改 滴答时钟源Timebase Source 轻松重新定义HAL_InitTick函数:武侠大侠的进阶秘籍 ⚡️
亲爱的朋友们,今天我们将走进 STM32 的江湖,学习如何通过修改 Timebase Source
,重新定义 HAL_InitTick
这个弱函数。让我们像武林高手一样,轻松自如地掌控时基,决胜千里之外!😎
在 STM32 中,HAL_InitTick
是一个 弱函数(__weak
),意味着你可以在代码中重定义它。这一招就像是一个绝世武功的秘籍,能够让你根据不同的需求,自定义时基源,从而实现更精细的控制。今天,我会带你一步步揭开这门绝技的神秘面纱。
什么是 Timebase Source? ⏰
在 STM32 这片浩瀚的江湖中,Timebase Source 就像是一名神秘的大宗师,掌控着所有的时间滴答。你可能没怎么注意过它,但它的力量却时刻影响着你。它负责提供系统的滴答定时器(SysTick),也就是你在使用 HAL_Delay()
或 HAL_GetTick()
时最依赖的武器。
每当你调用 HAL_Delay()
,你的程序就像被大宗师点了穴道,定时开始计算出时间延迟。而在 STM32 中,默认的 Timebase Source 就是SysTick,不过如果你想要像武侠小说中的江湖大佬一样,不拘一格、自由掌控,那你完全可以选择其他定时器,来改变时基来源。Timebase Source 就是那把随时可以切换的“武器”。🗡️
Timebase Source 的作用:一键解锁系统滴答秘籍 🕰️
你问:“那 Timebase Source 到底能帮我做什么呢?”别着急,听我细细道来!😏
1. 提供精准的时间滴答:让你可以控制延迟、等待、时间流转 🕐
这招就像是掌门人指点迷津,让你轻松掌控程序中的时间流转。当你用 HAL_Delay()
来控制延时时,实际上是依赖 Timebase Source 提供的滴答信号。这不就像是某位大侠一剑穿心,每一滴血、每一秒钟都掌控得精准无误?
2. 任务调度:让 RTOS 和系统更快更稳 ⚡
如果你在江湖中混得久了,肯定知道FreeRTOS这位大侠。它需要时间基准来管理任务调度,而默认的 SysTick
为其提供了必要的时间滴答。如果你切换到 其他定时器,比如 TIM6
,就能为 FreeRTOS 提供更高精度的时基,做到任务调度更灵活、速度更快,如同大侠拔剑出鞘,快如闪电!
3. 低功耗模式:江湖也需要休养生息 🛌
武侠江湖中,谁不喜欢悠闲时光?但系统可不能总是忙碌不休,得学会休息和恢复。当 STM32 进入低功耗模式时,SysTick
不再工作,但你可以通过选择 TIM6
或 TIM7
等定时器,使其依旧为你提供时基信号,让你在休眠时依然能够维持精确的时间管理。这招就像金庸大侠笔下的隐士高人,虽深居简出,但一旦出手,便是风云变化!
什么是 HAL_InitTick
? 🕰️
HAL_InitTick
是 STM32 HAL 库中用于初始化系统时基的函数。它的作用是配置一个定时器(通常是 SysTick)来生成系统时钟的滴答信号。这些滴答信号是 HAL 库(比如 HAL_Delay()
)等功能所依赖的基准。
然而,这个函数是弱函数(__weak
),意味着你可以在应用程序中对它进行重定义,从而实现定制化的时基源。
__weak HAL_StatusTypeDef HAL_InitTick(uint32_t TickPriority)
{
/* Configure the SysTick to have interrupt in 1ms time basis*/
if (HAL_SYSTICK_Config(SystemCoreClock / (1000U / uwTickFreq)) > 0U)
{
return HAL_ERROR;
}
/* Configure the SysTick IRQ priority */
if (TickPriority < (1UL << __NVIC_PRIO_BITS))
{
HAL_NVIC_SetPriority(SysTick_IRQn, TickPriority, 0U);
uwTickPrio = TickPriority;
}
else
{
return HAL_ERROR;
}
/* Return function status */
return HAL_OK;
}
如上所示,HAL_InitTick
配置了系统时基源,通常是 SysTick,并设置了中断优先级。在 CubeMX
中修改 Timebase Source
后,你将能够定制时基来源,选择 SysTick
以外的定时器(比如 TIM6
),实现更高精度或低功耗的控制。
**如何通过修改 Timebase Source
轻松重新定义 HAL_InitTick
1. 打开 CubeMX,切换时基来源 🔧
首先,大家得进入 CubeMX,这就像是你开启了武林秘籍,进入了更深的江湖。
- 打开 CubeMX,进入 System Core 部分的 Timebase Source 配置。
- 你会看到一个默认选项——SysTick,不过如果你想学更高深的武技,可以选择
TIM6
、TIM7
或其他定时器,带来更强大的控制力。
2. 生成代码,加入初始化配置 💻
一旦你选择了新的 Timebase Source,点击 Project 生成代码,就像是你从武馆里拿到了新武器,准备出招了!
生成代码后,你会发现会生成一个stm32f4xx_hal_timebase_tim.c,其中 HAL_InitTick()
函数里自动为你配置了你所选择的时基源。如果你选择了 TIM6
,代码就会像这样:
HAL_StatusTypeDef HAL_InitTick(uint32_t TickPriority) {
RCC_ClkInitTypeDef clkconfig;
uint32_t uwTimclock, uwAPB1Prescaler = 0U, uwPrescalerValue = 0U, pFLatency;
HAL_StatusTypeDef status;
__HAL_RCC_TIM6_CLK_ENABLE(); // 启用TIM6时钟
HAL_RCC_GetClockConfig(&clkconfig, &pFLatency); // 获取时钟配置
uwAPB1Prescaler = clkconfig.APB1CLKDivider; // 获取APB1分频系数
uwTimclock = (uwAPB1Prescaler == RCC_HCLK_DIV1) ? HAL_RCC_GetPCLK1Freq() : 2UL * HAL_RCC_GetPCLK1Freq(); // 计算TIM6时钟频率
uwPrescalerValue = (uint32_t)((uwTimclock / 1000000U) - 1U); // 计算预分频值
htim6.Instance = TIM6; // 设置TIM6实例
htim6.Init.Period = (1000000U / 1000U) - 1U; // 设置周期为1ms
htim6.Init.Prescaler = uwPrescalerValue; // 设置预分频值
htim6.Init.ClockDivision = 0;
htim6.Init.CounterMode = TIM_COUNTERMODE_UP;
htim6.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE;
status = HAL_TIM_Base_Init(&htim6); // 初始化TIM6
if (status == HAL_OK) {
status = HAL_TIM_Base_Start_IT(&htim6); // 启动TIM6并使能中断
if (status == HAL_OK) {
HAL_NVIC_EnableIRQ(TIM6_DAC_IRQn); // 启用TIM6中断
if (TickPriority < (1UL << __NVIC_PRIO_BITS)) {
HAL_NVIC_SetPriority(TIM6_DAC_IRQn, TickPriority, 0U); // 配置中断优先级
uwTickPrio = TickPriority;
} else {
status = HAL_ERROR; // 如果优先级无效,返回错误
}
}
}
return status; // 返回函数状态
}
这段代码就像是给你传授了定时器控制秘籍,让你能够根据需求灵活地使用它。
3. 修改中断处理函数 🛡️
这招可以比喻为你用一根暗器来打败对手!如果你选择了 TIM6
,SysTick_Handler()
这个经典的武技就会变成 TIM6_DAC_IRQHandler()
。别担心,这只是换了一个名字,能力却依旧强大!
void TIM6_DAC_IRQHandler(void)
{
HAL_TIM_IRQHandler(&htim6); // 处理中断
}
为什么要更改 Timebase Source?一切为了更强大的武力! 💥
你问:“大侠,我现在用 SysTick
也挺好的,改了有什么好处?” 哼!你不尝试怎么知道?一旦你掌握了这些招数,你将走得更远!
1. 低功耗模式:让你的 MCU 躲在暗处悄悄发力 💤
当 STM32 进入低功耗模式时,SysTick
停止工作,TIM6 仍能继续工作。就像是你在江湖上隐匿一段时间,别人以为你退隐了,结果你已经在背后悄悄做足了准备,等时机一到就能一鸣惊人!
2. 更灵活的定时控制:像大侠拔剑一样迅速 ⚔️
TIM6
和 TIM7
能提供更灵活的时基控制,你可以自由设置它们的分频因子、时钟源。就像一位武林高手,手中的武器不仅威力无穷,且随时可以调整到最佳状态!
3. FreeRTOS 的任务调度:让你快人一步! ⏳
如果你正在使用 FreeRTOS,切换 Timebase Source
后,你可以获得更高精度的时基,避免了 SysTick
在某些高精度任务下的限制。就像一个身手敏捷的大侠,任务调度更精准,分秒必争!
正点原子的官方例程:双管齐下,兼顾 FreeRTOS 与 HAL 库基准时钟
在正点原子(STM32)官方例程中,常常会遇到如下操作:
void SysTick_Handler(void)
{
HAL_IncTick(); // 增加 HAL 库的 Tick 值
if (xTaskGetSchedulerState() != taskSCHEDULER_NOT_STARTED) // 判断是否开始调度
{
xPortSysTickHandler(); // FreeRTOS 的滴答处理
}
}
这段代码巧妙地将 FreeRTOS 和 HAL 库的时间基准结合在同一个 SysTick_Handler
中。我们来看看它是如何工作的:
HAL_IncTick()
:这段代码用来更新 HAL 库的uwTick
,即为 HAL 库提供时基。xTaskGetSchedulerState()
:这一步判断 FreeRTOS 调度器是否已经启动,只有在启动后,才会调用xPortSysTickHandler()
来处理 FreeRTOS 的任务调度。
通过这种方式,FreeRTOS 和 HAL 库共享一个系统滴答定时器,互不干扰,优雅地解决了两个系统对时基的需求。这就像是江湖中的两大门派,不仅能够和平共处,还能齐心协力,打下一片江山。