当前位置: 首页 > article >正文

初学stm32 --- FreeRTOS移植

目录

移植前准备

1. 基础工程

 2. FreeRTOS 源码

添加 FreeRTOS 文件

1. 添加 FreeRTOS 源码

2. 将文件添加到工程

3. 添加头文件路径

4. 添加 FreeRTOSConfig.h 文件

(1) FreeRTOSConfig.h 获取途径一

(2) FreeRTOSConfig.h 获取途径二

(3) FreeRTOSConfig.h 获取途径三

修改 SYSTEM 文件

1. sys.h 文件

 2. usart.c 文件

3. delay.c 文件

(1) 删除适用于 µC/OS 但不适用于 FreeRTOS 的相关代码

(2) 添加 FreeRTOS 的相关代码

(3) 修改部分内容

(a) SysTick_Handler()

(b) delay_init()

(c) delay_us()

修改中断相关文件


移植前准备

        在开始移植 FreeRTOS 之前,需要提前准备好一个用于移植 FreeRTOS 的基础工程,和FreeRTOS 的源码

1. 基础工程

         由于本文的后续一些实验当中需要用到 LED、 LCD、定时器、串口、内存管理等外设及功能,因此就以正点原子标准例程-HAL 库版本的内存管理的实验工程为基础工程进行 FreeRTOS的移植。由于内存管理实验例程的 BSP 文件夹中可能不包含定时器的驱动文件,因此如果内存管理试验力撑的 BSP 文件夹如果不包含 TIMER 文件夹的话,需要从定时器相关实验的 BSP 文件夹中拷贝一份 TIMER 到 FreeRTOS 移植基础工程当中。
 

 2. FreeRTOS 源码

         本教程所使用的 FreeRTOS 内核源码的版本 V10.4.6,即 FreeRTOS v202112.00。 在第一章中已经详细的介绍了如何从 FreeRTOS 的官网获取 FreeRTOS 的源码,同样的,开发板资料盘中也提供了本教程所使用的 FreeRTOS 源码,即 FreeRTOS v202112.00(FreeRTOS 内核 V10.4.6),路径为:软件资料→FreeRTOS 学习资料→FreeRTOSv202112.00.zip。

 

添加 FreeRTOS 文件

        在准备好基础工程和 FreeRTOS 的源码后,接下来就可以开始进行 FreeRTOS 的移植了。

1. 添加 FreeRTOS 源码

        在基础工程的 Middlewares 文件夹中新建一个 FreeRTOS 子文件夹,如下图所示:

        图中的 FreeRTOS 就是新建的文件夹,这里要说明的是图中的其他文件夹为内存管理实验工程中原本就存在的,对于正点原子的不同 STM32 开发板图 中的文件可能有所不同,但只需新建一个 FreeRTOS 子文件即可。

        接着就需要将 FreeRTOS 的源代码添加到刚刚新建的 FreeRTOS 子文件中了。将 FreeRTOS内核源码的 Source 文件夹下的所有文件添加到工程的 FreeRTOS 文件夹中,如下图所示:

 

        图 2.1.2.2 中各文件和文件夹的描述在 1.3.2 小节中已经说明,对于在正点原子的 STM32 系列开发板上移植 FreeRTOS, portable 文件夹中的文件只需要根据使用的编译器(AC5 或 AC6)使用到图 1.3.2.3 中的三个文件夹,其余用不到的文件,读者可以自行决定删除与否。

