【ARM驱动】【FreeROTS移植到ARM驱动平台介绍】
1. 问题背景
HME的H7/P2等系列是SOC产品,系列芯片的架构是相同的,ARM是都Correx-M3,FPGA在资源、接口上有所差异。FPGA工程师按照正常的开发即可,ARM工程师拿到HME的驱动文件就可以进行裸片开发。但是,有些客户需要移植FreeRTOS,因此对FreeRTOS移植到HEM ARM驱动做以介绍。
2. 概念介绍
裸机:裸机又称为前后台系统,前台系统指中断服务函数,后台系统指大循环,即应用程序。
裸机缺点:
a. 实时性差,应用程序轮流执行;
b. 空等待,CPU不执行其他代码;
c. 结构臃肿,实现功能都放在无限循环。
RTOS特点:RTOS全称为:Real Time OS,就是实时操作系统,强调的是:实时性。
a. 分而治之,实现功能划分为多个任务;
b. 延时函数,任务调动;
c. 抢占式,高优先级任务抢占低优先级任务;
d. 任务堆栈,每个任务都有自己的栈空间;
注意1:中断可以打断任意任务
注意2:任务可以同等优先级
关于FreeRTOS的细节,可以通过官网或者B站上各位大神的讲解学习。
3. 移植步骤
针对HME的驱动平台移植,可以做步骤:.
(1)获取FreeRTOS源码;
(2) FreeRTOS移植以及系统配置
(1)获取FreeRTOS源码
FreeRTOS官网:https://www.freertos.org/
如下图所示:

点击1或者3“红框”可以下载最新默认的版本,如图所示当前时间最新版本是FreeRTOS 202406. 04 LTS;
点击2 “红框”有关于版本下载信息的说明;
要想获取之前的版本需要通过GitHub获取。
下图是官网下载的V202406.01版本

代码移植过程中,主要关心“红框”的源码。
以下红框是移植过程中必须的文件以及文件夹。

portable文件夹
FreeRTOS操作系统归根结底是软件层面实现的,那FreeRTOS是如何跟硬件联系在一起的呢?
portable文件夹里面的内容就是连接桥梁。

在Coretex-M3上开发,需要关注Keil、MemMang、RVDS。

(2) FreeRTOS移植
移植步骤:
1. 添加FreeRTOS源码
将FreeRTOS源码添加至基础工程、头文件路径等;
源码添加:

因为该系列芯片采用Coretex-M3,堆栈使用heap_4.c,以及RVDS\ARM_CM3下的port.c文件。
头文件路径添加:

