STM32源码阅读记录之HAL库(SystemTick)

问题

  1. HAL_Delay是怎么做到可以延迟控制的?

分析记录

步骤01:看函数本身

void HAL_Delay(uint32_t Delay);

/**
  * @brief 此函数根据变量递增提供最小延迟(以毫秒为单位)。
  * @note 在默认的实现中,SysTick计时器是时基的来源。它用于以固定的时间间隔生成中断,其中 uwTick 递增
  * @note 此函数声明为__weak在用户文件中出现其他实现时要覆盖。
  * @param Delay 指定延迟时间长度(以毫秒为单位)。
  * @retval None
  */
__weak void HAL_Delay(uint32_t Delay)
{
  // 获取一个计数值,简单来说就是一个记录当前时刻的记录值
  uint32_t tickstart = HAL_GetTick();
 
  uint32_t wait = Delay;

  /* 添加频率以保证最短的等待时间 */
  if (wait < HAL_MAX_DELAY)
  {
    wait += (uint32_t)(uwTickFreq);
  }
  //  死循环,直到不满足要求时结束
  while ((HAL_GetTick() - tickstart) < wait)
  {
  }
}

uint32_t HAL_GetTick(void)

/**
  * @brief 提供以毫秒为单位的刻度值。
  * @note  此函数声明为__weak在用户文件中出现其他实现时要覆盖。
  * @retval tick value
  */
__weak uint32_t HAL_GetTick(void)
{
  return uwTick;
}

void HAL_IncTick(void)

/**
  * @brief 调用此函数以递增用作应用程序时基的全局变量“uwTick”。
  * @note 在默认实现中,此变量在 SysTick ISR 中每 1 毫秒递增一次。
  * @note 此函数声明为__weak在用户文件中出现其他实现时要覆盖。
  * @retval None
  */
__weak void HAL_IncTick(void)
{
  uwTick += uwTickFreq;
}

步骤02:观察相关联的变量

在这里插入图片描述

步骤03:观察变化的地方

在这里插入图片描述1. 在SysTick_Handler中被调用
在这里插入图片描述

步骤04:中断要执行是需要开启的

  1. 在程序的开头HAL_Init();
    在这里插入图片描述在这里插入图片描述
/**
  * @brief  此函数用于初始化 HAL 库;它必须是在主程序中执行的第一个指令(在调用任何其他 HAL 函数之前), 它执行以下操作:
  *           1. 配置闪存预取。
  *           2. 将 SysTick 配置为每 1 毫秒生成一次中断,
  *           3. 由 HSI 计时(在此阶段,时钟尚未配置,因此系统从内部 HSI 以 16 MHz 运行).
  *           4. 将 NVIC 组优先级设置为 4
  *           5. 调用用户文件 “stm32f1xx_hal_msp.c” 中定义的 HAL_MspInit() 回调函数来执行全局低级硬件初始化
  *
  * @note   SysTick 用作 HAL_Delay() 函数的时基,应用程序需要确保 SysTick 时基始终设置为 1 毫秒,以便具有正确的 HAL 操作
  * @retval HAL status
  */
HAL_StatusTypeDef HAL_Init(void) {
    /* 配置闪存预取 */
#if (PREFETCH_ENABLE != 0)
#if defined(STM32F101x6) || defined(STM32F101xB) || defined(STM32F101xE) || defined(STM32F101xG) || \
    defined(STM32F102x6) || defined(STM32F102xB) || \
    defined(STM32F103x6) || defined(STM32F103xB) || defined(STM32F103xE) || defined(STM32F103xG) || \
    defined(STM32F105xC) || defined(STM32F107xC)

    /* 预取缓冲区在超值行设备上不可用 */
    __HAL_FLASH_PREFETCH_BUFFER_ENABLE();
#endif
#endif /* PREFETCH_ENABLE */

    /* NVIC 组优先级设置 */
    HAL_NVIC_SetPriorityGrouping(NVIC_PRIORITYGROUP_4);

    /*将 SysTick 配置为每 1 毫秒生成一次中断(重置后的默认时钟为 HSI) */
    HAL_InitTick(TICK_INT_PRIORITY);

    /* 初始化低级硬件 */
    HAL_MspInit();

    /* 返回函数状态 */
    return HAL_OK;
}

