【工具使用】STM32CubeMX-FreeRTOS操纵体系-信号标志、互斥锁、信号量篇

[复制链接]
发表于 2025-6-12 08:28:46 | 显示全部楼层 |阅读模式
一、概述

    无论是新手照旧大佬,基于STM32单片机的开辟,使用STM32CubeMX都是可以极大提升开辟效率的,并且其界面化的开辟,也大大低落了新手对STM32单片机的开辟门槛。
    本文重要报告STM32芯片FreeRTOS信号标志、互斥锁和信号量的设置及其相关知识。
二、软件阐明

    STM32CubeMX是ST官方出的一款针对ST的MCU/MPU跨平台的图形化工具,支持在Linux、MacOS、Window体系下开辟,其对接的底层接口是HAL库,另外习惯于寄存器开辟的同学们,也可以使用LL库。STM32CubeMX除了集成MCU/MPU的硬件抽象层,另外还集成了像RTOS,文件体系,USB,网络,表现,嵌入式AI等中心件,这样开辟者就能够很轻松的完成MCU/MPU的底层驱动的设置,留出更多精力开辟上层功能逻辑,能够更进一步进步了嵌入式开辟效率。
    演示版本 6.1.0
三、FreeRTOS功能简介

    嵌入式初学者一样平常使用的是裸机开辟,而大多数所谓的进阶课程,就是使用操纵体系开辟。实在两者并不存在很大的差距,使用操纵体系,更多是在裸机开辟的基础上,限制在操纵体系要求的框架下举行开辟,同时必要注意操纵体系的一些特性,以防止出现问题。具体操纵体系开辟与裸机开辟的区别如下:
维度FreeRTOS(RTOS 开辟)裸机开辟(轮询 / 停止驱动)任务管理多任务并行(抢占式 / 协作式),自动调理单线程轮询 + 停止处理,手动和谐任务优先级及时性高(可精确控制任务执行顺序和响应时间)依赖停止优先级和轮询顺序,复杂场景易卡顿体系复杂度适合复杂逻辑(如多外设、通信协议、用户界面)适合简单逻辑(如单一传感器采集、LED 控制)代码结构模块化(任务独立,通过 IPC 通信)线性代码 + 全局变量,耦合度高资源占用需额外内存(栈空间、内核数据结构)资源占用极小(仅代码和须要数据)开辟成本学习成本较高(需明白 RTOS 概念)门槛低,适合快速实现简单功能可维护性任务隔离性好,扩展新功能更方便功能扩展可能需修改全局逻辑,维护困难     提到操纵体系,第一反应也是各人最早接触的,应该就是Windows体系了(当然新生代可能第一接触的是苹果的IOS体系或华为的鸿蒙体系),但由于Windows的交互友爱性,让各人很难感知到它的存在;而学习了嵌入式后,又知道了Linux这个天花板般的操纵体系存在,固然是个开源体系,但其体量也让大多数人忘而生畏,从而使操纵体系蒙上一层秘密的面纱。而今天的主角FreeRTOS,作为小体量且开源的操纵体系,正是敲开操纵体系秘密大门的砖头,带着我们了解其中的奥妙。
    FreeRTOS 是一款开源及时操纵体系(RTOS),专为嵌入式体系计划,尤实在用于资源受限的微控制器(MCU)。由 Richard Barry 开辟并发布首个版本(V1.0),最初名为 FreeRTOS Kernel,旨在提供轻量级、可移植的多任务处理本领。早期版本以代码简洁、易于移植为特点,迅速在嵌入式社区盛行。2012 年,成立 Real Time Engineers Ltd 公司,推动 FreeRTOS 贸易化,推出付费技术支持和扩展组件(如文件体系、TCP/IP 栈)。渐渐支持更多硬件平台(如 ARM Cortex-M、ESP32、RISC-V 等),并构建生态体系,包括中心件和工具链。2016 年,亚马逊(AWS)收购 FreeRTOS,将其纳入 IoT 战略,推出 AWS IoT Greengrass for FreeRTOS,强化物联网物联网(IoT)毗连本领(如 MQTT、OTA 升级)。最新版本(停止 2025 年)为 V202212.00,连续优化及时性能安全性和云集成,并提供免费的认证服务(如功能安全认证 IEC 61508)。
    FreeRTOS 以及时性、轻量级、可设置性为焦点,功能模块包括:

  • 任务调理器


  • 抢占式调理:支持多任务按优先级运行,高优先级任务可停止低优先级任务,确保及时响应。
  • 协作式调理(可选):任务自动释放控制权,适合对及时性要求不高的场景。
  • 支持 任务优先级(最多 32 级,可设置)和 时间片轮转(同优先级任务分时执行)。

  • 任务间通信(IPC)


  • 队列(Queue):任务 / 停止间传递数据,支持先辈先出(FIFO)或优先级队列。
  • 信号量(Semaphore):包括二进制信号量、计数信号量,用于资源同步与互斥(如 mutex 互斥信号量)。
  • 事件组(Event Group):实现任务间多事件同步。

  • 内存管理


  • 提供多种内存分配战略:
    – 静态分配:编译时分配固定内存,适合关键任务,避免内存碎片。
    – 动态分配:运行时动态申请内存(类似 C 语言 malloc),需注意碎片问题。
  • 支持用户自界说内存管理方案。

  • 定时器与停止管理


  • 软件定时器:基于体系时钟的周期性或一次性定时器,支持回调函数。
  • 停止安全接口:允许在停止服务步调(ISR)中安全访问 RTOS 资源(如队列、信号量)。

  • 可设置性与移植性


  • 通过头文件(FreeRTOSConfig.h)设置内核参数(如任务数目、栈大小、调理器行为),灵活适配不同硬件。
  • 提供标准接口,移植到新平台只需实现少量汇编代码(如上下文切换)。

  • 生态与扩展组件


  • 中心件:集成文件体系(如 FATFS)、TCP/IP 栈(LwIP)、USB 协议栈、图形界面(GUI)等。
  • 物联网物联网支持:通过 AWS IoT 组件实现装备与云端通信,支持 MQTT、TLS 加密、装备管理等。
  • 安全与认证:提供功能安全版本(如 FreeRTOS-Safety),通过 IEC 61508、ISO 26262 等认证,适合工业、汽车电子等场景。
    除了FreeRTOS,其他还有很多其他优秀的嵌入式操纵体系,其中就包括很多人学校里会学到的uC/OS-II,FreeRTOS与其他常见的嵌入式操纵体系对好比下:
维度FreeRTOSuC/OS-IIRTX(ARM)RIOT OS许可证开源(GPLv2,修改需开源)/ 贸易许可开源(需购买贸易许可用于产品)贸易许可(需授权)开源(BSD-2-Clause)及时性抢占式调理,微秒级响应抢占式调理,支持优先级继续抢占式调理,支持 CMSIS-RTOS 标准抢占式 + 协作式,适合 IoT 低功耗代码复杂度简洁,焦点代码约 10k 行 C 语言模块化计划,代码量较大集成于 Keil 工具链,抽象层美满面向 IoT,轻量级(<10KB 内存)生态体系丰富(AWS IoT、中心件、社区支持)成熟(工业、汽车范畴案例多)与 ARM 工具链深度整合(Keil、IAR)专注 IoT,支持传感器网络和低功耗协议资源占用极小(ROM: ~4KB,RAM: ~1KB)中等(ROM: ~10KB,RAM: ~2KB)中等(依赖组件数目)极轻量(适合 8/16 位 MCU)典型应用IoT 装备、消费电子、工业控制医疗装备、航空航天、汽车电子嵌入式体系开辟(ARM Cortex-M 系列)物联网物联网边缘装备、传感器节点     FreeRTOS 依附轻量、开源、易移植的特性,成为嵌入式范畴最盛行的 RTOS 之一,尤其适合 IoT 和中小型及时体系。其与 AWS 的深度整合进一步强化了物联网本领,而功能安全认证版本则拓展了工业和汽车电子市场。相比裸机开辟,它能显着提升复杂体系的计划效率和及时性,但需权衡资源占用和学习成本。对于开辟者而言,若项目必要多任务调理、及时响应或将来扩展,FreeRTOS 是理想选择;若需求简单或资源受限,裸机开辟仍具优势。
四、FreeRTOS设置及应用

4.1 FreeRTOS设置阐明


    具体参考《【工具使用】STM32CubeMX-FreeRTOS操纵体系-任务、延时、定时器篇》,这里就不再赘述了。
4.2 接口阐明

    使用CubeMX生成的工程,会将FreeRTOS的接口再封装一层同一接口CMSIS-RTOS,这是 ARM 界说的一套 RTOS 抽象层标准,旨在通过同一接口屏蔽不同 RTOS 的差别。这里我们先来认识几个比较常用的接口。
