FreeRTOS学习笔记-第一个项目工程

本工程采用STM32F4单片机,实现的功能有:PC与HMI双串口通讯、TF卡文件系统

1 硬件介绍

本项目硬件包含如下模块:

  1. 串口,Usart1连接PC,Usart2连接HMI(陶晶驰)
  2. 5个按键作为外部中断输入,5个指示灯做输出
  3. 一个TF卡(SDIO接口)实现FATFS
  4. 一个状态灯功能为:初始化过程中指示异常,正常运行中显示CPU的闲忙指示
  5. 1个采用模拟IIC的EEPROM

2 工程框架介绍

与祼机系统不同,在OS中需要特别注意线程安全,在多串口通讯以及多任务中的串口数据处理都可能产生异常,下面介绍本工程的程序框架:

  1. 为每个串口创建两个队列(也叫邮箱),分别存放接收和要发送的数据(接收和发送都采用中断方式)。
  2. 编写一个线程安全的串口输出函数,采用互斥量做锁,可用在任意任务中,除了中断服务程序(中断中可以采用入队的方法将数据放到发送队列)。
  3. 创建一个专门处理串口接收的数据的任务,阻塞在任务通知状态,当发生串口中断,中断程序会执行入队操作,并发送任务通知,解除任务的阻塞状态,执行消息出队、解析操作。
  4. 创建一个存放log的队列。
  5. 编写一个线程安全的添加log的函数,采用互斥量做锁,可用在任何任务中,除了中断服务程序。
  6. 创建一个专门保存log的任务,优先级较低,阻塞在读log队列的状态,当队列中有数据,执行出队、保存log(如串口打印、保存到TF卡)的操作。

3 CubeMX配置

3.1 时钟配置

电路板采用外部晶振,RCC设置如下:
在这里插入图片描述
时钟配置如下:
在这里插入图片描述

3.2 GPIO配置

在这里插入图片描述
IIC的两个引脚需要设置为Open Drain模式,5个按键设置为外部中断

3.3 系统配置

在RTOS中,单片机硬件的SysTick时钟被强制用作时间片的产生时钟,因此需要另外选择一个定时器产生HAL库的时钟,否则HAL_Delay、HAL_GetTick函数都将无法使用。选择一个空闲的定时器即可
在这里插入图片描述

3.4 SDIO配置

选择4位宽总线通讯模式,提高通讯效率,其它参数保持默认即可
在这里插入图片描述
为SDIO分配DMA:
在这里插入图片描述
若硬件电路没有放置上拉电阻,可以在这里开启内部上拉:
在这里插入图片描述

3.5 FATFS配置

基本保持默认,开启必要的功能即可
在这里插入图片描述
在这里插入图片描述
选择TF卡插入检测的引脚:
在这里插入图片描述

3.6 FreeRTOS配置

基本参数如下配置
在这里插入图片描述
在这里插入图片描述
说明几个关键参数:

  1. TICK_RATE_HZ,此参数设置按时间片调度的周期,即SysTick的定时周期,设置100Hz表示时间片长度为10ms,若设置过短,会导致调度器占用较多的CPU时间,降低CPU效率,设置过长可能降低任务实时性,需要根据项目情况设置,一般20~100Hz都能满足项目要求。
  2. Memory Management scheme,选择动态内存分配的方案,FreeRTOS提供了5套动态内存管理方案,heap_4是比较好的一种
  3. USE_TRACE_FACILITY、USE_STATS_FORMATING_FUNCTION,使能这两项可以启用获取任务状态(函数vTaskList)功能,项目开发阶段非常实用,可以获取任务的状态、栈剩余等。

我们尽量不要用CubeMX创建任务、信号量、事件等,因为不方便修改,但CubeMX会强制创建一个默认的任务,为了不浪费这个任务,我们在该任务中执行初始化,动态创建项目需要各种任务、信号量等,执行完成后删除该任务以节省内存。
在这里插入图片描述
任务优先级要设置为最高,因为在该任务中会执行创建任务的程序,任务一旦创建即会加入就绪列表,会打断初始化程序。
任务的入口函数类型选择weak,这样我们只要在项目的c文件中实现“StartTask”这个函数即可,而不必修改freertos.c文件。
任务创建类型选择动态,只有动态创建的任务才可以删除。
其它标签页都不需要设置

3.7 中断配置

在这里插入图片描述
从这页可以看到,产生时间片的System tick timer的优先级为15最低
建议取消下面这两项的勾选,在串口的中断服务函数中将不再调用HAL的中断处理函数
在这里插入图片描述
至此,CubeMX配置完成,生成代码。

4 编写代码

代码生成后,main.c文件完全不需要修改,它会启动调度器,此时只有一个默认任务会执行,该任务的入口函数在freertos.c中,如下,前面有__weak的修饰符,表示该函数为虚函数。
在这里插入图片描述
我们不需要修改freertos.c文件,另外新建一个work.c文件,编写默认的启动任务函数:

//默认任务,仅用作初始化,优先级别最高,其中可以使用printf函数
void StartTask(void *argument)
{
   
   
	printf("System started\r\n");
	printf("Start initialization\r\n");
	
	MyMSH_ExecInit();		//执行自动初始化,执行所有已注册的初始化函数();
	
	printf("Initialization complete!");
	vTaskDelete(NULL);		//初始化完成,删除本任务
}

值得注意的的,该任务的优先级最高,相当于挂起了任务调度器,因此在该任务中还可以使用printf函数。
初始化完成后执行vTaskDelete函数,参数为NULL表示删除自身,任务被删除后,其它就绪态的任务才会开始执行。
项目工程共需建立如下几个文件:
1.work.c --项目的功能基本都在这时实现
2.work.h
3.init.c --项目的初始化程序文件,MyMSH_ExecInit()要执行的函数都在这里
4.MyPrintf.c --串口打印和添加Log的函数
5.MyPrintf.h
6.file.c --文件操作的程序文件
需要添加的标准函数库有:
1.MyMSH.lib --实现自动初始化函数MyMSH_ExecInit()和执行命令的函数MyMSH_ExecCmd
2.MyIIC.lib --模拟IIC总线的函数库
下面开始按模块编写程序

4.1 任务初始化

前面讲了,要在默认任务中动态创建所有任务、信号量、事件等,这些创建全都在init.c文件中实现,举例如下:

4.1.1 创建互斥量

//信号量、互斥量初始化
void Semaphore_Init(void)
{
   
   
	Mutex_Printf = xSemaphoreCreateMutex();
	if (Mutex_Printf == NULL)
	{
   
   
		Error_Handle(0, "Creat Mutex_Printf failed!\r\n");
	}
	printf ("Mutex, Free heap:%d\r\n", xPortGetFreeHeapSize());
}
MSH_INIT_EXPORT(1, Semaphore_Init, Creat all Semaphore);

注1:Error_Handle()函数将会执行初始化异常时的错误处理。
注2:xPortGetFreeHeapSize()函数用于获取空闲堆大小,适用于程序调试阶段。
注3:通过宏MSH_INIT_EXPORT注册过的函数,都将自动通过MyMSH_ExecInit()函数调用。

4.1.2 创建消息队列(邮箱)

//队列初始化,创建要使用的所有队列
void Queue_Init(void)
{
   
   
	Queue_PC_RX = xQueueCreate(200, 1);											//创建存放从PC接收到的数据的消息队列
	if (Queue_PC_RX == NULL)
	{
   
   
		Error_Handle(0, "Creat PC receive queue failed!\r\n");
	}
	printf ("Queue, Free heap:%d\r\n", xPortGetFreeHeapSize());
	__HAL_UART_ENABLE_IT(&UART_PC, UART_IT_RXNE);								//创建成功后,开启接收中断
	
	Queue_PC_TX = xQueueCreate(200, 1);											//创建存放要发送到PC的数据的消息队列
	if (Queue_PC_TX == NULL)
	{
   
   
		Error_Handle(0, "Creat PC send queue failed!\r\n");
	}
	printf ("Queue, Free heap:%d\r\n", xPortGetFreeHeapSize());
	
	Queue_HMI_RX = xQueueCreate(200, 1);										//创建存放从HMI接收到的数据的消息队列
	if (Queue_HMI_RX == NULL)
	{
   
   
		Error_Handle(0, "Creat HMI receive queue failed!\r\n");
	}
	printf ("Queue, Free heap:%d\r\n", xPortGetFreeHeapSize());
	__HAL_UART_ENABLE_IT(&UART_HMI, UART_IT_RXNE);								//创建成功后,开启接收中断
	
	Queue_HMI_TX = xQueueCreate(200, 1);										//创建存放要发送到HMI的数据的消息队列
	if (Queue_HMI_TX == NULL)
	{
   
   
		Error_Handle(0, "Creat HMI send queue failed!\r\n");
	}
	printf ("Queue, Free heap:%d\r\n", xPortGetFreeHeapSize());
	
	Queue_Log = xQueueCreate(200, 4);											//创建存放log的消息队列
	if (Queue_Log == NULL)
	{
   
   
		Error_Handle(0, "Creat log queue failed!");
	}
	printf ("Queue, Free heap:%d\r\n", xPortGetFreeHeapSize());
}
MSH_INIT_EXPORT(1, Queue_Init, Fifo for PC and HMI init);

创建4个存放串口接收和发送数据的消息队列,本程序中在创建完队列后才开启串口接收中断,__HAL_UART_ENABLE_IT(&UART_HMI, UART_IT_RXNE),也可以放在其它地方开启。

4.1.3 创建事件组