步骤04:查看HAL_InitTick执行函数

在这里插入图片描述

/**
  * @brief 此函数配置时基源。时间源配置为具有 1ms 时基,具有专用的 Tick 中断优先级
  * @note 此函数在程序开始时由 HAL_Init() 重置后或在时钟由 HAL_RCC_ClockConfig() 重新配置时自动调用。
  * @note 在默认实现中,SysTick 计时器是时基的来源。它用于以固定的时间间隔生成中断。
  *       如果从外设 ISR 进程调用 HAL_Delay(),则必须小心,SysTick 中断的优先级必须高于外设中断(数值较低)。否则,调用方 ISR 进程将被阻止。
  * @param TickPriority Tick interrupt priority.
  * @retval HAL status
  */
__weak HAL_StatusTypeDef HAL_InitTick(uint32_t TickPriority) {
    /* 将系统滴答配置为在 1ms 时间内中断*/
    if (HAL_SYSTICK_Config(SystemCoreClock / (1000U / uwTickFreq)) > 0U) {
        return HAL_ERROR;
    }

    /* 配置系统滴答 IRQ 优先级 */
    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;
}

步骤05: 查看HAL_SYSTICK_Config函数

  1. 这一步就是对外再包装了一层
    在这里插入图片描述

步骤06: 查看SysTick_Config函数(实际执行的)

在这里插入图片描述

步骤07: 重点记录SystemCoreClock的变化:

在这里插入图片描述在这里插入图片描述

第一次使用的地方

在这里插入图片描述

第一次变化的地方

在这里插入图片描述

查看HAL_RCC_ClockConfig执行函数

在这里插入图片描述在这里插入图片描述