4.2.1 信号标志

1. osSignalSet - 设置线程的信号标志
  1. int32_t osSignalSet(osThreadId thread_id, int32_t signals);
复制代码
    向指定线程(thread_id)的信号标志寄存器中 按位设置 一个或多个信号标志(signals)。信号标志是线程的私有资源,每个线程有一组独立的标志位(通常是 32 位整数),用于表现异步事件的发生。
参数:
thread_id:目的线程的 ID(通过 osThreadCreate 或 osThreadGetId 获取)。
signals:要设置的信号标志(如 0x01、0x02 等,可通过按位或组合多个标志)。
返回值:
osOK:设置成功。
osErrorOS:操纵失败(如线程 ID 无效)。
2. osSignalClear - 扫除线程的信号标志
  1. int32_t osSignalClear(osThreadId thread_id, int32_t signals);
复制代码
    从指定线程的信号标志寄存器中 按位扫除 一个或多个信号标志(signals)。返回值为扫除前的标志位状态,可用于判断哪些标志被扫除前已设置。
参数:
thread_id:目的线程的 ID。
signals:要扫除的信号标志(与 osSignalSet 中的标志位对应)。
返回值:
扫除前的信号标志值(若成功)。
0x80000000:参数无效(如线程 ID 不存在)。
注:通常在目的线程处理完信号后调用,避免重复处理同一事件。扫除操纵是线程安全的,由RTOS内核保证原子性。
3. osSignalWait - 等待线程的信号标志
  1. osEvent osSignalWait(int32_t signals, uint32_t millisec);
复制代码
    使当前线程进入 阻塞状态,等待自身的信号标志满意指定条件。可指定等待所有标志位均被设置(signals 非零,按位与匹配)或恣意一个标志位被设置(signals 为 0)。
参数:
signals:
非零值:等待所有指定位均被设置(如 signals=0x03 表现等待标志 0 和 1 同时置位)。
0:等待恣意一个标志位被设置。
millisec:超时时间(单位:毫秒),0 表现永不超时。
返回值:
osEvent 结构体:
-status:状态码(如 osEventSignal 表现信号触发,osEventTimeout 表现超时)。
-value.signals:实际触发的信号标志值(仅当 .status 为 osEventSignal 时有效)。
使用流程

  • 当前线程调用 osSignalWait 阻塞等待信号。
  • 其他线程通过 osSignalSet 触发信号。
  • 若满意条件或超时,当前线程恢复运行,通过返回值判断事件类型并处理。
4.2.2 互斥锁

1. 创建互斥锁(osMutexCreate)
  1. osMutexCreate(const osMutexDef_t *mutex_def)
复制代码
    创建并初始化一个互斥锁。
参数:
mutex_def:互斥锁界说(通过 osMutex(name) 获取地址)。
返回值:
osMutexId:互斥锁 ID(非 NULL 表现成功);
NULL:创建失败(如内存不足)。
2. 等待获取互斥锁(osMutexWait)
  1. osMutexWait(osMutexId mutex_id, uint32_t millisec)
复制代码
    等待获取互斥锁(阻塞当前任务直到获取成功或超时)。
参数:
mutex_id:互斥锁 ID(由 osMutexCreate 返回);
millisec:超时时间(单位:毫秒):
0:不等待,立刻返回;
osWaitForever:无限等待(需包罗头文件 cmsis_os.h)。
返回值:
osOK:成功获取锁;
osErrorTimeout:超时未获取;
osErrorParameter:参数无效(如 mutex_id 为 NULL)。
注:优先级继续:若 RTOS 支持(如 FreeRTOS),低优先级任务持有锁时,高优先级任务等待会临时提升前者优先级,避免优先级反转。递归锁:同一任务可多次调用 osMutexWait,但需对应次数的 osMutexRelease 释放。
3. 释放互斥锁(osMutexRelease)
  1. osMutexRelease(osMutexId mutex_id)
复制代码
    释放已持有的互斥锁。
参数:
mutex_id:互斥锁 ID。
返回值:
osOK:释放成功;
osErrorResource:调用者未持有该锁(非法释放);
osErrorParameter:参数无效。
注:必须由持有锁的任务调用,停止服务步调(ISR)不可调用(需用信号量更换)。释放后,等待该锁的任务中优先级最高的任务将被唤醒。
4. 删除互斥锁(osMutexDelete)
  1. osMutexDelete(osMutexId mutex_id)