2. 将文件添加到工程

        打 开 基 础 工 程 , 新 建 两 个 文 件 分 组 , 分 别 为 Middlewares/FreeRTOS_CORE 和Middlewares/FreeRTOS_PORT,如下图所示:

        Middlewares/FreeRTOS_CORE 分组用于存放 FreeRTOS 的内核 C 源码文件, 将“1. 添加FreeRTOS 源码”步骤中的 FreeRTOS 目录下所有的 FreeRTOS 的内核 C 源文件添加到Middlewares/FreeRTOS_CORE 分组中。

        Middlewares/FreeRTOS_PORT 分组用于存放 FreeRTOS 内核的移植文件,需要添加两类文件到这个分组,分别为 heap_x.c 和 port 文件。

        首先是 heap_x.c, 在路径 FreeRTOS/portable/MemMang 下有五个 C 源文件,这五个 C 源文件对应了五种 FreeRTOS 提供的内存管理算法,读者在进行 FreeRTOS 移植的时候可以根据需求选择合适的方法,具体这五种内存管理的算法,在后续 FreeRTOS 内存管理章节会具体分析,这里就先使用 heap_4.c,将 heap_4.c 添加到 Middlewares/FreeRTOS_PORT 分组中。

        接着是 port 文件, port 文件是 FreeRTOS 这个软件与 MCU 这个硬件连接的桥梁,因此对于正点原子的 STM32 系列不同的开发板,所使用的 port 文件是不同的。 port 文件的路径在FreeRTOS/portable/RVDS 或 FreeRTOS/portable/GCC 下。进入到 FreeRTOS/portable/RVDS 或FreeRTOS/portable/GCC,可以看到 FreeRTOS 针对不同的 MCU 提供了不同的 port 文件,具体正点原子的 STM32 系列开发板与不同 port 文件的对应关系如下表所示:

         只需将开发板芯片对应的 port 文件添加到 Middlewares/FreeRTOS_PORT 分组中即可。

        将所有 FreeRTOS 相关的所需文件添加到工程后,如下图所示:

3. 添加头文件路径

        接下来添加 FreeRTOS 源码的头文件路径,需要添加两个头文件路径,毋庸置疑,其中一个头文件路径就是 FreeRTOS/include,另外一个头文件路径为 port 文件的路径,根据表 2.1.2.4中不同类型开发板与 port 文件的对应关系进行添加即可。 添加完成后如下图所示(这里以正点原子的 STM32F1 系列开发板为例,其他类型的开发板类似):

4. 添加 FreeRTOSConfig.h 文件

        FreeRTOSConfig.h 是 FreeRTOS 操作系统的配置文件, FreeRTOS 操作系统是可裁剪的,用户可以根据需求对 FreeRTOS 进行裁剪,裁剪掉不需要用到的 FreeRTOS 功能,以此来节约 MCU中寸土寸金的内存资源。那么 FreeRTOSConfig.h 文件从哪里来呢?主要有三个途径:

(1) FreeRTOSConfig.h 获取途径一

        第一种途径就是用户自行编写,用户可以根据自己的需求编写 FreeRTOSConfig.h 对FreeRTOS 操作系统进行裁剪。 FreeRTOS 官网的在线文档中就详细地对 FreeRTOSConfig.h 中各个配置项进行了描述,网页链接: https://www.freertos.org/a00110.html。当然,对于 FreeRTOS 新手来说,笔者是不建议自行编写的。

(2) FreeRTOSConfig.h 获取途径二

        第二种途径就是 FreeRTOS 内核的演示工程,在“FreeRTOS 文件预览”这一小节中,介绍了 Demo 文件夹, Demo 文件夹中包含了 FreeRTOS 官方提供的演示工程,在这些演示工程当中就包含了每个演示工程对应的 FreeRTOSConfig.h 文件,需要注意的是,有些演示工程使用的是老版本的 FreeRTOS,因此部分演示工程的 FreeRTOSConfig.h 文件并不能够很好的适用于新版本的 FreeRTOS。任意打开其中一个演示工程, 如下图所示:

        读者可以在 Demo 文件夹中找到与自己所使用芯片相似的演示工程中的 FreeRTOSConfig.h文件,并根据自己的需求,稍作修改。

