快速移植CMSISFreeRTOSv11.1.0最新版

下载并安装CMSISfreeRTOS固件包

Releases · ARM-software/CMSIS-FreeRTOS (github.com)

image-20240911165119304

打开MDK,点击CMSIS选项

image-20240911165253998

image-20240912131755644

使能CMSIS库

更改CMSIS RTOS2为freeRTOS

image-20240911165339216

然后点击OK,可以看到IDE左边已经有了freeRTOS的库了

image-20240911165431569

配置FreeRTOSConfig.h文件

image-20240911165504417

如果使用CUBEMX生成工程,请按下面操作把主频配置一下

image-20240911165822014

后面一些选项的定义可以参考这篇博文https://blog.csdn.net/qq_45396672/article/details/120877303,笔者只讲一些需要关注的选项

image-20240911171110967

如果使用的是32位MCU,建议将此选项更改为TICK_TYPE_WIDTH_32_BITS,FreeRTOS默认每1ms执行一次时钟中断,执行时都会更新 TickType_t 变量,使用64位虽然不会影响程序运行,但执行操作需要更多的时间来完成,这可能会导致中断处理效率变低,特别是在高频中断下(因为32位机一次最多处理32位的数据)

image-20240911190221810

修改configQUEUE_REGISTRY_SIZE以便调试时追踪队列和信号量

image-20240911191954141

使用vTaskList() 和 vTaskGetRunTimeStats()这两个函数时,默认分配的缓冲区大小,默认的值是65535字节,一般不建议设置这么大的值,可根据自己使用的MCU RAM大小进行分配,注释中提到应使用vTaskListTasks()和 vTaskGetRunTimeStatistics()这两个函数来替代上面提到的两个函数

image-20240911192642921

这个选项意味着定时器任务拥有很高的优先级,如果你不打算将定时器用于高精度计时作用或者别的高实时性事件,可以将其调低

image-20240911194510844

这个选项用于设置系统的堆大小,默认4096(太少了,除非你的MCU RAM真的很少,不然不应该设置这么低),这个值可以根据你的MCU RAM大小来设置,像是我目前使用的STM32H750 RAM大小为128K(DTCM域),我为其设置32K的堆栈大小

image-20240912105929591

这个选项可以让FreeRTOS 自动为你在内存分配过程中提供保护机制,检查内存分配和释放中的问题,建议在调试的阶段开启。

image-20240912110219346

建议在Interrupt nesting behaviour configuration下方添加下面的代码

 /*
 * The CMSIS-RTOS V2 FreeRTOS wrapper is dependent on the heap implementation used
 * by the application thus the correct define need to be enabled below
 */

/* Cortex-M specific definitions. */
#ifdef __NVIC_PRIO_BITS
 /* __BVIC_PRIO_BITS will be specified when CMSIS is being used. */
 #define configPRIO_BITS         __NVIC_PRIO_BITS
#else
 #define configPRIO_BITS         4
#endif

/* The lowest interrupt priority that can be used in a call to a "set priority"
function. */
#define configLIBRARY_LOWEST_INTERRUPT_PRIORITY   15

/* The highest interrupt priority that can be used by any interrupt service
routine that makes calls to interrupt safe FreeRTOS API functions.  DO NOT CALL
INTERRUPT SAFE FREERTOS API FUNCTIONS FROM ANY INTERRUPT THAT HAS A HIGHER
PRIORITY THAN THIS! (higher priorities are lower numeric values. */
#define configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY 5

