STM32F1与STM32CubeIDE编程实例-CMSIS-RTOS V2-互斥(Mutex)管理

互斥(Mutex)管理

CMSIS-RTOS v2 (CMSIS-RTOS2) 为基于 Arm® Cortex® 处理器的设备提供通用 RTOS 接口。 它为需要 RTOS 功能的软件组件提供标准化 API,从而为用户和软件行业带来巨大的好处。

本文将详细介绍,在CMSIS-RTOS V2中如何使用Mutex。

1、Mutex介绍

互斥(广泛称为 Mutex)在各种操作系统中用于资源管理。 微控制器设备中的许多资源可以重复使用,但一次只能由一个线程使用(例如通信通道、内存和文件)。 互斥锁用于保护对共享资源的访问。 创建互斥体,然后在线程之间传递(它们可以获取和释放互斥体)。

在这里插入图片描述

1.2 CMSIS-RTOS中的Mutex

互斥锁是信号量的特殊版本。 与信号量一样,它是令牌的容器。 但是,互斥体不能拥有多个令牌,而是只能携带一个(代表资源)。 因此,互斥令牌是二进制和有界的,即它要么可用,要么被拥有线程阻塞。 互斥锁的优点是它引入了线程所有权。 当一个线程获得一个互斥体并成为其所有者时,从该线程获得的后续互斥体将立即成功,没有任何延迟(如果指定了 osMutexRecursive)。 因此,互斥锁获取/释放可以嵌套。

CMSIS-RTOS 互斥状态如下:

在这里插入图片描述

1
2
注意:与可以从 ISR 释放的二进制信号量不同,不能从中断服务例程 (ISR) 调用互斥体管理功能。

1.3 Mutex相关定义

1)osMutexId_t:Mutex ID

2)osMutexAtt_t:Mutex属性

1
2
3
4
5
6
7
8
9
/// Attributes structure for mutex.
typedef struct {
const char \*name; ///< name of the mutex
uint32\_t attr_bits; ///< attribute bits
void \*cb_mem; ///< memory for control block
uint32\_t cb_size; ///< size of provided memory for control block
} osMutexAttr\_t;


CMSIS-RTOS V2中Mutex相关的API如下:

  • **osMutexId_t osMutexNew (const osMutexAttr_t *attr)**:创建并初始化一个新的互斥对象并返回指向互斥对象标识符的指针,如果出错则返回 NULL。 它可以在 RTOS 启动之前安全地调用(调用 osKernelStart),但不能在初始化之前调用(调用 osKernelInitialize)。参数attr 设置互斥对象属性(参考osMutexAttr_t)。 如果设置为 NULL,将使用默认属性。

注意:不能在中断服务中调用此函数

简单示例如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#include "cmsis\_os2.h"

osMutexId\_t mutex_id;

const osMutexAttr\_t Thread_Mutex_attr = {
"myThreadMutex", // human readable mutex name
osMutexRecursive | osMutexPrioInherit, // attr\_bits
NULL, // memory for control block
0U // size for control block
};

void CreateMutex (void) {
mutex_id = osMutexNew(&Thread_Mutex_attr);
if (mutex_id != NULL) {
// Mutex object created
}
}

  • **const char * osMutexGetName (osMutexId_t mutex_id)**:返回指向由参数 mutex_id 标识的互斥锁名称字符串的指针,如果出错则返回 NULL。

注意:不能在中断服务函数中调用此函数

  • **osStatus_t osMutexAcquire (osMutexId_t mutex_id, uint32_t timeout)**:函数一直等待,直到参数 mutex_id 指定的互斥对象可用。 如果没有其他线程获得互斥锁,该函数立即返回并阻塞互斥锁对象。

    • 参数 timeout 指定系统等待获取互斥锁的时间。 在系统等待期间,调用此函数的线程将进入 BLOCKED 状态。 参数 timeout 可以有以下值:

      • 当 timeout 为 0 时,函数立即返回(即尝试语义)。
      • 当超时设置为 osWaitForever 时,该函数将等待无限时间,直到互斥体可用(即等待语义)。
      • 所有其他值在内核滴答中指定超时时间(即定时等待语义)。
    • 可能的 osStatus_t 返回值:

      • osOK:已获得互斥体。
      • osErrorTimeout:在给定时间内无法获取互斥锁。
      • osErrorResource:未指定超时时无法获取互斥锁。
      • osErrorParameter:参数 mutex_id 为 NULL 或无效。
      • osErrorISR:不能从中断服务程序调用。注意:不能在中断服务中调用此函数简单示例如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#include "cmsis\_os2.h"

void WaitMutex (void) {
osMutexId\_t mutex_id;
osStatus\_t status;

mutex_id = osMutexNew(NULL);
if (mutex_id != NULL) {
status = osMutexAcquire(mutex_id, 0U);
if (status != osOK) {
// handle failure code
}
}
}

  • **osStatus_t osMutexRelease (osMutexId_t mutex_id)**:释放由参数 mutex_id 指定的互斥锁。 当前等待此互斥锁的其他线程将进入 READY 状态。可能的 osStatus_t 返回值:

    • osOK:互斥锁已正确释放。
      osErrorResource:无法释放互斥锁(未获取互斥锁或正在运行的线程不是所有者)。
      osErrorParameter:参数 mutex_id 为 NULL 或无效。
      osErrorISR:不能从中断服务例程调用 osMutexRelease。注意:不能在中断函数中调用此函数