2. FreeRTOSConfig.h
添加FreeRTOSConfig.h配置文件,并修改。
适配configCPU_CLOCK_HZ为HME的系统宏;
适配configSUPPORT_DYNAMIC_ALLOCATION为1,动态方式创建任务;
这移植配置未使能静态方式创建任务编译宏。
示例代码如下:
#ifndef FREERTOS_CONFIG_H
#define FREERTOS_CONFIG_H
#include <hme_conf.h>
#include <stdio.h>//断言
#define vAssertCalled(char,int) printf("Error:%s,%d\r\n",char,int)
#define configASSERT(x) if((x)==0) vAssertCalled(__FILE__,__LINE__)/***************************************************************************************************************/
/* FreeRTOS基础配置配置选项 */
/***************************************************************************************************************/
#define configUSE_PREEMPTION 1 //1使用抢占式内核,0使用协程
#define configUSE_TIME_SLICING 1 //1使能时间片调度(默认式使能的)
#define configUSE_PORT_OPTIMISED_TASK_SELECTION 1 //1启用特殊方法来选择下一个要运行的任务//一般是硬件计算前导零指令,如果所使用的//MCU没有这些硬件指令的话此宏应该设置为0!
#define configUSE_TICKLESS_IDLE 0 //1启用低功耗tickless模式
#define configUSE_QUEUE_SETS 1 //为1时启用队列
//#define configCPU_CLOCK_HZ (SystemCoreClock) //CPU频率
#define configCPU_CLOCK_HZ (HME_SYS_CLK) //CPU频率
#define configTICK_RATE_HZ (1000) //时钟节拍频率,这里设置为1000,周期就是1ms
#define configMAX_PRIORITIES (32) //可使用的最大优先级
#define configMINIMAL_STACK_SIZE ((unsigned short)130) //空闲任务使用的堆栈大小
#define configMAX_TASK_NAME_LEN (16) //任务名字字符串长度#define configUSE_16_BIT_TICKS 0 //系统节拍计数器变量数据类型,//1表示为16位无符号整形,0表示为32位无符号整形
#define configIDLE_SHOULD_YIELD 1 //为1时空闲任务放弃CPU使用权给其他同优先级的用户任务
#define configUSE_TASK_NOTIFICATIONS 1 //为1时开启任务通知功能,默认开启
#define configUSE_MUTEXES 1 //为1时使用互斥信号量
#define configQUEUE_REGISTRY_SIZE 8 //不为0时表示启用队列记录,具体的值是可以//记录的队列和信号量最大数目。
#define configCHECK_FOR_STACK_OVERFLOW 0 //大于0时启用堆栈溢出检测功能,如果使用此功能//用户必须提供一个栈溢出钩子函数,如果使用的话//此值可以为1或者2,因为有两种栈溢出检测方法。
#define configUSE_RECURSIVE_MUTEXES 1 //为1时使用递归互斥信号量
#define configUSE_MALLOC_FAILED_HOOK 0 //1使用内存申请失败钩子函数
#define configUSE_APPLICATION_TASK_TAG 0
#define configUSE_COUNTING_SEMAPHORES 1 //为1时使用计数信号量/***************************************************************************************************************/
/* FreeRTOS与内存申请有关配置选项 */
/***************************************************************************************************************/
#define configSUPPORT_DYNAMIC_ALLOCATION 1 //支持动态内存申请
#define configTOTAL_HEAP_SIZE ((size_t)(20*1024)) //系统所有总的堆大小/***************************************************************************************************************/
/* FreeRTOS与钩子函数有关的配置选项 */
/***************************************************************************************************************/
#define configUSE_IDLE_HOOK 0 //1,使用空闲钩子;0,不使用
#define configUSE_TICK_HOOK 0 //1,使用时间片钩子;0,不使用/***************************************************************************************************************/
/* FreeRTOS与运行时间和任务状态收集有关的配置选项 */
/***************************************************************************************************************/
#define configGENERATE_RUN_TIME_STATS 0 //为1时启用运行时间统计功能
#define configUSE_TRACE_FACILITY 1 //为1启用可视化跟踪调试
#define configUSE_STATS_FORMATTING_FUNCTIONS 1 //与宏configUSE_TRACE_FACILITY同时为1时会编译下面3个函数//prvWriteNameToBuffer(),vTaskList(),//vTaskGetRunTimeStats()/***************************************************************************************************************/
/* FreeRTOS与协程有关的配置选项 */
/***************************************************************************************************************/
#define configUSE_CO_ROUTINES 0 //为1时启用协程,启用协程以后必须添加文件croutine.c
#define configMAX_CO_ROUTINE_PRIORITIES ( 2 ) //协程的有效优先级数目/***************************************************************************************************************/
/* FreeRTOS与软件定时器有关的配置选项 */
/***************************************************************************************************************/
#define configUSE_TIMERS 1 //为1时启用软件定时器
#define configTIMER_TASK_PRIORITY (configMAX_PRIORITIES-1) //软件定时器优先级
#define configTIMER_QUEUE_LENGTH 5 //软件定时器队列长度
#define configTIMER_TASK_STACK_DEPTH (configMINIMAL_STACK_SIZE*2) //软件定时器任务堆栈大小/***************************************************************************************************************/
/* FreeRTOS可选函数配置选项 */
/***************************************************************************************************************/
#define INCLUDE_xTaskGetSchedulerState 1
#define INCLUDE_vTaskPrioritySet 1
#define INCLUDE_uxTaskPriorityGet 1
#define INCLUDE_vTaskDelete 1
#define INCLUDE_vTaskCleanUpResources 1
#define INCLUDE_vTaskSuspend 1
#define INCLUDE_vTaskDelayUntil 1
#define INCLUDE_vTaskDelay 1
#define INCLUDE_eTaskGetState 1
#define INCLUDE_xTimerPendFunctionCall 1/***************************************************************************************************************/
/* FreeRTOS与中断有关的配置选项 */
/***************************************************************************************************************/
#ifdef __NVIC_PRIO_BITS#define configPRIO_BITS __NVIC_PRIO_BITS
#else#define configPRIO_BITS 4
#endif#define configLIBRARY_LOWEST_INTERRUPT_PRIORITY 15 //中断最低优先级
#define configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY 5 //系统可管理的最高中断优先级
#define configKERNEL_INTERRUPT_PRIORITY ( configLIBRARY_LOWEST_INTERRUPT_PRIORITY << (8 - configPRIO_BITS) )
#define configMAX_SYSCALL_INTERRUPT_PRIORITY ( configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY << (8 - configPRIO_BITS) )/***************************************************************************************************************/
/* FreeRTOS与中断服务函数有关的配置选项 */
/***************************************************************************************************************/
#define xPortPendSVHandler PendSV_Handler
#define vPortSVCHandler SVC_Handler#endif /* FREERTOS_CONFIG_H */
3. 修改中断相关文件
hem_it.c,修改代码如下。
#include "FreeRTOS.h"
#include "task.h"void SysTick_Handler(void)
{if(xTaskGetSchedulerState()!=taskSCHEDULER_NOT_STARTED)//系统已经运行{xPortSysTickHandler();}
}
4. 添加应用程序
freertos_demo.c内容:
/*FreeRTOS*********************************************************************************************/
#include "FreeRTOS.h"
#include "task.h"#include "freertos_demo.h"/******************************************************************************************************/
/*FreeRTOS配置*//* START_TASK 任务 配置* 包括: 任务句柄 任务优先级 堆栈大小 创建任务*/
#define START_TASK_PRIO 1 /* 任务优先级 */
#define START_STK_SIZE 128 /* 任务堆栈大小 */
TaskHandle_t StartTask_Handler; /* 任务句柄 */
void start_task(void *pvParameters); /* 任务函数 *//* TASK0 任务 配置* 包括: 任务句柄 任务优先级 堆栈大小 创建任务*/
#define TASK0_PRIO 2 /* 任务优先级 */
#define TASK0_STK_SIZE 128 /* 任务堆栈大小 */
TaskHandle_t Task0Task_Handler; /* 任务句柄 */
void task0(void *pvParameters); /* 任务函数 *//* TASK1 任务 配置* 包括: 任务句柄 任务优先级 堆栈大小 创建任务*/
#define TASK1_PRIO 3 /* 任务优先级 */
#define TASK1_STK_SIZE 128 /* 任务堆栈大小 */
TaskHandle_t Task1Task_Handler; /* 任务句柄 */
void task1(void *pvParameters); /* 任务函数 *//* TASK2 任务 配置* 包括: 任务句柄 任务优先级 堆栈大小 创建任务*/
#define TASK2_PRIO 4 /* 任务优先级 */
#define TASK2_STK_SIZE 128 /* 任务堆栈大小 */
TaskHandle_t Task2Task_Handler; /* 任务句柄 */
void task2(void *pvParameters); /* 任务函数 *//******************************************************************************************************//*** @brief FreeRTOS例程入口函数* @param 无* @retval 无*/
void freertos_demo(void)
{ 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); /* 任务句柄 */vTaskStartScheduler();
}/*** @brief start_task* @param pvParameters : 传入参数(未用到)* @retval 无*/
void start_task(void *pvParameters)
{taskENTER_CRITICAL(); /* 进入临界区 *//* 创建任务0 */xTaskCreate((TaskFunction_t )task0,(const char* )"task0",(uint16_t )TASK0_STK_SIZE,(void* )NULL,(UBaseType_t )TASK0_PRIO,(TaskHandle_t* )&Task0Task_Handler);/* 创建任务1 */xTaskCreate((TaskFunction_t )task1,(const char* )"task1",(uint16_t )TASK1_STK_SIZE,(void* )NULL,(UBaseType_t )TASK1_PRIO,(TaskHandle_t* )&Task1Task_Handler);/* 创建任务2 */xTaskCreate((TaskFunction_t )task2,(const char* )"task2",(uint16_t )TASK2_STK_SIZE,(void* )NULL,(UBaseType_t )TASK2_PRIO,(TaskHandle_t* )&Task2Task_Handler);vTaskDelete(StartTask_Handler); /* 删除开始任务 */taskEXIT_CRITICAL(); /* 退出临界区 */
}/*** @brief task0* @param pvParameters : 传入参数(未用到)* @retval 无*/
void task0(void *pvParameters)
{ while(1){printf("Welcome to use HME H7 series FPGA chips! task0\r\n");HME_GPIO_Toggle(DEMO_GPIO, GPIO_PIN0);vTaskDelay(1000); /* 延时1000ticks */}
}/*** @brief task1* @param pvParameters : 传入参数(未用到)* @retval 无*/
void task1(void *pvParameters)
{while(1){printf("Welcome to use HME H7 series FPGA chips! task1\r\n");HME_GPIO_Toggle(DEMO_GPIO, GPIO_PIN1);vTaskDelay(800); /* 延时800ticks */}
}/*** @brief task2* @param pvParameters : 传入参数(未用到)* @retval 无*/
void task2(void *pvParameters)
{while(1){printf("Welcome to use HME H7 series FPGA chips! task2\r\n");vTaskDelay(600); /* 延时600ticks */}
}
测试说明:通过task0、task1、task2三个任务分别实现打印的功能,同时task0、task1中有灯翻转,且task0频率小于task1的频率。
main.c内容:
#include <stdio.h>
#include <string.h>
#include "hme_includes.h"
#include "hme_retarget.h"
#include "freertos_demo.h"
#include "flash_operation.h"uint32_t HAL_Phase0(void)
{/*Initialize necessary debugging functions*/HME_UART_InitTypeDef init;HME_UART_StructInit(&init);HME_UART_Init(DEMO_UART, &init);/*Enable Interrupt Vector */NVIC_EnableIRQ(DEMO_UART_IRQn);/*Enable Reception Function*/HME_UART_ITConfig(DEMO_UART, UART_Int_Received_Data_Available, true);HME_GPIO_InitTypeDef GPIO_Init;HME_GPIO_DeInit(DEMO_GPIO);GPIO_Init.Mode = PIN_MODE_OUTPUT;GPIO_Init.Pins = GPIO_PIN0 | GPIO_PIN1;HME_GPIO_Init(DEMO_GPIO, &GPIO_Init);return RET_OK;
}int main(void)
{uint32_t ret = RET_OK;ret = HAL_Phase0();if (ret != RET_OK) {printf("Phase0 has failed!\r\n");return ret;}printf("Welcome to use HME H7 series FPGA chips!\r\n");;ret = SpiFlash_InitAddrSpace(DEMO_SPI, FLASH_ADDR_OFFSET);if (ret != RET_OK) {printf("SpiFlash_InitAddrSpace has failed!\r\n");return ret;}SpiFlash_WriteData(DEMO_SPI, FLASH_ADDR_OFFSET);freertos_demo();while (1) {};
}
4. 问题记录
4.1 RAM空间不够
编译提示如下:
.\Objects\HME_MCU.axf: Error: L6406E: No space in execution regions with .ANY selector matching xxx.o(.bss).
由于移植FreeRTOS的代码,因此需要扩展RAM。
解决方法,如下图:

4.2 任务卡死现象
通过debug发现是vTaskDelay卡死,目前发现有两种原因导致。
1. 未适配hem_it.c中的SysTick_Handler,或者适配有问题;
解决方法:按照以上的介绍适配。
2. 任务的堆栈空间不足。
解决方法:扩展堆栈的空间
关于如何估算任务占用的堆栈空间,待......