复制代码
    删除一个不再使用的互斥锁,释放相关资源。
参数:
mutex_id:互斥锁 ID。
返回值:
osOK:删除成功;
osErrorResource:锁仍被持有(无法删除);
osErrorParameter:参数无效。
注:删除前需确保 没有任务持有该锁 且 无任务在等待该锁,否则会导致不可猜测行为。动态分配的互斥锁需删除以释放内存;静态分配的锁可省略删除(由用户手动管理内存)。
特性互斥锁(Mutex)信号量(Semaphore)用途保护独占资源(一对一)控制资源计数(一对多)递归性支持(同一任务可重入)不支持优先级继续支持(部门 RTOS)不支持停止安全否(需用信号量更换)是(通过 xxxFromISR 函数) 4.2.3 信号量

1. 信号量对象界说宏(osSemaphoreDef 和 osSemaphore)
  1. #if defined (osObjectsExternal)  // 外部定义(如在其他文件中声明)
  2. #define osSemaphoreDef(name)  \
  3. extern const osSemaphoreDef_t os_semaphore_def_##name
  4. #else  // 本地定义
  5.   #if (configSUPPORT_STATIC_ALLOCATION == 1)  // 静态内存分配
  6.   #define osSemaphoreDef(name)  \
  7.   const osSemaphoreDef_t os_semaphore_def_##name = { 0, NULL }
  8.   #define osSemaphoreStaticDef(name, control)  \
  9.   const osSemaphoreDef_t os_semaphore_def_##name = { 0, (control) }
  10.   #else  // 动态内存分配(默认)
  11.   #define osSemaphoreDef(name)  \
  12.   const osSemaphoreDef_t os_semaphore_def_##name = { 0 }
  13.   #endif
  14. #endif
  15. #define osSemaphore(name)  \
  16. &os_semaphore_def_##name  // 获取信号量对象地址
复制代码
    声明或界说信号量对象的结构体(osSemaphoreDef_t)。
关键细节:
静态分配(configSUPPORT_STATIC_ALLOCATION=1):需手动指定内存(如 osSemaphoreStaticDef 传入控制块地址),适合内存受限场景。
动态分配:RTOS 自动管理内存,使用更灵活。
命名规则:信号量对象名为 os_semaphore_def_,通过 osSemaphore(name) 访问其地址。
2. 创建信号量(osSemaphoreCreate)
  1. osSemaphoreId osSemaphoreCreate(const osSemaphoreDef_t *semaphore_def, int32_t count);
复制代码
    初始化信号量对象,指定初始可用资源数 count。
参数:
semaphore_def:信号量界说结构体指针(通过 osSemaphore(name) 获取)。
count:初始可用资源数:
二进制信号量:count = 1(初始可用)。
计数信号量:count = N(N 为资源总数)。
返回值:
成功:信号量 ID(非 NULL)。
失败:NULL(如内存不足)。
3. 等待信号量(osSemaphoreWait)
  1. int32_t osSemaphoreWait(osSemaphoreId semaphore_id, uint32_t millisec);
复制代码
    获取信号量(斲丧一个令牌),若资源不可用则阻塞等待。
参数:
semaphore_id:信号量 ID(由 osSemaphoreCreate 返回)。
millisec:超时时间(单位:毫秒):
0:不等待,立刻返回当前状态。
osWaitForever:无限等待(需 RTOS 支持)。
返回值:
osOK:成功。
其他:其他超时等异常,具体参考源码的界说
  1. typedef enum  {
  2.   osOK                    =     0,       ///< function completed; no error or event occurred.
  3.   osEventSignal           =  0x08,       ///< function completed; signal event occurred.
  4.   osEventMessage          =  0x10,       ///< function completed; message event occurred.
  5.   osEventMail             =  0x20,       ///< function completed; mail event occurred.
  6.   osEventTimeout          =  0x40,       ///< function completed; timeout occurred.
  7.   osErrorParameter        =  0x80,       ///< parameter error: a mandatory parameter was missing or specified an incorrect object.
  8.   osErrorResource         =  0x81,       ///< resource not available: a specified resource was not available.
  9.   osErrorTimeoutResource  =  0xC1,       ///< resource not available within given time: a specified resource was not available within the timeout period.
  10.   osErrorISR              =  0x82,       ///< not allowed in ISR context: the function cannot be called from interrupt service routines.
  11.   osErrorISRRecursive     =  0x83,       ///< function called multiple times from ISR with same object.
  12.   osErrorPriority         =  0x84,       ///< system cannot determine priority or thread has illegal priority.
  13.   osErrorNoMemory         =  0x85,       ///< system is out of memory: it was impossible to allocate or reserve memory for the operation.
  14.   osErrorValue            =  0x86,       ///< value of a parameter is out of range.
  15.   osErrorOS               =  0xFF,       ///< unspecified RTOS error: run-time error but no other error message fits.
  16.   os_status_reserved      =  0x7FFFFFFF  ///< prevent from enum down-size compiler optimization.
  17. } osStatus;