简单示例如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#include "cmsis\_os2.h"

osMutexId\_t mutex_id; // Mutex id populated by the function osMutexNew()

void ReleaseMutex (osMutexId\_t mutex_id) {
osStatus\_t status;

if (mutex_id != NULL) {
status = osMutexRelease(mutex_id);
if (status != osOK) {
// handle failure code
}
}
}

  • **osThreadId_t osMutexGetOwner (osMutexId_t mutex_id)**:返回获取由参数 mutex_id 指定的互斥锁的线程的线程 ID。 如果发生错误或互斥锁未被任何线程阻塞,则返回 NULL。

  • **osStatus_t osMutexDelete (osMutexId_t mutex_id)**:删除由参数 mutex_id 指定的互斥对象。 它释放用于互斥体处理的内部内存。 调用后,mutex_id 不再有效,无法使用。 可以使用函数 osMutexNew 再次创建互斥锁。可能的 osStatus_t 返回值:

    • osOK:互斥对象已被删除。
    • osErrorParameter:参数 mutex_id 为 NULL 或无效。
    • osErrorResource:互斥锁处于无效状态。
    • osErrorISR:不能从中断服务例程调用 osMutexDelete。注意:不能在中断函数中调用此函数

简单示例如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#include "cmsis\_os2.h"

osMutexId\_t mutex_id; // Mutex id populated by the function osMutexNew()

void DeleteMutex (osMutexId\_t mutex_id) {
osStatus\_t status;

if (mutex_id != NULL) {
status = osMutexDelete(mutex_id);
if (status != osOK) {
// handle failure code
}
}
}

2、在STM32CubeIDE中配置Mutex

第一步: 新建工程

新建工程,请参考前面文章:

串口配置,请参考前面文章:

第二步: 配置时钟

配置时钟,请参考前面文章:

第三步:配置FreeRTOS

配置FreeRTOS,及FreeRTOS的堆栈,请参考:

第四步:添加线程

创建线程,请参考前面文章:

在本次实例中定义的线程如下:
在这里插入图片描述

第五步:创建Mutex

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

第六步:保存生成代码

3、Mutex功能代码实现

main.c文件的main函数中,RTOS启动调用如下:

1
2
3
4
5
6
7
/\* Init scheduler \*/
osKernelInitialize(); /\* Call init function for freertos objects (in freertos.c) \*/
MX\_FREERTOS\_Init();

/\* Start scheduler \*/
osKernelStart();

freertos.c文件中,生成定义的Mutex、线程如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
osThreadId\_t defaultTaskHandle;                   
const osThreadAttr\_t defaultTask_attributes = {
.name = "defaultTask",
.stack_size = 128 \* 4,
.priority = (osPriority\_t) osPriorityNormal,
};
/\* Definitions for thread1 \*/
osThreadId\_t thread1Handle;
const osThreadAttr\_t thread1_attributes = {
.name = "thread1",
.stack_size = 512 \* 4,
.priority = (osPriority\_t) osPriorityNormal,
};
/\* Definitions for thread2 \*/
osThreadId\_t thread2Handle;
const osThreadAttr\_t thread2_attributes = {
.name = "thread2",
.stack_size = 512 \* 4,
.priority = (osPriority\_t) osPriorityNormal,
};
/\* Definitions for counterMutex \*/
osMutexId\_t counterMutexHandle;
const osMutexAttr\_t counterMutex_attributes = {
.name = "counterMutex"
};


1
2
3
4
5
                                         
void StartDefaultTask(void \*argument);
void Thread1Task(void \*argument);
void Thread2Task(void \*argument);

freertos.c文件中,线程执行函数如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
/\* USER CODE END Header\_StartDefaultTask \*/             
void StartDefaultTask(void \*argument)
{
/\* USER CODE BEGIN StartDefaultTask \*/
/\* Infinite loop \*/
for (;;) {
osDelay(1);
}
/\* USER CODE END StartDefaultTask \*/
}

/\* USER CODE BEGIN Header\_Thread1Task \*/
/\*\*
\* @brief Function implementing the thread1 thread.
\* @param argument: Not used
\* @retval None
\*/
/\* USER CODE END Header\_Thread1Task \*/
void Thread1Task(void \*argument)
{
/\* USER CODE BEGIN Thread1Task \*/
/\* Infinite loop \*/
for(;;)
{
osDelay(1);
}
/\* USER CODE END Thread1Task \*/
}

/\* USER CODE BEGIN Header\_Thread2Task \*/
/\*\*
\* @brief Function implementing the thread2 thread.
\* @param argument: Not used
\* @retval None
\*/
/\* USER CODE END Header\_Thread2Task \*/
void Thread2Task(void \*argument)
{
/\* USER CODE BEGIN Thread2Task \*/
/\* Infinite loop \*/
for(;;)
{
osDelay(1);
}
/\* USER CODE END Thread2Task \*/
}

