STM32标准库移植FreeRTOS(以F407为例)
前言
尝试过CubeMX直接生成HAL库+FreeRTOS;也尝试过CubeMX生成HAL库+手动移植FreeRTOS;正点原子也有手动HAL+手动FreeRTOS;
这里想要记录一下标准库+手动移植FreeRTOS
下载FreeRTOS源码
可以直接在浏览器搜索 “ FreeRTOS ” 下载,或者直接点此条博客的绑定资源。
新建新建stm32标准库空白工程模板
这个根据自己的已有模板创建使用,这里不做讲解。
移植
-
在工程文件夹内建立《
FreeRTOS
》文件夹,将资料\FreeRTOS源码\FreeRTOSv9.0.0\FreeRTOS\Source
里的文件全部拷入其中
-
删除工程
stm32标准库工程模板\FreeRTOS\portable
里的文件,仅保留《keil
》《MemMang
》《RVDS
》三个文件夹(或者在前一步拷贝的时候仅拷贝这三个文件夹)
-
打开工程,点开箱子图标新建两个Group名为《
FreeRTOS_Core
》《FreeRTOS_Port
》 -
在《
FreeRTOS_Core
》中添加源码《FreeRTOS
》文件夹里面的 五个.c文件
stm32标准库工程模板\FreeRTOS\
croutine.c
stm32标准库工程模板\FreeRTOS\event_groups.c
stm32标准库工程模板\FreeRTOS\list.c
stm32标准库工程模板\FreeRTOS\queue.c
stm32标准库工程模板\FreeRTOS\task.c
stm32标准库工程模板\FreeRTOS\timers.c
- 在 《
FreeRTOS_Port
》中添加源码《FreeRTOS
》文件夹里面的 两个.c文件
stm32标准库工程模板\FreeRTOS\portable\MemMang\
heap_4.c
stm32标准库工程模板\FreeRTOS\portable\RVDS\ARM_CM4F\port.c
-
进入工程内的
stm32f4xx_it.c
文件
注释 这三个函数或者 改成条件宏定义// void SysTick_Handler(void){
//};//void PendSV_Handler(void){
//};//void SVC_Handler(void){
//};
- 拷贝
资料\FreeRTOS源码\FreeRTOSv9.0.0\FreeRTOS\Demo\CORTEX_M4F_STM32F407ZG-SK\FreeRTOSConfig.h
的.h配置文件到stm32标准库工程模板\FreeRTOS\include
中,并将其添加进工程中(可以放进user的group里)
- 点开
FreeRTOSConfig.h
文件
将以下宏定义置0configUSE_IDLE_HOOK 0
configUSE_TICK_HOOK 0
configCHECK_FOR_STACK_OVERFLOW 0
configUSE_MALLOC_FAILED_HOOK 0
- 找到
__NVIC_PRIO_BITS
的宏定义(可在整个project中直接搜索),如果是4U
则改为4
- 点开
FreeRTOSConfig.h
文件
将:
/* Ensure stdint is only used by the compiler, and not the assembler. */
#ifdef __ICCARM__
#include <stdint.h>
extern uint32_t SystemCoreClock;
#endif
改为:(否则报错SystemCoreClock未定义)
#if defined (__ICCARM__) || defined (__CC_ARM) || defined (__GNUC__)
#include <stdint.h>
extern uint32_t SystemCoreClock;
#endif
- 点开魔术棒 在C/C++下的 Include Paths 添加路径
.\FreeRTOS\include
.\FreeRTOS\portable\RVDS\ARM_CM4F
- 编译通过
点灯试验
- 在main.c添加头文件 :
#include “FreeRTOS.h”
#include “task.h”
- 在main函数外上面定义任务
#define START_TASK_PRIO 1
#define START_STK_SIZE 120
void start_task(void * pvParameters); //任务函数
TaskHandle_t StartTask_Handler; //任务句柄
#define TASK1_TASK_PRIO 2
#define TASK1_STK_SIZE 120
void task1_task(void * pvParameters);
TaskHandle_t Task1Task_Handler; //任务句柄
#define TASK2_TASK_PRIO 3
#define TASK2_STK_SIZE 120
void task2_task(void * pvParameters);
TaskHandle_t Task2Task_Handler; //任务句柄
- 在main函数内部初始化外设 和 创建初始任务
注意优先级分组为4,全为抢占优先级,去除main函数内的while函数
xTaskCreate((TaskFunction_t ) start_task,
(char* ) "start_task",
(uint16_t ) START_STK_SIZE,
(void * ) NULL,
(UBaseType_t ) START_TASK_PRIO,
(TaskHandle_t* ) &StartTask_Handler);
vTaskStartScheduler(); //开启任务调度
- 在main函数外下面写任务函数
void start_task(void * pvParameters)
{
//创建Task1
taskENTER_CRITICAL(); /* 进入临界区 */
xTaskCreate((TaskFunction_t ) task1_task,
(char* ) "task1_task",
(uint16_t ) TASK1_STK_SIZE,
(void * ) NULL,
(UBaseType_t ) TASK1_TASK_PRIO,
(TaskHandle_t* ) &Task1Task_Handler);
//创建Task2
xTaskCreate((TaskFunction_t ) task2_task,
(char* ) "task2_task",
(uint16_t ) TASK1_STK_SIZE,
(void * ) NULL,
(UBaseType_t ) TASK2_TASK_PRIO,
(TaskHandle_t* ) &Task2Task_Handler);
vTaskDelete(StartTask_Handler); //NULL
taskEXIT_CRITICAL(); /* 退出临界区 */
}
void task1_task(void * pvParameters){
while(1){
}
}
void task2_task(void * pvParameters){
while(1){
}
}
- 编译下载
main函数
#include "stm32f4xx.h" // Device header
#include "delay.h"
#include "usart.h"
#include "LED.h"
#include "FreeRTOS.h"
#include "task.h"
#define START_TASK_PRIO 1
#define START_STK_SIZE 120
void start_task(void * pvParameters); //任务函数
TaskHandle_t StartTask_Handler; //任务句柄
#define TASK1_TASK_PRIO 2
#define TASK1_STK_SIZE 120
void task1_task(void * pvParameters);
TaskHandle_t Task1Task_Handler; //任务句柄
#define TASK2_TASK_PRIO 3
#define TASK2_STK_SIZE 120
void task2_task(void * pvParameters);
TaskHandle_t Task2Task_Handler; //任务句柄
int main(){
delay_init(168);
uart_init(9600);
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4);
printf("Start !\r\n");
LED_Init();
xTaskCreate((TaskFunction_t ) start_task,
(char* ) "start_task",
(uint16_t ) START_STK_SIZE,
(void * ) NULL,
(UBaseType_t ) START_TASK_PRIO,
(TaskHandle_t* ) &StartTask_Handler);
vTaskStartScheduler(); //开启任务调度
}
void start_task(void * pvParameters)
{
//创建Task1
taskENTER_CRITICAL(); /* 进入临界区 */
xTaskCreate((TaskFunction_t ) task1_task,
(char* ) "task1_task",
(uint16_t ) TASK1_STK_SIZE,
(void * ) NULL,
(UBaseType_t ) TASK1_TASK_PRIO,
(TaskHandle_t* ) &Task1Task_Handler);
//创建Task2
xTaskCreate((TaskFunction_t ) task2_task,
(char* ) "task2_task",
(uint16_t ) TASK1_STK_SIZE,
(void * ) NULL,
(UBaseType_t ) TASK2_TASK_PRIO,
(TaskHandle_t* ) &Task2Task_Handler);
vTaskDelete(StartTask_Handler); //NULL
taskEXIT_CRITICAL(); /* 退出临界区 */
}
void task1_task(void * pvParameters){
while(1){
LED1(1);
vTaskDelay(1000);//延时函数
LED1(0);
vTaskDelay(1000);
}
}
void task2_task(void * pvParameters){
while(1){
LED2(1);
vTaskDelay(1000);
LED2(0);
vTaskDelay(1000);
}
}
delay函数实现(不会引起任务调度)
- 包含头文件
#include “FreeRTOS.h”
#include “task.h”
- 重新定义
SysTick_Handler
函数
extern void xPortSysTickHandler(void);
/**
* @brief systick中断服务函数,使用OS时用到
* @param ticks : 延时的节拍数
* @retval 无
*/
void SysTick_Handler(void)
{
/* OS 开始跑了,才执行正常的调度处理 */
if (xTaskGetSchedulerState() != taskSCHEDULER_NOT_STARTED)
{
xPortSysTickHandler();
}
}
- 进入
FreeRTOSConfig.h
文件
注释掉(避免重定义,因为刚刚在delay函数里已经定义过了)
// #define xPortSysTickHandler SysTick_Handler
- 重新实现delay_init 、 delay_us、 delay_ms 函数
delay函数
#include "delay.h"
#include "sys.h"
//
#include "FreeRTOS.h"
#include "task.h"
static uint32_t g_fac_us = 0; /* us延时倍乘数 */
extern void xPortSysTickHandler(void);
/**
* @brief systick中断服务函数,使用OS时用到
* @param ticks : 延时的节拍数
* @retval 无
*/
void SysTick_Handler(void)
{
/* OS 开始跑了,才执行正常的调度处理 */
if (xTaskGetSchedulerState() != taskSCHEDULER_NOT_STARTED)
{
xPortSysTickHandler();
}
}
/**
* @brief 初始化延迟函数
* @param sysclk: 系统时钟频率, 即CPU频率(rcc_c_ck), 180MHz
* @retval 无
*/
void delay_init(uint16_t sysclk)
{
/* 如果需要支持OS */
uint32_t reload;
SysTick_CLKSourceConfig(SysTick_CLKSource_HCLK); /* SYSTICK使用外部时钟源,频率为HCLK */
g_fac_us = sysclk; /* 不论是否使用OS,g_fac_us都需要使用 */
/* 如果需要支持OS. */
reload = sysclk; /* 每秒钟的计数次数 单位为M */
reload *= 1000000 / configTICK_RATE_HZ; /* 根据delay_ostickspersec设定溢出时间,reload为24位
* 寄存器,最大值:16777216,在180M下,约合0.0932s左右 */
SysTick->CTRL |= SysTick_CTRL_TICKINT_Msk; /* 开启SYSTICK中断 */
SysTick->LOAD = reload; /* 每1/delay_ostickspersec秒中断一次 */
SysTick->CTRL |= SysTick_CTRL_ENABLE_Msk; /* 开启SYSTICK */
}
/**
* @brief 延时nus
* @param nus: 要延时的us数
* @note 注意: nus的值,不要大于93206us(最大值即2^24 / g_fac_us @g_fac_us = 180)
* @retval 无
*/
void delay_us(uint32_t nus)
{
uint32_t ticks;
uint32_t told, tnow, tcnt = 0;
uint32_t reload = SysTick->LOAD; /* LOAD的值 */
ticks = nus * g_fac_us; /* 需要的节拍数 */
told = SysTick->VAL; /* 刚进入时的计数器值 */
while (1)
{
tnow = SysTick->VAL;
if (tnow != told)
{
if (tnow < told)
{
tcnt += told - tnow; /* 这里注意一下SYSTICK是一个递减的计数器就可以了 */
}
else
{
tcnt += reload - tnow + told;
}
told = tnow;
if (tcnt >= ticks)
{
break; /* 时间超过/等于要延迟的时间,则退出 */
}
}
}
}
/**
* @brief 延时nms
* @param nms: 要延时的ms数 (0< nms <= 65535)
* @retval 无
*/
void delay_ms(uint16_t nms)
{
uint32_t i;
for (i=0; i<nms; i++)
{
delay_us(1000);
}
}