这段代码涉及 FreeRTOS 和 CMSIS-RTOS V2 中断优先级的配置,用于 Cortex-M 系列微控制器上。

  1. configPRIO_BITS
    • 这个宏定义了 Cortex-M 微控制器的优先级位数,用于配置中断优先级。Cortex-M 系列的不同型号支持不同数量的优先级位数。
    • __NVIC_PRIO_BITS 是从 CMSIS 库中获取的优先级位数常量,通常由具体的微控制器定义。对于STM32这个值通常是 4,表示有 16 个中断优先级(0 到 15)。如果使用其他厂家的MCU,需要注意到底有几个位表示中断优先级,例如我曾经调试TI的cc3200时,其中断优先级位数就是3位
    • 如果 CMSIS 没有定义 __NVIC_PRIO_BITS,则默认为 4
  2. configLIBRARY_LOWEST_INTERRUPT_PRIORITY
    • 这是应用程序中断的最低优先级。值为 15 表示最低优先级为 15(优先级值越大,优先级越低)。
    • 这意味着 15 是你在 NVIC_SetPriority() 函数中可以设置的最低中断优先级值。
    • 在大多数 Cortex-M 微控制器中,优先级 0 表示最高优先级,而 15 表示最低优先级(具体数字取决于 configPRIO_BITS 的定义)。
  3. configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY
    • 这是可以安全调用 FreeRTOS 中断安全 API 函数的最高中断优先级。即,使用 FreeRTOS 的 API 函数(如信号量、队列等)时,配置的中断优先级必须低于等于此值。
    • 该值通常较低,意味着优先级较高的中断不能调用 FreeRTOS 的 API 函数。此值被设置为 5,表示在优先级 5 及更高优先级的中断(数字越小,优先级越高)中不能调用 FreeRTOS 的 API 函数。
    • 如果你尝试从高优先级的中断中调用 FreeRTOS 的中断安全函数(如 xQueueSendFromISR()),可能会导致系统不稳定。

Cortex-M 微控制器的中断优先级从 0(最高优先级)到 15(最低优先级)。FreeRTOS 使用中断安全的 API 函数时有一定限制,中断优先级不能高于 configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY,即优先级值必须大于或等于 5

  • 中断优先级范围

    • 中断优先级范围:015(或 2^configPRIO_BITS - 1,基于微控制器的优先级位数)。
    • 调用 FreeRTOS API 的中断:优先级必须介于 configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY(5) 和 configLIBRARY_LOWEST_INTERRUPT_PRIORITY(15)之间。
  • configLIBRARY_LOWEST_INTERRUPT_PRIORITY = 15:设定了最低中断优先级为 15

  • configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY = 5:设定了可以安全调用 FreeRTOS 中断安全 API 的最高优先级为 5

  • 这个配置适用于 Cortex-M 系列的微控制器,确保系统中断优先级和 FreeRTOS 的任务调度机制能正确协调。

  • configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY 限制了调用 FreeRTOS 中断安全 API 的中断优先级,确保不会在过高优先级的中断中调用 FreeRTOS 的函数,以避免系统不稳定。

image-20240912113822088

修改图中两个配置项

/* configKERNEL_INTERRUPT_PRIORITY sets the priority of the tick and context
 * switch performing interrupts.  Not supported by all FreeRTOS ports.  See
 * https://www.freertos.org/RTOS-Cortex-M3-M4.html for information specific to
 * ARM Cortex-M devices. */
#define configKERNEL_INTERRUPT_PRIORITY          ( configLIBRARY_LOWEST_INTERRUPT_PRIORITY << (8 - configPRIO_BITS) )

/* configMAX_SYSCALL_INTERRUPT_PRIORITY sets the interrupt priority above which
 * FreeRTOS API calls must not be made.  Interrupts above this priority are never
 * disabled, so never delayed by RTOS activity.  The default value is set to the
 * highest interrupt priority (0).  Not supported by all FreeRTOS ports.
 * See https://www.freertos.org/RTOS-Cortex-M3-M4.html for information specific to
 * ARM Cortex-M devices. */
#define configMAX_SYSCALL_INTERRUPT_PRIORITY      ( configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY << (8 - configPRIO_BITS) )

/* Another name for configMAX_SYSCALL_INTERRUPT_PRIORITY - the name used depends
 * on the FreeRTOS port. */
#define configMAX_API_CALL_INTERRUPT_PRIORITY     ( configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY << (8 - configPRIO_BITS) )

之所以要用移位的方式,其实是因为NVIC优先级寄存器有8位,但是在MCU厂家设计时,通常只使用高4位或高3位等来表示中断优先级,

所以需要进行移位映射

configKERNEL_INTERRUPT_PRIORITY用于 FreeRTOS 时钟滴答中断任务上下文切换中断的优先级。

FreeRTOS 使用内核时钟滴答来维护任务调度,所以它需要一个较低优先级的中断,以便任务调度和时钟滴答不会干扰系统中更高优先级的中断

configMAX_SYSCALL_INTERRUPT_PRIORITY为可以安全调用 FreeRTOS API(如 xQueueSendFromISR)的中断优先级阈值

configMAX_API_CALL_INTERRUPT_PRIORITY的作用与configMAX_SYSCALL_INTERRUPT_PRIORITY相同,与其设置一样即可

image-20240912114121868

这是选项用于配置某些事件的钩子函数(也可称作回调函数),我一般只使用IDLE HOOK