(3) FreeRTOSConfig.h 获取途径三

        第三种途径,也是推荐的。可以从本套教程配套例程“FreeRTOS 移植实验” 的 User 子文件夹下找到FreeRTOSConfig.h文件,这个文件就是参考FreeRTOS官网中对FreeRTOSConfig.h文件的描述,并针对正点原子的 STM32 系列开发板编写的。这里要说明的是,本套教程是用于学习 FreeRTOS 的,因此在 FreeRTOSConfig.h 文件中并没有对 FreeRTOS 的功能作过多的裁剪,大部分的功能都保留了,只不过在后续的部分实验中还需要对 FreeRTOSConfig.h 文件作相应的修改,以满足实验的需求。

        本教程就以途径三进行讲解,只需将本套教程配套例程“FreeRTOS 移植实验” User 子文件夹下的 FreeRTOSConfig.h 文件添加到基础工程的 User 子目录下即可。这里要注意的是, 正点原子的 STM32 系列开发板对应的 FreeRTOSConfig.h 文件是不通用的,具体原因在后续分析FreeRTOSConfig.h 文件的时候会具体地讲解。

修改 SYSTEM 文件

        SYSTEM 文件夹中的文件一开始是针对 µC/OS 编写的,因此使用 FreeRTOS 的话,就需要作相应的修改。 SYSTEM 文件夹中一共需要修改三个文件,分别是 sys.h、 usart.c、 delay.c。

1. sys.h 文件

        sys.h文件的修改很简单, 在sys.h文件中使用了宏SYS_SUPPORT_OS来定义是否支持OS,因为要支持 FreeRTOS,因此应当将宏 SYS_SUPPORT_OS 定义为 1,具体修改如下所示:

/**
* SYS_SUPPORT_OS 用于定义系统文件夹是否支持 OS
* 0,不支持 OS
* 1,支持 OS
*/
#define SYS_SUPPORT_OS 1

 2. usart.c 文件

        usart.c 文件的修改也很简单,一共有两个地方需要修改, 首先就是串口的中断服务函数,原本在使用 µC/OS 的时候,进入和退出中断需要添加 OSIntEnter()和 OSIntExit()两个函数,这是 µC/OS 对于中断的相关处理机制,而 FreeRTOS 中并没有这种机制,因此将这两行代码删除,修改后串口的中断服务函数如下所示:

正点原子 STM32F1 系列:
void USART_UX_IRQHandler(void)
{
    HAL_UART_IRQHandler(&g_uart1_handle); /* 调用 HAL 库中断处理公用函数 */
while (HAL_UART_Receive_IT( &g_uart1_handle,(uint8_t *)g_rx_buffer,RXBUFFERSIZE) != HAL_OK)/* 重新开启中断并接收数据 */
    {
        /* 如果出错会卡死在这里 */
    }
}
正点原子 STM32F4/G4/F7/H7/H5 系列:
void USART_UX_IRQHandler(void)
{
    uint32_t timeout = 0;
    uint32_t maxDelay = 0x1FFFF;
    HAL_UART_IRQHandler(&g_uart1_handle); /* 调用 HAL 库中断处理公用函数 */
    timeout = 0;
    while (HAL_UART_GetState(&g_uart1_handle)
    != HAL_UART_STATE_READY) /* 等待就绪 */
    {
        timeout++; /* 超时处理 */
        if(timeout > maxDelay)
        {
        break;
        }
    }
    timeout=0;
/* 一次处理完成之后,重新开启中断并设置 RxXferCount 为 1 */
    while (HAL_UART_Receive_IT( &g_uart1_handle,
    (uint8_t *)g_rx_buffer,
    RXBUFFERSIZE) != HAL_OK)
    {
        timeout++;/* 超时处理 */
        if (timeout > maxDelay)
        {
        break;
        }
    }
}

        接下来 usart.c 要修改的第二个地方就是导入的头文件,因为在串口的中断服务函数当中已经删除了 µC/OS 的相关代码,并且也没有使用到 FreeRTOS 的相关代码,因此将 usart.c 中包含的关于 OS 的头文件删除,要删除的代码如下所示:

/* 如果使用 os,则包括下面的头文件即可. */
#if SYS_SUPPORT_OS
#include "includes.h" /* os 使用 */
#

3. delay.c 文件

        接下来修改 SYSTEM 文件夹中的最后一个文件——delay.c, delay.c 文件需要改动的地方比较多,大致可分为三个步骤:删除适用于 µC/OS 但不适用于 FreeRTOS 的相关代码、添加FreeRTOS 的相关代码、修改部分内容。

(1) 删除适用于 µC/OS 但不适用于 FreeRTOS 的相关代码

        一共需要删除 1 个全局变量、 6 个宏定义、 3 个函数,这些要删除的代码在使用 µC/OS 的时候会使用到,但是在使用 FreeRTOS 的时候无需使用,需要删除的代码如下所示:

/* 定义 g_fac_ms 变量, 表示 ms 延时的倍乘数,
* 代表每个节拍的 ms 数, (仅在使能 os 的时候,需要用到)
*/
static uint16_t g_fac_ms = 0;
/*
* 当 delay_us/delay_ms 需要支持 OS 的时候需要三个与 OS 相关的宏定义和函数来支持
* 首先是 3 个宏定义:
* delay_osrunning :用于表示 OS 当前是否正在运行,以决定是否可以使用相关函数
* delay_ostickspersec :用于表示 OS 设定的时钟节拍,
* delay_init 将根据这个参数来初始化 systick
* delay_osintnesting :用于表示 OS 中断嵌套级别,因为中断里面不可以调度,
* delay_ms 使用该参数来决定如何运行
* 然后是 3 个函数:
* delay_osschedlock :用于锁定 OS 任务调度,禁止调度
* delay_osschedunlock :用于解锁 OS 任务调度,重新开启调度
* delay_ostimedly :用于 OS 延时,可以引起任务调度.
*
* 本例程仅作 UCOSII 和 UCOSIII 的支持,其他 OS,请自行参考着移植
*/
/* 支持 UCOSII */
#ifdef OS_CRITICAL_METHOD /* OS_CRITICAL_METHOD 定义了
* 说明要支持 UCOSII
*/
#define delay_osrunning OSRunning /* OS 是否运行标记,0,不运行;1,在运行 */
#define delay_ostickspersec OS_TICKS_PER_SEC /* OS 时钟节拍,即每秒调度次数 */
#define delay_osintnesting OSIntNesting /* 中断嵌套级别,即中断嵌套次数 */
#endif
/* 支持 UCOSIII */
#ifdef CPU_CFG_CRITICAL_METHOD /* CPU_CFG_CRITICAL_METHOD 定义了
* 说明要支持 UCOSIII
*/
#define delay_osrunning OSRunning /* OS 是否运行标记,0,不运行;1,在运行 */
#define delay_ostickspersec OSCfg_TickRate_Hz /* OS 时钟节拍,即每秒调度次数 */
#define delay_osintnesting OSIntNestingCtr /* 中断嵌套级别,即中断嵌套次数 */
#endif
/**
* @brief us 级延时时,关闭任务调度(防止打断 us 级延迟)
* @param 无
* @retval 无
*/
static void delay_osschedlock(void)
{
#ifdef CPU_CFG_CRITICAL_METHOD /* 使用 UCOSIII */
    OS_ERR err;
    OSSchedLock(&err); /* UCOSIII 的方式,禁止调度,防止打断 us 延时 */
#else /* 否则 UCOSII */
    OSSchedLock(); /* UCOSII 的方式,禁止调度,防止打断 us 延时 */
#endif
}
/**
* @brief us 级延时时,恢复任务调度
* @param 无
* @retval 无
*/
static void delay_osschedunlock(void)
{
#ifdef CPU_CFG_CRITICAL_METHOD /* 使用 UCOSIII */
    OS_ERR err;
    OSSchedUnlock(&err); /* UCOSIII 的方式,恢复调度 */
#else /* 否则 UCOSII */
    OSSchedUnlock(); /* UCOSII 的方式,恢复调度 */
#endif
}
/**
* @brief us 级延时时,恢复任务调度
* @param ticks: 延时的节拍数
* @retval 无
*/
static void delay_ostimedly(uint32_t ticks)
{
#ifdef CPU_CFG_CRITICAL_METHOD
    OS_ERR err;
    OSTimeDly(ticks, OS_OPT_TIME_PERIODIC, &err); /* UCOSIII 延时采用周期模式 */
#else
    OSTimeDly(ticks); /* UCOSII 延时 */
#endif
}

