本系列主要作为自己第一次系统学习RTOS的记录,以正点原子的STM32F103战舰,keil环境编程为例。想要达到以下目标:
1:初步熟悉FreeRTOS的移植和使用,并迁移完成一个小型项目;
2:以FreeRTOS为入门,了解RTOS的本质,并提升阅读源码的能力;
本系列文章主要参考以下资料,本文仅作为汇总,并添加了一些个人理解。
韦东山freeRTOS快速入门
【安富莱】FreeRTOS操作系统教程-硬汉嵌入式论坛-Powered by Discuz!等
RTOS
按对外部事件的响应能力来分类,嵌入式操作系统有分时操作系统和实时操作系统。如果操作系统能使计算机系统及时的响应外部事件请求,并能控制所有实时设备和实时任务协调运行,且能在一个规定的时间内完成对事件的处理,那么这种系统就称为实时操作系统(RTOS)。
按时间的正确程度来分,实时操作系统又分为硬件的实时操作系统和软件的实时操作系统。系统必须在极其严格的时间内完成的任务叫做硬件的实时操作系统,如果不是很严格的话就是软件的实时操作系统。
FreeRTOS为一个完全免费、可商业化的实时操作系统,为不适合或不可在 eCOS、嵌入式 Linux(或实时 Linux)甚至 uCLinux 上开发的应用程序提供更小、更简单的实时处理系统。
官方中文网站:
RTOS - Free professionally developed and robust real time operating system for small embedded systems development
https://www.freertos.org/zh-cn-cmn-s/RTOS.html
文件结构
FreeRTO部分文件结构如下
|--FreeRTOS
| |--Demo //预先制作的示例工程
| |--Common //独立于demo的通用代码,大部分已废弃
| |--source //核心文件
| | |--task.c //必需,任务操作
| | |--list.c //必需,列表
| | |--queue.c //基本必需,提供队列操作、信号量(semaphore)操作
| | |--timer.c //可选,software timer
| | |--crountine.c //可选,提供event group功能
| | |--stream_buffer.c
| | |--event_groups.c
| | |--icnclude //FreeRTOS本身的头文件
| | |--portable //移植时涉及的文件
| | | |--RVDS //IDE环境,一般为keil或RVD
| | | | |--ARM_CM3 //cortexM3架构的移植文件
| | | | | |--port.c
| | | | | |--portmacro.h
| | | |......
| | | | |......
| | | | |--MemMang //内存管理文件,包含五种内存分配方式
| | | | | |--heap_1.c
| | | | | |--......
工程移植
此处应用的工程模板为自制,以stm32f1的工程为例子,参考为安富莱开发板的FreeRTOS教程;
首先在工程目录中建立FreeRTOS的文件目录,将FreeRTOSV8.2.3\FreeRTOS\Source里面如下所有文件移植到刚刚创建的目录中
然后在Keil工程中建立Source和Portable目录,将以下文件包含在里面,完成后如图所示:
其中heap4.c为系统内存分配方式,port.c和portmacro.h为RVDS中Cortex-M3对应的移植文件,
heap_4.c文件路径: FreeRTOS\Source\portable\MemMang
port.c和portmacro.h文件的路径:FreeRTOS\Source\portable\RVDS\ARM_CM3
接着从FreeRTOSV8.2.3\FreeRTOS\Demo\CORTEX_STM32F103_Keil目录下将FreeRTOSConfig.h复制到工程中,此文件为官方整理好的配置文件,用户可通过此文件配置系统,可自定义位置。
(此步可选)接着建立includes.h,可以用来集中管理头文件,完成后的目录如下:
includes.h可按照以下内容模板:
#ifndef __INCLUDES_
#define __INCLUDES_
/*
****************************************************************
* 标准库
****************************************************************
*/
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
/*
****************************************************************
* OS
****************************************************************
*/
#include "FreeRTOS.h"
#include "task.h"
#include "queue.h"
#include "croutine.h"
/*
****************************************************************
* 其他
****************************************************************
*/
#endif
添加文件路径
最后修改FreeRTOSConfig.h文件,在其中添加以下几行
#define vPortSVCHandler SVC_Handler
#define xPortPendSVHandler PendSV_Handler
#define xPortSysTickHandler SysTick_Handler
目的是更改底层映射函数(此步也可以通过其他方式完成,但此方法较为简单)
其中vPortSVCHandler,xPortPendSVHandler和xPortSysTickHandler是在port.c文件里面定义的。
SVC_Handler,PendSV_Handler和SysTick_Handler在startup_stm32f10x_hd.s文件里面进行了定义。
并在stm32f10x_it.c中将对应的函数注销:
接着编译时会出现以下错误
..\Object\Objects\project.axf: Error: L6218E: Undefined symbol xTaskGetCurrentTaskHandle (referred from stream_buffer.o).
查阅网上资料,修改FreeRTOS.h中206行定义即可
至此,编译0错误0警告,基本移植完成,注意在编译时会报很多FreeRTOS文件的警告,无视重新编译即可,其他问题则需逐个解决
数据类型、命名规则和注释风格
FreeRTOS核心源码文件的编写遵循MISRA代码规则,同时支持各种编译器。
FreeRTOS没有引入C99和C11的一些新特性和语法,除stdint.h文件(C99标准库)。
基础数据类型
FFreeRTOS面对不同架构的设备,主要包含四种数据类型,每个移植的版本都含有自己的portmacro.h 头文件,包含了TickType_t和BaseType_t:
- TickType_t:
- FreeRTOS配置了一个周期性的时钟中断:Tick Interrupt,每发生一次中断,中断次数累加,这被称为tick count,tick count这个变量的类型就是TickType_t;
- 如果用户使能了宏定义 configUSE_16_BIT_TICKS,那么TickType_t定义的就是16位无符号数,否则就是32位无符号数;
- 对于32位架构,一定要禁止configUSE_16_BIT_TICKS定义,将此宏设置为0,即配置成32位无符号数;
- BaseType_t
- 使用设备架构最基础和最高效的数据类型
- 由架构决定,32位架构就是32位有符号数、16位架构就是16位有效数据、8位架构同理;
- 如果BaseType_t被定义成了char型,要特别注意将其设置为有符号数,因为部分函数的返回值是用负数来表示错误类型。
- BaseType_t通常用作简单的返回值的类型,还有逻辑值,比如pdTRUE/pdFALSE
变量名
FreeRTOS主要以前缀区分变量
基础前缀 | 含义 |
---|---|
c | char,根据MISRA代码规则,char定义的变量只能用于ASCII字符,前缀使用c。 |
s | short,int16_t |
i | long, int32_t |
u | unsigned |
p | 指针 |
e | 枚举 |
x | 其他非标准的类型:结构体、task handle、queue handle等 |
复合前缀 | 含义 |
---|---|
pc | char指针,根据MISRA代码规则,char *定义的指针变量只能用于ASCII字符串,前缀使用pc。 |
uc | unsigned char |
pus | uint16_t定义的指针变量 |
函数名
函数名的前缀有2部分:返回值类型、在哪个文件定义
函数名前缀 | 含义 |
---|---|
prv | 加上了static声明的函数, private的缩写。 |
v | 无返回值类型的函数 |
c | 返回值类型为char的函数 |
Task | 根据文件名,文件中相应的函数定义时也将文件名加到函数命名中,如tasks.c中的函数,前缀都带有Task |
例子
函数名 | 含义 |
---|---|
vTaskPrioritySet | 返回值类型:void,在task.c中定义 |
xQueueReceive | 返回值类型:BaseType_t, 在queue.c中定义 |
pvTimerGetTimerID | 返回值类型:pointer to void,在tmer.c中定义 |
宏名
宏名主体部分以大写为主,可添加小写前缀,用来表示这个宏在哪个文件中定义,例如
函数名 | 含义:在哪个文件中定义 |
---|---|
port (比如portMAX_DELAY) | portable.h或portmacro.h |
task (比如taskENTER_CRITICAL()) | task.h |
pd (比如pdTRUE) | projdefs.h |
config (比如configUSE_PREEMPTION) | FreeRTOSConfig.h |
err (比如errQUEUE_FULL) | projdefs.h |
通用宏定义如下:
宏 | 值 |
---|---|
pdTRUE | 1 |
pdFALSE | 0 |
pdPASS | 1 |
pdFAIL | 0 |
点灯
为了验证工程移植正确,通过实际点亮LED灯验证,main.c如下:
#include "main.h"
#include "includes.h"
static void vTaskLED1(void *pvParameters);
static void vTaskLED2(void *pvParameters);
int main()
{
//SystemInit(); //设置系统时钟为72M;
//GPIO_PinRemapConfig(GPIO_Remap_SWJ_JTAGDisable, ENABLE);//禁止JTAG和SWJ功能,关闭调试,释放引脚
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); //设置NVIC中断分组2:2位抢占优先级,2位响应优先级
LED_Init();
/* 创建任务*/
xTaskCreate( vTaskLED1, /* 任务函数*/
"vTaskLED", /* 任务名*/
512, /*任务栈 大小,单位 word ,也就是 4 字节*/
NULL, /* 任务参数*/
1, /* 任务优先级*/
NULL /* 任务句柄*/
);
xTaskCreate( vTaskLED2, /* 任务函数*/
"vTaskLED", /* 任务名*/
512, /*任务栈 大小,单位 word ,也就是 4 字节*/
NULL, /* 任务参数*/
2, /* 任务优先级*/
NULL /* 任务句柄*/
);
/* 启动调度,开始执行任务*/
vTaskStartScheduler();
/* 如果程序运行到了这里就表示出错了, 一般是内存不足 */
/*
如果系统正常启动是不会运行到这里的,运行到这里极有可能是用于定时器任务或者空闲任务的
heap 空间不足造成创建失败,此时要加大 FreeRTOSConfig.h 文件中定义的 heap 大小:
#define configTOTAL_HEAP_SIZE ( ( size_t ) ( 17 * 1024 )*/
while(1);
}
/*
*********************************************************************************************************
* 函 数 名: vTaskLED1
* 功能说明: LED闪烁任务
* 形 参: pvParameters 是在创建该任务时传递的形参
* 返 回 值: 无
* 优 先 级: 1 (: 1 数值越小优先级越低,这个跟 uCOS 相反)
* 说 明:
*********************************************************************************************************
*/
static void vTaskLED1(void *pvParameters)
{
while(1)
{
LED_Toggle(1);
vTaskDelay(1000);
}
}
/*
*********************************************************************************************************
* 函 数 名: vTaskLED1
* 功能说明: LED闪烁任务
* 形 参: pvParameters 是在创建该任务时传递的形参
* 返 回 值: 无
* 优 先 级: 2
* 说 明:
*********************************************************************************************************
*/
static void vTaskLED2(void *pvParameters)
{
while(1)
{
LED_Toggle(2);
vTaskDelay(2000);
}
}
成功点亮LED!
标签:文件,定义,FreeRTOS,--,基础,任务,应用,include From: https://www.cnblogs.com/cloudraysun/p/17421932.html