<?xml version="1.0" encoding="UTF-8"?>
<rdf:RDF
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns="http://purl.org/rss/1.0/"
xmlns:dc="http://purl.org/dc/elements/1.1/">
<channel rdf:about="https://blog.atoery.cn/index.php/feed/rss/tag/freertos/">
<title>JRNitre&#039;s Blog - FreeRTOS</title>
<link>https://blog.atoery.cn/index.php/tag/freertos/</link>
<description></description>
<items>
<rdf:Seq>
<rdf:li resource="https://blog.atoery.cn/index.php/2025/04/19/100.html"/>
<rdf:li resource="https://blog.atoery.cn/index.php/2025/04/16/92.html"/>
<rdf:li resource="https://blog.atoery.cn/index.php/2025/04/14/90.html"/>
</rdf:Seq>
</items>
</channel>
<item rdf:about="https://blog.atoery.cn/index.php/2025/04/19/100.html">
<title>[嵌入式] STM32 在 FreeRTOS 下实现 us 级别延时</title>
<link>https://blog.atoery.cn/index.php/2025/04/19/100.html</link>
<dc:date>2025-04-19T18:57:48+08:00</dc:date>
<description>1.0 前言与概述最近在编写软件实现的 I2C 和 SPI 协议库，需要稳定的 1us 延时用作心跳函数。在 FreeRTOS 下编写 us 延时逻辑不太一样，故在文章中作记录。与裸机实现 us 延时方法相近，使用滴答定时器通过比较数值即可实现延时。在 RTOS 环境下实现思路如下：计算指定延时时长所需要的计数器的值关闭系统任务调度 - 防止延时器件被任务调度打断影响计数精度获取当前重装载值获取开始延时寄存器里面的计数值不断获取当前计数器的值如果当前计数器值大于设定的计数器的值，退出延时释放系统任务调度2.0 实现代码void delay_us(uint32_t us) {
  volatile uint32_t tcnt = 0;
  uint32_t reload = SysTick-&gt;LOAD;
  uint32_t ticks = us * (SystemCoreClock / 1000000);

  vTaskSuspendAll();  // 阻止 OS 调度
  volatile uint32_t told = SysTick-&gt;VAL;
  while(1) {
    volatile uint32_t tnow = SysTick-&gt;VAL;
    if(tnow != told) {
      if(tnow&lt;told) {
        tcnt += told - tnow;
      }else {
        tcnt += reload - tnow + told;
      }
      told = tnow;
      if(tcnt &gt;= ticks) {
        break;
      }
    }
  }
  xTaskResumeAll(); // 恢复 OS 调度
}3.0 实现过程分析3.1 初始化部分uint32_t tcnt = 0;：用于累计已经经过的时间uint32_t reload = SysTick-&gt;LOAD;：获取 SysTick 的重装载值，这是定时器的最大值uint32_t ticks = us * (SystemCoreClock / 1000000);：计算需要延时的 SysTick 的值3.2 暂停任务调度通过 FreeRTOS 提供的 vTaskSupendAll(); 函数，暂停操作系统的任务调度，防止在延时期间发生任务切换，影响延时精度。3.3 获取初始计数器值uint32_t told = SysTick-&gt;VAL;：获取 SysTick 定时器当前计数值3.4 主循环主循环中会一直循环，直到时间累计到目标值 ticks3.4.1 获取当前计数器值uint32_t tnow = SysTick-&gt;VAL;：获取当前 SysTick 计数器的值3.4.2 判断数值是否发生变化if(tnow != told)：如果 tnow 和 told 的值不等，则 SysTick 的值发生了变化，更新累计时间 tcnt3.4.3 计算时间差if(tnow &lt; told)：如果 tnow 小于 told，则说明计数器并未溢出，累加差值 tcnt = told - tnow；否则说明计数器发生了溢出，reload - tnow 得到 tnow 到 0 的剩余时间，再减去 told 计算出 reload 到 told 的时间。3.4.4 更新旧计数值told = tnow 更新 told 的值，便于下次比较3.4.5 判断是否达到目标延时如果累计时间 tcnt 大于或等于 ticks 则达到了目标延时时长，退出循环。3.4.6 恢复任务调度FreeRTOS 提供的 xTaskResumeAll(); 函数可以恢复操作系统的任务调度4.0 上述代码存在的问题上述的代码中，可以轻松的实现 us 级别的延时功能，但是存在一些问题；程序中为了保证延时的精度，在延时函数的核心功能代码中关闭了系统的任务调度。这会对其它线程的任务运行产生干扰。因为在系统的任务调度被暂停后，所有的任务切换都会被禁止，相当于回到了裸机状态，直到调度器被恢复。这种行为可能会对其它线程中要求实时性的任务产生负面影响，尤其是多任务系统。4.1 另一种实现 us 延时的方法通过 STM32 的硬件外设 TIM 定时器可以实现无需关闭任务调度实现 us 延时。4.2 实现代码void tim_delay_us (uint32_t us) {
  uint32_t start = __HAL_TIM_GET_COUNTER(&amp;htim2);  // 获取当前计数值
  uint32_t elapsed = 0;

  while (elapsed &lt; us) {
    uint32_t now = __HAL_TIM_GET_COUNTER(&amp;htim2);  // 获取当前计数值
    if (now &gt;= start) {
      elapsed = now - start;  // 正常情况
    } else {
      elapsed = 0xFFFFFFFF - start + now;  // 处理溢出
    }
  }
}通过硬件定时器的实现可在不占用调度器的情况下实现同样的延时效果。END 参考与声明[参考文章]STM32在FreeRTOS下的us延时本文部分内容和代码由 Ai 辅助生成</description>
</item>
<item rdf:about="https://blog.atoery.cn/index.php/2025/04/16/92.html">
<title>[FreeRTOS] 堆和栈的基本概念&amp;amp;在FreeRTOS中的使用</title>
<link>https://blog.atoery.cn/index.php/2025/04/16/92.html</link>
<dc:date>2025-04-16T21:41:29+08:00</dc:date>
<description>1.0 堆1.1 基本概念堆的本质是在 RAM 中的一段连续的内存区域，开发者可以主动申请一些内存块，其中与栈不同的是，堆的声明周期是完全由程序员进行控制的。1.2 堆的管理机制动态性碎片化风险手动管理1.3 例子在 C/C++ 中，定义一个变量时，其存储位置取决于其定义位置上下文：如果在函数内定义，则其存储在栈中。若在全局或静态作用域定义，则存储在静态存储区。真正的堆内存必须通过动态分配函数进行显示申请。char heap_buf[1024];    // 若在函数内进行申请，则存储在栈中，函数结束后自动释放-char* heap_buf = (char*)malloc(1024);   // 显式申请堆内存
2.0 栈2.1 基本概念栈是一种先进后出的数据结构，其基本操作通常包括：压栈和弹栈。2.2 栈的特点后进先出 (LIFO)高效性有限容量 (固定大小栈)自动管理：在嵌入式系统中，函数调用时使用的栈由编译器和处理器自动管理，无需程序员手动干预。3.0 RTOS 如何使用栈END 参考 & 声明[参考]FreeRTOS入门与工程实践 --由浅入深带你学习FreeRTOS（FreeRTOS教程 基于STM32，以实际项目为导向） -&gt; BV1Jw411i7Fz本文部分内容由 Ai 辅助生成</description>
</item>
<item rdf:about="https://blog.atoery.cn/index.php/2025/04/14/90.html">
<title>[FreeRTOS] 学习记录 - 创建一个多任务程序</title>
<link>https://blog.atoery.cn/index.php/2025/04/14/90.html</link>
<dc:date>2025-04-14T22:44:58+08:00</dc:date>
<description>HAL 库版本在使用 CubeMX 初始化的 FreeRTOS 工程后，可以选择默认创建一个线程（名称可配置），位于 freertos.c 中：/* creation of mainTask */
mainTaskHandle = osThreadNew(StartDefaultTask, NULL, &amp;mainTask_attributes);
/* USER CODE BEGIN RTOS_THREADS */创建一个新的任务，则需要使用 xTaskCreate 函数，这个函数的声明如下：BaseType_t xTaskCreate( TaskFunction_t pxTaskCode,
                        const char * const pcName,
                        const configSTACK_DEPTH_TYPE usStackDepth,
                        void * const pvParameters,
                        UBaseType_t uxPrioity,
                        TaskHandle_t * const pxCreateTask );TaskFunction_t pxTaskCode：指定的运行函数const char * const pcName：名字const configSTACK_DEPTH_TYPE usStackDepth：栈的深度void * const pvParameters：参数UBaseType_t uxPrioity：优先级TaskHandle_t * const pxCreateTask：句柄创建一个任务用于串口发送数据：xTaskCreate(usartTask, &quot;usartTask&quot;, 128, NULL, osPriorityNormal, NULL);void usartTask(void *argument){
        while(1){
            printf(&quot;usart task is run!\n&quot;);
            HAL_Delay(500);
    }
}标准库版本</description>
</item>
</rdf:RDF>