目录
获取FreeRTOS源码
FreeRTOS源码内容
FreeRTOS内核
Demo文件夹
Source文件夹
portable文件夹
FreeRTOS移植
移植步骤
移植详解
实验源码:
获取FreeRTOS源码
FreeRTOS官网:https://www.freertos.org/
FreeRTOS源码内容
FreeRTOS内核
Demo文件夹
Demo文件夹里面就是FreeRTOS的演示例程,如下所示:
支持多种芯片架构,支持多种不同型号芯片,对于入门学习FreeRTOS是十分有帮助的,在学习移植FreeRTOS的过程中就可以参考这些演示工程。
Source文件夹
FreeRTOS源码本尊
portable文件夹
FreeRTOS操作系统归根到底是一个软件层面的东西,那FreeRTOS是如何跟硬件联系在一起的呢?portable文件夹里面的东西就是连接桥梁
由于使用MDK开发,最后只需要一下3个文件夹
FreeRTOS移植
移植步骤
1.添加FreeRTOS源码,将FreeRTOS源码添加至基础工程、头文件路径等
2.FreeRTOSConfig.h,添加FreeRTOSConfig.h配置文件
3.修改SYSTEM文件,修改SYSTEM文件中的sys.c、delay.c、usart.c
4.修改中断相关文件,修改Systick中断、SVC中断、PendSV中断
5.添加应用程序,验证移植是否成功
移植详解
1.准备好一个基础例程,在基础例程里面,新建立一个FreeRTOS文件夹,并把官网下载好的FreeRTOS源码放到文件夹里。
2.在根据具体使用情况把portable文件夹里面不需要的文件删掉,留下自己想想要的
3.在基础工程中创建2个FreeRTOS文件夹,一个存放源码,一个端口文件夹,并添加文件。
4.添加FreeRTOS所需要的头文件
5.添加FreeRTOSConfig.h文件
FreeRTOSConfig.h 是 FreeRTOS 操作系统的配置文件, FreeRTOS 操作系统是可裁剪的,用户可以根据需求对FreeRTOS进行裁剪,裁剪掉不需要用到的FreeRTOS功能,以此来节约MCU内存资源。获取途径可以在Demo文件夹下示例复制一个。
6.修改中断相关文件
这三个函数分别为滴答定时器中断服务函数、SVC 中断服务函数和 PendSV 中断服务函数,将 stm32f10x_it.c 中的三个函数屏蔽掉,SysTick中断函数自己修改成FreeRTOS的心跳(也可以不屏蔽,直接在stm32f10x_it.c的SysTick中断函数里面写,这里是在外面写了所以屏蔽掉),SCVC和PendSV在FreeRTOS里面也定义了,所以屏蔽这3个函数就是为了不重复。
7.修改delay_ms()其实就是对 FreeRTOS 中的延时函数 vTaskDelay()的简单封装,所以在使用 delay_ms()的时候就会导致任务切换。
void delay_ms(u32 nms)
{
if(xTaskGetSchedulerState()!=taskSCHEDULER_NOT_STARTED)//系统已经运行
{
if(nms>=fac_ms) //延时的时间大于OS的最少时间周期
{
vTaskDelay(nms/fac_ms); //FreeRTOS延时
}
nms%=fac_ms; //OS已经无法提供这么小的延时了,采用普通方式延时
}
delay_us((u32)(nms*1000)); //普通方式延时
}
8.修改systick中断服务函数,FreeRTOS 的心跳就是由滴答定时器产生的,根据 FreeRTOS 的系统时钟节拍设置好滴答定 时器的周期,这样就会周期触发滴答定时器中断了。在滴答定时器中断服务函数中调用 FreeRTOS 的 API 函数 xPortSysTickHandler()。
void SysTick_Handler(void)
{
if(xTaskGetSchedulerState()!=taskSCHEDULER_NOT_STARTED)//系统已经运行
{
xPortSysTickHandler();
}
}
9.延时函数修改FreeRTOS 的系统时钟是由滴答定时器提供的,那么肯定要根据 FreeRTOS 的系统时钟节拍来初始化滴答定时器了,delay_init()就是来完成这个功能的。FreeRTOS 的系统时钟节拍由宏 configTICK_RATE_HZ 来设置,这个值我们可以自由设置,但是一旦设置好以后根据这个值来初始化滴答定时器,其实就是设置FreeRTOS的心跳,通过设置systick几个时钟周期中断一次,相当于几个systick中短周期代表一个FreeRTOS心跳1次。
void delay_init()
{
uint32_t reload;
SysTick_CLKSourceConfig(SysTick_CLKSource_HCLK);//选择外部时钟 HCLK
fac_us=SystemCoreClock/1000000; //不论是否使用OS,fac_us都需要使用
reload=SystemCoreClock/1000000; //每秒钟的计数次数 单位为M
reload*=1000000/configTICK_RATE_HZ; //根据configTICK_RATE_HZ设定溢出时间
//reload为24位寄存器,最大值:16777216,在72M下,约合0.233s左右
fac_ms=1000/configTICK_RATE_HZ; //代表OS可以延时的最少单位
SysTick->CTRL|=SysTick_CTRL_TICKINT_Msk; //开启SYSTICK中断
SysTick->LOAD=reload; //每1/configTICK_RATE_HZ秒中断一次
SysTick->CTRL|=SysTick_CTRL_ENABLE_Msk; //开启SYSTICK
}
实验源码:
/**
******************************************************************************
* @file : user_mian.h
* @brief : V1.00
******************************************************************************
* @attention
*
******************************************************************************
*/
/* Include 包含---------------------------------------------------------------*/
#include "stm32f10x.h"
#include <stdbool.h>
#include "user_gpio.h"
#include "user_delay.h"
#include "user_rcc_config.h"
#include "user_uart.h"
#include "FreeRTOS.h"
#include "task.h"
/* Typedef 类型----------------------------------------------------------------*/
/* Define 定义----------------------------------------------------------------*/
/* Macro 宏------------------------------------------------------------------*/
/* Variables 变量--------------------------------------------------------------*/
/* Constants 常量--------------------------------------------------------------*/
/* Function 函数--------------------------------------------------------------*/
//任务优先级
#define START_TASK_PRIO 1
//任务堆栈大小
#define START_STK_SIZE 128
//任务句柄
TaskHandle_t StartTask_Handler;
//任务函数
void start_task(void *pvParameters);
//任务优先级
#define LED0_TASK_PRIO 2
//任务堆栈大小
#define LED0_STK_SIZE 50
//任务句柄
TaskHandle_t LED0Task_Handler;
//任务函数
void led0_task(void *pvParameters);
//任务优先级
#define LED1_TASK_PRIO 3
//任务堆栈大小
#define LED1_STK_SIZE 50
//任务句柄
TaskHandle_t LED1Task_Handler;
//任务函数
void led1_task(void *pvParameters);
int main(void)
{
/*配置系统中断分组为4位抢占*/
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4);
/*延时函数初始化*/
delay_init();
/*RCC配置*/
Rcc_config();
/*GPIO初始化*/
Gpio_Init();
/*USART1初始化*/
Uart1_Init(9600);
/*创建开始任务*/
xTaskCreate((TaskFunction_t )start_task, //任务函数
(const char* )"start_task", //任务名称
(uint16_t )START_STK_SIZE, //任务堆栈大小
(void* )NULL, //传递给任务函数的参数
(UBaseType_t )START_TASK_PRIO, //任务优先级
(TaskHandle_t* )&StartTask_Handler); //任务句柄
vTaskStartScheduler(); //开启任务调度
}
/*!
\brief 开始任务函数
\param[in] 传递形参,创建任务时用户自己传入
\param[out] none
\retval none
*/
void start_task(void *pvParameters)
{
taskENTER_CRITICAL(); //进入临界区
//创建LED0任务
xTaskCreate((TaskFunction_t )led0_task,
(const char* )"led0_task",
(uint16_t )LED0_STK_SIZE,
(void* )NULL,
(UBaseType_t )LED0_TASK_PRIO,
(TaskHandle_t* )&LED0Task_Handler);
//创建LED1任务
xTaskCreate((TaskFunction_t )led1_task,
(const char* )"led1_task",
(uint16_t )LED1_STK_SIZE,
(void* )NULL,
(UBaseType_t )LED1_TASK_PRIO,
(TaskHandle_t* )&LED1Task_Handler);
vTaskDelete(StartTask_Handler); //删除开始任务
taskEXIT_CRITICAL(); //退出临界区
}
/*!
\brief led0_task任务函数
\param[in] 传递形参,创建任务时用户自己传入
\param[out] none
\retval none
*/
void led0_task(void *pvParameters)
{
while(1)
{
/*进入临界去*/
taskENTER_CRITICAL();
/*串口打印*/
printf("led0_task\r\n");
/*退出临界区*/
taskEXIT_CRITICAL();
//引脚拉低,GPIOE LED亮
GPIO_ResetBits(GPIOE,GPIO_Pin_5);
vTaskDelay(500);
//引脚拉高,GPIOE LED灭
GPIO_SetBits(GPIOE,GPIO_Pin_5);
vTaskDelay(500);
}
}
/*!
\brief led1_task任务函数
\param[in] 传递形参,创建任务时用户自己传入
\param[out] none
\retval none
*/
void led1_task(void *pvParameters)
{
while(1)
{
/*进入临界去*/
taskENTER_CRITICAL();
/*串口打印*/
printf("led1_task\r\n");
/*退出临界区*/
taskEXIT_CRITICAL();
//引脚拉低,GPIOB LED亮
GPIO_ResetBits(GPIOB,GPIO_Pin_5);
vTaskDelay(100);
//引脚拉高,GPIOB LED灭
GPIO_SetBits(GPIOB,GPIO_Pin_5);
vTaskDelay(100);
}
}
/************************************************************** END OF FILE ****/