Toggle navigation
首页
问答
文章
积分商城
专家
专区
更多专区...
文档中心
返回主站
搜索
提问
会员
中心
登录
注册
2024-RSOC
【2024-RSOC】DAY3 IPC学习记录
发布于 2024-07-24 22:39:13 浏览:428
订阅该版
[tocm] > 本文包括代码约13000字~~,预计浪费12分钟的生命~~ ## IPC学习 RT-Thread系统的IPC机制主要包括以下内容: > 以下图片使用typora的mermaid流程图语言制作,由于论坛的MARKDOWN不支持,已替换为图片 ![ipc.png](https://oss-club.rt-thread.org/uploads/20240724/4aff062150adb8c2a87c89e10d25a46f.png) ## 具体理解 这里重点理解信号量、互斥量和邮箱机制 ### 信号量 借用文档中心的解释: > 以生活中的停车场为例来理解信号量的概念: > > ①当停车场空的时候,停车场的管理员发现有很多空车位,此时会让外面的车陆续进入停车场获得停车位; > > ②当停车场的车位满的时候,管理员发现已经没有空车位,将禁止外面的车进入停车场,车辆在外排队等候; > > ③当停车场内有车离开时,管理员发现有空的车位让出,允许外面的车进入停车场;待空车位填满后,又禁止外部车辆进入。 > > 在此例子中,管理员就相当于信号量,管理员手中空车位的个数就是信号量的值(非负数,动态变化);停车位相当于公共资源(临界区),车辆相当于线程。车辆通过获得管理员的允许取得停车位,就类似于线程通过获得信号量访问公共资源。 很容易理解,在裸机开发或者一些软件的开发过程中,我们经常需要设置一些flag来让其他线程\函数来了解其他线程\函数的状态。而RT-Thread的信号量机制就在系统级实现了这一功能,同时,在多进程执行过程中,该机制还能起到主动唤醒\休眠特定线程的功能。 信号量的获取将导致信号量的值(value)减一,而释放则会加一。 在RT-Thread的信号量机制中,信号量的获取过程中,如果获取失败(即信号量为0)会造成线程等待,相反,获取成功则会造成线程被唤醒。信号量的释放会造成线程被休眠。 > 释放造成休眠这一设计本质上来说是为了提前将系统资源让出来。但是该功能在互斥量概念出来以后就不重要了。 那么我们就可以实现一种方式:初始化一个为0的信号量,进程1监听某硬件,一旦收到硬件信息则马上释放信号量,进程2不断获取信号量,一旦成功得到信号量,那么就执行某操作。 从实际表现来看,就实现了多进程模式下各个进程各干各的,互不干扰的前提下还能在特定操作下互相唤醒。 ### 互斥量 互斥量的本质是一种特殊的二值信号量,它的存在是为了避免多个进程在抢夺相同资源时意外出现的优先级反转现象。如下图,在下图情况下,M资源会被一直控制在优先级最低的进程C手中,而优先级最高的A则只能在就绪态无法运行,导致B在低优先级被意外执行。 ![](https://www.rt-thread.org/document/site/rt-thread-version/rt-thread-standard/programming-manual/ipc1/figures/06priority_inversion.png) 而互斥量的出现改变了这一现象。互斥量特有的优先级继承功能将阻断B的执行,如下图: ![](https://www.rt-thread.org/document/site/rt-thread-version/rt-thread-standard/programming-manual/ipc1/figures/06priority_inherit.png) 除此之外,互斥量拥有独占性(仅能由某一特定函数持有和释放,被持有期间不能被其他函数释放,相当于锁)和支持递归访问的特性,使其不能被二值信号量给替代。 ### 邮箱 邮箱服务是用于两个活动线程间的通信,和前文的信号量和互斥量不一样,邮箱不具备唤醒\休眠某线程的功能。 邮箱是在内存中被单独划分出来的一片区域,该区域用于存储线程发出来的,准备~~扔给~~交给其他线程的信息。 ### 其他 + 事件集,是一系列事件的集合体,一旦其中的事件被以指定的方式启动,将触发某个线程执行某操作,可以认为成多重信号量。 + 消息队列,邮箱仅能支持4bit数据投递,而消息队列可以自定义消息长度,除此之外在我眼中没有较大区别。 ## API ### 信号量 创建: 静态 ```c rt_err_t rt_sem_init(rt_sem_t sem, const char *name, rt_uint32_t value, rt_uint8_t flag); ``` 动态: ```c rt_sem_t rt_sem_create(const char *name, rt_uint32_t value, rt_uint8_t flag); ``` 脱离\删除: 静态: ```c rt_err_t rt_sem_detach(rt_sem_t sem); ``` 动态: ```c rt_err_t rt_sem_delete(rt_sem_t sem); ``` 获取: ```c rt_err_t rt_sem_take (rt_sem_t sem, rt_int32_t time); ``` 无等待获取: ```c rt_err_t rt_sem_trytake(rt_sem_t sem); ``` 释放: ```c rt_err_t rt_sem_release(rt_sem_t sem); ``` ### 互斥量 ![](https://www.rt-thread.org/document/site/rt-thread-version/rt-thread-standard/programming-manual/ipc1/figures/06mutex_ops.png) ### 邮箱 ![](https://www.rt-thread.org/document/site/rt-thread-version/rt-thread-standard/programming-manual/ipc2/figures/07mb_ops.png) ## 实操 说实话,RT-Thread这一套IPC机制具有较高的可玩性,可以实现非常复杂的操作。 这里我选择在老师的例程上进行修改,主要由于在课后尝试调试老师课上的代码出现了一些问题*绝对不是因为太懒了*。 首先展示原程序: ```c #include
#include
#include
#ifndef RT_USING_NANO #include
#endif /* RT_USING_NANO */ #define PIN_KEY0 GET_PIN(C, 0) // PC0: KEY0 --> KEY #define GPIO_LED_B GET_PIN(F, 11) #define GPIO_LED_R GET_PIN(F, 12) #define THREAD_PRIORITY 25 #define THREAD_STACK_SIZE 1024 #define THREAD_TIMESLICE 5 static rt_thread_t tid1 = RT_NULL; static rt_thread_t tid2= RT_NULL; static rt_sem_t dynamic_sem = RT_NULL; static void key_name_entry(void *parameter); static void led_name_entry(void *parameter); int main(void) { rt_pin_mode(GPIO_LED_R, PIN_MODE_OUTPUT); rt_pin_mode(PIN_KEY0, PIN_MODE_INPUT_PULLUP); dynamic_sem = rt_sem_create("dsem", 0, RT_IPC_FLAG_PRIO); if (dynamic_sem == RT_NULL) { rt_kprintf("create dynamic semaphore failed.\n"); return -1; } else { rt_kprintf("create done. dynamic semaphore value = 0.\n"); } tid1 = rt_thread_create("key_thread", key_name_entry, RT_NULL, THREAD_STACK_SIZE, THREAD_PRIORITY, THREAD_TIMESLICE); if (tid1 != RT_NULL) rt_thread_startup(tid1); tid2 = rt_thread_create("led_thread", led_name_entry, RT_NULL, THREAD_STACK_SIZE, THREAD_PRIORITY, THREAD_TIMESLICE); if (tid2 != RT_NULL) rt_thread_startup(tid2); } static void key_name_entry(void *parameter) { rt_uint32_t count = 0; while (1) { if (rt_pin_read(PIN_KEY0) == PIN_LOW) { rt_thread_mdelay(100); if (rt_pin_read(PIN_KEY0) == PIN_LOW) { rt_kprintf("KEY0 pressed!\r\n"); rt_sem_release(dynamic_sem); } } rt_thread_mdelay(10); } } static void led_name_entry(void *parameter) { rt_uint32_t result = 0; while (1) { result = rt_sem_take(dynamic_sem, RT_WAITING_FOREVER); if (result == RT_EOK) { rt_kprintf("LED HIGH\r\n"); rt_pin_write(GPIO_LED_R, PIN_HIGH); } else { rt_kprintf("LED HLOW\r\n"); rt_pin_write(GPIO_LED_R, PIN_LOW); } } } ``` 上述程序的目标是实现在按下按钮后实现LED灯关闭,然而在实操过程中发现并不能自行恢复到点亮的状态,推测问题存在于第78行如果无法获取信号量,线程会直接被休眠。 第一次~~魔改~~修改我直接将信号量机制换成了mailbox机制,程序如下: ```c #include
#include
#include
#ifndef RT_USING_NANO #include
#endif /* RT_USING_NANO */ #define PIN_KEY0 GET_PIN(C, 0) #define GPIO_LED_B GET_PIN(F, 11) #define GPIO_LED_R GET_PIN(F, 12) #define THREAD_PRIORITY 25 #define THREAD_STACK_SIZE 1024 #define THREAD_TIMESLICE 5 #define KEY_ON 1 #define KEY_OFF 0 static struct rt_mailbox mb; static char mb_pool[128]; static rt_thread_t tid1 = RT_NULL; static rt_thread_t tid2= RT_NULL; static void key_name_entry(void *parameter); static void led_name_entry(void *parameter); int led_key_mb(void) { rt_pin_mode(GPIO_LED_R, PIN_MODE_OUTPUT); rt_pin_mode(PIN_KEY0, PIN_MODE_INPUT_PULLUP); rt_err_t result; result = rt_mb_init(&mb, "mbt", &mb_pool[0], sizeof(mb_pool) / sizeof(rt_ubase_t), RT_IPC_FLAG_PRIO); if (result != RT_EOK) { rt_kprintf("init mailbox failed.\n"); return -1; } tid1 = rt_thread_create("key_thread", key_name_entry, RT_NULL, THREAD_STACK_SIZE, THREAD_PRIORITY, THREAD_TIMESLICE); if (tid1 != RT_NULL) rt_thread_startup(tid1); tid2 = rt_thread_create("led_thread", led_name_entry, RT_NULL, THREAD_STACK_SIZE, THREAD_PRIORITY, THREAD_TIMESLICE); if (tid2 != RT_NULL) rt_thread_startup(tid2); } static void key_name_entry(void *parameter) { while (1) { rt_mb_send(&mb,KEY_OFF); if (rt_pin_read(PIN_KEY0) == PIN_LOW) { rt_thread_mdelay(100); if (rt_pin_read(PIN_KEY0) == PIN_LOW) { rt_kprintf("KEY0 pressed!\r\n"); rt_mb_urgent(&mb,KEY_ON); rt_thread_mdelay(5); } } rt_thread_mdelay(1000); } } static void led_name_entry(void *parameter) { rt_ubase_t rec=KEY_OFF; while (1) { rt_mb_recv(&mb,&rec,RT_WAITING_FOREVER); if (rec == KEY_ON) { rt_kprintf("LED HIGH\r\n"); rt_pin_write(GPIO_LED_R, PIN_HIGH); } else { rt_kprintf("LED HLOW\r\n"); rt_pin_write(GPIO_LED_R, PIN_LOW); } } } MSH_CMD_EXPORT(led_key_mb,"test") ``` 这次修改后运行结果在预料之内,但是仍不能灵活的变灯。因此决定换回信号量模式并加以修改: ```c #include
#include
#include
#ifndef RT_USING_NANO #include
#endif /* RT_USING_NANO */ #define PIN_KEY0 GET_PIN(C, 0) // PC0: KEY0 --> KEY #define GPIO_LED_B GET_PIN(F, 11) #define GPIO_LED_R GET_PIN(F, 12) #define THREAD_PRIORITY 25 #define THREAD_STACK_SIZE 1024 #define THREAD_TIMESLICE 5 static rt_thread_t tid1 = RT_NULL; static rt_thread_t tid2= RT_NULL; static rt_sem_t dynamic_sem = RT_NULL; static void key_name_entry(void *parameter); static void led_name_entry(void *parameter); int led_key_fix(void) { rt_pin_mode(GPIO_LED_R, PIN_MODE_OUTPUT); rt_pin_mode(PIN_KEY0, PIN_MODE_INPUT_PULLUP); dynamic_sem = rt_sem_create("dsem", 0, RT_IPC_FLAG_PRIO); if (dynamic_sem == RT_NULL) { rt_kprintf("create dynamic semaphore failed.\n"); return -1; } else { rt_kprintf("create done. dynamic semaphore value = 0.\n"); } tid1 = rt_thread_create("key_thread", key_name_entry, RT_NULL, THREAD_STACK_SIZE, THREAD_PRIORITY, THREAD_TIMESLICE); if (tid1 != RT_NULL) rt_thread_startup(tid1); tid2 = rt_thread_create("led_thread", led_name_entry, RT_NULL, THREAD_STACK_SIZE, THREAD_PRIORITY, THREAD_TIMESLICE); if (tid2 != RT_NULL) rt_thread_startup(tid2); } static void key_name_entry(void *parameter) { rt_uint32_t count = 0; while (1) { if (rt_pin_read(PIN_KEY0) == PIN_LOW) { rt_thread_mdelay(100); if (rt_pin_read(PIN_KEY0) == PIN_LOW) { rt_kprintf("KEY0 pressed!\r\n"); rt_sem_release(dynamic_sem); } } rt_thread_mdelay(10); } } static void led_name_entry(void *parameter) { rt_uint32_t result = 0; while (1) { result = rt_sem_trytake(dynamic_sem); if (result == RT_EOK) { rt_kprintf("LED HLOW\r\n"); rt_pin_write(GPIO_LED_R, PIN_LOW); } else { rt_kprintf("LED HIGH\r\n"); rt_pin_write(GPIO_LED_R, PIN_HIGH); } } } MSH_CMD_EXPORT(led_key_fix, led_key_fix example); ``` 改版的实际体验比较糟糕,串口一直在输出`LED HIGH`,但是按下按键可以发现LED一直在不断的闪烁,说明方向正确。但实际问题在研究了一个下午之后实在没有找到。如果大佬们发现了记得告诉我。最后我选择了这个写法: ```c #include
#include
#include
#ifndef RT_USING_NANO #include
#endif /* RT_USING_NANO */ #define PIN_KEY0 GET_PIN(C, 0) #define GPIO_LED_B GET_PIN(F, 11) #define GPIO_LED_R GET_PIN(F, 12) #define THREAD_PRIORITY 25 #define THREAD_STACK_SIZE 1024 #define THREAD_TIMESLICE 5 static rt_thread_t tid1 = RT_NULL; static rt_thread_t tid2 = RT_NULL; static rt_sem_t dynamic_sem = RT_NULL; static void key_name_entry(void *parameter); static void led_name_entry(void *parameter); int led_key_try(void) { dynamic_sem = rt_sem_create("dsem", 0, RT_IPC_FLAG_PRIO); if (dynamic_sem == RT_NULL) { rt_kprintf("create dynamic semaphore failed.\n"); return -1; } else { rt_kprintf("create done. dynamic semaphore value = 0.\n"); } rt_pin_mode(GPIO_LED_R, PIN_MODE_OUTPUT); rt_pin_mode(PIN_KEY0, PIN_MODE_INPUT_PULLUP); tid1 = rt_thread_create("key_thread", key_name_entry, RT_NULL, THREAD_STACK_SIZE, THREAD_PRIORITY, THREAD_TIMESLICE); if (tid1 != RT_NULL) rt_thread_startup(tid1); tid2 = rt_thread_create("led_thread", led_name_entry, RT_NULL, THREAD_STACK_SIZE, THREAD_PRIORITY, THREAD_TIMESLICE); if (tid2 != RT_NULL) rt_thread_startup(tid2); } static void key_name_entry(void *parameter) { while (1) { if (rt_pin_read(PIN_KEY0) == PIN_LOW) { rt_thread_mdelay(100); if (rt_pin_read(PIN_KEY0) == PIN_LOW) { rt_kprintf("KEY0 pressed!\r\n"); rt_sem_release(dynamic_sem); } } else{ rt_pin_write(GPIO_LED_R, PIN_LOW); } } } static void led_name_entry(void *parameter) { rt_ubase_t result=0; while (1) { result=rt_sem_take(dynamic_sem,RT_WAITING_FOREVER); if (result==RT_EOK) { rt_kprintf("LED HIGH\r\n"); rt_pin_write(GPIO_LED_R, PIN_HIGH); } } } MSH_CMD_EXPORT(led_key_try,"test"); ``` 其实这个写法并不完美,违背了最初使用多线程的初衷:各线程各干各的互不干扰。所以如果各位大佬们有更好的方法可以尽快告诉我。
0
条评论
默认排序
按发布时间排序
登录
注册新账号
关于作者
Lzh200
这家伙很懒,什么也没写!
文章
4
回答
0
被采纳
0
关注TA
发私信
相关文章
推荐文章
1
RT-Thread应用项目汇总
2
玩转RT-Thread系列教程
3
国产MCU移植系列教程汇总,欢迎查看!
4
机器人操作系统 (ROS2) 和 RT-Thread 通信
5
五分钟玩转RT-Thread新社区
6
【技术三千问】之《玩转ART-Pi》,看这篇就够了!干货汇总
7
关于STM32H7开发板上使用SDIO接口驱动SD卡挂载文件系统的问题总结
8
STM32的“GPU”——DMA2D实例详解
9
RT-Thread隐藏的宝藏之completion
10
【ART-PI】RT-Thread 开启RTC 与 Alarm组件
热门标签
RT-Thread Studio
串口
Env
LWIP
SPI
AT
Bootloader
Hardfault
CAN总线
FinSH
ART-Pi
USB
DMA
文件系统
RT-Thread
SCons
RT-Thread Nano
线程
MQTT
STM32
RTC
FAL
rt-smart
ESP8266
I2C_IIC
WIZnet_W5500
UART
ota在线升级
PWM
cubemx
freemodbus
flash
packages_软件包
BSP
潘多拉开发板_Pandora
定时器
ADC
GD32
flashDB
socket
中断
Debug
编译报错
msh
SFUD
keil_MDK
rt_mq_消息队列_msg_queue
ulog
C++_cpp
at_device
本月问答贡献
踩姑娘的小蘑菇
7
个答案
3
次被采纳
a1012112796
13
个答案
2
次被采纳
张世争
9
个答案
2
次被采纳
rv666
5
个答案
2
次被采纳
用户名由3_15位
11
个答案
1
次被采纳
本月文章贡献
程序员阿伟
9
篇文章
2
次点赞
hhart
3
篇文章
4
次点赞
大龄码农
1
篇文章
5
次点赞
RTT_逍遥
1
篇文章
2
次点赞
ThinkCode
1
篇文章
1
次点赞
回到
顶部
发布
问题
投诉
建议
回到
底部