<think>好的,我现在需要帮助用户了解如何将STM32的标准移植到RT-Thread操作系统上。首先,我需要回顾用户提供的引用内容,这些引用都是关于STM32移植RT-Thread的教程和注意事项。用户明确提到了标准的移植,所以我要重点参考引用[2]和引用[4],因为引用[2]的标题直接提到了标准移植,而引用[4]虽然提到的是HAL,但可能包含一些通用的步骤。 接下来,我需要整理移植的基本步骤。根据引用[2],移植过程大致分为准备工程、添加RT-Thread Nano源码、修改配置文件、编写应用代码、修改启动文件和编译调试。此外,引用[3]提到了启动线程调用的函数rt_thread_startup,这可能是在应用实例中需要使用的。引用[4]则介绍了Nano的特点,说明其轻量级,适合小型应用,这可能影响配置时的选项选择。 用户的问题是关于标准的移植,所以需要确保步骤中的工程是基于标准的,比如在准备工程时,用户需要有一个使用标准STM32裸机工程。然后,添加RT-Thread Nano源码时,可能需要从官网下载,如引用[1]提到的官网下载链接。修改配置文件如rtconfig.h,调整内核组件和时钟配置,这部分需要根据具体的MCU型号来设置,比如STM32F103的时钟频率。 在修改启动文件方面,引用[3]提到需要手动启动线程调度,所以在启动文件中可能需要添加相关的函数调用,比如在main函数初始化后创建线程并启动。同时,中断处理可能需要修改,比如SysTick和PendSV的中断服务函数替换为RT-Thread提供的,这部分需要参考引用[2]中的步骤。 在编写应用代码时,用户需要创建线程并定义线程入口函数,然后在main函数中初始化硬件并启动调度器。引用[3]中的rt_thread_startup函数就是用来启动线程的,需要确保正确调用。 最后,编译和调试阶段需要检查是否有错误,比如链接错误或者配置错误,并根据提示进行修正。可能的问题包括头文件路径不正确、系统时钟配置错误或者堆栈大小不足等。 现在需要将以上步骤结构化,用清晰的中文逐步说明,同时注意LaTeX格式的正确使用,比如行内公式用$...$,独立公式用$$...$$,但在这个问题中可能涉及较少的数学公式,更多是代码和步骤。需要确保引用标识正确添加,例如在提到官网下载时引用[^1],提到标准移植步骤时引用[^2],线程启动时引用[^3],Nano特点时引用[^4]。 同时,生成的相关问题需要围绕移植过程中可能涉及的性能分析、优化方法、应用场景等,参考示例中的结构,比如如何优化RT-Thread的内存占用,或者RT-Thread在STM32上的应用场景。 总结,回答需要分步骤详细说明移植过程,引用对应的参考资料,确保用户能够按照步骤操作,并解答可能的相关问题。</think>### STM32标准移植RT-Thread操作系统方法 以下是基于STM32标准移植RT-Thread Nano操作系统的详细步骤: --- #### **1. 准备工作** - **下载RT-Thread Nano源码** 从RT-Thread官网下载Nano版本源码包,解压后找到`rt-thread-nano`文件夹,包含内核源码和配置文件。 - **准备STM32标准工程** 创建一个基于标准STM32裸机工程(如使用Keil或STM32CubeIDE)[^2]。 --- #### **2. 添加RT-Thread源码到工程** - **复制内核文件** 将Nano源码中的`include`、`libcpu`、`src`文件夹复制到工程目录下。 - **添加头文件路径** 在IDE中配置头文件路径,包含`include`和`libcpu/arm/cortex-m3`(根据MCU架构选择)。 - **添加源文件** 将`src/*.c`和`libcpu/arm/cortex-m3/context_gcc.S`(或`context_iar.S`)添加到工程。 --- #### **3. 修改配置文件** - **调整`rtconfig.h`** 在`include`文件夹中修改`rtconfig.h`,启用所需功能(如信号量、线程等),并配置系统时钟和堆栈大小: ```c #define RT_THREAD_PRIORITY_MAX 8 // 最大优先级 #define RT_TICK_PER_SECOND 1000 // 系统时钟频率 #define RT_USING_HEAP 1 // 启用动态内存管理 ``` - **适配系统时钟** 在`board.c`中实现`SysTick_Handler`,并初始化系统时钟(如使用外部晶振需配置PLL)。 --- #### **4. 修改启动文件** - **替换中断向量表** 在STM32标准的启动文件(如`startup_stm32f10x_hd.s`)中,将`SysTick_Handler`和`PendSV_Handler`替换为RT-Thread的中断服务函数: ```c void SysTick_Handler(void) { rt_tick_increase(); } // 替换SysTick void PendSV_Handler(void) { /* RT-Thread上下文切换 */ } ``` - **初始化线程栈** 在启动文件中预留堆空间,例如修改`Heap_Size`为`0x800`(2KB)。 --- #### **5. 编写应用代码** - **创建线程** 在`main.c`中定义线程入口函数并启动线程调度: ```c static void led_thread_entry(void *param) { while(1) { GPIO_WriteBit(GPIOA, GPIO_Pin_0, !GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_0)); rt_thread_delay(500); // 延时500ms } } int main(void) { // 硬件初始化(如GPIO、时钟) SystemInit(); GPIO_Init(...); // 创建线程并启动调度 rt_thread_t thread = rt_thread_create("led", led_thread_entry, RT_NULL, 512, 3, 10); rt_thread_startup(thread); // 启动线程 rt_system_scheduler_start(); // 启动调度器 } ``` --- #### **6. 编译与调试** - **解决常见问题** - **内存不足**:增大`Heap_Size`或优化线程栈大小。 - **中断冲突**:检查是否遗漏中断服务函数的重定向。 - **时钟配置错误**:确认`SystemCoreClock`与`RT_TICK_PER_SECOND`匹配[^4]。 ---
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

詩不诉卿

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值