LV01-01-AliOSThings-12-任务间通信-06-互斥锁和临界区使用实例

本文主要是任务之间的通信——互斥锁的使用实例的相关笔记,若笔记中有错误或者不合适的地方,欢迎批评指正😃。

点击查看使用工具及版本
Windows版本 windows11
Ubuntu版本 Ubuntu22.04的64位版本
VMware® Workstation 16 Pro 16.2.3 build-19376536
终端软件 MobaXterm(Professional Edition v23.0 Build 5042 (license))
点击查看本文参考资料
分类 网址 说明
官方网站 阿里云 阿里云官网主页
阿里生活物联平台 生活物联网平台(飞燕平台)主页
AliGenie 天猫精灵开放平台AliGenie主页
阿里物联网平台 阿里物联网平台主页
Bluetooth 技术网站 蓝牙协议规范什么的可以来这里找
Telink Telink | Chips for a Smarter IoT (telink-semi.com)
Telink中文官网
开发手册 AliOS Things开发指南 AliOS Things开发指南,这里是最新版本,可以直接从官网找到
AliOS Things开发指南 AliOS Things应用开发指南,这里应该是3.3版本的完整开发文档
AliOS Things开发指南(3.0) AliOS Things应用开发指南,这里应该是3.0版本的完整开发文档
生活物联网平台开发文档 生活物联网平台(飞燕平台)开发文档
《设备端开发指南》
Wi-Fi IoT品类定义与功能开发 天猫精灵IoT开放平台——Wi-Fi IoT品类定义与功能开发
硬件平台 mk3080 WiFi开发板 WiFi开发板使用指南-阿里云开发者社区
esp8266开发板 一个教程:ESP8266-NodeMCU开发板详解-太极创客 (taichi-maker.com)
TLSR8258 Datasheet Datasheet for Telink BLE + IEEE802.15.4 MultiStandard Wireless SoC TLSR8258
参考资料 AliOS Things 3.0 应用开发指南 这个只是一篇参考文章,里面是一些环境搭建相关的,可以参考
IP知识百科 - 华为 (huawei.com) IP的一些相关知识点
点击查看相关文件下载
分类 网址 说明
蓝牙规范相关文档 Core Specification 5.2 核心规格 5.2,该规范定义了创建可互操作的Bluetooth 设备所需的技术。
《Core_v5.2.pdf》
Mesh Model(v1.1) 本Bluetooth 规范定义了模型(以及它们所需的状态和消息),这些模型用于在mesh 网络中的节点上执行基本功能,超出了Bluetooth Mesh 配置文件 规范中定义的基础模型。
本规范包括定义跨设备类型标准功能的通用模型,以及支持关键mesh 场景的模型,如照明控制、传感器、时间和场景。
《MshMDL_v1.1.pdf》
Mesh Profile(v1.0.1) 该Bluetooth 规范定义了基本要求,以实现可互操作的mesh 网络解决方案,用于Bluetooth 低能量无线技术。
《MshPRFv1.0.1.pdf》
Mesh Device Properties 本规范包含Bluetooth Mesh 配置文件 和Bluetooth Mesh 模型规范所要求的设备属性的定义。
但是跟之前的有些区别,我主要看的之前的版本:《MMeshDeviceProperties_v1.2.pdf》
GATT Specification Supplement GATT Specification Supplement | Bluetooth® Technology Website。
好像可以在线看:《GATT Specification Supplement》
Assigned Numbers GATT的一些类型定义可以在这里找。
AliOS Things alios-things/AliOS-Things Gitee上的AliOSThings SDK源码仓库
alibaba/AliOS-Things GitHub上的AliOSThings SDK源码仓库
天猫精灵蓝牙Mesh协议栈 alibaba-archive/genie-bt-mesh-stack GitHub上的天猫精灵蓝牙Mesh协议栈源码仓库。
之前是在alibaba/genie-bt-mesh-stack这个仓库。
写笔记的时候最新提交为faf523618a6a2560090fc423222b9db80984bb7a
蓝牙Mesh设备开发指南 阿里云生活服务平台开发手册——蓝牙设备开发一节中的内容

一、多任务竞争资源分析

1. 我们只有一个串口

image-20200810154328192

如图所示,其实我们的开发板通过串口向屏幕打印数据的时候,串口其实你就相当于各个任务的共享资源了,同一时刻只能有一个任务使用串口,否则数据打印就会出现错乱。

2. 一个实例

我们写一个实例,来验证一下不对串口进行保护会发生什么。这里以在mk3080开发板运行为例,其他也都一样。

2.1 两个任务

首先我们创建两个任务,它们需要有一个优先级:

image-20231206192505192

2.2 task1任务

我们再来看一下两个任务的内容,task1任务函数如下所示:

