0. 说明与环境
- 移植的开发板为野火指南者(stm32f103vet6)
- 采用的是stm32标准库
- 移植的是freertos的
FreeRTOSv202112.00
版本
1. 文件目录与工程目录
文件目录
├─Doc
│ └─Readme.txt
├─Libraries
├─Middleware
│ ├─Freertos_portable
│ └─Freertos_source ------ // freertos源码
├─project
└─User
├─exti
│ ├─bsp_exti.c
│ └─bsp_exti.h
├─FreeRTOSConfig.h
├─key
│ ├─bsp_key.c
│ └─bsp_key.h
├─Lcd_Driver
│ ├─Lcd_Driver.c
│ └─Lcd_Driver.h
├─led
│ ├─bsp_led.c
│ └─bsp_led.h
├─main.c
├─stm32f10x_conf.h
├─stm32f10x_it.c
├─stm32f10x_it.h
└─uart
├─bsp_usart.c
└─bsp_usart.h
keil工程目录
Freertos_source
:FreeRTOS 内核的源代码,我们移植 FreeRTOS 的时候就需要这部分源代码
├─.gitmodules
├─croutine.c
├─event_groups.c
├─include
│ ├─atomic.h
│ ├─croutine.h
│ ├─deprecated_definitions.h
│ ├─event_groups.h
│ ├─FreeRTOS.h
│ ├─list.h
│ ├─message_buffer.h
│ ├─mpu_prototypes.h
│ ├─mpu_wrappers.h
│ ├─portable.h
│ ├─projdefs.h
│ ├─queue.h
│ ├─semphr.h
│ ├─StackMacros.h
│ ├─stack_macros.h
│ ├─stdint.readme
│ ├─stream_buffer.h
│ ├─task.h
│ └─timers.h
├─list.c
├─queue.c
├─stream_buffer.c
├─tasks.c
└─timers.c
Freertos_portable
:FreeRTOS是软件,我们的开发版是硬件,软硬件必须有桥梁来连接,这些与处理器架构相
关的代码, 可以称之为 RTOS 硬件接口层
├─Keil
│ └─See-also-the-RVDS-directory.txt
├─MemMang
│ ├─heap_1.c
│ ├─heap_2.c
│ ├─heap_3.c
│ ├─heap_4.c
│ ├─heap_5.c
│ └─ReadMe.url
├─readme.txt
└─RVDS
└─ARM_CM3
├─port.c
└─portmacro.h
-
heap_4.c
:heap_4.c 是FreeRTOS中提供的一种内存管理实现,用于动态管理RTOS系统的内存,实现动态任务分配、变量分配等。 该文件的主要内容包括:- 实现了多个内存分配、释放函数,包括
pvPortMalloc
、vPortFree
、vPortInitialiseBlocks
等。 - 实现了内存块的管理数据结构,其中包括两个链表,一个链表用于存储已分配内存块,另一个链表用于存储未分配内存块。
- 实现了内存分配算法,包括首次适配、最小适配等算法。
- 实现了内存保护机制,通过配置硬件能力,可以使得实时嵌入式系统在申请与释放内存时进行内存边界的检查,预防指针溢出等错误。 总之,heap_4.c 提供了一套相对完整的内存管理实现方案,可以帮助用户方便地管理系统内存,并提供多样化的内存分配算法与保护机制。
- 实现了多个内存分配、释放函数,包括
-
port.c
:FreeRTOS 的 port.c 是一个抽象层代码,用于将 FreeRTOS 内核代码与特定于处理器的代码分离开来,以便在不同的处理器架构上移植 FreeRTOS。 port.c 是 FreeRTOS 移植层的关键部分,它提供了处理器体系结构的抽象接口,包括:- 任务上下文切换函数,在任务之间进行上下文切换时调用。
- 时钟节拍定时器初始化函数,为 FreeRTOS 提供时基。
- 硬件中断处理函数,在中断处理程序中显示系统状态。
- 实现系统调用函数,例如 vTaskDelay() 和 vTaskSuspend(),这些函数由核心内存管理代码调用。 port.c 还提供了多个处理器架构的实现,包括包括ARM,AVR32,RX,PowerPC等。用户可以基于这些实现来进行芯片平台移植,就可以在目标硬件平台上使用 FreeRTOS 操作系统。 总之,port.c 为移植 FreeRTOS 提供了一个抽象层,使得 FreeRTOS 可以轻松地移植到不同的处理器平台上。
由于stm32f103x采用的是Cortex-M3 ,因此我们要移植的是ARM_CM3这个文件夹里面的文件
-
portmacro.h
portmacro.h 是 FreeRTOS 移植层中的一个头文件,提供了与处理器架构相关的巨集和函数。主要内容包括:-
任务栈大小的定义,根据处理器不同而有所区别。
-
定义了 FreeRTOS 运行所需的所有基本数据类型和运算符,因为不同处理器架构的基础数据类型和运算符可能存在差异。
-
实现了 FreeRTOS 中使用的多个原子操作,例如互斥锁、临界区限制和原子操作。
-
定义了挂起和恢复中断的宏,可以确保临界区代码的正确性。
-
预定义了内核如何处理中断的方式。
-
2. main.c代码
#include "FreeRTOS.h"
#include "task.h"
#include "bsp_usart.h"
#include "bsp_exti.h"
#include "bsp_led.h"
static TaskHandle_t AppTaskCreate_Handle = NULL;
static TaskHandle_t LED1_Task_Handle = NULL;
static TaskHandle_t LED2_Task_Handle = NULL;
static void AppTaskCreate(void);
static void LED1_Task(void* pvParameters);
static void LED2_Task(void* pvParameters);
static void BSP_Init(void);
int main(void)
{
BaseType_t xReturn = pdPASS;/* 定义一个创建信息返回值,默认为 pdPASS */
BSP_Init();
printf("freertos初体验!!\n");
xReturn = xTaskCreate((TaskFunction_t) AppTaskCreate,/* 任务入口函数 */
(const char*)"MAIN_Task",/* 任务名字 */
(uint16_t) 512,/* 任务栈大小 */
(void*) NULL,/* 任务入口函数参数 */
(UBaseType_t)1,/* 任务的优先级 */
(TaskHandle_t*) &AppTaskCreate_Handle);/* 任务控制块指针 */
if(pdPASS == xReturn)
vTaskStartScheduler();
else
return -1;
while(1);
}
static void AppTaskCreate(void)
{
BaseType_t xReturn = pdPASS;
taskENTER_CRITICAL();
xReturn = xTaskCreate((TaskFunction_t) LED1_Task,/* 任务入口函数 */
(const char*)"LED1_Task",/* 任务名字 */
(uint16_t) 512,/* 任务栈大小 */
(void*) NULL,/* 任务入口函数参数 */
(UBaseType_t)2,/* 任务的优先级 */
(TaskHandle_t*) &LED1_Task_Handle);/* 任务控制块指针 */
if (pdPASS == xReturn)
printf("创建LED1任务成功!!!!\n");
xReturn = xTaskCreate((TaskFunction_t) LED2_Task,
(const char*)"LED2_Task",
(uint16_t) 512,
(void*) NULL,
(UBaseType_t)3,
(TaskHandle_t*) &LED2_Task_Handle);
if (pdPASS == xReturn)
printf("创建LED2任务成功!!!!");
vTaskDelete(AppTaskCreate_Handle);
taskEXIT_CRITICAL();
}
static void BSP_Init(void)
{
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4);
LED_GPIO_Config();
USART_Config();
}
static void LED1_Task(void* parameter)
{
while(1)
{
LED1_ON;
vTaskDelay(500);
printf("LED1 ON!! \n\r");
LED1_OFF;
vTaskDelay(500);
printf("LED1 OFF!! \n\r");
}
}
static void LED2_Task(void* parameter)
{
while(1)
{
LED2_ON;
vTaskDelay(1000);
printf("LED2 ON!!\n\r");
LED2_OFF;
vTaskDelay(1000);
printf("LED2 OFF!!\n\r");
}
}
这段代码是一个简单的 FreeRTOS 任务创建示例。下面是代码的功能和流程:
头文件及任务用到的其他函数,在主函数前声明。
- 在 main 函数中首先初始化硬件资源,并使用 xTaskCreate() 函数创建名为 AppTaskCreate 的任务。此任务作为一个调度器,负责创建其余两个任务 LED1_Task 和 LED2_Task。
- 在 AppTaskCreate() 中,使用 xTaskCreate() 函数创建 LED1_Task 和 LED2_Task 两个任务。它们分别控制两个 LED 灯的闪烁。这两个任务的创建和启动是在临界区内完成的。
- 创建任务后,调用 vTaskDelete() 函数删除 AppTaskCreate 任务,以节省系统资源。
- LED1_Task 和 LED2_Task 任务具有不同的优先级,任务间使用 vTaskDelay() 函数进行合理的时间调度。
- LED1_Task 和 LED2_Task 中都包含了简单的 printf() 语句,用于输出任务运行状态,可使用串口通信进行调试。 总之,这段代码是使用 FreeRTOS 多任务操作系统创建了三个任务,其中一个是调度器,另外两个则是实际执行的任务,它们分别驱动不同的 LED 灯周期性地闪烁。该代码通过任务优先级和时间调度等方式,实现了多任务之间的协调和资源共享。
3. 实验现象
由于野火的板子灯只有一个,但是可以通过不同IO口的输出来控制灯的颜色,因此实验现象就是一会闪蓝灯一会闪红灯,并在串口输出LED1和LED2的信息
查看这个串口打印出来的数据的时间可以发现,和我们预设的延迟500ms和1s基本一致,这也有力的验证了实时操作系统的实时性。