整个专栏主要是博主结合自身对FreeRTOS的实战学习以及源码分析,基于STM32F767 Nucleo-144平台,在CubeIDE下进行开发,结合官方的HAL库,将硬件环节的问题减少到最小,将精力主要放在RTOS的学习上.
文章目录1 FreeRTOS1.1 获取源码1.2 源码结构2 CubeMX 整合 RTOS3 新建RTOS任务4 总结 1 FreeRTOSFreeRTOS是免费的嵌入式实时系统,可以访问官网,至少目前是免费,并且社区相当活跃,但是以后会不会被割韭菜就不清楚了,所以感觉还是支持国产比较好,大家可以参考一下RT-Thread;
1.1 获取源码我们可以登陆到官网下载源码的压缩包,如下图所示;
FreeRTOS还将源码托管到github上,登陆官网可以看到下图的两个仓库;
但是本文暂不会介绍如何移植RTOS,在远古时期,因为第三方的支持力度不够,因此有一些平台只能自己移植,现在第三方的支持相当于给力,直接拿来用即可.
1.2 源码结构自动生成的代码中找到FreeRTOS的源码如下;
对于相应的文件夹和源码文件做一下介绍;
CMSIS_RTOS_V2
:这是API的版本,对应的还有CMSIS_RTOS_V1;
portable
:这里主要是移植的时候需要修改的一些文件,针对不同的MCU以及不同的编译器,需要对这块进行修改;
portmacro.h
:定义编译器相关的数据类型和中断处理的宏定义;
port.c
:实现任务的堆栈初始化、systick
和任务上下文切换;
MemMang
:内存管理的文件,目前主要用heap_1.c
,heap_2.c
,heap_3.c
,heap_4.c
,heap_5.c
;
list.c
:双向链表的实现,主要供给内核调度器使用;
queue.c
: 队列的实现,主要支持中断环境和信号量控制;
croutine.c
:任务使用同一个堆栈,这样使得减小RAM的使用,但是在使用上会受到相当大的限制;
task.c
:每个任务都有各自的独立堆栈,支持完全的抢占式调度;
2 CubeMX 整合 RTOS
在配置工程的时候或者打开后缀名为.ioc
的cubemx
配置文件;
点击菜单栏Pinout&Configuration
;
点击Middleware
选择FREERTOS
;
具体如下图所示;
最终生成的文件结构如下图所示;
这时候已经将FreeRTOS集成到工程中了,*****,简直不能再方便了;
直接生成的工程中,系统已经创建好了一个任务StartDefaultTask
;
整体的main.c
代码如下;
#include "main.h"
#include "cmsis_os.h"
/* Definitions for defaultTask */
osThreadId_t defaultTaskHandle;
const osThreadAttr_t defaultTask_attributes = {
.name = "defaultTask",
.priority = (osPriority_t) osPriorityNormal,
.stack_size = 128 * 4
};
void SystemClock_Config(void);
static void MX_GPIO_Init(void);
void StartDefaultTask(void *argument);
int main(void)
{
HAL_Init();
SystemClock_Config();
MX_GPIO_Init();
osKernelInitialize();
defaultTaskHandle = osThreadNew(StartDefaultTask, NULL, &defaultTask_attributes);
/* Start scheduler */
osKernelStart();
/* We should never get here as control is now taken by the scheduler */
while (1)
{
}
}
void SystemClock_Config(void)
{
RCC_OscInitTypeDef RCC_OscInitStruct = {0};
RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};
/** Configure the main internal regulator output voltage
*/
__HAL_RCC_PWR_CLK_ENABLE();
__HAL_PWR_VOLTAGESCALING_CONFIG(PWR_REGULATOR_VOLTAGE_SCALE3);
/** Initializes the CPU, AHB and APB busses clocks
*/
RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSI;
RCC_OscInitStruct.HSIState = RCC_HSI_ON;
RCC_OscInitStruct.HSICalibrationValue = RCC_HSICALIBRATION_DEFAULT;
RCC_OscInitStruct.PLL.PLLState = RCC_PLL_NONE;
if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
{
Error_Handler();
}
/** Initializes the CPU, AHB and APB busses clocks
*/
RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
|RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;
RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_HSI;
RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV1;
RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;
if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_0) != HAL_OK)
{
Error_Handler();
}
}
static void MX_GPIO_Init(void)
{
/* GPIO Ports Clock Enable */
__HAL_RCC_GPIOA_CLK_ENABLE();
}
void StartDefaultTask(void *argument)
{
for(;;)
{
osDelay(1);
}
}
void Error_Handler(void)
{
}
#ifdef USE_FULL_ASSERT
/**
* @brief Reports the name of the source file and the source line number
* where the assert_param error has occurred.
* @param file: pointer to the source file name
* @param line: assert_param error line source number
* @retval None
*/
void assert_failed(uint8_t *file, uint32_t line)
{
}
#endif /* USE_FULL_ASSERT */
这里可以看到整体的流程为两部分HAL层和OS层;
HAL:
硬件初始化HAL_Init
;
时钟配置 SystemClock_Config
;
BSP初始化 MX_GPIO_Init
;
OS:
内核初始化osKernelInitialize
;
创建任务/线程 osThreadNew
;
任务调度 osKernelStart
整体流程图如下所示;
osThreadId_t defaultTaskHandle;
定义一个变量,后面创建任务会分配一个id
作为该任务的唯一标识符/身份证;
osThreadAttr_t defaultTask_attributes;
作为定义一个任务时的参数,包括任务的优先级,所需要分配的栈空间大小.以及任务的名字等等;具体结构体osThreadAttr_t
如下所示;
typedef struct {
const char *name; ///< name of the thread
uint32_t attr_bits; ///< attribute bits
void *cb_mem; ///< memory for control block
uint32_t cb_size; ///< size of provided memory for control block
void *stack_mem; ///< memory for stack
uint32_t stack_size; ///< size of stack
osPriority_t priority; ///< initial thread priority (default: osPriorityNormal)
TZ_ModuleId_t tz_module; ///< TrustZone module identifier
uint32_t reserved; ///< reserved (must be 0)
} osThreadAttr_t;
创建任务回调函数,这里有一个生命周期的问题,通常在任务中放一个死循环,以便于任务保持存活,可以被一直调度;
void StartDefaultTask(void *argument)
{
for(;;)
{
osDelay(1);
}
}
最后,调用osThreadNew
创建任务;该函数声明如下所示;
osThreadId_t osThreadNew ( osThreadFunc_t func,
void *argument,
const osThreadAttr_t *attr);
4 总结
通过cube自动生成了一个FREERTOS工程,简单分析了一下如何创建一个任务,后续需要在实践中深入到源码中学习FreeRTOS
;