FreeRTOS(Free Real-Time Operating System)是一个常用的实时操作系统内核,具有高可用性、可裁剪性、可移植性和可扩展性等特性。它是一个基于事件驱动的,用于嵌入式系统的实时操作系统,广泛应用于航空、航天、工业自动化、医疗设备、智能家居、汽车电子等领域。FreeRTOS提供了任务管理、时间管理、内存管理、中断处理、软件定时器和通信机制等功能。其内核非常小巧,仅需几KB的内存和一些最基本的处理器资源即可运行。同时FreeRTOS还提供多种语言接口,并且支持多种处理器架构,如ARM、AVR、PIC、MIPS等,因此备受广大嵌入式软件开发人员的欢迎。
FreeRTOS提供的对开发者的支持还是很多的,官网 也有中文版的开发文档,可以查阅API函数、内核机制,实在有问题解决不了还有一个官方的论坛,可以注册一个账号在上面提问。
官网有最新版和最新长期支持版(LTS),如果是为了学习建议直接下最新版,源码里提供一些demo项目可以参考。
完成上面的步骤后,可以尝试编译一下,会报一个找不到 FreeRTOSConfig.h 的错误,因为 FreeRTOSConfig.h 这个文件根据官方来说是需要用户自己创建的,算作用户文件,不算内核,它是一个配置文件,define一些宏来选择相应的功能。
那我们就创建一个 FreeRTOSConfig.h 到 user group 下,这里给一个有注释的模板,以后再有需要可以用这个改,不过如果你是Keil/GCC需要修改一两行。
#ifndef FREERTOS_CONFIG_H
#define FREERTOS_CONFIG_H
/*-----------------------------------------------------------
* Application specific definitions.
* 可以在下面这个网站查看对应选项的官方解释,此文件的注释只做参考
* See http://www.freertos.org/a00110.html
*----------------------------------------------------------*/
//#include "gd32f30x.h"
/* 这个是踩过的坑,因为汇编也要用到FreeRTOSConfig.h文件,所以有些关键字
* 如 enum 就会报 bad instruction 的错误,所以最好还是直接把如
* SystemCoreClock 这样的宏的值直接粘贴过来用,像我下面那样,就不要再包含
* gd32f30x.h 了。*/
/* Ensure stdint is only used by the compiler, and not the assembler. */
//这个地方__ICCARM__表示是IAR的编译器,若是Keil或GCC要改成__CC_ARM、__GNUC__
#if defined __ICCARM__
#include
extern uint32_t SystemCoreClock;
#endif
//assert断言
/* Normal assert() semantics without relying on the provision of an assert.h
header file. */
#define configASSERT( x ) if( ( x ) == 0 ) { taskDISABLE_INTERRUPTS(); for( ;; ); }
/*****************************************************************************
* 基础配置项
****************************************************************************/
//调度器:抢占式 1 协作式 0
#define configUSE_PREEMPTION 1
//1 使能时间片调度(默认是使能的)
#define configUSE_TIME_SLICING 1
//是否使用硬件计算前导0的方式选择下一个可执行任务
#define configUSE_PORT_OPTIMISED_TASK_SELECTION 1
/* 置1:使能低功耗tickless 模式;置0:保持系统节拍(tick)中断一直运行 */
#define configUSE_TICKLESS_IDLE 0
//写入实际CPU内核时钟频率
#define configCPU_CLOCK_HZ ((uint32_t)(120000000))//( SystemCoreClock )
//1s中断的次数
#define configTICK_RATE_HZ ( ( TickType_t ) 1000 )
//可用的最大优先级
#define configMAX_PRIORITIES ( 32 )
//空闲任务堆栈大小
#define configMINIMAL_STACK_SIZE ( ( unsigned short ) 128 )
//任务名称最大长度
#define configMAX_TASK_NAME_LEN ( 16 )
//系统节拍计数器变量的数据类型,1 16位无符号整数,0 32位无符号整数
#define configUSE_16_BIT_TICKS 0
//空闲任务放弃CPU使用权给其他同级任务(建议所有其他任务的优先级高于空闲任务)
#define configIDLE_SHOULD_YIELD 1
//启用队列
#define configUSE_QUEUE_SETS 1
//开启任务通知功能,默认开启
#define configUSE_TASK_NOTIFICATIONS 1
//互斥信号量
#define configUSE_MUTEXES 1
//可以注册的信号量的个数
#define configQUEUE_REGISTRY_SIZE 10
//使用递归互斥信号量
#define configUSE_RECURSIVE_MUTEXES 1
//可注册的消息队列的个数
#define configUSE_APPLICATION_TASK_TAG 0
//使用计数信号量
#define configUSE_COUNTING_SEMAPHORES 1
/*****************************************************************************
* 内存申请相关
****************************************************************************/
//支持动态内存申请
#define configSUPPORT_DYNAMIC_ALLOCATION 1
//支持静态内存
#define configSUPPORT_STATIC_ALLOCATION 0
//总堆栈大小
#define configTOTAL_HEAP_SIZE ( ( size_t ) ( 36 * 1024 ) )
/*****************************************************************************
* 钩子函数相关
****************************************************************************/
//空闲钩子(空闲任务执行的应用程序函数,void vApplicationIdleHook( void );
#define configUSE_IDLE_HOOK 0
//时间片钩子函数 void vApplicationTickHook( void );
#define configUSE_TICK_HOOK 0
//malloc失败时(返回NULL)调用的钩子函数 void vApplicationMallocFailedHook( void );
#define configUSE_MALLOC_FAILED_HOOK 0
//堆栈溢出检测 大于0时启用,必须由用户提供堆栈溢出钩子函数
#define configCHECK_FOR_STACK_OVERFLOW 0
/*****************************************************************************
* 运行时间和任务状态收集相关
****************************************************************************/
//启用运行时间统计功能
#define configGENERATE_RUN_TIME_STATS 0
//启用可视化跟踪调试
#define configUSE_TRACE_FACILITY 0
/* 与宏configUSE_TRACE_FACILITY 同时为 1 时会编译下面 3 个函数
* prvWriteNameToBuffer()
* vTaskList(),
* vTaskGetRunTimeStats()
*/
#define configUSE_STATS_FORMATTING_FUNCTIONS 0
/*****************************************************************************
* 协程相关
****************************************************************************/
//启用协程,必须添加 croutine.c
#define configUSE_CO_ROUTINES 0
//协程有效优先级数目
#define configMAX_CO_ROUTINE_PRIORITIES ( 2 )
/*****************************************************************************
* 软件定时器相关
****************************************************************************/
//启用软件定时
#define configUSE_TIMERS 1
//软件定时的优先级
#define configTIMER_TASK_PRIORITY ( configMAX_PRIORITIES - 1 )
//软件定时队列的个数
#define configTIMER_QUEUE_LENGTH 10
//软件定时器服务/守护任务的堆栈深度
#define configTIMER_TASK_STACK_DEPTH ( configMINIMAL_STACK_SIZE * 2 )
/*****************************************************************************
* 可选函数配置选项
****************************************************************************/
/* Set the following definitions to 1 to include the API function, or zero
to exclude the API function. */
#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_xTaskGetSchedulerState 1
#define INCLUDE_eTaskGetState 1
#define INCLUDE_xTimerPendFunctionCall 1
/*****************************************************************************
* 中断相关,主要是优先级
****************************************************************************/
/* Cortex-M specific definitions. */
#ifdef __NVIC_PRIO_BITS
/* __BVIC_PRIO_BITS will be specified when CMSIS is being used. */
#define configPRIO_BITS 4//__NVIC_PRIO_BITS
#else
#define configPRIO_BITS 4 /* 15 priority levels */
#endif
//中断最低优先级
/* The lowest interrupt priority that can be used in a call to a "set priority"
function. */
#define configLIBRARY_LOWEST_INTERRUPT_PRIORITY 0xf
//系统可管理的最高优先级
/* 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
/* Interrupt priorities used by the kernel port layer itself. These are generic
to all Cortex-M ports, and do not rely on any particular library functions. */
#define configKERNEL_INTERRUPT_PRIORITY ( configLIBRARY_LOWEST_INTERRUPT_PRIORITY << (8 - configPRIO_BITS) )
/* !!!! configMAX_SYSCALL_INTERRUPT_PRIORITY must not be set to zero !!!!
See http://www.FreeRTOS.org/RTOS-Cortex-M3-M4.html. */
#define configMAX_SYSCALL_INTERRUPT_PRIORITY ( configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY << (8 - configPRIO_BITS) )
/*****************************************************************************
* 中断服务函数相关
****************************************************************************/
/* Definitions that map the FreeRTOS port interrupt handlers to their CMSIS
standard names. */
#define vPortSVCHandler SVC_Handler
#define xPortPendSVHandler PendSV_Handler
//#define xPortSysTickHandler SysTick_Handler//不使用默认的systick中断函数
#endif /* FREERTOS_CONFIG_H */
还差最后一步了, SVC_Handler 和 PendSV_Handler 会有重复的问题,对于gd32的芯片来说,这是因为在 gd32f30x_it.c 已经有这俩函数了(虽然是空的),而 FreeRTOS 需要用到这两个函数,前者是中断系统调用,后者是嵌套中断时用的(或者说选取中断级别高的进入),有兴趣可以去 portasm.s 查看汇编代码,FreeRTOS 都帮我们实现好了。因此这里的直接注释掉就好,然后再在这里实现SysTick_Handler函数,就大功告成了,这个函数是系统时钟中断。
/*!
\brief this function handles SVC exception
\param[in] none
\param[out] none
\retval none
*/
/*void SVC_Handler(void)
{
}*/
/*!
\brief this function handles PendSV exception
\param[in] none
\param[out] none
\retval none
*/
/*void PendSV_Handler(void)
{
}*/
/*!
\brief this function handles SysTick exception
\param[in] none
\param[out] none
\retval none
*/
//其实主要还是调用 FreeRTOS 提供的系统时钟中断函数,只不过加了一个判断调度器是否启动的宏
extern void xPortSysTickHandler(void);
void SysTick_Handler(void)
{
#if (INCLUDE_xTaskGetSchedulerState == 1 )
if (xTaskGetSchedulerState() != taskSCHEDULER_NOT_STARTED) {
#endif /* INCLUDE_xTaskGetSchedulerState */
xPortSysTickHandler();
#if (INCLUDE_xTaskGetSchedulerState == 1 )
}
#endif /* INCLUDE_xTaskGetSchedulerState */
}
有兴趣可以看看源码怎么操作的,不过本文只聊怎么通过编译。
在main.c添加一些代码然后编译一下:
没有意外的话应该就成功了。。。
#include "FreeRTOS.h"
#include "task.h"
int main()
{
while (1);
}