0. 引言
之前freeRTOS用过几次,但是项目赶工的急,一直没有认真的去里面看过,最近正好有空,准备到里面看下源码实现,毕竟核心文件就这么几个,认真看一下流程,对操作系统的雏形也就有一个认识了。而且一些常用的功能,队列,内存管理,信号量之类的,都有,基本上彻底弄懂一个,对以后理解别的也有帮助。
1. FreeRTOS介绍
FreeRTOS其实不用再多介绍了,现在太火了,主要是免费,感觉很多厂家的出场demo都会带。
而且FreeRTOS是一个十分小巧的系统,占用资源也不多,甚至可以在stm32f103c8t6(64k flash ,20k ram)上跑起来。具体能裁剪到多小倒是没有看到有介绍,但是基本够用。(这里移到c8t6上也只是为了学习一下流程,如果真的使用可以用个大点的MCU。)
先放上官网,FreeRTOS官网。在这里可以轻松下载到最新的freeRTOS源码、组件、demo、新功能,而且现在的官网比以前好看多了。
现在的:
以前的(网摘):
现在清爽多了。
2. FreeRTOS 文件结构
现在(2020.4.11)最新的是v10.3.1,下载之后,打开文件夹,文件列表如下:
FreeRTOSv10.3.1
|
| -- FreeRTOS
| -- Demo
| -- License
| -- Source
| -- include
| -- portable
| -- croutine.c
| -- event_groups.c
| -- list.c
| -- queue.c
| -- stream_buffer.c
| -- tasks.c
| -- timers.c
|
| -- FreeRTOS-Plus
| -- Demo
| -- Source
| -- FreeRTOS-Plus-CLI
基本功能:
- FreeRTOS文件夹
- Demo 放的是一些主流芯片主流IDE的示例代码,刚开始建立工程的时候可以参考里面的 FreeRTOSConfig.h
- License 放的是许可证
- Source文件夹下就是源代码了。
- source/include是API的头文件
- source/portable 目录下的文件提供一些会被 FreeRTOS 核心代码调用的函数,这些函数的实现与运行环境有关系,或者是存在多种实现方式
另外的几个.c,看他们放在这么明显的位置上,就知道是FreeRTOS的关键了。
- source/croutine.c 协程相关 ,以前片子资源不够的时候用,现在用的人很少了,
- source/event_groups.c 事件组
- source/stream_buffer.c 流缓冲区
- source/timers.c 软定时器
- source/list.c (核心)列表结构描述,在内核整体控制上都使用了列表格式数据处理,一切数据结构的基础
- source/queue.c (核心)消息队列用于task间通信和同步
- source/tasks.c (核心)任务相关的函数
- FreeRTOS-Plus里主要是一些内核以外的附加的组件
- FreeRTOS-Plus-IO:提供不同硬件设备I/O引脚的通信接口
- FreeRTOS-Plus-TCP:TCP/IP协议组件,
- FreeRTOS-Plus-UDP:UDP组件
- FreeRTOS-Plus-CLI : 指令交互
- FreeRTOS-Plus-Trace:可视化跟踪
- Reliance-Edge
- WolfSSL
3. stm32f103c8t6移植FreeRTOS
3.1 文件需要
这里只是为了把系统跑起来,所以只移植了核心组件。
我们需要移植的,最少的文件只需要:
- FreeRTOSv10.3.1\FreeRTOS\Source\tasks.c, queue.c and list.c 3个文件(kernel核心)
- FreeRTOSv10.3.1\FreeRTOS\Source\portable\RVDS\ARM_CM3\port.c + portmacro.h(3个中断都是在这里做的,pendSV,SVC,tick)
- FreeRTOSv10.3.1\FreeRTOS\Source\portable\MemMang \ heap_4.c(内存管理,5中选1)
- FreeRTOSv10.3.1\FreeRTOS\Source\include 所有(API接口)
- FreeRTOSv10.3.1\FreeRTOS\Demo\CORTEX_STM32F103_Keil\FreeRTOSConfig.h(freeRTOS系统裁剪配置文件)
3.2 移植步骤
3.2.1 工程添加文件
将文件全部添加到工程中,把头文件路径添加到工程中
3.2.2 修改main
之前main是按这个格式写的:
//全局变量
int main()
{
//局部变量
bsp初始化
while(1)
{
//任务
}
}
需要修改为task的,这边随手写了一个测试的。
#include <stdio.h>
#include "hdl_sys.h" //Öжϡ¢GPIO¡¢Êý¾ÝÀàÐÍ
#include "hdl_systickdelay.h" //sysÑÓʱ
#include "hdl_led.h" //Êä³öIO¿Ú
#include "hdl_key.h" //ÊäÈëIO¿Ú
#include "hdl_usart1.h" //´®¿Ú1
#include "hdl_usart2.h" //´®¿Ú2
#include "hdl_usart3.h" //´®¿Ú2
#include "hdl_timer.h" //¶¨Ê±Æ÷
#include "hdl_iwdg.h"
#include "FreeRTOS.h"
#include "task.h"
//°æ±¾ºÅ
static Uint8 Version[4] = {20,4,9,1};
TaskHandle_t StartTask_Handler;
void uart1_task(void *pvParameters)
{
while(1)
{
printf("Uart1 PA9 send PA10 rec @ %lld ms\r\n",GetSysMs());
vTaskDelay(2000);
}
}
void uart2_task(void *pvParameters)
{
while(1)
{
UART2_Put_String("´®¿Ú¶þPA2£¬PA3·¢ËÍÕý³£\r\n");
vTaskDelay(1000);
}
}
void start_task(void *pvParameters)
{
taskENTER_CRITICAL();
xTaskCreate((TaskFunction_t )uart1_task,
(const char* )"uart1_task",
(uint16_t )128,
(void* )NULL,
(UBaseType_t )2,
(TaskHandle_t* )NULL);
xTaskCreate((TaskFunction_t )uart2_task,
(const char* )"uart2_task",
(uint16_t )128,
(void* )NULL,
(UBaseType_t )3,
(TaskHandle_t* )NULL);
vTaskDelete(StartTask_Handler);
taskEXIT_CRITICAL();
}
int main(void)
{
DelayInit(72); //ϵͳʱÖÓÉèÖÃ
NvicConfiguration(); //ÖжÏÔ´·Ö×é
Uart1Init(115200L); //´®¿Ú1³õʼ»¯
Uart2Init(115200L); //´®¿Ú2³õʼ»¯
//Uart3Init(115200L); //´®¿Ú3³õʼ»¯
//Timer2Init(9,7199); //719±íÃ÷72M/(7199+1)=10KhzµÄ¼ÆÊýƵÂÊ£¬¼ÆÊýµ½10£¨9+1£©Îª1ms, ΪϵͳÌṩʱÖÓ
//IWDG_Init(4,625*5); //Óë·ÖƵÊýΪ64,ÖØÔØֵΪ625,Òç³öʱ¼äΪ3s
printf("\r\n\r\n***************************************\r\n");
printf("** STM32 taotao Demo **\r\n");
printf("** Complie Time:%s %s **\r\n", __TIME__,__DATE__);
printf("** Version:%d.%d.%d.%d **\r\n", Version[0],Version[1],Version[2],Version[3]);
printf("***************************************\r\n");
xTaskCreate((TaskFunction_t )start_task,
(const char* )"start_task",
(uint16_t )128,
(void* )NULL,
(UBaseType_t )1,
(TaskHandle_t* )&StartTask_Handler);
vTaskStartScheduler();
while(1)
{
}
}
3.3.3 修改堆栈大小
因为使用的是stm32f103c8t6,ram只有20k,所以选择吧heap设置小一些,毕竟只是个测试程序,能用就行。
3.3.3 修改中断函数名
这么修改之后,运行依然跑不起来,一旦全速运行就会出现HardFault_Handler。
之前一直不理解,跟着堆栈跑觉得挺正常的,怎么一进中断就HardFault_Handler了,后来搜了一下,发现是因为名字不一样。
修改中断函数名
- 把stm32f10x_it.c里面把 SVC_Handler PendSV_Handler SysTick_Handler 这3个函数注释掉,
- 然后在freertosconfig.h里面定义这几个
#define vPortSVCHandler SVC_Handler
#define xPortPendSVHandler PendSV_Handler
/#define xPortSysTickHandler SysTick_Handler
这个很好理解,因为在启动文件中,向量表定义的这三个中断的名称就是
- SVC_Handler
- PendSV_Handler
- SysTick_Handler
和FreeRTOS默认的名字不同,需要转一下,到 - vPortSVCHandler
- xPortPendSVHandler
- xPortSysTickHandler 。