image-20231206192629302

2.3 task2任务

task2任务函数如下图所示:

image-20231206192855558

2.4 实例源码

完整源码如下,也可以看这里:helloworld_mk3080/example/03_mutex_intrpt_none.c · sumumm/AliOS-Things-Prj - 码云 - 开源中国 (gitee.com)

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
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
/** =====================================================
* Copyright © hk. 2022-2025. All rights reserved.
* File name : 03_mutex_intrpt.c
* Author : 上上签
* Date : 2023-12-05
* Version :
* Description:
* ======================================================
*/

#include <stdio.h>
#include <stdlib.h>
#include <aos/kernel.h>
#include "aos/init.h"
#include "board.h"
#include <k_api.h>
#include "aos/hal/gpio.h"
#include "ulog/ulog.h"

#define RED_LED 0 // PA14
#define GRE_LED 1 // PA15

#define KEY1_PORT 8 // PA5
#define KEY2_PORT 2 // PA22
#define KEY3_PORT 4 // PA23

#define TASK1_NAME "task1"
#define TASK2_NAME "task2"
#define APP_MOUDLE_NAME "app_task"

aos_task_t task1_handle;
aos_task_t task2_handle;

gpio_dev_t led_gpio_dev[2] = {0};
gpio_dev_t key_gpio_dev;
uint32_t key_value;
void task1_entry(void *arg)
{
const char * task_name;
task_name = aos_task_name();
while(1)
{
printf("++++++++++%s test!++++++++++\r\n", task_name);
aos_msleep(10);

}
}

void task2_entry(void *arg)
{
const char * task_name;
task_name = aos_task_name();
while(1)
{
printf("%s test 1\r\n", task_name);
printf("%s test 2\r\n", task_name);
printf("%s test 3\r\n", task_name);
printf("%s test 4\r\n", task_name);
printf("%s test 5\r\n", task_name);
printf("%s test 6\r\n", task_name);
printf("%s test 7\r\n", task_name);
printf("%s test 8\r\n", task_name);
printf("%s test 9\r\n", task_name);
printf("%s test 10\r\n", task_name);
printf("%s test 11\r\n", task_name);
printf("%s test 12\r\n", task_name);
printf("%s test 13\r\n", task_name);
printf("%s test 14\r\n", task_name);
printf("%s test 15\r\n", task_name);
printf("%s test 16\r\n", task_name);
printf("%s test 17\r\n", task_name);
printf("%s test 18\r\n", task_name);
printf("%s test 19\r\n", task_name);
printf("%s test 20\r\n", task_name);
}
}

int led_init(void)
{
//由于PA14时rtl8710的jtag接口,我要使用GPIO功能,必须先关闭jtag借口
sys_jtag_off();
led_gpio_dev[0].port = RED_LED;
led_gpio_dev[0].config = OUTPUT_PUSH_PULL;
led_gpio_dev[0].priv = NULL;
//gpio PA14初始化
hal_gpio_init(&led_gpio_dev[0]);

led_gpio_dev[1].port = GRE_LED;
led_gpio_dev[1].config = OUTPUT_PUSH_PULL;
led_gpio_dev[1].priv = NULL;
//gpio PA14初始化
hal_gpio_init(&led_gpio_dev[1]);

return 0;
}

int application_start(int argc, char *argv[])
{
int ret = 0;
int index = 0;

led_init();
key_gpio_dev.port = KEY1_PORT;
//根据原理图分析配置位输入上拉模式
key_gpio_dev.config = INPUT_PULL_UP;
key_gpio_dev.priv = NULL;
//key 初始化
hal_gpio_init(&key_gpio_dev);

aos_set_log_level(AOS_LL_INFO);

// 创建两个指定优先级的任务
ret = aos_task_new_ext(&task1_handle, TASK1_NAME, task1_entry, NULL, 4*128, 58);
if(ret != 0)
{
LOGE(APP_MOUDLE_NAME,"task key create err\r\n");
}
ret = aos_task_new_ext(&task2_handle, TASK2_NAME, task2_entry, NULL, 4*128, 59);
if(ret != 0)
{
LOGE(APP_MOUDLE_NAME,"task led create err\r\n");
}

while(1)
{
aos_msleep(1000);
hal_gpio_output_toggle(&led_gpio_dev[1]);
}

}

2.5 测试结果

烧写完成后,我们运行一下看一下运行结果:

image-20231206193904544

可以看到,task1和task2出现竞争,我们的task2应该是要完整的打印完1-20的,但是实际被task1打断了3次,甚至于一行都没完整打印完就被打断了,这样是很危险的,怎么办》这时候就用到互斥锁啦。

二、互斥锁

这里以mk3080开发板为例。

1. 使用实例