各个钩子函数的作用:

  1. configUSE_IDLE_HOOK:

    • 作用:当 FreeRTOS 进入空闲任务时,会调用空闲钩子(Idle Hook)。空闲任务是 FreeRTOS 中当没有其他任务处于就绪状态时执行的任务。空闲任务在系统中一直存在,并且是优先级最低的任务。

    • 何时设置为 1:如果你希望在系统空闲时执行一些后台任务(如省电模式、监控等),可以启用该钩子函数,并提供实现。

    • 如何使用:启用后,你需要实现 vApplicationIdleHook() 函数。例子:

      void vApplicationIdleHook(void) {
          // 空闲时执行的操作,例如进入低功耗模式
      }
      
  2. configUSE_TICK_HOOK:

    • 作用:每次时钟滴答(tick)中断发生时,都会调用 Tick Hook 函数。时钟滴答中断是 FreeRTOS 用于任务调度和时间管理的关键机制,通常在固定的时间间隔触发。

    • 何时设置为 1:如果你希望在每个时钟滴答中断时执行一些额外的操作(如周期性任务),可以启用该钩子函数。

    • 如何使用:启用后,你需要实现 vApplicationTickHook() 函数。例子:

      void vApplicationTickHook(void) {
          // 在每个时钟滴答中断时执行的操作
      }
      
  3. configUSE_MALLOC_FAILED_HOOK:

    • 作用:当调用 pvPortMalloc() 分配内存失败时,系统会调用 Malloc Failed Hook 函数。这个钩子可以用来处理内存分配失败的情况。

    • 何时设置为 1:如果你希望在内存分配失败时进行错误处理或系统复位,可以启用该钩子函数。

    • 如何使用:启用后,你需要实现 vApplicationMallocFailedHook() 函数。例子:

      void vApplicationMallocFailedHook(void) {
          // 处理内存分配失败的操作,例如记录日志或复位系统
      }
      
  4. configUSE_DAEMON_TASK_STARTUP_HOOK:

    • 作用:在 FreeRTOS 的守护任务(Daemon Task)启动时调用此钩子函数。守护任务是 FreeRTOS 中用于处理定时器服务和延迟任务的任务。

    • 何时设置为 1:如果你希望在系统启动时执行一些初始化操作,可以启用该钩子函数。

    • 如何使用:启用后,你需要实现 vApplicationDaemonTaskStartupHook() 函数。例子:

      void vApplicationDaemonTaskStartupHook(void) {
          // 守护任务启动时的初始化操作
      }
      
  • configUSE_IDLE_HOOK = 1:如果你想在系统空闲时执行后台任务或节能操作,可以启用这个钩子。
  • configUSE_TICK_HOOK = 1:当你需要在每个时钟滴答时进行一些额外的处理(如任务监控、性能统计等),可以启用这个钩子。
  • configUSE_MALLOC_FAILED_HOOK = 1:对于内存资源紧张的系统,启用这个钩子可以帮助检测并处理内存分配失败的情况。
  • configUSE_DAEMON_TASK_STARTUP_HOOK = 1:如果你需要在 FreeRTOS 启动时执行一些初始化任务,可以启用该钩子。

image-20240912114612354

configCHECK_FOR_STACK_OVERFLOW 是 FreeRTOS 提供的用于检查任务堆栈溢出情况的配置选项,如果不需要可以设置为0

当设置为1或2时,需要提供一个回调函数

void vApplicationStackOverflowHook( TaskHandle_t xTask,
                                    char *pcTaskName );

可以这样写

void vApplicationStackOverflowHook( TaskHandle_t xTask,
                                    char *pcTaskName )
{
printf("Stack overflow detected in task: %s\n", pcTaskName);
 taskDISABLE_INTERRUPTS();
for( ;; );
}// 系统停止

image-20240912132513084

如果你的MCU不带MPU的话,可以把MPU的选项全部注释

若带有MPU,可以参考https://www.freertos.org/zh-cn-cmn-s/Security/04-FreeRTOS-MPU-memory-protection-unit#upgrade-information

image-20240912135734791

建议开启队列集

如果此时直接编译,并且你使用的是CUBEMX的话,肯定会报下面三个错误

image-20240912140142303

1.如果你是使用STM32CUBEMX生成的工程,请在stm32fxxx_it.c中将SVC、PendSV、Systic这三个中断函数给注释掉,port.c中定义了这三个中断函数,用于设置任务切换和恢复任务的栈空间。并且每次使用CUBEMX更新之后都要注释一遍

