开发环境:
MDK:Keil 5.30
开发板:N32G4FRML-STB 开发板
MCU:N32G4FRMEL7
FreeRTOS:FreeRTOSv202012.04-LTS
1 前言
在移植之前,请准备一份可以正常使用printf串口输出的裸机工程,FreeRTOS源码请到FreeRTOS官网获取。
FreeRTOS官网
笔者是从Github获取的源码,笔者这里使用是FreeRTOSv202012.04-LTS版本,从 FreeRTOSv10.4.1版本之后分为了LTS版本和Debug版本,版本命名也改为了日期命名。自亚马逊收购FreeRTOS之后,内核也没多大变化,V10.0后的版本差异不大,主要是添加了IOT等一些组件,丰富了FreeRTOS而已,最新的最稳定的版本是FreeRTOSv202012.04-LTS。
2 移植FreeRTOS
2.1 复制FreeRTOS源码
在基础工程中新建一个名为 FreeRTOS 的文件夹,如下图所示。将FreeRTOS源码下的FreeRTOSv202012.04-LTS\FreeRTOS\FreeRTOS-Kernel文件复制到裸机工程中。
对于portable 文件夹,我们只需要留下MemMang 和 RVDS这两个文件夹,其他的都可以删除掉。
2.2 添加源码到MDK工程
打开基础工程,新建分组 FreeRTOS,然后向这个分组中添加文件:
这里重点来说说分组中的 port.c 和 heap_4.c 是怎么来的, port.c 是 RVDS 文件夹下的 ARM_CM4中的文件,因为N32G4FRMEL7是 Cortex-M4内核的, 因此要选择 ARM_CM4中的 port.c 文件。 heap_4.c 是 MemMang 文件夹中的,前面说了 MemMang 是跟内存管理相关的,里面有 5 个 c 文件: heap_1.c、 heap_2.c、 heap_3.c、 heap_4.c 和 heap_5.c。 这 5 个 c 文件是五种不同的内存管理方法,这 5 个文件都可以用来作为FreeRTOS 的内存管理文件, 只是它们的实现原理不同, 各有利弊。 这里我们选择 heap_4.c。就先选择 heap_4.c,毕竟本章的重点是 FreeRTOS 的移植。
添加头文件路径:
头文件路径添加完成以后编译一下,看看有没有什么错误, 结果会发现提示打不开“FreeRTOSConfig.h”这个文件,如图所示:
这是因为缺少 FreeRTOSConfig.h 文件,这个文件在哪里找呢?我们可以找找 FreeRTOS 的官方移植工程中会不会有这个文件,打开FreeRTOS的移植工程文件,选择一个工程,打开以后就可看到FreeRTOSConfig.h文件,将FreeRTOSConfig.h文件复制到User文件夹下。FreeRTOSConfig.h 是FreeRTOS 的配置文件,一般的操作系统都有裁剪、 配置功能, 而这些裁剪及配置都是通过一个文件来完成的, 基本都是通过宏定义来完成对系统的配置和裁剪的,LTS版本是没有的,需要到Debug版本获取,任意一个都可以。
2.3 修改文件
修改FreeRTOS.h下INCLUDE_xTaskGetCurrentTaskHandle宏定义,从0改为1
FreeRTOS为开发者考虑得特别多,PendSV_Handler()与SVC_Handler()这两个很重要的函数都帮我们实现了,在在port.c文件中已经实现xPortPendSVHandler()与vPortSVCHandler()函数,防止我们自己实现不了,那么在n32g4fr_it.c中就需要我们注释掉PendSV_Handler()与SVC_Handler()这两个函数了,并在FreeRTOSConfig.h文件添加以下代码:
SysTick中断服务函数是一个非常重要的函数,FreeRTOS所有跟时间相关的事情都在里面处理,SysTick就是FreeRTOS的一个心跳时钟,驱动着FreeRTOS的运行,就像人的心跳一样,假如没有心跳,我们就相当于“死了”,同样的,FreeRTOS没有了心跳,那么它就会卡死在某个地方,不能进行任务调度,不能运行任何的东西,因此我们需要实现一个FreeRTOS的心跳时钟,FreeRTOS帮我们实现了SysTick的启动的配置:在port.c文件中已经实现vPortSetupTimerInterrupt()函数,并且FreeRTOS通用的SysTick中断服务函数也实现了:在port.c文件中已经实现xPortSysTickHandler()函数,所以移植的时候只需要我们在n32g4fr_it.c文件中实现我们对应平台上的SysTick_Handler()函数即可,需要添加以下代码:
当然啦,也可以不必修改,可以在FreeRTOSConfig.h文件定义,这样就避免去修改FreeRTOS的源文件。
#define INCLUDE_xTaskGetCurrentTaskHandle 1
3 应用编写
主函数如下:
#include <stdio.h>
#include "FreeRTOS.h"
#include "task.h"
#include <stdint.h>
#include "n32g4fr_bsp_led.h"
#include "n32g4fr_bsp_systick.h"
#include "n32g4fr_bsp_usart.h"
#define START_TASK_PRIO 1
#define START_STK_SIZE 128
#define LED1_TASK_PRIO 2
#define LED1_STK_SIZE 50
#define LED2_TASK_PRIO 3
#define LED2_STK_SIZE 50
TaskHandle_t LED1Task_Handler;
TaskHandle_t LED2Task_Handler;
ST_BSP_LED_Dev BSP_LED_Dev0 = LED_DEV0_CONFIG;
ST_BSP_LED_Dev BSP_LED_Dev1 = LED_DEV1_CONFIG;
ST_BSP_USART_Dev BSP_USART_Dev0 = USART_DEV0_CONFIG;
TaskHandle_t StartTask_Handler;
void start_task(void *pvParameters);
void LED1_Task(void *pvParameters);
void LED2_Task(void *pvParameters);
void BSP_Init(void);
int main(void)
{
BSP_Init();
xTaskCreate((TaskFunction_t )start_task,
(const char* )"start_task",
(uint16_t )START_STK_SIZE,
(void* )NULL,
(UBaseType_t )START_TASK_PRIO,
(TaskHandle_t* )&StartTask_Handler);
printf("Create Task...\r\n");
vTaskStartScheduler();
return 0;
}
void start_task(void *pvParameters)
{
taskENTER_CRITICAL();
xTaskCreate((TaskFunction_t )LED1_Task,
(const char* )"LED1_Task",
(uint16_t )LED1_STK_SIZE,
(void* )NULL,
(UBaseType_t )LED1_TASK_PRIO,
(TaskHandle_t* )&LED1Task_Handler);
xTaskCreate((TaskFunction_t )LED2_Task,
(const char* )"LED2_Task",
(uint16_t )LED2_STK_SIZE,
(void* )NULL,
(UBaseType_t )LED2_TASK_PRIO,
(TaskHandle_t* )&LED2Task_Handler);
vTaskDelete(StartTask_Handler);
taskEXIT_CRITICAL();
}
void LED1_Task(void* parameter)
{
while (1)
{
BSP_LED_On(&BSP_LED_Dev0);
vTaskDelay(500);
printf("LED_Task Running,LED1_ON\r\n");
BSP_LED_Off(&BSP_LED_Dev0);
vTaskDelay(500);
printf("LED_Task Running,LED1_OFF\r\n");
}
}
void LED2_Task(void* parameter)
{
while (1)
{
BSP_LED_On(&BSP_LED_Dev1);
vTaskDelay(500);
printf("LED_Task Running,LED2_ON\r\n");
BSP_LED_Off(&BSP_LED_Dev1);
vTaskDelay(500);
printf("LED_Task Running,LED2_OFF\r\n");
}
}
void BSP_Init(void)
{
NVIC_PriorityGroupConfig( NVIC_PriorityGroup_4 );
BSP_LED_Init(&BSP_LED_Dev0);
BSP_LED_Init(&BSP_LED_Dev1);
BSP_USART_Init(&BSP_USART_Dev0, 115200, 0, 1);
RCC_EnableAPB2PeriphClk(RCC_APB2_PERIPH_AFIO, ENABLE);
GPIO_ConfigPinRemap(GPIO_RMP_SW_JTAG_NO_NJTRST, ENABLE);
}
编译下载程序运行即可,通过串口打印信息。