freeRTOS
- FreeRTOS 是一个开源的实时操作系统(RTOS),专为嵌入式系统特别是微控制器设计。
- 它是轻量级的,提供了一个多任务环境,允许多个任务在单个处理器上并发运行。
- 主要特性:抢占式多任务、任务调度、可移植性,任务间通信,内存管理
- 虽然 FreeRTOS 使用了“任务”这一术语,但它们在功能和概念上类似于传统操作系统中的“线程”。这两个术语都表示了并行执行的基本单位,并且都涉及到优先级、堆栈管理、调度等关键概念。
- 在 FreeRTOS 中,任务的管理和调度是通过 FreeRTOS 提供的 API 实现的,而在传统操作系统中,线程的管理则是由操作系统的线程库或内核提供的。
常用函数解析
TaskHandle_t 创建任务句柄
TaskHandle_t 实际上是一个指向任务控制块(TCB)的指针,TCB 是 FreeRTOS 内部用于存储任务状态的数据结构。用法
保存任务句柄: 在调用 xTaskCreate 创建任务时,可以将任务句柄传递到一个 TaskHandle_t 变量中。这个句柄可以用来引用任务,以便后续对任务进行操作。
TaskHandle_t taskFlushled;
void task_fun_flushled(void *args) {while (1) {HAL_GPIO_TogglePin(GPIOC, GPIO_PIN_6|GPIO_PIN_7|GPIO_PIN_8);osDelay(1000); }
}void setup() {BaseType_t ret = xTaskCreate(task_fun_flushled, "taskFlushled", 128, NULL, osPriorityNormal, &taskFlushled );if (ret == pdFALSE) {printf("任务创建失败\n");}
}
xTaskCreate() 任务创建函数
BaseType_t xTaskCreate(TaskFunction_t pvTaskCode,const char * const pcName,const configSTACK_DEPTH_TYPE usStackDepth,void *pvParameters,UBaseType_t uxPriority,TaskHandle_t *pxCreatedTask);
参数TaskFunction_t pvTaskCode: 指向任务执行的函数,这个函数需要符合 void func(void *) 的形式。const char * const pcName: 任务的名字,用于调试。const configSTACK_DEPTH_TYPE usStackDepth: 任务的堆栈大小,以字为单位。void *pvParameters: 传递给任务的参数。UBaseType_t uxPriority: 任务的优先级,数值越大,优先级越高。TaskHandle_t *pxCreatedTask: 用于传出任务句柄的指针,可以用于后续的任务引用或删除等操作。即为任务本身返回值pdFALSE:表示任务创建失败
-------------------优先级--------------------------
typedef enum {osPriorityIdle = -3, osPriorityLow = -2, osPriorityBelowNormal = -1, osPriorityNormal = 0, osPriorityAboveNormal = +1, osPriorityHigh = +2, osPriorityRealtime = +3, osPriorityError = 0x84
} osPriority;
-------------------------------------------------------------具体用法xTaskCreate(task_fun_printf,"taskPrintf",128,NULL,osPriorityNormal,&taskPrintHandle);
-----以下是具体执行函数----------
任务不是只执行一次所以有一个函数体
并且不能光是你一个人执行,故有osDelay函数void task_fun_printf(void *args)
{while(1){printf("%s-%d\r\n",__func__,__LINE__);osDelay(1000);}
}
----------以下是任务句柄,当然它应该放在最前面----------------
TaskHandle_t taskPrintHandle;
portSET_INTERRUPT_MASK_FROM_ISR() 禁用中断
这个宏在中断服务例程(ISR)中使用。
确保操作的原子性和数据的一致性。
防止中断处理过程中被其他中断打断。portSET_INTERRUPT_MASK_FROM_ISR(): 设置中断掩码,禁用中断。
portCLEAR_INTERRUPT_MASK_FROM_ISR(): 恢复中断掩码,重新启用中断。用法int __io_putchar(int ch)
{UBaseType_t vxx;vxx = portSET_INTERRUPT_MASK_FROM_ISR();HAL_UART_Transmit(&huart1, (uint8_t *)&ch, 1, 1);portCLEAR_INTERRUPT_MASK_FROM_ISR(vxx);return ch;
}
defaultTask 默认任务
defaultTask 在系统中扮演了多个角色,包括初始化系统、**保持**系统运行(占位)、优化任务调度、处理异常和调试开发。该任务由STM32CubeIDE自动创建,代码如下
osThreadDef(defaultTask, StartDefaultTask, osPriorityNormal, 0, 128);
osThreadId defaultTaskHandle = osThreadCreate(osThread(defaultTask), NULL);
void StartDefaultTask(void const * argument)
{for(;;){osDelay(1); }
}
osKernelStart() 启动内核调度器
osKernelStart() 是 FreeRTOS 中的一个函数,用于启动 RTOS 内核的调度器。
在调用 osKernelStart() 后,系统的控制权将转交给 RTOS 内核。通常,main() 函数中的 while (1) 循环会继续存在,但它不会执行任何操作,因为控制权已交给内核调度。
vTaskResume 恢复(激活)被挂起的任务
函数原型
void vTaskResume(TaskHandle_t xTaskToResume);它的作用是将任务从挂起状态重新设置为就绪状态,使得该任务可以被调度和执行。升级版
xTaskResumeFromISR()
允许在 ISR 中将任务的状态从挂起变为就绪,从而使任务能够在 ISR 处理完后尽快被调度执行。
vTaskSuspend 挂起任务
函数原型:void vTaskSuspend(TaskHandle_t xTaskToSuspend);用于将一个正在运行或就绪的任务挂起。挂起的任务将不会被调度或执行,直到它被恢复。升级版
vTaskSuspendFromISR
vTaskDelete 删除任务
函数原型:
void vTaskDelete(TaskHandle_t xTaskToDelete);
用于删除一个任务。调用这个函数会释放与任务相关的所有资源,并将任务从系统中移除。xTaskToDelete: 要删除的任务的句柄。如果句柄为 NULL,则当前正在运行的任务将被删除。如果要删除的任务不是当前任务,传入任务句柄即可。
注意,删除任务的时候要注意同步问题;一般是 自我删除;void task_test_fun(void *args){while(1){干活; if(干完了){break;}}vTaskDelete(NULL); }
疑问为什么删除osDelay后,其它函数也会执行
- 各个任务在 FreeRTOS 中是独立的,除非有显式的同步机制(如信号量、互斥量等),一个任务的执行状态不会直接影响其他任务。
osDelay和HAL_Delay的区别
osDelay
- osDelay 是 FreeRTOS 提供的延迟函数
- osDelay 会将当前任务挂起一段指定的时间,在这段时间内,调度器可以将 CPU 分配给其他任务执行。等延迟时间结束后,当前任务会被重新放入就绪队列,等待调度器再次调度。
HAL_Delay
- HAL_Delay 是 STM32 HAL 库中的一个延迟函数。它的工作原理是利用 SysTick 定时器,通过轮询计数器来产生延迟。在调用 HAL_Delay() 时,程序会进入一个忙等待循环(也叫阻塞等待),直到指定的时间过去。这意味着在这段延迟时间内,微控制器不会执行其他任务,完全被阻塞住了。
在freeRTOS中禁止使用HAL_Delay
- 在多任务环境中使用 HAL_Delay 会阻塞整个系统,导致其他任务无法执行,系统无法实现真正的并发。
代码示例
1.RCC设置Crystal
2.USART1设置为异步通信
3.FREERTOS 的interface选择CMSIS_C1(简单但功能有限)
4.配置测试管脚,我用的是PC6,7,8作为LED灯管脚具体代码如下 功能:交替打印连续数
#include "main.h"
#include "cmsis_os.h" UART_HandleTypeDef huart1;
osThreadId defaultTaskHandle; void SystemClock_Config(void);
static void MX_GPIO_Init(void);
static void MX_USART1_UART_Init(void);
void StartDefaultTask(void const * argument); int __io_putchar(int ch)
{UBaseType_t vxx; vxx = portSET_INTERRUPT_MASK_FROM_ISR(); HAL_UART_Transmit(&huart1, (uint8_t *)&ch, 1, 1); portCLEAR_INTERRUPT_MASK_FROM_ISR(vxx); return ch;
}int cnt = 0;
TaskHandle_t taskOuHandle;
TaskHandle_t taskJiHandle; void task_fun_ou(void *args)
{int Acnt = 0; while (1) {printf(" %d \r\n", Acnt); Acnt += 2; vTaskResume(taskJiHandle); vTaskSuspend(taskOuHandle); }
}void task_fun_ji(void *args)
{vTaskSuspend(taskJiHandle); int Bcnt = 1; while (1) {printf(" %d \r\n", Bcnt); Bcnt += 2; vTaskResume(taskOuHandle); vTaskSuspend(taskJiHandle); osDelay(1000); }
}int main(void)
{HAL_Init(); SystemClock_Config(); MX_GPIO_Init(); MX_USART1_UART_Init(); osThreadDef(defaultTask, StartDefaultTask, osPriorityNormal, 0, 128); defaultTaskHandle = osThreadCreate(osThread(defaultTask), NULL); BaseType_t ret; ret = xTaskCreate(task_fun_ou, "taskPrintfOU", 128, NULL, osPriorityNormal, &taskOuHandle); if (ret == pdFALSE) {printf("xTaskCreate for print err\r\n"); return -34; }ret = xTaskCreate(task_fun_ji, "taskPrintfODD", 128, NULL, osPriorityNormal, &taskJiHandle); if (ret == pdFALSE) {printf("xTaskCreate for flushled err\r\n"); return -34; }osKernelStart(); while (1) {}
}