我们修改一下代码,添加互斥锁进行保护,如下所示,也可以看这里:helloworld_mk3080/example/03_mutex.c · sumumm/AliOS-Things-Prj - 码云 - 开源中国 (gitee.com)

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
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
/** =====================================================
* Copyright © hk. 2022-2025. All rights reserved.
* File name : 03_mutex.c
* Author : 上上签
* Date : 2023-12-05
* Version :
* Description:
* ======================================================
*/

#include <stdio.h>
#include <stdlib.h>
#include <aos/kernel.h>
#include "aos/init.h"
#include "board.h"
#include <k_api.h>
#include "aos/hal/gpio.h"
#include "ulog/ulog.h"

#define RED_LED 0 // PA14
#define GRE_LED 1 // PA15

#define KEY1_PORT 8 // PA5
#define KEY2_PORT 2 // PA22
#define KEY3_PORT 4 // PA23

#define TASK1_NAME "task1"
#define TASK2_NAME "task2"
#define APP_MOUDLE_NAME "app_task"

aos_task_t task1_handle;
aos_task_t task2_handle;

gpio_dev_t led_gpio_dev[2] = {0};
gpio_dev_t key_gpio_dev;
uint32_t key_value;

aos_mutex_t printf_mutex;

void task1_entry(void *arg)
{
const char * task_name;
task_name = aos_task_name();
while(1)
{
aos_mutex_lock(&printf_mutex, AOS_WAIT_FOREVER);
printf("++++++++++%s test!++++++++++\r\n", task_name);
aos_mutex_unlock(&printf_mutex);
aos_msleep(10);
}
}

void task2_entry(void *arg)
{
const char * task_name;
task_name = aos_task_name();
while(1)
{
aos_mutex_lock(&printf_mutex, AOS_WAIT_FOREVER);
printf("%s test 1\r\n", task_name);
printf("%s test 2\r\n", task_name);
printf("%s test 3\r\n", task_name);
printf("%s test 4\r\n", task_name);
printf("%s test 5\r\n", task_name);
printf("%s test 6\r\n", task_name);
printf("%s test 7\r\n", task_name);
printf("%s test 8\r\n", task_name);
printf("%s test 9\r\n", task_name);
printf("%s test 10\r\n", task_name);
printf("%s test 11\r\n", task_name);
printf("%s test 12\r\n", task_name);
printf("%s test 13\r\n", task_name);
printf("%s test 14\r\n", task_name);
printf("%s test 15\r\n", task_name);
printf("%s test 16\r\n", task_name);
printf("%s test 17\r\n", task_name);
printf("%s test 18\r\n", task_name);
printf("%s test 19\r\n", task_name);
printf("%s test 20\r\n", task_name);
aos_mutex_unlock(&printf_mutex);
}
}

int led_init(void)
{
//由于PA14时rtl8710的jtag接口,我要使用GPIO功能,必须先关闭jtag借口
sys_jtag_off();
led_gpio_dev[0].port = RED_LED;
led_gpio_dev[0].config = OUTPUT_PUSH_PULL;
led_gpio_dev[0].priv = NULL;
//gpio PA14初始化
hal_gpio_init(&led_gpio_dev[0]);

led_gpio_dev[1].port = GRE_LED;
led_gpio_dev[1].config = OUTPUT_PUSH_PULL;
led_gpio_dev[1].priv = NULL;
//gpio PA14初始化
hal_gpio_init(&led_gpio_dev[1]);

return 0;
}

int application_start(int argc, char *argv[])
{
int ret = 0;
int index = 0;

led_init();
key_gpio_dev.port = KEY1_PORT;
//根据原理图分析配置位输入上拉模式
key_gpio_dev.config = INPUT_PULL_UP;
key_gpio_dev.priv = NULL;
//key 初始化
hal_gpio_init(&key_gpio_dev);

aos_set_log_level(AOS_LL_INFO);

// 初始化互斥锁
ret = aos_mutex_new(&printf_mutex);
if(ret != 0)
{
LOGE(APP_MOUDLE_NAME, "New mutex erro\r\n");
}

// 创建两个指定优先级的任务
ret = aos_task_new_ext(&task1_handle, TASK1_NAME, task1_entry, NULL, 8*128, 58);
if(ret != 0)
{
LOGE(APP_MOUDLE_NAME,"task key create err\r\n");
}
ret = aos_task_new_ext(&task2_handle, TASK2_NAME, task2_entry, NULL, 8*128, 59);
if(ret != 0)
{
LOGE(APP_MOUDLE_NAME,"task led create err\r\n");
}

while(1)
{
aos_msleep(1000);
hal_gpio_output_toggle(&led_gpio_dev[1]);
}

}

2. 测试结果

我们更新代码,看一下这次的串口数据:

image-20231206194438636

