本文主要是介绍【GD32】FreeRTOS实时操作系统移植(GD32F470ZGT6),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
1. 简介
在日常的应用开发项目中,常常需要单片机具有处理多种任务的需求,如果使用裸机开发那么肯定是不现实的,因为受限于IO与处理器的巨大速度差异,在裸机下处理器常常要等待当前IO操作完成才能进行下一个任务,效率大打折扣。
因此使用实时操作系统成为首选,市面上比较火的的实时操作系统有很多,像这里要介绍的FreeRTOS就是其中之一。FreeRTOS可以说是非常老牌的实时操作系统了,发布至今已经有十几年了,但受欢迎程度至今依然非常高,可以说是初学者必学的第一个RTOS系统。
FreeRTOS目前是被亚马逊收购了,收购后主要的改变就是加入了亚马逊的AWS物联网框架,不过我们只需要移植内核就够了。更多的文档可以在FreeRTOS官网上找到。
2. 移植
移植的FreeRTOS版本为FreeRTOS 202406.01 LTS。
2.1 添加文件
下载源码后解压,进入“FreeRTOS-Kernel”这个文件夹,里面就是内核源码,把它复制到项目的根目录下。
之后在Keil中添加对应的文件。首先是导入源码根目录的所有.c文件;然后进入portable ->MemMang这个文件夹,里面存放的是一些内存管理相关的代码,每一个文件代表了一种内存管理方式,所以并不是全部都要导入,一般来说导入heap_4.c,这个文件代表使用硬件堆内存进行内存管理,如果你使用的是外部RAM,那么应该导入heap_5.c,它可以允许外部RAM分配。最后,如果你的使用AC5编译器,那么下一步要导入RVDS -> ARM_CM4F中的.c文件;如果你使用的是AC6编译器,那么导入GCC -> ARM_CM4F中的.c文件。
所有导入的文件如下所示。
当然还要添加头文件路径,我使用的是AC6编译器,所以路径添加如下。
FreeRTOS提供了丰富的选项给用户进行内核的裁切,在examples -> template_configuration下的FreeRTOSConfig.h文件中。里面的配置有大几百项,都有详细的说明,只不过是全英文的,所以下面给一个我使用的精简版带中文注释的配置文件。
#ifndef FREERTOS_CONFIG_H
#define FREERTOS_CONFIG_H#include "gd32f4xx.h"#if defined(__ICCARM__) || defined(__CC_ARM) || defined(__GNUC__)#include <stdint.h>extern uint32_t SystemCoreClock;
#endif/* 断言 */
#include <stdio.h>
#define vAssertCalled(char, int) printf("Error:%s,%d\r\n", char, int)
#define configASSERT(x) if((x)==0) vAssertCalled(__FILE__, __LINE__)#define configUSE_PREEMPTION 1/* 使用时间片调度 */
#define configUSE_TIME_SLICING 1
/* 优化的任务调度算法 */
#define configUSE_PORT_OPTIMISED_TASK_SELECTION 1
/* 低功耗内核定时器 */
#define configUSE_TICKLESS_IDLE 0
/* 处理器时钟频率 */
#define configCPU_CLOCK_HZ (SystemCoreClock)
/* 每秒TICK数 */
#define configTICK_RATE_HZ (( TickType_t )1000)
/* 最大优先级数 */
#define configMAX_PRIORITIES (32)
/* 最小任务栈空间 */
#define configMINIMAL_STACK_SIZE ((unsigned short)128)
/* 最大任务名长度 */
#define configMAX_TASK_NAME_LEN (16)
/* 定时器宽度 */
#define configTICK_TYPE_WIDTH_IN_BITS TICK_TYPE_WIDTH_32_BITS
/* 空闲任务让度 */
#define configIDLE_SHOULD_YIELD 0
/* 使用任务队列 */
#define configUSE_QUEUE_SETS 0
/* 使用任务通知 */
#define configUSE_TASK_NOTIFICATIONS 1
/* 使用互斥量 */
#define configUSE_MUTEXES 0
/* 使用递归互斥量 */
#define configUSE_RECURSIVE_MUTEXES 0
/* 使用计数信号量 */
#define configUSE_COUNTING_SEMAPHORES 0
/* 使用应用任务标签 */
#define configUSE_APPLICATION_TASK_TAG 0/* 使用动态任务创建 */
#define configSUPPORT_DYNAMIC_ALLOCATION 1
/* 使用静态任务创建 */
#define configSUPPORT_STATIC_ALLOCATION 0
/* 堆空间大小 */
#define configTOTAL_HEAP_SIZE ((size_t)(36*1024)) /* 使用空闲钩子 */
#define configUSE_IDLE_HOOK 0
/* 使用tick钩子 */
#define configUSE_TICK_HOOK 0
/* 使用内存空间分配失败钩子 */
#define configUSE_MALLOC_FAILED_HOOK 0
/* 堆溢出检查 */
#define configCHECK_FOR_STACK_OVERFLOW 0 /* 创建运行状态 */
#define configGENERATE_RUN_TIME_STATS 0
/* 使用跟踪功能 */
#define configUSE_TRACE_FACILITY 0
/* 使用状态格式化函数 */
#define configUSE_STATS_FORMATTING_FUNCTIONS 0#if ( configUSE_TRACE_FACILITY == 1 )
#include "trcRecorder.h"
#endif/* 使用协程 */
#define configUSE_CO_ROUTINES 0
/* 协程任务优先级 */
#define configMAX_CO_ROUTINE_PRIORITIES ( 2 ) /* 使用定时器 */
#define configUSE_TIMERS 0
/* 定时器任务优先级 */
#define configTIMER_TASK_PRIORITY (configMAX_PRIORITIES-1)
/* 定时器队列长度 */
#define configTIMER_QUEUE_LENGTH 10
/* 定时器任务栈空间 */
#define configTIMER_TASK_STACK_DEPTH (configMINIMAL_STACK_SIZE*2) /* 获取任务状态函数 */
#define INCLUDE_xTaskGetSchedulerState 1
#define INCLUDE_eTaskGetState 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_xTimerPendFunctionCall 0
/* 获取当前任务句柄函数 */
#define INCLUDE_xTaskGetCurrentTaskHandle 1
/* */
#define INCLUDE_uxTaskGetStackHighWaterMark 0
/* 获取空闲任务句柄 */
#define INCLUDE_xTaskGetIdleTaskHandle 0/* 任务优先级宽度 */
#ifdef __NVIC_PRIO_BITS#define configPRIO_BITS __NVIC_PRIO_BITS
#else#define configPRIO_BITS 4
#endif
/* 内核优先级数 */
#define configKERNEL_INTERRUPT_PRIORITY ( 15 << (8 - configPRIO_BITS) ) /* 240 */
/* 系统优先级数 */
#define configMAX_SYSCALL_INTERRUPT_PRIORITY ( 5 << (8 - configPRIO_BITS) ) /* 80 *//* 系统中断移植 */
#define xPortPendSVHandler PendSV_Handler
#define vPortSVCHandler SVC_Handler#endif /* FREERTOS_CONFIG_H */
2.2 代码移植
导入文件后,还需要对原来的代码进行一些调整。
首先,以前我们需要自己实现SysTick定时,但FreeRTOS内部已经帮我们实现了,所以SysTick的中断需改写成下面。
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 */
}
另外,实时操作系统的运行是依靠PendSV和SVC这两个中断的,FreeRTOS内部有对应的代码实现,因此我们需要在之前的文件中注释掉这两个中断的实现。
做完这些步骤以后,项目正常来说就能编译成功了。
2.3 测试例程
简单写一个点灯例程测试FreeRTOS的运行。
static void led_init(void)
{rcu_periph_clock_enable(RCU_GPIOE);gpio_mode_set(GPIOE, GPIO_MODE_OUTPUT, GPIO_PUPD_NONE, GPIO_PIN_3);gpio_output_options_set(GPIOE, GPIO_OTYPE_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_3);
}static void led_task(void* args)
{led_init();for(;;){gpio_bit_set(GPIOE, GPIO_PIN_3);printf("LED OFF\n");vTaskDelay(1000 / portTICK_PERIOD_MS);gpio_bit_reset(GPIOE, GPIO_PIN_3);printf("LED ON\n");vTaskDelay(1000 / portTICK_PERIOD_MS);}
}int main(void)
{ nvic_priority_group_set(NVIC_PRIGROUP_PRE4_SUB0);DEBUG_Init();xTaskCreate(led_task, "InitTask", 1024, NULL, 5, NULL);vTaskStartScheduler();while(1);
}
首先把中断组设成4位抢占位,这个非常重要,不然在调用含ISP结尾的API时可能会出问题。
调xTaskCreate创建任务,第一个参数是任务函数指针;第二个参数是任务的名字;第三个参数是任务栈空间,如果任务的调用深度越深和里面定义的变量越多,那么这个就要对应的设大点,不然会导致栈溢出;第四个参数是用户数据指针,如果要传数据给任务就可以设置;第五个参数是任务的优先级,数字越大优先级越高,这个跟单片机的优先级逻辑是相反的;第六个参数是任务句柄,如果不需要获取任务的句柄那么可以传空指针。
调vTaskStartScheduler启动任务调度。
一般来说,在led_task任务函数里面,先初始化LED灯,然后在死循环里面每隔一秒改变一次灯的状态,使用vTaskDelay函数进行延时,里面的数要除portTICK_PERIOD_MS这个宏转成毫秒值。
2.4 运行效果
下载后应该能看到板子上的灯闪烁,串口打印如下。
这篇关于【GD32】FreeRTOS实时操作系统移植(GD32F470ZGT6)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!