线程执行代码功能实现:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
/\* USER CODE END Header\_StartDefaultTask \*/
void StartDefaultTask(void \*argument)
{
/\* USER CODE BEGIN StartDefaultTask \*/
/\* Infinite loop \*/
for (;;) {
osDelay(1);
}
/\* USER CODE END StartDefaultTask \*/
}

/\* USER CODE BEGIN Header\_Thread1Task \*/
/\*\*
\* @brief Function implementing the thread1 thread.
\* @param argument: Not used
\* @retval None
\*/
/\* USER CODE END Header\_Thread1Task \*/
void Thread1Task(void \*argument)
{
/\* USER CODE BEGIN Thread1Task \*/
/\* Infinite loop \*/
for (;;) {
osDelay(1000);
osMutexAcquire(counterMutexHandle, 1000);
counter--;
printf("thread1:counter = %d\r\n", counter);
} osMutexRelease(counterMutexHandle);
}
/\* USER CODE BEGIN Header\_Thread2Task \*/
/\*\*
\* @brief Function implementing the thread2 thread.
\* @param argument: Not used
\* @retval None
\*/
/\* USER CODE END Header\_Thread2Task \*/
void Thread2Task(void \*argument)
{
/\* USER CODE BEGIN Thread2Task \*/
/\* Infinite loop \*/
for (;;) {
osDelay(1000);
osMutexAcquire(counterMutexHandle, 1000);
counter++;
printf("thread2:counter = %d\r\n", counter);
osMutexRelease(counterMutexHandle);
}
}

freertos.c文件中的MX_FREERTOS_Init函数中,创建Mutex:

1
2
3
/\* creation of counterMutex \*/
counterMutexHandle = osMutexNew(&counterMutex_attributes);

创建线程:

1
2
3
4
5
6
7
8
9
/\* creation of defaultTask \*/
defaultTaskHandle = osThreadNew(StartDefaultTask, NULL, &defaultTask_attributes);

/\* creation of thread1 \*/
thread1Handle = osThreadNew(Thread1Task, NULL, &thread1_attributes);

/\* creation of thread2 \*/
thread2Handle = osThreadNew(Thread2Task, NULL, &thread2_attributes);

最后编译生成固件并下载到开发板中。

4、STM32F1与STM32CubeIDE快速入门系列文章

序号 内容
1 STM32F1与STM32CubeIDE快速入门-开发环境搭建
2 STM32F1与STM32CubeIDE快速入门-STM32F1微控制器概述
3 STM32F1与STM32CubeIDE快速入门-GPIO概述与点亮LED
4 STM32F1与STM32CubeIDE快速入门-按键与LED控制
5 STM32F1与STM32CubeIDE快速入门-中断、NVIC与EXTI概述
6 STM32F1与STM32CubeIDE快速入门-外部中断配置与功能实现
7 STM32F1与STM32CubeIDE快速入门-USART/UART串口通信
8 STM32F1与STM32CubeIDE快速入门-定时器(Timer)概述
9 STM32F1与STM32CubeIDE快速入门-定时器定时模式
10 STM32F1与STM32CubeIDE快速入门-定时器计数模式
11 STM32F1与STM32CubeIDE快速入门-定时器PWM模式
12 STM32F1与STM32CubeIDE快速入门-定时器编码(Encoder)模式
13 STM32F1与STM32CubeIDE快速入门-定时器输入捕获模式(Input Capture Mode)实现频率计数
14 STM32F1与STM32CubeIDE快速入门-DMA概述
15 STM32F1与STM32CubeIDE快速入门-USART通过DMA进行数据接收与发送
16 STM32F1与STM32CubeIDE快速入门-ADC概述
17 STM32F1与STM32CubeIDE快速入门-ADC轮询方式实现PWM调光器
18 STM32F1与STM32CubeIDE快速入门-ADC中断方式实现PWM调光器
19 STM32F1与STM32CubeIDE快速入门-ADC通过DMA方式与PWM实现调光器
20 STM32F1与STM32CubeIDE快速入门-DAC概述
21 STM32F1与STM32CubeIDE快速入门-SPI概述
22 STM32F1与STM32CubeIDE快速入门-M25P16串行闪存驱动
23 STM32F1与STM32CubeIDE快速入门-I2C概述
24 STM32F1与STM32CubeIDE快速入门-I2C驱动LCD1602显示屏(基于PCF8574)
25 STM32F1与STM32CubeIDE快速入门-独立看门狗(IWDG)
26 STM32F1与STM32CubeIDE快速入门-OLED-SSD1306-I2C驱动
27 STM32F1与STM32CubeIDE编程实例-CMSIS-RTOS V2配置(基于FreeRTOS)
28 STM32F1与STM32CubeIDE编程实例-CMSIS-RTOS V2-线程管理
29 STM32F1与STM32CubeIDE编程实例-CMSIS-RTOS v2-延时
30 STM32F1与STM32CubeIDE编程实例-CMSIS-RTOS V2-定时器管理

文章来源: https://iotsmart.blog.csdn.net/article/details/124674779