复制代码
4. 释放信号量(osSemaphoreRelease)
  1. osStatus osSemaphoreRelease(osSemaphoreId semaphore_id);
复制代码
    释放信号量(归还一个令牌),唤醒等待该信号量的线程。
参数:
semaphore_id:信号量 ID。
返回值:
osOK:成功。
其他同等待信号量
5. 删除信号量(osSemaphoreDelete)
  1. osStatus osSemaphoreDelete(osSemaphoreId semaphore_id);
复制代码
    烧毁信号量对象,释放其占用的内存。
参数:
semaphore_id:信号量 ID。
返回值:
osOK:成功(需确保无线程等待该信号量)。
其他同等待信号量
注:删除前需确保没有线程正在等待该信号量,否则可能导致不可猜测行为。动态分配的信号量需删除以释放内存,静态分配的信号量需手动管理生命周期。
特性信号量互斥锁典型用途资源计数、线程同步互斥访问(需优先级继续)计数支持支持计数(N ≥ 1)仅二进制(N = 1)优先级继续不支持支持(避免优先级反转)释放者恣意线程可释放仅持有锁的线程可释放 4.3 应用设置

    在之前线程、延时、定时器篇中设置的基础上,再使能信号标志、互斥锁、信号量的功能。并且由于信号量、互斥锁这些更多应用在两个或多个线程之间,为了演示其一样平常的应用方式,这里必要多设置几个线程。由于这里用到信号量的资源计数功能,以是要在CubeMX中使能USE_COUNTING_SEMAPHORES的功能,启用信号量计数。互斥锁必要使能USE_MUTEXES,这里一样平常默认是使能的。

4.4 代码实现