2.由于FreeRTOS使用Systick作为整个系统的时基,所以我们需要更换一个定时器作为STM32 HAL库的时基,以便我们使用HAL_GetTick()等函数,如果使用CUBEMX配置的话,在SYS中更改Timebase Source,选择任意一个基本定时器即可,以下图为例。

image-20240912140347363

测试

回到mian.c文件

添加头文件,添加测试代码

//FreeRTOS includes
#include "FreeRTOS.h"
#include "task.h"
#include "queue.h"
#include <stdio.h>
#include "semphr.h"
#include "event_groups.h"
#include "timers.h"

TaskHandle_t  xHandleTask1;
TaskHandle_t  xHandleTask2;
//空闲钩子函数
void vApplicationIdleHook()
{
    while(1)
    {
    
    }
}


void Task1Funtion(void *param)

{
volatile int i;

    while(1)
    {

    }
}
void Task2Funtion(void *param)
{
volatile int i;
    while(1)
		{

		}

}

int main(void)
{
  /* USER CODE BEGIN 1 */

  /* USER CODE END 1 */

  /* MCU Configuration--------------------------------------------------------*/

  /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
  HAL_Init();

  /* USER CODE BEGIN Init */

  /* USER CODE END Init */

  /* Configure the system clock */
  SystemClock_Config();

  /* USER CODE BEGIN SysInit */

  /* USER CODE END SysInit */

  /* Initialize all configured peripherals */
  MX_GPIO_Init();
  MX_USART1_UART_Init();
  /* USER CODE BEGIN 2*/

xTaskCreate(Task1Funtion,"TASK1",100,NULL,1,&xHandleTask1);
xTaskCreate(Task2Funtion,"TASK2",100,NULL,1,&xHandleTask2);

vTaskStartScheduler();//开启调度
  /* USER CODE END 2 */

  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while (1)
  {
    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */

  }
  /* USER CODE END 3 */
}

image-20240912142154471

如果你像我一样报了这些错误,那么需要手动添加一下这些函数

void vApplicationGetIdleTaskMemory( StaticTask_t **ppxIdleTaskTCBBuffer, StackType_t **ppxIdleTaskStackBuffer, uint32_t *pulIdleTaskStackSize )
{
    /* 静态变量保存任务控制块和堆栈。 */
    static StaticTask_t xIdleTaskTCB;
    static StackType_t xIdleTaskStack[ configMINIMAL_STACK_SIZE ];

    /* 返回指向控制块和堆栈的指针 */
    *ppxIdleTaskTCBBuffer = &xIdleTaskTCB;
    *ppxIdleTaskStackBuffer = xIdleTaskStack;
    *pulIdleTaskStackSize = configMINIMAL_STACK_SIZE;
}

void vApplicationGetTimerTaskMemory( StaticTask_t **ppxTimerTaskTCBBuffer, StackType_t **ppxTimerTaskStackBuffer, uint32_t *pulTimerTaskStackSize )
{
    /* 静态变量保存定时器任务的控制块和堆栈。 */
    static StaticTask_t xTimerTaskTCB;
    static StackType_t xTimerTaskStack[ configTIMER_TASK_STACK_DEPTH ];

    /* 返回指向控制块和堆栈的指针 */
    *ppxTimerTaskTCBBuffer = &xTimerTaskTCB;
    *ppxTimerTaskStackBuffer = xTimerTaskStack;
    *pulTimerTaskStackSize = configTIMER_TASK_STACK_DEPTH;
}
void configureTimerForRunTimeStats(void)
{
    /* 在这里初始化你的硬件计时器。 */
    /* 例如,启动一个定时器或使能某个计数器。 */
    
    return;
}
unsigned long getRunTimeCounterValue(void)
{
    /* 读取和返回硬件计时器的当前计数值。 */
    //return (unsigned long)TIM_GetCounter();  /* 示例代码,实际根据硬件不同进行修改 */
	return 0;
}

编译下载

image-20240912143129619

像这样在两个任务中打断点,如果能进入其中一个任务的断点,那么取消这个任务的断点,然后继续全速运行,程序能停在另一个任务断点即说明移植成功(这么做的原因是因为这个任务还没执行够1ms,所以并没有切换任务,不取消断点的话就会移植重复停在这个断点)

本文仅代表个人移植经验与愚见,并不能用作专用教程,望谅解

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值