目录
完整工程代码:
零. 参考
一. 外围配置
二, 放进来freertos源代码
三.修改makefile
四. 解决修改函数重定义报错
五. 让Freertos的systick工作起来
六. 验证
完整工程代码:
https://download.csdn.net/download/u011493332/87821404
零. 参考
正在入门freertos, 网上教程还是以正宗的freertos的API为主, CMSIS又分了V1,V2, 看起来有点混乱, 干脆自己移植一个原版freertos, 省心
文章参考野火的教程:
FreeRTOS 内核实现与应用开发实战指南
—基于野火 STM32 全系列(M3/4/7)开发板
第13章 移植 FreeRTOS 到 STM32
他这个教程里非要修改人家freertos原始的目录结构, 我不想改人家原始目录结构
他的教程是Keil的, 我环境是GCC工具链的
一. 外围配置
1. 点一个灯
2. systick用定时器
3.
SWD RCC
二, 放进来freertos源代码
freertos源代码压缩包的目录结构, 用到的是Source文件夹
在生成的工程里, 新建一个目录, 名字随意, 我这里是freertos_z, 把Source文件夹放进来, 新建一个config文件夹跟Source文件夹同级, 其实这个中间件文件夹应该放纯源码, config属于是用户文件, 按cubemx的文件管理习惯, 应该放在main.h旁边更好, 介意的话就那么做, 改改makefile就行
这时候, Souce文件夹里的portable文件夹里有很多文件, 是针对各种编译器的, 我们当前的工程是用GCC工具链的, 所以删掉那些无关的, 其实删不删都行, makefile里面指定留着的就可以了
删之后, 只留了memMang和GCC/Arm_CM3, 因为我当前是一个STM32103RC的板子. 别的板子根据实际内核选择保留
添加config文件. 我这里用了一个以前cubemx生成的config文件
/* USER CODE BEGIN Header */
/*
* FreeRTOS Kernel V10.0.1
* Copyright (C) 2017 Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
* the Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*
* http://www.FreeRTOS.org
* http://aws.amazon.com/freertos
*
* 1 tab == 4 spaces!
*/
/* USER CODE END Header */
#ifndef FREERTOS_CONFIG_H
#define FREERTOS_CONFIG_H
/*-----------------------------------------------------------
* Application specific definitions.
*
* These definitions should be adjusted for your particular hardware and
* application requirements.
*
* These parameters and more are described within the 'configuration' section of the
* FreeRTOS API documentation available on the FreeRTOS.org web site.
*
* See http://www.freertos.org/a00110.html
*----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
/* Section where include file can be added */
/* USER CODE END Includes */
/* Ensure definitions are only used by the compiler, and not by the assembler. */
#if defined(__ICCARM__) || defined(__CC_ARM) || defined(__GNUC__)
#include <stdint.h>
extern uint32_t SystemCoreClock;
#endif
#define configUSE_PREEMPTION 1
#define configSUPPORT_STATIC_ALLOCATION 1
#define configSUPPORT_DYNAMIC_ALLOCATION 1
#define configUSE_IDLE_HOOK 0
#define configUSE_TICK_HOOK 0
#define configCPU_CLOCK_HZ ( SystemCoreClock )
#define configTICK_RATE_HZ ((TickType_t)1000)
#define configMAX_PRIORITIES ( 56 )
#define configMINIMAL_STACK_SIZE ((uint16_t)128)
#define configTOTAL_HEAP_SIZE ((size_t)1024*4)
#define configMAX_TASK_NAME_LEN ( 16 )
#define configUSE_TRACE_FACILITY 1
#define configUSE_16_BIT_TICKS 0
#define configUSE_MUTEXES 1
#define configQUEUE_REGISTRY_SIZE 8
#define configUSE_RECURSIVE_MUTEXES 1
#define configUSE_COUNTING_SEMAPHORES 1
#define configUSE_PORT_OPTIMISED_TASK_SELECTION 0
/* Co-routine definitions. */
#define configUSE_CO_ROUTINES 0
#define configMAX_CO_ROUTINE_PRIORITIES ( 2 )
/* Software timer definitions. */
#define configUSE_TIMERS 1
#define configTIMER_TASK_PRIORITY ( 2 )
#define configTIMER_QUEUE_LENGTH 10
#define configTIMER_TASK_STACK_DEPTH 256
/* Set the following definitions to 1 to include the API function, or zero
to exclude the API function. */
#define INCLUDE_vTaskPrioritySet 1
#define INCLUDE_uxTaskPriorityGet 1
#define INCLUDE_vTaskDelete 1
#define INCLUDE_vTaskCleanUpResources 0
#define INCLUDE_vTaskSuspend 1
#define INCLUDE_vTaskDelayUntil 1
#define INCLUDE_vTaskDelay 1
#define INCLUDE_xTaskGetSchedulerState 1
#define INCLUDE_xTimerPendFunctionCall 1
#define INCLUDE_xQueueGetMutexHolder 1
#define INCLUDE_uxTaskGetStackHighWaterMark 1
#define INCLUDE_eTaskGetState 1
/*
* The CMSIS-RTOS V2 FreeRTOS wrapper is dependent on the heap implementation used
* by the application thus the correct define need to be enabled below
*/
#define USE_FreeRTOS_HEAP_4
/* Cortex-M specific definitions. */
#ifdef __NVIC_PRIO_BITS
/* __BVIC_PRIO_BITS will be specified when CMSIS is being used. */
#define configPRIO_BITS __NVIC_PRIO_BITS
#else
#define configPRIO_BITS 4
#endif
/* The lowest interrupt priority that can be used in a call to a "set priority"
function. */
#define configLIBRARY_LOWEST_INTERRUPT_PRIORITY 15
/* The highest interrupt priority that can be used by any interrupt service
routine that makes calls to interrupt safe FreeRTOS API functions. DO NOT CALL
INTERRUPT SAFE FREERTOS API FUNCTIONS FROM ANY INTERRUPT THAT HAS A HIGHER
PRIORITY THAN THIS! (higher priorities are lower numeric values. */
#define configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY 5
/* Interrupt priorities used by the kernel port layer itself. These are generic
to all Cortex-M ports, and do not rely on any particular library functions. */
#define configKERNEL_INTERRUPT_PRIORITY ( configLIBRARY_LOWEST_INTERRUPT_PRIORITY << (8 - configPRIO_BITS) )
/* !!!! configMAX_SYSCALL_INTERRUPT_PRIORITY must not be set to zero !!!!
See http://www.FreeRTOS.org/RTOS-Cortex-M3-M4.html. */
#define configMAX_SYSCALL_INTERRUPT_PRIORITY ( configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY << (8 - configPRIO_BITS) )
/* Normal assert() semantics without relying on the provision of an assert.h
header file. */
/* USER CODE BEGIN 1 */
#define configASSERT( x ) if ((x) == 0) {taskDISABLE_INTERRUPTS(); for( ;; );}
/* USER CODE END 1 */
/* Definitions that map the FreeRTOS port interrupt handlers to their CMSIS
standard names. */
#define vPortSVCHandler SVC_Handler
#define xPortPendSVHandler PendSV_Handler
/* IMPORTANT: This define is commented when used with STM32Cube firmware, when the timebase source is SysTick,
to prevent overwriting SysTick_Handler defined within STM32Cube HAL */
#define xPortSysTickHandler SysTick_Handler
/* USER CODE BEGIN Defines */
/* Section where parameter definitions can be added (for instance, to override default ones in FreeRTOS.h) */
/* USER CODE END Defines */
#endif /* FREERTOS_CONFIG_H */
三.修改makefile
添加参与编译的c文件, 和.h文件路径
...
######################################
# source
######################################
# C sources
C_SOURCES = \
Core/src/app.c \
Core/Src/main.c \
Middlewares/Third_Party/freertos_z/Source/croutine.c \
Middlewares/Third_Party/freertos_z/Source/event_groups.c \
Middlewares/Third_Party/freertos_z/Source/list.c \
Middlewares/Third_Party/freertos_z/Source/queue.c \
Middlewares/Third_Party/freertos_z/Source/stream_buffer.c \
Middlewares/Third_Party/freertos_z/Source/tasks.c \
Middlewares/Third_Party/freertos_z/Source/timers.c \
Middlewares/Third_Party/freertos_z/Source/portable/MemMang/heap_4.c \
Middlewares/Third_Party/freertos_z/Source/portable/GCC/ARM_CM3/port.c \
...
...
# C includes
C_INCLUDES = \
-ICore/Inc \
-IMiddlewares/Third_Party/freertos_z/Source/include \
-IMiddlewares/Third_Party/freertos_z/Source/portable/GCC/ARM_CM3 \
-IMiddlewares/Third_Party/freertos_z/config \
-IDrivers/STM32F1xx_HAL_Driver/Inc/Legacy \
-IDrivers/STM32F1xx_HAL_Driver/Inc \
-IDrivers/CMSIS/Device/ST/STM32F1xx/Include \
-IDrivers/CMSIS/Include
...
路径以实际文件位置为准
四. 解决修改函数重定义报错
这个时候, 可以尝试编译一次, 不过必然报错, 如果makefile里文件路径都对的话, 最后会留下3个报错, 3个都是函数重复定义
因为freertos使用了3个自己的中断处理函数, cubemx默认也自动生成他们, 就是FreeRTOSConfig.h最后几行这几个宏
freertos把原来cubemx的中断函数重新定义了一个名字, 以此来实现可移植, 同时也在平台的port.c里重新定义了他们三个.
这里不能简单的把stm32f1xx_it.c里的三个中断删了, 那样用cubemx重新生成工程时候他们还会出来, 所以这里应该在cubemx里面设置, 取消他们的生成
取消掉之后, 用cubemx重新生成工程代码, 这时, 中断.c里面这3个函数没有了. 编译系统将会去编译port.c里面的三个中断函数, 到这里, 严格说, 就已经是移植完成状态. 但是还差系统systick的设置
五. 让Freertos的systick工作起来
一开始, 我们设置了systick使用tim2, 这样生成两段代码, 一是Core\Src\stm32f1xx_it.c里面
还有 main.c里面的回调函数
这里只要修改回调函数就可以了, 让tim2中断发生时候, 更新freertos的systick, 同时屏蔽hal的systick. 再一次同时, 还要不让cubemx的自动生成干扰我们的修改, 修改后如下, 内容参考了野火的教程
代码
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
/* USER CODE BEGIN Callback 0 */
#if 0
/* USER CODE END Callback 0 */
if (htim->Instance == TIM2) {
HAL_IncTick();
}
/* USER CODE BEGIN Callback 1 */
#endif
if (htim->Instance == TIM2) {
#if (INCLUDE_xTaskGetSchedulerState == 1 )
if (xTaskGetSchedulerState() != taskSCHEDULER_NOT_STARTED) {
#endif /* INCLUDE_xTaskGetSchedulerState */
xPortSysTickHandler();
#if (INCLUDE_xTaskGetSchedulerState == 1 )
}
#endif /* INCLUDE_xTaskGetSchedulerState */
}
/* USER CODE END Callback 1 */
}
到这里移植全部完成
剩下的就是验证了
六. 验证
我添加里app.c和app.h, 代码都在app.c里写, main.c只调用app.c里一个函数
app.c:
#include "app.h"
/* FreeRTOS 头文件 */
#include "FreeRTOS.h"
#include "task.h"
#include <stdio.h>
/**************************** 任务句柄 ********************************/
/*
* 任务句柄是一个指针,用于指向一个任务,当任务创建好之后,它就具有了一个任务句柄
* 以后我们要想操作这个任务都需要通过这个任务句柄,如果是自身的任务操作自己,那么
* 这个句柄可以为 NULL。
*/
/* 创建任务句柄 */
static TaskHandle_t AppTaskCreate_Handle;
/* LED 任务句柄 */
static TaskHandle_t LED_Task_Handle;
/******************************* 内核对象句柄 **************************/
/*
* 信号量,消息队列,事件标志组,软件定时器这些都属于内核的对象,要想使用这些内核
* 对象,必须先创建,创建成功之后会返回一个相应的句柄。实际上就是一个指针,后续我
* 们就可以通过这个句柄操作这些内核对象。
*
*
内核对象说白了就是一种全局的数据结构,通过这些数据结构我们可以实现任务间的通信,
* 任务间的事件同步等各种功能。至于这些功能的实现我们是通过调用这些内核对象的函数
* 来完成的
*
*/
/************************** 全局变量声明 *******************************/
/*
* 当我们在写应用程序的时候,可能需要用到一些全局变量。
*/
/* AppTaskCreate 任务任务堆栈 */
static StackType_t AppTaskCreate_Stack[128];
/* LED 任务堆栈 */
static StackType_t LED_Task_Stack[128];
/* AppTaskCreate 任务控制块 */
static StaticTask_t AppTaskCreate_TCB;
/* AppTaskCreate 任务控制块 */
static StaticTask_t LED_Task_TCB;
/* 空闲任务任务堆栈 */
static StackType_t Idle_Task_Stack[configMINIMAL_STACK_SIZE];
/* 定时器任务堆栈 */
static StackType_t Timer_Task_Stack[configTIMER_TASK_STACK_DEPTH];
/* 空闲任务控制块 */
static StaticTask_t Idle_Task_TCB;
/* 定时器任务控制块 */
static StaticTask_t Timer_Task_TCB;
/*
*************************************************************************
* 函数声明
*************************************************************************
*/
static void AppTaskCreate(void); /* 用于创建任务 */
static void LED_Task(void *pvParameters); /* LED_Task 任务实现 */
/**
* 使用了静态分配内存,以下这两个函数是由用户实现,函数在 task.c 文件中有引用
*当且仅当 configSUPPORT_STATIC_ALLOCATION 这个宏定义为 1 的时候才有效
*/
void vApplicationGetTimerTaskMemory(StaticTask_t **ppxTimerTaskTCBBuffer,
StackType_t **ppxTimerTaskStackBuffer,
uint32_t *pulTimerTaskStackSize);
void vApplicationGetIdleTaskMemory(StaticTask_t **ppxIdleTaskTCBBuffer,
StackType_t **ppxIdleTaskStackBuffer,
uint32_t *pulIdleTaskStackSize);
/***********************************************************************
* @ 函数名 : AppTaskCreate
* @ 功能说明: 为了方便管理,所有的任务创建函数都放在这个函数里面
* @ 参数 : 无
* @ 返回值 : 无
***************************************************************/
static void AppTaskCreate(void)
{
taskENTER_CRITICAL(); // 进入临界区
/* 创建 LED_Task 任务 */
LED_Task_Handle = xTaskCreateStatic((TaskFunction_t)LED_Task, // 任务函数
(const char *)"LED_Task", // 任务名称
(uint32_t)128, // 任务堆栈大小
(void *)NULL, // 传递给任务函数的参数
(UBaseType_t)4, // 任务优先级
(StackType_t *)LED_Task_Stack, // 任务堆栈
(StaticTask_t *)&LED_Task_TCB); // 任务控制块
if (NULL != LED_Task_Handle) /* 创建成功 */
printf("LED_Task create success\n");
else
printf("LED_Task create failed\n");
vTaskDelete(AppTaskCreate_Handle); // 删除 AppTaskCreate 任务
taskEXIT_CRITICAL(); // 退出临界区
}
/**************************************************************
* @ 函数名 : LED_Task
* @ 功能说明: LED_Task 任务主体
* @ 参数 :
* @ 返回值 : 无
********************************************************************/
static void LED_Task(void *parameter)
{
while (1) {
vTaskDelay(500);
HAL_GPIO_WritePin(LED2_GPIO_Port, LED2_Pin, GPIO_PIN_RESET);
vTaskDelay(500);
HAL_GPIO_WritePin(LED2_GPIO_Port, LED2_Pin, GPIO_PIN_SET);
}
}
/**
**********************************************************************
* @brief 获取空闲任务的任务堆栈和任务控制块内存
* ppxTimerTaskTCBBuffer : 任务控制块内存
* ppxTimerTaskStackBuffer : 任务堆栈内存
* pulTimerTaskStackSize : 任务堆栈大小
* @author fire
* @version V1.0
* @date 2018-xx-xx
**********************************************************************
*/
void vApplicationGetIdleTaskMemory(StaticTask_t **ppxIdleTaskTCBBuffer,
StackType_t **ppxIdleTaskStackBuffer,
uint32_t *pulIdleTaskStackSize)
{
*ppxIdleTaskTCBBuffer = &Idle_Task_TCB; /* 任务控制块内存 */
*ppxIdleTaskStackBuffer = Idle_Task_Stack; /* 任务堆栈内存 */
*pulIdleTaskStackSize = configMINIMAL_STACK_SIZE; /* 任务堆栈大小 */
}
/**
*********************************************************************
* @brief 获取定时器任务的任务堆栈和任务控制块内存
* ppxTimerTaskTCBBuffer : 任务控制块内存
* ppxTimerTaskStackBuffer : 任务堆栈内存
* pulTimerTaskStackSize : 任务堆栈大小
* @author fire
* @version V1.0
* @date 2018-xx-xx
**********************************************************************
*/
void vApplicationGetTimerTaskMemory(StaticTask_t **ppxTimerTaskTCBBuffer,
StackType_t **ppxTimerTaskStackBuffer,
uint32_t *pulTimerTaskStackSize)
{
*ppxTimerTaskTCBBuffer = &Timer_Task_TCB; /* 任务控制块内存 */
*ppxTimerTaskStackBuffer = Timer_Task_Stack; /* 任务堆栈内存 */
*pulTimerTaskStackSize = configTIMER_TASK_STACK_DEPTH; /* 任务堆栈大小 */
}
void app_initial(void)
{
// vTaskStartScheduler();
}
extern UART_HandleTypeDef huart1;
void app_start(void)
{
HAL_UART_Transmit(&huart1, (uint8_t *)"TEST\r\n", 16, 100);
printf("app start\r\n");
/* 创建 AppTaskCreate 任务 */
AppTaskCreate_Handle = xTaskCreateStatic((TaskFunction_t)AppTaskCreate,
(const char *)"AppTaskCreate", // 任务名称
(uint32_t)128, // 任务堆栈大小
(void *)NULL, // 传递给任务函数的参数
(UBaseType_t)3, // 任务优先级
(StackType_t *)AppTaskCreate_Stack,
(StaticTask_t *)&AppTaskCreate_TCB);
if (NULL != AppTaskCreate_Handle) /* 创建成功 */
vTaskStartScheduler(); /* 启动任务,开启调度 */
while (1)
; /* 正常不会执行到这里 */
}
也是参考野火的代码. 删了他代码里的bsp初始化, 因为cubemx已经帮我们完成了
完毕!