(2) 添加 FreeRTOS 的相关代码

        在使用 ARMClang(AC6)的情况下可跳过这个步骤,而在使用 ARMCC(AC5)的情况下只需要在 delay.c 文件中使用 extern 关键字导入一个 FreeRTOS 函数——xPortSysTickHandler()即可,这个函数是用于处理 FreeRTOS 系统时钟节拍的,本教程是使用 SysTick 作为 FreeRTOS操作系统的心跳,因此需要在 SysTick 的中断服务函数中调用这个函数,因此将代码添加到SysTick 中断服务函数之前,代码修改如下:
 

extern void xPortSysTickHandler(void);
/**
* @brief systick 中断服务函数,使用 OS 时用到
* @param ticks: 延时的节拍数
* @retval 无
*/
void SysTick_Handler(void)
{
/* 代码省略 */
}

(3) 修改部分内容

        最后要修改的内容包括两个,分别是包含头文件和 4 个函数。

        首先来看需要修改的 4 个函数,分别是 SysTick_Handler()、 delay_init()、 delay_us()和delay_ms()。

(a) SysTick_Handler()

        这个函数是 SysTick 的中断服务函数,由于 FreeRTOS 在针对 ARMClang(AC6)编译器的port 文件中实现了该函数,而在针对 ARMCC(AC5)编译器的 port 文件中没有实现改函数,因此该函数的修改针对实际使用的编译器类型(ARMClang 或 ARMCC)分为两种情况。

        对于使用 ARMCC( AC5)的情况, 只需在这个函数中不断调用上个步骤中导入的函数xPortSysTickHandler(),代码修改后如下所示:

/**
* @brief systick 中断服务函数,使用 OS 时用到
* @param ticks: 延时的节拍数
* @retval 无
*/
void SysTick_Handler(void)
{
    HAL_IncTick();
    /* OS 开始跑了,才执行正常的调度处理 */
    if (xTaskGetSchedulerState() != taskSCHEDULER_NOT_STARTED)
    {
        xPortSysTickHandler();
    }
}

        对于使用 ARMClang(AC6)的情况,由于 port 文件中定义并实现了 SysTick_Handler 函数,因此首先需要屏蔽图1.3.2.3中GCC目录下对应的port文件中定义和实现的SysTick_Handler函数,该函数在 port 文件中的定义和实现如下所示

/**
* @brief SysTick handler.
*/
void SysTick_Handler( void ) PRIVILEGED_FUNCTION;
void SysTick_Handler( void ) /* PRIVILEGED_FUNCTION */
{
    uint32_t ulPreviousMask;
    ulPreviousMask = portSET_INTERRUPT_MASK_FROM_ISR();
    {
        /* Increment the RTOS tick. */
        if( xTaskIncrementTick() != pdFALSE )
        {
        /* Pend a context switch. */
        portNVIC_INT_CTRL_REG = portNVIC_PENDSVSET_BIT;
        }
    }
    portCLEAR_INTERRUPT_MASK_FROM_ISR( ulPreviousMask );
}

        接着参考该 SysTick_Handler 函数的实现,实现 delay.c 文件中的 SysTick_Handler 函数,如下所示:

/**
* @brief systick 中断服务函数,使用 OS 时用到
* @param ticks: 延时的节拍数
* @retval 无
*/
void SysTick_Handler(void)
{
    uint32_t ulPreviousMask;
    HAL_IncTick();
    /* OS 开始跑了,才执行正常的调度处理 */
    if (xTaskGetSchedulerState() != taskSCHEDULER_NOT_STARTED)
    {
        ulPreviousMask = portSET_INTERRUPT_MASK_FROM_ISR();
        {
        /* Increment the RTOS tick. */
        if( xTaskIncrementTick() != pdFALSE )
        {
            /* Pend a context switch. */
            portNVIC_INT_CTRL_REG = portNVIC_PENDSVSET_BIT;
            }
        }
        portCLEAR_INTERRUPT_MASK_FROM_ISR( ulPreviousMask );
    }
}
(b) delay_init()

        函 数 delay_init() 主 要 用 于 初 始 化 SysTick 。 这 里 要 说 明 的 是 , 在 后 续 调 用 函 数vTaskStartScheduler()(这个函数在下文中讲解到 FreeRTOS 任务调度器的时候会具体分析)的时候, FreeRTOS 会按照 FreeRTOSConfig.h 文件的配置对 SysTick 进行初始化,因此 delay_init()函数初始化的 SysTick 主要使用在 FreeRTOS 开始任务调度之前。函数 delay_init()要修改的部分主要为 SysTick 的重装载值以及删除不用的代码,代码修改如下

正点原子 STM32F1 系列:
void delay_init(uint16_t sysclk)
{
#if SYS_SUPPORT_OS
    uint32_t reload;
#endif
    SysTick->CTRL = 0;
    HAL_SYSTICK_CLKSourceConfig(SYSTICK_CLKSOURCE_HCLK_DIV8);
    g_fac_us = sysclk / 8;
#if SYS_SUPPORT_OS
    reload = sysclk / 8;
    /* 使用 configTICK_RATE_HZ 计算重装载值
    * configTICK_RATE_HZ 在 FreeRTOSConfig.h 中定义
    */
    reload *= 1000000 / configTICK_RATE_HZ;
    /* 删除不用的 g_fac_ms 相关代码 */
    SysTick->CTRL |= 1 << 1;
    SysTick->LOAD = reload;
    SysTick->CTRL |= 1 << 0;
#endif
}
正点原子 STM32F4/G4/F7/H7/H5 系列:
void delay_init(uint16_t sysclk)
{
#if SYS_SUPPORT_OS
    uint32_t reload;
#endif
    HAL_SYSTICK_CLKSourceConfig(SYSTICK_CLKSOURCE_HCLK);
    g_fac_us = sysclk;
#if SYS_SUPPORT_OS
    reload = sysclk;
    /* 使用 configTICK_RATE_HZ 计算重装载值
    * configTICK_RATE_HZ 在 FreeRTOSConfig.h 中定义
    */
    reload *= 1000000 / configTICK_RATE_HZ;
    /* 删除不用的 g_fac_ms 相关代码 */
    SysTick->CTRL |= SysTick_CTRL_TICKINT_Msk;
    SysTick->LOAD = reload;
    SysTick->CTRL |= SysTick_CTRL_ENABLE_Msk;
#endif
}

        可以看到在正点原子 STM32 系列开发板的标准例程源码中, STM32F1 系列的函数delay_init()将 SysTick 的时钟频率设置为 CPU 时钟频率的 1/8,而 STM32F4/G4/F7/H7/H5 系列的函数 delay_init()则将 SysTick 的时钟频率设置为与 CPU 相同的时钟频率,由于 FreeRTOS 在配置 SysTick 时,并不会配置 SysTick 的时钟源,因此这将导致正点原子 STM32F1 系列与正点原子 STM32F4/G4/F7/H7/H5 系列的 FreeRTOSConfig.h 文件有所差异,这是读者在使用正点原子提供的 FreeRTOSConfig.h 文件时需要注意的地方。

