freeRTOS打开方法
middleware and software, interface:CMSIS V2. 打开以后修改HAL时基,避免HAL与freeRTOS抢sysTick,随便选一个不用的就可以。
可以看到TIM10已经变成不可用状态,配置都是灰色的
TASK建立
打开Tasks and Queues,点击add
| 参数 | 含义 | 你该怎么填 |
|---|---|---|
| Task Name | 任务名字 | 自己起名,比如motor_xm_ol、oledTask、adcTask。建议用英文、数字、下划线 |
| Priority | 任务优先级 | 决定谁先运行。优先级高的任务会抢占优先级低的任务 |
| Stack Size (Words) | 任务栈大小,单位是word | STM32 是 32 位 MCU,1 word = 4 字节,所以 128 words = 512 字节 |
| Entry Function | 任务入口函数名 | 这个任务真正执行的函数,比如StartTask02 |
| Code Generation Option | 代码生成方式 | 一般保持Default就行 |
| Parameter | 传给任务函数的参数 | 不需要传参就填NULL |
| Allocation | 任务内存分配方式 | Dynamic动态分配,简单方便;Static静态分配,更可控 |
| Buffer Name | 静态分配时用的栈缓冲区名 | 只有Allocation = Static时才需要 |
| Control Block Name | 静态分配时用的任务控制块名 | 只有Allocation = Static时才需要 |
在Core/Src/目录下的freertos.c文件里面,可以看到我在cubeMX里面添加的几个任务
条件触发TASK的编写
在当前项目中,我计划是一些按键对应一个task,另一些按键对应另一个task。也就是需要flag来标记,单个任务中的不同程序。其中一个task,是通过按键触发电机的手动移动,思路是按键按下对应移动方向的按键以后,在中断回调函数里面修改标志位,任务监测到标志位变化后执行task内相对应的代码。目前的移动方向有四个,在freertos.c这里添加代码
/* USER CODE END Header_move_manualT */ void move_manualT(void *argument) { /* USER CODE BEGIN move_manualT */ uint32_t flags; /* Infinite loop */ for(;;) { flags = osThreadFlagsWait( FLAG_XM | FLAG_XP | FLAG_YM | FLAG_YP, osFlagsWaitAny, osWaitForever ); if (flags & FLAG_XM) { motor_move_XM_OL(1000); } if (flags & FLAG_XP) { motor_move_XP_OL(1000); } if (flags & FLAG_YM) { motor_move_YM_OL(1000); } if (flags & FLAG_YP) { motor_move_YP_OL(1000); } } /* USER CODE END move_manualT */ }然后在main.c里面写按键的中断回调函数
/* USER CODE BEGIN 4 */ void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin) { static uint32_t last_tick = 0; uint32_t now = HAL_GetTick(); if (now - last_tick < 20) return; last_tick = now; if (GPIO_Pin == GPIO_PIN_15) { //X- osThreadFlagsSet(move_manualHandle, FLAG_XM); } else if (GPIO_Pin == GPIO_PIN_10) { //X+ osThreadFlagsSet(move_manualHandle, FLAG_XP); } else if (GPIO_Pin == GPIO_PIN_11) { //Y- osThreadFlagsSet(move_manualHandle, FLAG_YM); } else if (GPIO_Pin == GPIO_PIN_12) { //Y+ osThreadFlagsSet(move_manualHandle, FLAG_YP); } else if (GPIO_Pin == GPIO_PIN_13) { //Z+ } else if (GPIO_Pin == GPIO_PIN_13) { //Z- } };另外标志位是两个c文件都要读取的,我们另开一个flags.h文件,并在main.c和freertos.c里面都include它
#ifndef _FLAGS_H_ #define _FLAGS_H_ #define FLAG_XM (1U << 0) #define FLAG_XP (1U << 1) #define FLAG_YM (1U << 2) #define FLAG_YP (1U << 3) #endifmain.c里面的osThreadFlagsSet函数中的的第一个参数 是freertos.c里面task的句柄,同样需要另外建立一个user_freertos.h文件,来重新声明一下这些句柄,加上extern。
#ifndef __APP_FREERTOS_H #define __APP_FREERTOS_H #include "cmsis_os.h" extern osThreadId_t oled_dispHandle; extern osThreadId_t move_manualHandle; extern osThreadId_t move_autoHandle; extern osThreadId_t ESPtransferHandle; extern osThreadId_t USBtransferHandle; #endif互斥任务的设置
在当前的应用场景中,手动控制电机移动的任务和自动控制电机按程序移动的任务是分开的。另外USB和ESP8266读写SPI flash的程序也是分开的。因此需要对这几个task做互斥。首先在cubeMX里面的freertos-mutexes 点击add,参数先选dynamic。
生成代码以后进入freertos.c文件可以看到生成了一个新的句柄,
/* Definitions for motor_mutex */ osMutexId_t motor_mutexHandle; const osMutexAttr_t motor_mutex_attributes = { .name = "motor_mutex" };函数结构如下
void StartTask1(void *argument) { for (;;) { osThreadFlagsWait(FLAG_TASK1, osFlagsWaitAny, osWaitForever); osMutexAcquire(allMotorMutexHandle, osWaitForever); Task1_Function(); osMutexRelease(allMotorMutexHandle); } }可以看到增加了两个函数,这俩函数就是互斥锁,第一个函数是用来上锁,第二个函数用来解锁。