4.4.1 信号标志

    信号标志实现一个功能:响应线程B和C的按键信号,在线程A中表现不同的灯。这里让按下KEY_R键闪LED_ACT,按下KEY_W键闪LED_READY。(由于按键在不同的线程里,而闪灯在同一个线程中,以是实际表现效果是按下按键后会延迟闪灯)
  1. /* USER CODE BEGIN 4 */
  2. #define LEDACT_EVENT        (1 << 0)
  3. #define LEDREADY_EVENT      (1 << 1)
  4. osThreadId LEDThreadID;
  5. /* USER CODE END 4 */
  6. /* USER CODE BEGIN Header_StartDefaultTask */
  7. /**
  8.   * @brief  Function implementing the defaultTask thread.
  9.   * @param  argument: Not used
  10.   * @retval None
  11.   */
  12. /* USER CODE END Header_StartDefaultTask */
  13. void StartDefaultTask(void const * argument)
  14. {
  15.   /* USER CODE BEGIN 5 */
  16.   /* 获取当前线程ID */
  17.   LEDThreadID = osThreadGetId();
  18.   /* Infinite loop */
  19.   for(;;)
  20.   {
  21.     osEvent ret = osSignalWait(LEDACT_EVENT | LEDREADY_EVENT, 500);
  22.     if (ret.value.signals == LEDACT_EVENT)
  23.     {
  24.         HAL_GPIO_WritePin(LED_ACT_GPIO_Port, LED_ACT_Pin, GPIO_PIN_RESET);
  25.         osDelay(500);
  26.         HAL_GPIO_WritePin(LED_ACT_GPIO_Port, LED_ACT_Pin, GPIO_PIN_SET);
  27.         osDelay(500);
  28.     }
  29.     if (ret.value.signals == LEDREADY_EVENT)
  30.     {
  31.         HAL_GPIO_WritePin(LED_READY_GPIO_Port, LED_READY_Pin, GPIO_PIN_RESET);
  32.         osDelay(500);
  33.         HAL_GPIO_WritePin(LED_READY_GPIO_Port, LED_READY_Pin, GPIO_PIN_SET);
  34.         osDelay(500);
  35.     }
  36.   }
  37.   /* USER CODE END 5 */
  38. }
  39. /* USER CODE BEGIN Header_StartTask03 */
  40. /**
  41. * @brief Function implementing the myTask03 thread.
  42. * @param argument: Not used
  43. * @retval None
  44. */
  45. /* USER CODE END Header_StartTask03 */
  46. void StartTask03(void const * argument)
  47. {
  48.   /* USER CODE BEGIN StartTask03 */
  49.   /* Infinite loop */
  50.   for(;;)
  51.   {
  52.     if (GPIO_PIN_RESET == HAL_GPIO_ReadPin(KEY_R_GPIO_Port, KEY_R_Pin))
  53.     {
  54.         /* 经典5ms消抖 */
  55.         osDelay(5);
  56.         if (GPIO_PIN_RESET == HAL_GPIO_ReadPin(KEY_R_GPIO_Port, KEY_R_Pin))
  57.         {
  58.             /* 直到放开才算完成一次按键 */
  59.             while (GPIO_PIN_RESET == HAL_GPIO_ReadPin(KEY_R_GPIO_Port, KEY_R_Pin));
  60.             osSignalSet(LEDThreadID, LEDACT_EVENT);
  61.         }
  62.     }
  63.   }
  64.   /* USER CODE END StartTask03 */
  65. }
  66. /* USER CODE BEGIN Header_StartTask04 */
  67. /**
  68. * @brief Function implementing the myTask04 thread.
  69. * @param argument: Not used
  70. * @retval None
  71. */
  72. /* USER CODE END Header_StartTask04 */
  73. void StartTask04(void const * argument)
  74. {
  75.   /* USER CODE BEGIN StartTask04 */
  76.   /* Infinite loop */
  77.   for(;;)
  78.   {
  79.       if (GPIO_PIN_RESET == HAL_GPIO_ReadPin(KEY_W_GPIO_Port, KEY_W_Pin))
  80.       {
  81.           /* 经典5ms消抖 */
  82.           osDelay(5);
  83.           if (GPIO_PIN_RESET == HAL_GPIO_ReadPin(KEY_W_GPIO_Port, KEY_W_Pin))
  84.           {
  85.               /* 直到放开才算完成一次按键 */
  86.               while (GPIO_PIN_RESET == HAL_GPIO_ReadPin(KEY_W_GPIO_Port, KEY_W_Pin));
  87.               osSignalSet(LEDThreadID, LEDREADY_EVENT);
  88.           }
  89.       }
  90.   }
  91.   /* USER CODE END StartTask04 */
  92. }
复制代码

4.4.2 互斥锁

    互斥锁实现一个功能:保护一个临界资源的使用(即保护共享资源,防止重入的问题)。这里为了直观演示且增大复现的概率,把单片机的主频调成1MHz,让循环的时间拉长,然后对比下加互斥锁和不加互斥锁的区别。
  1. /* USER CODE BEGIN Header_StartTask02 */
  2. /**
  3. * @brief Function implementing the myTask02 thread.
  4. * @param argument: Not used
  5. * @retval None
  6. */
  7. #define BUFFSIZE            (2000)
  8. uint32_t ShareBuff[BUFFSIZE];
  9. /* 互斥锁相关定义 */
  10. osMutexDef(FlashLEDMute);
  11. osMutexId FlashLEDMuteID;
  12. /* USER CODE END Header_StartTask02 */
  13. void StartTask02(void const * argument)
  14. {
  15.   /* USER CODE BEGIN StartTask02 */
  16.   uint32_t i = 0;
  17.   FlashLEDMuteID = osMutexCreate(osMutex(FlashLEDMute));
  18.   /* Infinite loop */
  19.   for(;;)
  20.   {
  21.     //if (osOK == osMutexWait(FlashLEDMuteID, 500))
  22.     {
  23.         for (i = 0; i < BUFFSIZE; i++)
  24.         {
  25.             ShareBuff[i] = 1;
  26.         }
  27.         for (i = 0; i < BUFFSIZE; i++)
  28.         {
  29.             if (1 != ShareBuff[i])
  30.                 break;
  31.         }
  32.         if (i == BUFFSIZE)
  33.         {
  34.             HAL_GPIO_WritePin(LED_READY_GPIO_Port, LED_READY_Pin, GPIO_PIN_RESET);
  35.         }
  36.         else
  37.         {
  38.             HAL_GPIO_WritePin(LED_READY_GPIO_Port, LED_READY_Pin, GPIO_PIN_SET);
  39.         }
  40.         //osMutexRelease(FlashLEDMuteID);
  41.     }
  42.     osDelay(1);
  43.   }
  44.   /* USER CODE END StartTask02 */
  45. }
  46. /* USER CODE BEGIN Header_StartTask03 */
  47. /**
  48. * @brief Function implementing the myTask03 thread.
  49. * @param argument: Not used
  50. * @retval None
  51. */
  52. /* USER CODE END Header_StartTask03 */
  53. void StartTask03(void const * argument)
  54. {
  55.   /* USER CODE BEGIN StartTask03 */
  56.   /* Infinite loop */
  57.   for(;;)
  58.   {
  59.     //if (osOK == osMutexWait(FlashLEDMuteID, 500))
  60.     {
  61.         for (uint32_t i = 0; i < BUFFSIZE; i++)
  62.         {
  63.             ShareBuff[i] = 2;
  64.         }
  65.         //osMutexRelease(FlashLEDMuteID);
  66.     }
  67.     osDelay(1);
  68.   }
  69.   /* USER CODE END StartTask03 */
  70. }