可以看到task2的数据都是完整打印完毕的,说明互斥锁生效了。

三、临界区

这里还是以mk3080开发板为例,上面我们通过互斥锁达到了目的,其实临界区也一样可以。

1. 使用实例

示例代码如下,也可以看这里:helloworld_mk3080/example/03_intrpt.c · sumumm/AliOS-Things-Prj - Gitee.com

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
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
/** =====================================================
* Copyright © hk. 2022-2025. All rights reserved.
* File name : 04_mutex.c
* Author : 上上签
* Date : 2023-12-06
* Version :
* Description:
* ======================================================
*/

#include <stdio.h>
#include <stdlib.h>
#include <aos/kernel.h>
#include "aos/init.h"
#include "board.h"
#include <k_api.h>
#include "aos/hal/gpio.h"
#include "ulog/ulog.h"

#define RED_LED 0 // PA14
#define GRE_LED 1 // PA15

#define KEY1_PORT 8 // PA5
#define KEY2_PORT 2 // PA22
#define KEY3_PORT 4 // PA23

#define TASK1_NAME "task1"
#define TASK2_NAME "task2"
#define APP_MOUDLE_NAME "app_task"

aos_task_t task1_handle;
aos_task_t task2_handle;

gpio_dev_t led_gpio_dev[2] = {0};
gpio_dev_t key_gpio_dev;
uint32_t key_value;

void task1_entry(void *arg)
{
const char * task_name;
task_name = aos_task_name();
while(1)
{
printf("++++++++++%s test!++++++++++\r\n", task_name);
aos_msleep(10);
}
}

void task2_entry(void *arg)
{
const char * task_name;
task_name = aos_task_name();
CPSR_ALLOC();
while(1)
{
RHINO_CRITICAL_ENTER();
printf("%s test 1\r\n", task_name);
printf("%s test 2\r\n", task_name);
printf("%s test 3\r\n", task_name);
printf("%s test 4\r\n", task_name);
printf("%s test 5\r\n", task_name);
printf("%s test 6\r\n", task_name);
printf("%s test 7\r\n", task_name);
printf("%s test 8\r\n", task_name);
printf("%s test 9\r\n", task_name);
printf("%s test 10\r\n", task_name);
printf("%s test 11\r\n", task_name);
printf("%s test 12\r\n", task_name);
printf("%s test 13\r\n", task_name);
printf("%s test 14\r\n", task_name);
printf("%s test 15\r\n", task_name);
printf("%s test 16\r\n", task_name);
printf("%s test 17\r\n", task_name);
printf("%s test 18\r\n", task_name);
printf("%s test 19\r\n", task_name);
printf("%s test 20\r\n", task_name);
RHINO_CRITICAL_EXIT();
}
}

int led_init(void)
{
//由于PA14时rtl8710的jtag接口,我要使用GPIO功能,必须先关闭jtag借口
sys_jtag_off();
led_gpio_dev[0].port = RED_LED;
led_gpio_dev[0].config = OUTPUT_PUSH_PULL;
led_gpio_dev[0].priv = NULL;
//gpio PA14初始化
hal_gpio_init(&led_gpio_dev[0]);

led_gpio_dev[1].port = GRE_LED;
led_gpio_dev[1].config = OUTPUT_PUSH_PULL;
led_gpio_dev[1].priv = NULL;
//gpio PA14初始化
hal_gpio_init(&led_gpio_dev[1]);

return 0;
}

int application_start(int argc, char *argv[])
{
int ret = 0;
int index = 0;

led_init();
key_gpio_dev.port = KEY1_PORT;
//根据原理图分析配置位输入上拉模式
key_gpio_dev.config = INPUT_PULL_UP;
key_gpio_dev.priv = NULL;
//key 初始化
hal_gpio_init(&key_gpio_dev);

aos_set_log_level(AOS_LL_INFO);

// 创建两个指定优先级的任务
ret = aos_task_new_ext(&task1_handle, TASK1_NAME, task1_entry, NULL, 8*128, 58);
if(ret != 0)
{
LOGE(APP_MOUDLE_NAME,"task key create err\r\n");
}
ret = aos_task_new_ext(&task2_handle, TASK2_NAME, task2_entry, NULL, 8*128, 59);
if(ret != 0)
{
LOGE(APP_MOUDLE_NAME,"task led create err\r\n");
}

while(1)
{
aos_msleep(1000);
hal_gpio_output_toggle(&led_gpio_dev[1]);
}
}

2. 测试结果

跟上面互斥锁的效果一样,但是虽然保护了task2不被task1打断,但是由于开关中断,且task2执行时间较长,这好像就影响到了task1,并非是task2执行完毕就执行task1,这应该是系统调度出现了问题导致。

image-20231206195235387