Table of contents
1. Obtain FreeRTOS source code
2. FreeRTOS system source code content
3. FreeRTOS system source code porting
1. Obtain FreeRTOS source code
Come to FreeRTOS official website: https://www.freertos.org/
What I mainly provide here is the V10.4.6 version of FreeRTOS
1. Enter the official website and click Download FreeRTOS
2. Click Download
2. FreeRTOS system source code content
Closely related to us is the FreeRTOS kernel, we open the freeRTOS system kernel folder
The contents of each folder are
name | describe |
Demo | FreeRTOS Demo Routine |
License | Licensing related to FreeRTOS |
Source | FreeRTOS source code |
Test | Common and porting layer test code |
Demo folder
In the Demo folder is the demonstration routine of FreeRTOS, which supports a variety of chip architectures, including F1, F4, and F7. Demonstration works.
Source folder
The Source folder is the instance of the source code
Next, let's take a look at the meaning of each file
name | describe |
include | Contains header files for FreeRTOS |
portable | Contains porting files for FreeRTOS |
croytin.c | Coroutine related files |
event_group.c | Event related documents |
list.c | list related files |
tail.c | Queue related files |
stream_buffer.c | Stream buffer related files |
tasks.c | Software related documents |
timers.c | Software timer related documents |
The bold red ones must be added, and others can be used or not, depending on your own needs.
portable folder
In the final analysis, the FreeRTOS operating system is a software-level thing, so how does FreeRTOS work with hardware?
The stuff in the portable folder is the bridge
Since we use MDK for development, here we focus on introducing some of the migration files.
name | describe |
Keil | point to the RBDS folder |
RVDS | Migration files for different core chips |
Meng Mang | memory management file |
3. FreeRTOS system source code porting
Preparation for transplantation:
1. FreeRTOS source code
2. Basic engineering
Migration steps:
1. Add FreeRTOS source code (add FreeRTOS) source code to the basic project, header file path, etc.
2. FreeRTOSConfig, add FreeRTOSconfig.h configuration file
3. Modify the SYSTEM file, modify sys.c, delay.c, usart.c in the SYSTEM file
4. Modify interrupt related files, modify Systick interrupt, SVC interrupt, PendSV interrupt.
5. Add application programs and check whether the porting is successful.
Start porting:
1. Add FreeRTOS source code
Create a new FreeRTOS subfolder in the Middlewares folder of the basic project, as shown in the following figure:
Then you need to add the source code of FreeRTOS to the newly created FreeRTOS subfile. Add all the files under the Source folder of the FreeRTOS kernel source code to the FreeRTOS folder of the project, as shown in the following figure:
Among them, we introduced earlier, portable, only three files are actually used, and we can delete other unused files
In MemMang, it is our memory management algorithm. In fact, we only use algorithm 4, that is, only use heap_4.c. RVDS is a bridge between software and hardware. Different kernels have different files.
2. Add the file to the project
Open the project and create two new file groups, namely Middlewares/FreeRTOS_CORE and Middlewares/FreeRTOS_PORT, as shown in the figure below:
We add the C source code of the kernel to Middlewares/FreeRTOS_CORE, and the Middlewares/FreeRTOS_PORT group is used to store the migration files of the FreeRTOS kernel. Two files need to be added to this group, namely heap_x.c and port.c. Different development boards have different port.c folders to port.
STM32 series development board type | The folder where port.c is located |
STM32F1 | ARM_CM3 |
STM32F4 | ARM_CM4F |
STM32F7 | ARM_CM7/r0p1 |
STM32H7 | ARM_CM7/r0p1 |
After adding, as shown in the figure below:
3. Add header file path
We need to add two header files in total, one is the include header file of the FreeRTOS system, and the other is the header file of the hardware connection. The path after adding is shown in the figure below:
4. Add the FreeRTOSConfig.h file
FreeRTOSConfig.h is the configuration file of the FreeRTOS operating system. The FreeRTOS operating system can be tailored. Users can tailor FreeRTOS according to their needs, and cut out the functions that do not need FreeRTOS, so as to save memory resources in the MCU.
5. Modify the SYSTEM file
We modify sys.h, delay.h, usart.h respectively
5.1、sys.h
Change the 1 in #define SYS_SUPPORT_OS 1 to 0
/**
* SYS_SUPPORT_OS用于定义系统文件夹是否支持OS
* 0,不支持OS
* 1,支持OS
*/
#define SYS_SUPPORT_OS 1
5.2、usart.c
The modification of the usart.c file is also very simple. There are two places that need to be modified. The first is the interrupt service function of the serial port. When using µC/OS, OSIntEnter() and OSIntExit() need to be added to enter and exit the interrupt. function, which is the relevant processing mechanism of µC/OS for interrupts, but there is no such mechanism in FreeRTOS, so these two lines of code are deleted, and the interrupt service function of the modified serial port is as follows:
/**
* @brief 串口1中断服务函数
* @param 无
* @retval 无
*/
void USART_UX_IRQHandler(void)
{
uint32_t timeout = 0;
uint32_t maxDelay = 0x1FFFF;
HAL_UART_IRQHandler(&g_uart1_handle); /* 调用HAL库中断处理公用函数 */
timeout = 0;
while (HAL_UART_GetState(&g_uart1_handle) != HAL_UART_STATE_READY) /* 等待就绪 */
{
timeout++; /* 超时处理 */
if(timeout > maxDelay)
{
break;
}
}
timeout=0;
/* 一次处理完成之后,重新开启中断并设置RxXferCount为1 */
while (HAL_UART_Receive_IT(&g_uart1_handle, (uint8_t *)g_rx_buffer, RXBUFFERSIZE) != HAL_OK)
{
timeout++; /* 超时处理 */
if (timeout > maxDelay)
{
break;
}
}
}
#endif
Next, the second place to modify usart.c is the imported header file, because the related code of µC/OS has been deleted in the interrupt service function of the serial port, and the related code of FreeRTOS has not been used, so the usart. The header file about OS included in c is deleted, the code to be deleted is as follows:
/* 如果使用os,则包括下面的头文件即可 */
#if SYS_SUPPORT_OS
#include "os.h" /* os 使用 */
#endif
5.3、delay.c
Next, modify the last file in the SYSTEM folder—delay.c, the delay.c file needs to be changed more, which can be roughly divided into three steps: delete the relevant code that is applicable to µC/OS but not applicable to FreeRTOS , Add the relevant code of FreeRTOS, and modify some content.
(1) Remove relevant code that works for µC/OS but not for FreeRTOS
A total of 1 global variable, 6 macro definitions, and 3 functions need to be deleted. These codes to be deleted will be used when using µC/OS, but they do not need to be used when using FreeRTOS. The codes to be deleted are as follows:
static uint32_t g_fac_us = 0; /* us延时倍乘数 */
/* 如果SYS_SUPPORT_OS定义了,说明要支持OS了(不限于UCOS) */
#if SYS_SUPPORT_OS
/* 添加公共头文件 ( ucos需要用到) */
#include "os.h"
/* 定义g_fac_ms变量, 表示ms延时的倍乘数, 代表每个节拍的ms数, (仅在使能os的时候,需要用到) */
static uint16_t g_fac_ms = 0;
/*
* 当delay_us/delay_ms需要支持OS的时候需要三个与OS相关的宏定义和函数来支持
* 首先是3个宏定义:
* delay_osrunning :用于表示OS当前是否正在运行,以决定是否可以使用相关函数
* delay_ostickspersec:用于表示OS设定的时钟节拍,delay_init将根据这个参数来初始化systick
* delay_osintnesting :用于表示OS中断嵌套级别,因为中断里面不可以调度,delay_ms使用该参数来决定如何运行
* 然后是3个函数:
* delay_osschedlock :用于锁定OS任务调度,禁止调度
* delay_osschedunlock:用于解锁OS任务调度,重新开启调度
* delay_ostimedly :用于OS延时,可以引起任务调度.
*
* 本例程仅作UCOSII的支持,其他OS,请自行参考着移植
*/
/* 支持UCOSII */
#define delay_osrunning OSRunning /* OS是否运行标记,0,不运行;1,在运行 */
#define delay_ostickspersec OS_TICKS_PER_SEC /* OS时钟节拍,即每秒调度次数 */
#define delay_osintnesting OSIntNesting /* 中断嵌套级别,即中断嵌套次数 */
/**
* @brief us级延时时,关闭任务调度(防止打断us级延迟)
* @param 无
* @retval 无
*/
void delay_osschedlock(void)
{
OSSchedLock(); /* UCOSII的方式,禁止调度,防止打断us延时 */
}
/**
* @brief us级延时时,恢复任务调度
* @param 无
* @retval 无
*/
void delay_osschedunlock(void)
{
OSSchedUnlock(); /* UCOSII的方式,恢复调度 */
}
/**
* @brief us级延时时,恢复任务调度
* @param ticks: 延时的节拍数
* @retval 无
*/
void delay_ostimedly(uint32_t ticks)
{
OSTimeDly(ticks); /* UCOSII延时 */
}
/**
* @brief systick中断服务函数,使用OS时用到
* @param ticks : 延时的节拍数
* @retval 无
*/
void SysTick_Handler(void)
{
/* OS 开始跑了,才执行正常的调度处理 */
if (delay_osrunning == OS_TRUE)
{
/* 调用 uC/OS-II 的 SysTick 中断服务函数 */
OS_CPU_SysTickHandler();
}
HAL_IncTick();
}
#endif
(2) Add the relevant code of FreeRTOS
Just use the extern keyword to import a FreeRTOS function —— xPortSysTickHandler() in the delay.c file. This function is used to process the FreeRTOS system clock beat. This tutorial uses SysTick as the heartbeat of the FreeRTOS operating system, so it needs Call this function in the SysTick interrupt service function, so add the code before the SysTick interrupt service function, and modify the code as follows:
void SysTick_Handler(void)
{
代码省略
}
#endif
(3) Modify part of the content
The last thing to modify includes two, including the header file and 4 functions. First look at the four functions that need to be modified, namely SysTick_Handler(), delay_init(), delay_us() and delay_ms().
(a) SysTick_Handler()
This function is the interrupt service function of SysTick. In this function, the function xPortSysTickHandler() imported in the previous step needs to be called repeatedly. The modified code is as follows:
/**
* @brief systick中断服务函数,使用OS时用到
* @param ticks: 延时的节拍数
* @retval 无
*/
void SysTick_Handler(void)
{
HAL_IncTick();
if (xTaskGetSchedulerState() != taskSCHEDULER_NOT_STARTED) /* OS开始跑了,才执行正常的调度处理 */
{
xPortSysTickHandler();
}
}
(b) delay_init()
The function delay_init() is mainly used to initialize SysTick. What should be explained here is that when the function vTaskStartScheduler() is subsequently called (this function will be analyzed in detail when explaining the FreeRTOS task scheduler below), FreeRTOS will initialize SysTick according to the configuration of the FreeRTOSConfig.h file, so delay_init( ) function initialized SysTick is mainly used before FreeRTOS starts task scheduling. The part to be modified in the function delay_init() is mainly the reload value of SysTick and the deletion of unused codes. The code modification is as follows:
void delay_init(uint16_t sysclk)
{
#if SYS_SUPPORT_OS /* 如果需要支持OS */
uint32_t reload;
#endif
HAL_SYSTICK_CLKSourceConfig(SYSTICK_CLKSOURCE_HCLK);/* SYSTICK使用内核时钟源,同CPU同频率 */
g_fac_us = sysclk; /* 不论是否使用OS,g_fac_us都需要使用 */
#if SYS_SUPPORT_OS /* 如果需要支持OS. */
reload = sysclk; /* 每秒钟的计数次数 单位为M */
reload *= 1000000 / configTICK_RATE_HZ; /* 根据delay_ostickspersec设定溢出时间,reload为24位
寄存器,最大值:16777216,在168M下,约合0.099s左右 */
SysTick->CTRL |= SysTick_CTRL_TICKINT_Msk; /* 开启SYSTICK中断 */
SysTick->LOAD = reload; /* 每1/delay_ostickspersec秒中断一次 */
SysTick->CTRL |= SysTick_CTRL_ENABLE_Msk; /* 开启SYSTICK */
#endif
}
(c) delay_us()
The function delay_us() is used for microsecond-level CPU busy delay. The original function delay_us() has added custom functions delay_osschedlock() and delay_osschedunlock() before and after the delay to lock and unlock the µC/OS task scheduler. This makes the delay more accurate. It is not necessary to add these two functions in FreeRTOS, but it should be noted that this will reduce the precision of the microsecond-level delay of the function delay_us(). The modified code of the function delay_us() is as follows:
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; /* 时间超过/等于要延迟的时间,则退出 */
}
}
}
}
(d) delay_ms()
The function delay_ms() is used for millisecond-level CPU busy delay. The original function delay_ms() will judge whether µC/OS is running. If µC/OS is running, use µC/OS OS delay to perform millisecond-level delay. , otherwise call the function delay_us() to perform a millisecond-level CPU busy delay. In FreeRTOS, the function delay_ms() can be defined as only CPU busy delay. When OS delay is required, call the OS delay function vTaskDelay() provided by FreeRTOS (this will be explained later in the explanation of FreeRTOS time management. function analysis) to perform system tick-level delay, the modified code of the function delay_ms() is as follows:
void delay_ms(uint16_t nms)
{
uint32_t i;
for (i=0; i<nms; i++)
{
delay_us(1000);
}
}
(e) Include the header
According to the modification of the above steps, the related functions of FreeRTOS are used in the delay.c file, so it is necessary to include the related header files of FreeRTOS in the delay.c file, and remove the existing µC/OS related header files. First look at the µC/OS-related header files included in the delay.c file before modification:
/* 添加公共头文件 (FreeRTOS 需要用到) */
#include "FreeRTOS.h"
#include "task.h"
5.5. Modify the interrupt timer function
During the transplantation of FreeRTOS, there will be these few to three important interrupts, namely the FreeRTOS system time base timer interrupt (SysTick interrupt), SVC interrupt, PendSV interrupt (SVC interrupt and PendSV interrupt are explained below. FreeRTOS interrupt and FreeRTOS task It will be analyzed in detail when switching), the interrupt service functions of these three interrupts are defined in the files provided by the HAL library, for the STM32 development boards with different punctual atoms, they correspond to different files, and the specific correspondence is shown in the following table:
STM32 series development board type of punctual atom | The file where the interrupt service function is located |
STM32F1 | stm32f1xx_it.c |
STM32F4 | stm32f4xx_it.c |
STM32F7 | stm32f7xx_it.c |
STM32H7 | stm32h7xx_it.c |
Among them, the interrupt service function of SysTick has been defined in the delay.c file, and FreeRTOS also provides the interrupt service function of SVC and PendSV, so the three interrupt service functions provided by the HAL library need to be commented out, and the macro switch is used here In this way, the three interrupt service functions in the HAL library are not included in the compilation. The macros used are defined in sys.h, so it is also necessary to import the sys.h header file. Refer to the above table to find the corresponding file and modify it. After modification The code looks like this:
/*导入sys.h头文件*/
#include "./SYSTEM/SYS/sys.h"
#if(!SYS_SUPPORT_OS)
void SVC_Handler(void)
{
}
#endif
#if(!SYS_SUPPORT_OS)
void PendSV_Handler(void)
{
}
#endif
#if(!SYS_SUPPORT_OS)
void SysTick_Handler(void)
{
HAL_IncTick();
}
#endif
Finally, it is also the last place to be modified for porting FreeRTOS. The FreeRTOSConfig.h file has the following definition: #define configPRIO_BITS __NVIC_PRIO_BITS
we need to
#define __NVIC_PRIO_BITS 4U
changed to
#define __NVIC_PRIO_BITS 4
5.6. Optional steps
This step is optional, but the author strongly recommends that readers complete it, because it will be used in subsequent experiments and standardize the project. This section can be divided into 3 small parts, which are respectively modifying the project target name, removing the USMART debugging component, and adding the timer driver.
1. Modify the project target name
Change the previous basic project name to FreeRTOS
2. Remove the USMART debugging component
3. Add timer driver
Since the basic timer peripherals of STM32 need to be used in subsequent experiments, it is necessary to add timer-related driver files to the project.
5.7. Add application
After transplanting FreeRTOS, of course, it is necessary to test whether the transplantation is successful. In this step, a total of 1 file needs to be modified and 2 files added, the modified 1 file is main.c, and the added 2 files are freertos_demo.c and freertos_demo.h. For main.c, it mainly completes some hardware initialization in the main() function, and finally calls the freertos_demo() function in the freertos_demo.c file. And freertos_demo.c is used to write FreeRTOS related application code.
#include "./SYSTEM/sys/sys.h"
#include "./SYSTEM/usart/usart.h"
#include "./SYSTEM/delay/delay.h"
#include "./BSP/LED/led.h"
#include "./BSP/LCD/lcd.h"
#include "./BSP/KEY/key.h"
#include "./BSP/SRAM/sram.h"
#include "./MALLOC/malloc.h"
#include "freertos_demo.h"
int main(void)
{
HAL_Init(); /* 初始化 HAL 库 */
sys_stm32_clock_init(RCC_PLL_MUL9); /* 设置时钟, 72Mhz */
delay_init(72); /* 延时初始化 */
usart_init(115200); /* 串口初始化为 115200 */
led_init(); /* 初始化 LED */
lcd_init(); /* 初始化 LCD */
key_init(); /* 初始化按键 */
sram_init(); /* SRAM 初始化 */
my_mem_init(SRAMIN); /* 初始化内部 SRAM 内存池 */
my_mem_init(SRAMEX); /* 初始化外部 SRAM 内存池 */
freertos_demo(); /* 运行 FreeRTOS 例程 */
}