复制代码

下面是加了互斥锁后
  1. /* USER CODE BEGIN Header_StartTask02 */
  2. /**
  3. * @brief Function implementing the myTask02 thread.
  4. * @param argument: Not used
  5. * @retval None
  6. */
  7. #define BUFFSIZE            (2000)
  8. uint32_t ShareBuff[BUFFSIZE];
  9. /* 互斥锁相关定义 */
  10. osMutexDef(FlashLEDMute);
  11. osMutexId FlashLEDMuteID;
  12. /* USER CODE END Header_StartTask02 */
  13. void StartTask02(void const * argument)
  14. {
  15.   /* USER CODE BEGIN StartTask02 */
  16.   uint32_t i = 0;
  17.   FlashLEDMuteID = osMutexCreate(osMutex(FlashLEDMute));
  18.   /* Infinite loop */
  19.   for(;;)
  20.   {
  21.     if (osOK == osMutexWait(FlashLEDMuteID, 500))
  22.     {
  23.         for (i = 0; i < BUFFSIZE; i++)
  24.         {
  25.             ShareBuff[i] = 1;
  26.         }
  27.         for (i = 0; i < BUFFSIZE; i++)
  28.         {
  29.             if (1 != ShareBuff[i])
  30.                 break;
  31.         }
  32.         if (i == BUFFSIZE)
  33.         {
  34.             HAL_GPIO_WritePin(LED_READY_GPIO_Port, LED_READY_Pin, GPIO_PIN_RESET);
  35.         }
  36.         else
  37.         {
  38.             HAL_GPIO_WritePin(LED_READY_GPIO_Port, LED_READY_Pin, GPIO_PIN_SET);
  39.         }
  40.         osMutexRelease(FlashLEDMuteID);
  41.     }
  42.     osDelay(1);
  43.   }
  44.   /* USER CODE END StartTask02 */
  45. }
  46. /* USER CODE BEGIN Header_StartTask03 */
  47. /**
  48. * @brief Function implementing the myTask03 thread.
  49. * @param argument: Not used
  50. * @retval None
  51. */
  52. /* USER CODE END Header_StartTask03 */
  53. void StartTask03(void const * argument)
  54. {
  55.   /* USER CODE BEGIN StartTask03 */
  56.   /* Infinite loop */
  57.   for(;;)
  58.   {
  59.     if (osOK == osMutexWait(FlashLEDMuteID, 500))
  60.     {
  61.         for (uint32_t i = 0; i < BUFFSIZE; i++)
  62.         {
  63.             ShareBuff[i] = 2;
  64.         }
  65.         osMutexRelease(FlashLEDMuteID);
  66.     }
  67.     osDelay(1);
  68.   }
  69.   /* USER CODE END StartTask03 */
  70. }