(c) delay_us()

        函数 delay_us()用于微秒级的 CPU 忙延时,原本的函数 delay_us()延时的前后加入了自定义函数 delay_osschedlock()和 delay_osschedunlock()用于锁定和解锁 µC/OS 的任务调度器,以此来让延时更加准确。在 FreeRTOS 中可以不用加入这两个函数,但是要注意的是,这会让函数delay_us()的微秒级延时的精度有所下降, 函数 delay_us()修改后的代码如下所示:

void delay_us(uint32_t nus)
{
    uint32_t ticks;
    uint32_t told, tnow, tcnt = 0;
    uint32_t reload = SysTick->LOAD;
    /* 删除适用于 µC/OS 用于锁定任务调度器的自定义函数 */
    ticks = nus * g_fac_us;
    told = SysTick->VAL;
    while (1)
    {
        tnow = SysTick->VAL;
        if (tnow != told)
        {
            if (tnow < told)
            {
                tcnt += told - tnow;
            }
            else
            {
                tcnt += reload - tnow + told;
            }
            told = tnow;
            if (tcnt >= ticks)
            {
                break;
            }
        }
    }
    /* 删除适用于 µC/OS 用于解锁任务调度器的自定义函数 */
}

(d) delay_ms()

        函数 delay_ms()用于毫秒级的 CPU 忙延时,原本的函数 delay_ms()会判断 µC/OS 是否运行,如果 µC/OS 正在运行的话,就使用 µC/OS 的 OS 延时进行毫秒级的延时,否则就调用函数delay_us()进行毫秒级的 CPU 忙延时。在 FreeRTOS 中,可以将函数 delay_ms()定义为只进行CPU 忙延时,当需要 OS 延时的时候,调用 FreeRTOS 提供的 OS 延时函数 vTaskDelay()(在下文讲解 FreeRTOS 时间管理的时候会对此函数进行分析)进行系统节拍级延时,函数 delay_ms()修改后的代码如下所示:

void delay_ms(uint16_t nms)
{
    uint32_t i;
    for (i=0; i<nms; i++)
    {
        delay_us(1000);
    }
}


(e) 包含头文件

        根据上述步骤的修改, delay.c 文件中使用到了 FreeRTOS 的相关函数,因此就需要在 delay.c文件中包含 FreeRTOS 的相关头文件,并且移除掉原本存在的 µC/OS 相关头文件。先看一下修改前 delay.c 文件中包含的 µC/OS 相关的头文件:

/* 添加公共头文件 ( ucos 需要用到) */
#include "includes.h

        修改成如下内容:

/* 添加公共头文件 (FreeRTOS 需要用到) */
#include "FreeRTOS.h"
#include "task.h"

        至此, SYSTEM 文件夹针对 FreeRTOS 的修改就完成了。

修改中断相关文件

        在 FreeRTOS 的移植过程中会这几到三个重要的中断,分别是 FreeRTOS 系统时基定时器的中断(SysTick 中断)、SVC 中断、PendSV 中断(SVC 中断和 PendSV 中断在下文讲解 FreeRTOS中断和 FreeRTOS 任务切换的时候会具体分析),这三个中断的中断服务函数在 HAL 库提供的文件中都有定义,对于正点原子不同的 STM32 开发板,对应了不同的文件,具体对应关系如下表所示:

        其中, SysTick 的中断服务函数在 delay.c 文件中已经定义了,并且 FreeRTOS 也提供了 SVC和 PendSV 的中断服务函数,因此需要将 HAL 库提供的这三个中断服务函数注释掉,这里采用宏开关的方式让 HAL 库中的这三个中断服务函数不加入编译,使用的宏在 sys.h 中定义,因此还需要导入 sys.h 头文件, 请读者按照表 2.1.4.1 找到对应的文件进行修改,修改后的代码如下所示:

/* 仅展示修改部分,其余代码未修改、不展示 */
/* Includes --------------------------------------*/
/* 导入 sys.h 头文件 */
#include "./SYSTEM/SYS/sys.h"
/**
* @brief This function handles SVCall exception.
* @param None
* @retval None
*/
/* 加入宏开关 */
#if (!SYS_SUPPORT_OS)
void SVC_Handler(void)
{ }
#endif
/**
* @brief This function handles PendSVC exception.
* @param None
* @retval None
*/
/* 加入宏开关 */
#if (!SYS_SUPPORT_OS)
void PendSV_Handler(void)
{ }
#endif
/**
* @brief This function handles SysTick Handler.
* @param None
* @retval None
*/
/* 加入宏开关 */
#if (!SYS_SUPPORT_OS)
void SysTick_Handler(void)
{
    HAL_IncTick();
}
#endif

        最后,也是移植 FreeRTOS 要修改的最后一个地方, FreeRTOSConfig.h 文件中有如下定义:

#define configPRIO_BITS __NVIC_PRIO_BITS

        对于这个宏定义,在下文讲解到 ARM Corten-M 和 FreeRTOS 中断的时候会具体分析。可以看到,这个宏定义将 configPRIO_BITS 定义成__NVIC_PRIO_BITS,而__NVIC_PRIO_BITS在 HAL 库中有相关定义,对于正点原子不同的 STM32 开发板, __NVIC_PRIO_BITS 定义在不同的文件中,具体的对应关系如下表所示:

        找到对应的文件进行修改。 虽然不同类型的开发板对应的文件不同,但是__NVIC_PRIO_BITS 都被定义成了相同的值,如下所示:

#define __NVIC_PRIO_BITS 4U

        这个值是正确的,但是如果将__NVIC_PRIO_BITS 定义成 4U 的话,在编译 FreeRTOS 工程的时候, Keil 会报错,具体的解决方法就是将 4U 改成 4,代码修改后如下所示:

#define __NVIC_PRIO_BITS 4

        到此为止, FreeRTOS 就移植完毕了,整体来说难度并不是很高,但是作为 FreeRTOS 的初学者,有些读者可能不明白移植过程中涉及到的一些修改步骤,对于这个问题,笔者建议耐心跟着本教程的步骤完成,在后续的将讲解中,会一一的为读者解决这些问题,学习本来就是一个循序渐进的过程,不能想着要一口吃成大胖子


http://www.kler.cn/a/520214.html

相关文章:

  • ComfyUI实现老照片修复——AI修复老照片(ComfyUI-ReActor / ReSwapper)解决天坑问题及加速pip下载
  • 从0到1:C++ 开启游戏开发奇幻之旅(一)
  • 案例研究丨浪潮云洲通过DataEase推进多维度数据可视化建设
  • Cpp::静态 动态的类型转换全解析(36)
  • 【机器学习】自定义数据集使用框架的线性回归方法对其进行拟合
  • Python:元组构造式和字典推导式
  • vue3中src的目录分析
  • Dev-C++分辨率低-解决办法
  • 2024年AI发展的感知回顾
  • STM32 ADC
  • 基于OSAL的嵌入式裸机事件驱动框架——整体架构调度机制
  • Python NumPy(2):NumPy 数组属性、创建数据
  • JWT实现单点登录
  • Sklearn 中的逻辑回归
  • 基于SpringBoot+WebSocket的前后端连接,并接入文心一言大模型API
  • vim如何设置自动缩进
  • RISC-V读书笔记3
  • Cursor 帮你写一个小程序
  • 关于安卓greendao打包时报错问题修复
  • 开放自己本机的mysql允许别人连接
  • coffee销售数据集分析:基于时间趋势分析的实操练习
  • 第04章 11 VTK中,Filter的多输入输出的示例说明
  • vue3 获取百度天气
  • Day32:字符串的复制
  • x5music3.0 admin_index.php 后台权限绕过漏洞复现(附脚本)
  • Leetcode 3433. Count Mentions Per User