//事件初始化
void Even_Init(void)
{
   
   
	Events = xEventGroupCreate();
 
	if (Events == NULL
<think>好的,我需要帮助用户找到关于HAL库和FreeRTOS学习资料或笔记。首先,用户提供的引用内容提到了FreeRTOS的移植步骤、源码结构以及中断处理机制,这些可能都是用户关心的重点。接下来,我应该整理这些引用中的信息,并结合常见的HAL库和FreeRTOS学习资源给出建议。 首先,用户提到的引用[2]和[3]都涉及FreeRTOS在STM32上的移植,特别是HAL库的集成。这说明用户可能在STM32平台上开发,需要具体的移植步骤和验证方法。引用[4]详细介绍了ARM Cortex-M的中断机制,这对于理解FreeRTOS的任务调度和中断处理非常重要。 接下来,我应该推荐用户查阅官方文档,比如AWS FreeRTOS的官方指南,这通常是最权威的资料。然后,考虑到用户可能喜欢实践教程,可以建议参考社区博客或GitHub上的项目,特别是那些结合HAL库和FreeRTOS的示例代码。此外,书籍推荐也是必要的,比如专门讲解FreeRTOS和STM32开发的书籍。 用户还可能对实际应用案例感兴趣,所以需要提及实际项目中的应用场景,如物联网设备,并结合引用[1]中提到的AWS FreeRTOS的应用。最后,确保回答结构清晰,分点列出学习路径、推荐资料、移植指南和深入理解部分,方便用户逐步学习。 需要注意的是,所有提到的参考资料要正确标注引用标识,并检查是否有遗漏的重要资源。同时,生成的相关问题要围绕用户可能的后续疑问,比如移植步骤、中断配置、学习资源推荐等,帮助用户进一步探索。</think>### HAL库与FreeRTOS学习资源指南 #### 一、学习路径建议 1. **HAL库基础** - 学习STM32CubeMX工具配置外设,生成HAL库初始化代码,理解HAL的模块化设计(如GPIO、UART、ADC等驱动接口)。 - 关键文档:ST官方《STM32CubeMX用户手册》和《HAL库API参考手册》[^2]。 2. **FreeRTOS核心概念** - 掌握任务管理(`xTaskCreate`)、队列(`xQueueCreate`)、信号量(`xSemaphoreCreateBinary`)和中断服务例程(ISR)的集成方法。 - 重点理解优先级反转问题及互斥量(Mutex)的解决方案[^1]。 3. **HAL与FreeRTOS结合** - 在HAL库的中断回调函数中调用FreeRTOS的API(如`xQueueSendFromISR`),需注意中断优先级与FreeRTOS内核配置的兼容性[^4]。 --- #### 二、推荐学习资料 1. **官方资源** - **AWS FreeRTOS开发者指南**:涵盖任务调度、内存管理及安全通信(如MQTT)。 - **STM32CubeIDE集成开发环境**:内置FreeRTOS模板项目,支持一键生成带RTOS的工程框架。 2. **实战教程** - **移植FreeRTOS到HAL库**:引用[3]中提到的源码移植步骤,需重点关注`FreeRTOSConfig.h`配置文件中的堆大小(`configTOTAL_HEAP_SIZE`)和时钟源(`configSYSTICK_CLOCK_HZ`)设置。 - 示例代码片段: ```c // FreeRTOS任务与HAL中断协同 void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart) { BaseType_t xHigherPriorityTaskWoken = pdFALSE; xQueueSendFromISR(xUartQueue, &received_data, &xHigherPriorityTaskWoken); portYIELD_FROM_ISR(xHigherPriorityTaskWoken); } ``` 3. **书籍与社区** - 《Mastering STM32》第二版:第12章详解FreeRTOS与HAL库的集成案例。 - STM32中文社区论坛:搜索“HAL+FreeRTOS”关键词,可找到实际项目中的死锁调试记录。 --- #### 三、关键配置注意事项 | 配置项 | 说明 | |-------------------------|----------------------------------------------------------------------| | `configUSE_PREEMPTION` | 设置为1启用抢占式调度,确保高优先级任务及时响应 | | `configMAX_SYSCALL_INTERRUPT_PRIORITY` | 需与NVIC中断优先级分组匹配,避免内核API在中断中被阻塞[^4] | | `configTICK_RATE_HZ` | 通常设置为1000Hz,对应1ms时间片,影响任务延迟精度[^3] | --- #### 四、进阶学习方向 1. **动态内存分配优化** - 替换默认的`heap_4.c`方案,使用内存池(Memory Pool)减少碎片,适用于长期运行的嵌入式系统。 2. **低功耗模式集成** -FreeRTOS空闲任务中调用HAL库的`HAL_SuspendTick()`函数,实现STM32的STOP模式节能。 3. **调试工具链** - 使用SEGGER SystemView可视化分析任务调度时序,定位优先级冲突问题。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值