复制代码

    如果这里单看StartTask02任务里的操纵,是完全看不出有什么问题的,这就是使用操纵体系与裸机开辟最大的区别。操纵体系是存在多线程的,线程之间的轮转方式,除了通过一些osDelay延时接口,或信号量等等待接口对当前任务举行让出外,还有固定的时间片轮转机制,也就是当一个线程运行到达一个时间片的长度时(一个时间片一样平常就是操纵体系设置的时基,这里是1ms),会跳出查找是否有同等优先级的其他线程必要运行,如果有会执行轮转。因此当线程中执行大量操纵,且同其他线程共用同一个资源时,就容易在资源使用的界限被其他线程误修改,且当中的时序不可控,最终表现出来就是征象很随机,问题概率性出现。这就是嵌入式经常讲的重入问题。以是在操纵体系中,为了防止这种共享资源应用异常,可以使用互斥锁对该资源举行保护,防止使用过程中异常切出。
4.4.3 信号量

    信号量实现一个功能:按照按键按下的次数闪烁对应的次数。这里设置按多少次KEY_W键亮LED_READY灯多少次,最大次数为5次。不外这个应用方式用得不是很适当,由于创建信号量的时间,初始就存在5个信号量,必要斲丧完再等按键新释放的信号量,跟一开始的计划想法不大一样,不外这里用来学习接口的用法也足够了。
  1. /* USER CODE BEGIN Header_StartTask02 */
  2. /**
  3. * @brief Function implementing the myTask02 thread.
  4. * @param argument: Not used
  5. * @retval None
  6. */
  7. osSemaphoreDef(FlashLED);
  8. osSemaphoreId FlashLEDID;
  9. /* USER CODE END Header_StartTask02 */
  10. void StartTask02(void const * argument)
  11. {
  12.   /* USER CODE BEGIN StartTask02 */
  13.   /* Infinite loop */
  14.   for(;;)
  15.   {
  16.     /* 一直等,直到有接收到信号量再执行 */
  17.     if (osOK == osSemaphoreWait(FlashLEDID, osWaitForever))
  18.     {
  19.         /* 1s消耗一个信号量 */
  20.         HAL_GPIO_WritePin(LED_READY_GPIO_Port, LED_READY_Pin, GPIO_PIN_RESET);
  21.         osDelay(500);
  22.         HAL_GPIO_WritePin(LED_READY_GPIO_Port, LED_READY_Pin, GPIO_PIN_SET);
  23.         osDelay(500);
  24.     }
  25.   }
  26.   /* USER CODE END StartTask02 */
  27. }
  28. /* USER CODE BEGIN Header_StartTask03 */
  29. /**
  30. * @brief Function implementing the myTask03 thread.
  31. * @param argument: Not used
  32. * @retval None
  33. */
  34. /* USER CODE END Header_StartTask03 */
  35. void StartTask03(void const * argument)
  36. {
  37.   /* USER CODE BEGIN StartTask03 */
  38.   /* 最多闪5下 */
  39.   FlashLEDID = osSemaphoreCreate(osSemaphore(FlashLED), 5);
  40.   /* Infinite loop */
  41.   for(;;)
  42.   {
  43.     if (GPIO_PIN_RESET == HAL_GPIO_ReadPin(KEY_W_GPIO_Port, KEY_W_Pin))
  44.     {
  45.         /* 经典5ms消抖 */
  46.         osDelay(5);
  47.         if (GPIO_PIN_RESET == HAL_GPIO_ReadPin(KEY_W_GPIO_Port, KEY_W_Pin))
  48.         {
  49.             /* 直到放开才算完成一次按键 */
  50.             while (GPIO_PIN_RESET == HAL_GPIO_ReadPin(KEY_W_GPIO_Port, KEY_W_Pin));
  51.             osSemaphoreRelease(FlashLEDID);
  52.         }
  53.     }
  54.   }
  55.   /* USER CODE END StartTask03 */
  56. }
复制代码

五、注意事项

1、使用操纵体系要注意共享资源的使用,如果存在两个任务同时读写同一个共用内存时,要注意是否存在重入问题,如果必要保护该临界资源,可以通过互斥锁的方式举行保护。
2、信号量在创建的时间设置最大资源个数时,会同步创建同样多的资源数。
六、相关链接

对于刚入门的小同伴可以先看下STM32CubeMX的基础使用及Keil的基础使用。
【工具使用】STM32CubeMX-基础使用篇
【工具使用】Keil5软件使用-基础使用篇
【工具使用】STM32CubeMX-FreeRTOS操纵体系-任务、延时、定时器篇

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
继续阅读请点击广告

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

×
回复

使用道具 举报

×
登录参与点评抽奖,加入IT实名职场社区
去登录
快速回复 返回顶部 返回列表