版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
1、温州大学物理与电子信息工程学院 杨卫波嵌入式系统原理与应用第12章 任务间通信与同步*嵌入式系统原理与应用2pRTX配置下是有并发的,但任一个时刻点上只有一个程序在处理机上运行。实际上并行是会涉及很多问题的,如多人同时在干活,他们需要共享到一些资源,那就涉及到沟通协调的额外付出了。pRTX的进程间通讯主要依赖于四种机制:事件(Event)、互斥锁(Mutex)、信号量(Semaphore)、和邮箱(Mailbox)。前三种机制侧重进程间的同步,邮箱侧重进程间的数据通讯。前言任务间的同步任务间的同步任务间的同步 - 各任务运行的先后、触发等关系备注:数据采集任务A 和数据处理任务B 之间存在同步
2、关系任务间的互斥任务间的互斥任务间的互斥 - 共享资源的申请使用备注:数据采集任务A 和数据处理任务B 之间存在互斥关系12.1 几个重要的概念p临界段:代码的临界段也称为临界区,一旦这部分代码开始执行,则不允许任何中断打断。为确保临界段代码的执行不被中断,在进入临界段之前须关中断,而临界段代码执行完毕后,要立即开中断。 p由于Cortex-M3/M4的RTX内核库中没有关闭中断的操作,也就是说 RTX 的源码中不存在临界段。p用户写应用的时候也有临界段的问题,比如以下两种: u读取或者修改变量(特别是任务间通信的全局变量)的代码,一般来说这是最常见的临界代码。u调用公共函数的代码,特别是不可
3、重入的函数,如果多个任务都访问这个函数,结果是可想而知的。 几个重要的概念p中断锁:因为Cortex-M3/M4的RTX源码中没有关闭中断的操作,所以也就没有提供开关中断函数。p用户在自己的应用代码采用裸机时如何开关中断的,在使用了RTX后仍然使用以前的开关中断函数即可。任务锁p任务锁:为了防止当前任务的执行被其它高优先级的任务打断而提供的锁机制就是任务锁任务锁。实现任务锁可以通过给调度器加锁或者直接关闭RTOS内核定时器(就是前面一直说的系统滴答定时器)来实现。 u通过给调度器加锁实现给调度器加锁的话,就无法实现任务切换,高优先级任务也就无法抢占低优先级任务的执行,同时高优先级任务也是无法向
4、低优先级任务切换的。这种方式只是禁止了调度器工作,并没有关闭任何中断。u通过关闭 RTOS 内核定时器实现关闭了 RTOS 内核定时器的话,也就关闭了通过 RTOS 内核定时器中断实现任务切换的功能,因为在退出定时器中断时需要检测当前需要执行的最高优先级任务,如果有高优先级任务就绪的话需要做任务切换。RTX 操作系统是采用的这种方式实现任务锁的。12.2 RTX 任务锁的实现函数原型: voidvoid tsk_lock ( tsk_lock (voidvoid); ); 函数描述:禁止 RTX 内核定时器中断,因此也就禁止了任务切换。 注意以下问题: p函数 tsk_lock 不支持嵌套调用
5、;p不允许在中断服务程序中调用 tsk_lock;pRTX 内核定时器被关闭期间,RTX 内核任务调度器和需要时间片调度的任务被阻塞。p设置的任务延迟时间不再工作。p因此,强烈建议关 RTX 内核定时器中断的时间越短越好。RTX 任务锁的实现函数原型: voidvoid tsk_unlock ( tsk_unlock (voidvoid); ); 函数描述: 用于使能 RTX 内核定时器中断,因此也就重新开启任务切换。tsk_unlock 一定要跟 tsk_lock 配套使用。 使用这个函数要注意以下问题:函数 tsk_lock 不支持嵌套调用;不允许在中断服务程序中调用 tsk_lock。使
6、用举例: #include#include voidvoid protect_critical_op() protect_critical_op() tsk_lock(); tsk_lock(); do_critical_op(); do_critical_op(); tsk_unlock();tsk_unlock(); 12.2.3 例程说明实验内容: 1. K1 按键按下,串口打印。2. 在调用 printf 函数的地方都加上任务锁,防止多个任务调用此函数造成冲突,以至于串口打印出现乱码。3. 各个任务实现的功能如下: AppTaskUserIF任务 :按键消息处理。 AppTaskLED
7、 任务:LED 闪烁,并串口打印任务正在运行。 AppTaskMsgPro 任务:消息处理,用作 LED 闪烁和串口打印任务正在运行。 AppTaskStart 任务:启动任务,最高优先级任务,实现按键扫描。RTX配置四个 RTX 任务的实现_task _task voidvoid AppTaskMsgPro( AppTaskMsgPro(voidvoid) ) whilewhile (1) (1) bsp_LedToggle(1); bsp_LedToggle(1); bsp_LedToggle(4);bsp_LedToggle(4);/ /* * 开启任务锁开启任务锁 */tsk_lock
8、();tsk_lock();printf(printf( 任务任务AppTaskMsgPro正在运行正在运行rn););/ /* * 关闭任务锁关闭任务锁 */tsk_unlock();tsk_unlock();os_dly_wait(300);os_dly_wait(300); RTX 任务调试信息12.3 事件标志组事件标志组是实现多任务同步的有效机制之一。初学者会问采用事件标志组多麻烦,搞个全局变量不是更简单,其实不然。在裸机编程时,使用全局变量的确比较方便,但相比事件标志组主要有如下三个问题: p使用事件标志组可以让 RTOS 内核有效的管理任务,全局变量是无法做到的,任务的超时等机制
9、需要用户自己去实现。p使用了全局变量就要防止多任务的访问冲突,使用事件标志组已经处理好了这个问题。用户无需担心。 p使用事件标志组可以有效的解决中断服务程序和任务之间的同步问题。12.3.1 事件标志组的实现创建 2 个任务 Task1 和 Task2,运行过程描述如下: p任务 Task1 运行过程中调用函数 os_evt_wait_and,等待事件标志位被设置,任务 Task1 由运行态进入到挂起态p任务 Task2 设置了任务 Task1 的事件标志,任务 Task1 由挂起态进入到就绪态,在调度器的作用下由就绪态又进入到运行态。各个任务之间使用事件标志组实现任务的通信或者同步。RTX
10、每个任务创建的时候,会自动创建 16 个事件标志,事件标志被存储到每个任务的任务控制块中,即每个任务支持 16 个事件标志。12.3.2 中断方式事件标志组的实现 指中断函数和 RTX 任务之间使用事件标志运行条件:创建 1 个任务和一个串口接收中断运行过程描述如下: p任务 Task1 运行过程中调用函数 os_evt_wait_and,等待事件标志位被设置,任务 Task1 由运行态进入到挂起态pTask1 挂起的情况下,串口接收到数据进入到了串口中断服务程序,在串口中断服务程序中设置 Task1 的事件标志,任务 Task1 由挂起态进入到就绪态,在调度器的作用下由就绪态又进入到运行态。
11、12.3.2 中断方式事件标志组的实现 实际应用中,中断方式的消息机制切记注意以下四个个问题: p中断函数的执行时间越短越好,防止其它低于这个中断优先级的异常不能得到及时响应。p实际应用中,建议不要在中断中实现消息处理,用户可以在中断服务程序里面发送消息通知任务,在任务中实现消息处理,这样可以有效的保证中断服务程序的实时响应。同时此任务也需要设置为高优先级,以便退出中断函数后任务可以得到及时执行。p中断服务程序中一定要调用专用于中断的事件标志设置函数 isr_evt_setisr_evt_set。p在 RTX 操作系统中实现中断函数跟裸机编程是一样的。中断方式*嵌入式系统原理与应用18p节拍时
12、钟任务不仅仅在时钟节拍中断产生时运行,而且当一个中断调用isr_函数时也执行。这是因为中断不能使当前任务等待,因此也不能进行任务的切换。p中断可以产生事件、信号量或是消息(使用isr_库函数)来表明一个更高优先级的任务正在等待。p更高优先级的任务将抢占当前的任务,但必须在中断函数结束后才能进行。这样就强制产生在当前中断结束后运行的时钟节拍中断,而且激发任务调度。任务调度处理所有的任务,并将就绪的最高优先级的任务投入运行,最高优先级的任务因此可以持续的运行。*嵌入式系统原理与应用19PendSV中断12.3.3 事件标志组 API 函数使用如下6个函数可以实现 RTX 的事件标志组: pos_e
13、vt_clros_evt_clr();();pos_evt_getos_evt_get();();pos_evt_setos_evt_set();();pos_evt_wait_andos_evt_wait_and();();pos_evt_wait_oros_evt_wait_or();();pisr_evt_setisr_evt_set();();函数 os_evt_setvoidvoid os_evt_set ( U16 event_flags, /* 16位的事件标志设置位的事件标志设置 */ OS_TID task ); /* 要设置事件标志的任务要设置事件标志的任务ID */ 函数
14、描述:用于设置指定任务的事件标志。 p第1个参数表示16个可设置的事件标志位。因为RTX的每个任务创建时有16个可设置的事件标志,这里用 U16 类型的变量 event_flag 就可以表示,变量 event_flag 的某个位设置为 1,那么指定 RTX 任务的事件标志相应位就设置为 1。变量 event_flag 设置为 0 的位对 RTX 任务的事件标志相应位没有影响。比如设置变量 event_flag = 0 x0003 就表示将 RTX 任务事件标志的位 0 和位 1 设置为 1,其余位没有变化。p第 2 个参数是任务 ID。函数 os_evt_set使用这个函数要注意:此函数是用于
15、任务代码中调用的,故不可以在中断服务程序中调用此函数,中断服务程序中使用的是 isr_evt_set#include #include _task void task1 (_task void task1 (voidvoid) . ) . os_evt_set (0 x0003, tsk2); os_evt_set (0 x0003, tsk2); . . 函数 isr_evt_set voidvoid isr_evt_set ( U16 event_flags, /* 16位的事件标志设置位的事件标志设置 */ OS_TID task ); /* 要设置事件标志的任务要设置事件标志的任务ID
16、 */ 函数描述函数描述:用于设置指定任务的事件标志。 p第1个参数表示16个可设置的事件标志位。p第 2 个参数是任务 ID。使用这个函数要注意以下问题: 1.此函数是用于中断服务程序中调用的。2. 调用不能太频繁,太频繁的话会大大增加系统内核的开销,会造成事件标志得不到及时处理从而造成丢失事件标志的情况。#include #include voidvoid EXTI0_IRQHandler ( EXTI0_IRQHandler (voidvoid) . ) . isr_evt_set (0 x0003, tsk2); . isr_evt_set (0 x0003, tsk2); . 函数
17、os_evt_wait_andOS_RESULT os_evt_wait_and ( U16 wait_flags, /* 16位的事件标志等待位的事件标志等待 */ U16 timeout ); /* 超时时间设置超时时间设置 */ 函数描述函数描述:用于等待事件标志被设置。 p第1 个参数表示任务等待的事件标志位。因为 RTX 的每个任务创建时有 16 个可以设置的事件标志,这里用 U16 类型的变量 event_flag 就可以设置,变量 event_flag 的那位设置为 1,那么 RTX 任务的事件标志就等待那个位被设置为 1。而且要所有要求的位都被设置为 1 才可以。比如设置变量
18、event_flag = 0 x0003 就表示 RTX 任务在等待事件标志的位 0 和位 1 都被设置为 1。p第 2 个参数表示设在的等待时间,范围 0-0 xFFFF,当参数设置为 0-0 xFFFE 时,表示等这么多个时钟节拍,参数设置为 0 xFFFF 时表示无限等待直到事件标志满足要求。函数 os_evt_wait_andp函数返回 OS_R_EVT 表示等待的事件标志位都被设置了,也就是返回成功。返回 OS_R_TMO 表示超时。使用这个函数要注意以下问题: 1. 当要求的事件标志位都被设置为 1 时或者设置的超时时间溢出时,函数 os_evt_wait_and 才会返回。 2.
19、 如果函数 os_evt_wait_and 返回前所要求的事件标志位都设置了,那么此函数会在返回前将相应的事件标志位清零,其它位不受此影响。函数os_evt_wait_oros_evt_wait_or(U16wait_flags,U16timeout);与事件等待,参数意义与和事件等待相同。这两个操作的差别在于(其实应该比较一目了然)对于和事件等待而言,所有标示都满足了,进程才会被唤醒,对于与事件等待而言,任一标示状态满足了就会唤醒进程。 例子:os_evt_wait_and (0 xABCD, 0 xFFFF);os_evt_wait_or (0 xABCD, 0 xFFFF);p第一个语句
20、的话,进程会等待15,13,11,9,8,7,6,3,2,0位(0 xABCD)的标示全部被设为1时才会被唤醒;p第二个语句而言,15,13,11,9,8,7,6,3,2,0位中的任一一位标示被设为1,进程就会被唤醒。使用举例#include#include #define#define BIT_0BIT_0 (1 0) (1 0) #define#define BIT_1BIT_1 (1 1) (1 1) #define#define BIT_ALLBIT_ALL ( (BIT_0BIT_0 | | BIT_1BIT_1) ) _task _task voidvoid AppTaskMsgP
21、ro( AppTaskMsgPro(voidvoid) ) OS_RESULT xResult;OS_RESULT xResult;constconst uint16_t usMaxBlockTime = 500; uint16_t usMaxBlockTime = 500; / /* * 延迟周期延迟周期 */whilewhile (1) (1) xResult = os_evt_wait_and(xResult = os_evt_wait_and(BIT_ALLBIT_ALL, usMaxBlockTime);, usMaxBlockTime);switchswitch (xResult)
22、 (xResult) casecase OS_R_EVT: OS_R_EVT:/ /* * 接收到接收到bit1和和bit0都被设置的消息都被设置的消息 */printf(printf( 接收到接收到bit0和和bit1都被设置的消息都被设置的消息rn););breakbreak; ;casecase OS_R_TMO: OS_R_TMO: / /* * 超时超时 */bsp_LedToggle(1);bsp_LedToggle(1);bsp_LedToggle(4);bsp_LedToggle(4);breakbreak; ;defaultdefault: : / /* * 其他值不处理其他
23、值不处理 */breakbreak; ; *嵌入式系统原理与应用28os_evt_get()12.3.4 例程说明(任务间通信)内容: 1. K1 按键按下,串口打印。2. K2 键按下,直接发送事件标志给任务AppTaskMsgPro,设置 bit0 3. K3 键按下,直接发送事件标志给任务 AppTaskMsgPro,设置 bit1 4. 任务 AppTaskMsgPro 只有接收到 bit0 和 bit1 都被设置了才执行串口打印信息。各个任务实现的功能如下: AppTaskUserIF 任务:按键消息处理。 AppTaskLED 任务:LED 闪烁。 AppTaskMsgPro 任务
24、:消息处理,等待任务 AppTaskUserIF 发来的事件标志。 AppTaskStart 任务 :启动任务,最高优先级任务,实现按键扫描。 RTX 配置RTX 任务调试信息Event Value:任务 AppTaskMsgPro 当前的事件标志数值。Event Mask:任务 AppTaskMsgPro 等待的事件标志数值。12.3.4 例程说明(中断方式通信) 内容: 1. K1 按键按下,串口打印。2. K2键按下,启动单次定时器中断,50ms后在定时器中断给任务AppTaskMsgPro发送事件标志,设置 bit0。3. K3键按下,启动单次定时器中断,50ms后在定时器中断给任务A
25、ppTaskMsgPro发送事件标志,设置 bit1。 4. 任务 AppTaskMsgPro 只有接收到 bit0 和 bit1 都被设置了才执行串口打印信息。各个任务实现的功能如下: AppTaskUserIF 任务:按键消息处理。 AppTaskLED 任务:LED 闪烁。 AppTaskMsgPro 任务 :消息处理,等待定时器中断发来的事件标志。 AppTaskStart 任务 :启动任务,也是最高优先级任务,这里实现按键扫描。*嵌入式系统原理与应用33#define#define EVT_KEYEVT_KEY 0 x0001 0 x0001OS_TID pr_task;OS_TID
26、 pr_task;intint num_ints; num_ints;/ /* *- External 0 Interrupt Service Routine- External 0 Interrupt Service Routine-* */ /voidvoid ext0_int( ext0_int(voidvoid) _irq ) _irq isr_evt_set(isr_evt_set(EVT_KEYEVT_KEY, pr_task); , pr_task); / /* * Send event to process_task Send event to process_task* */
27、 /. _task _task voidvoid process_task( process_task(voidvoid) ) num_ints = 0;num_ints = 0;whilewhile (1) (1) os_evt_wait_or(os_evt_wait_or(EVT_KEYEVT_KEY, 0 xffff);, 0 xffff);num_ints+;num_ints+; voidvoid init_task( init_task(voidvoid) _task ) _task .pr_task = os_tsk_create(process_task,100);pr_task
28、 = os_tsk_create(process_task,100);os_tsk_delete_self(); os_tsk_delete_self(); / /* * Terminate this task Terminate this task * */ / RTX核中使用中断任务process_task仅是记录发生中断的次数*嵌入式系统原理与应用34分析程序的功能#include RTL.hOS_TID id1, id2;_task void task1 (void);_task void task2 (void);_task void task1 (void) id1 = os_ts
29、k_self (); id2 = os_tsk_create (task2, 0); for (;) os_evt_set (0 x0004, id2); os_evt_wait_or (0 x0004, 0 xffff); os_dly_wait (5); _task void task2 (void) for (;) os_evt_wait_or (0 x0004, 0 xffff); os_dly_wait (2); os_evt_set (0 x0004, id1); int main (void) os_sys_init (task1);12.4 信号量信号量(semaphores)
30、是20世纪60年代中期Edgser Dijkstra发明的。最初目的是为了给共享资源建立一个表示该共享资源被占用情况的标志。当一个任务在访问共享资源前,先对这个标志进行查询,在了解资源被占用的情况后,再来决定自己的行为。 如有个 30 人的电脑机房,创建信号量的初始化值是30,表示30 个可用资源。要求一个同学使用一台电脑,有一个同学使用一台电脑,信号量的数值就减一。当信号量的数值为 0时,还有同学没有电脑可以使用,那么这个同学就得等待,直到有同学离开,有一个同学离开,那么信号量的数值就加 1,依次类推。刚才没有电脑用的同学此时就有电脑可以用了。这么一个过程就是使用信号量来管理共享资源的过程。
31、 12.4 信号量平时使用信号量主要实现以下两个功能: p两个任务或者中断函数跟任务之间的同步功能,和事件标志组是类似的。其实就是共享资源为 1 的时候。p多个共享资源的管理,就像上面机房上机的例子。 简单说来,信号量有三个部分:u第一部分就是一个代币容器(一个整形变量),记录着可用的资源数;u第二部分就是取用资源(P,尝试的荷兰语)的操作;u第三个就是还回资源(V, 增加的荷兰语)的操作。当资源数量为0时任何取用资源的操作都会被阻断,直到资源数量增加。12.4.2 RTX 任务间信号量的实现指各个任务之间使用信号量实现任务的同步或者资源共享功能。运行过程描述如下: pTask1运行中调用os
32、_sem_wait获取信号量资源,如果信号量没有被Task2占用,Task1 将直接获取资源。如果信号被 Task2 占用,Task1 将由运行态转到挂起状态,等待资源可用。一旦获取了资源并使用完毕后会通过函数 os_sem_send 释放掉资源pTask2运行中调用os_sem_wait获取信号量资源,如果信号量没有被任务Task2占用,Task1将直接获取资源。如果信号被Task2 占用,Task1将由运行态转到挂起状态,等待资源可以。一旦获取了资源并使用完毕后会通过函数 os_sem_send 释放掉资源。运行条件:创建 2 个任务 Task1 和 Task2,创建信号量可用资源为 1。
33、12.4.3 中断方式信号量的实现指中断函数和 RTX 任务之间使用信号量,主要是用于实现任务同步。p创建 1 个任务 Task1 和一个串口接收中断。p信号量的初始值为 0,串口中断调用函数 isr_sem_send 释放信号量,任务 Task1 调用函数 os_sem_wait 获取信号量资源。运行过程描述p任务 Task1 运行过程中调用函数 os_sem_wait,由于信号量的初始值是 0,没有信号量资源可用,任务 Task1 由运行态进入到挂起态。pTask1 挂起的情况下,串口接收到数据进入到了串口中断服务程序,在串口中断服务程序中调用函数 isr_sem_send 释放信号量资源
34、,信号量数值加 1,此时信号量计数值为 1,任务 Task1 由挂起态进入到就绪态,在调度器的作用下由就绪态又进入到运行态,任务 Task1 获得信号量后,信号量数值减1,此时信号量计数值又变成了 0。p再次循环执行时,任务 Task1 调用函数 os_sem_wait 由于没有资源可用再次进入到挂起态,等待串口释放信号量资源,如此往复循环。注意以下四个问题p中断函数的执行时间越短越好,防止其它低于这个中断优先级的异常不能得到及时响应。p实际应用中,建议不要在中断中实现消息处理,用户可以在中断服务程序里面发送消息通知任务,在任务中实现消息处理,这样可以有效的保证中断服务程序的实时响应。同时此任
35、务也需要设置为高优先级,以便退出中断函数后任务可以得到及时执行。p中断服务程序中一定要调用专用于中断的信号量设置函数 isr_sem_send。p在 RTX 操作系统中实现中断函数和裸机编程是一样的。12.4.4 信号量 API 函数使用如下4个函数可以实现 RTX 的信号量: pos_sem_initos_sem_initpos_sem_sendos_sem_sendpisr_sem_sendisr_sem_sendpu os_sem_wait u os_sem_wait 函数 os_sem_initvoid os_sem_init ( OS_ID semaphore, /* os_sem类
36、型变量类型变量 */ U16 token_count ); /* 信号量初始值信号量初始值 */ 函数描述函数描述:用于信号量的初始化并设置初始值 p第1个参数填写数据类型为OS_SEM的变量,同时也作为ID标识;p第2个参数是信号量初始值,也就是可用资源个数。使用举例使用举例: #include #include OS_SEM semaphore; OS_SEM semaphore; static void AppObjCreate(void)static void AppObjCreate(void) /* 创建信号量计数值是创建信号量计数值是0, 用于任务同步用于任务同步 */os_se
37、m_init(&semaphore, 0);os_sem_init(&semaphore, 0); 为什么变量semaphore 前面加一个取地址符&?函数 os_sem_sendOS_RESULT os_sem_send ( OS_ID semaphore ); /* OS_SEM类型变量类型变量 */ 函数描述:函数描述:用于释放信号量,调用后信号量计数值加 1。 p第 1 个参数参数填写数据类型为 OS_SEM 的变量,同时也作为 ID 标识。p返回值永远是 OS_R_OK。p使用此函数前一定要调用函数 os_sem_init 进行初始化。使用举例:使用举例: #include #inc
38、lude OS_SEM semaphore; OS_SEM semaphore; _task void task1 (void) . _task void task1 (void) . os_sem_init (&semaphore, 0); os_sem_init (&semaphore, 0); os_sem_send (&semaphore); . os_sem_send (&semaphore); . 函数 isr_sem_sendvoid isr_sem_send ( OS_ID semaphore ); /* OS_SEM类型变量类型变量 */ 函数描述:函数描述:用于释放信号量,
39、调用后信号量计数值加 1。 p第 1 个参数参数填写数据类型为 OS_SEM 的变量,同时也作为 ID 标识。p使用此函数前一定要调用函数 os_sem_init 进行初始化。使用举例:使用举例: #include #include OS_SEM semaphore; OS_SEM semaphore; void EXTI0_IRQHandler (void) . void EXTI0_IRQHandler (void) . isr_sem_send (&semaphore1); . isr_sem_send (&semaphore1); . 函数 os_sem_waitOS_RESULT o
40、s_sem_wait ( OS_ID semaphore, /* OS_SEM类型变量类型变量 */ U16 timeout ); /* 超时时间设置超时时间设置 */ 函数描述:用于获取信号量,如果当前的信号量计数值大于函数描述:用于获取信号量,如果当前的信号量计数值大于 0,那么调用函数,那么调用函数 os_sem_wait 后可以成功获取信号量,并将信号量的计数值减后可以成功获取信号量,并将信号量的计数值减 1。如果信号。如果信号量计数值等于量计数值等于 0,调用此函数的任务将由运行态转到挂起态,等待信号量资源,调用此函数的任务将由运行态转到挂起态,等待信号量资源可用,也就是等待信号量计
41、数值大于可用,也就是等待信号量计数值大于 0。 p第 1 个参数参数填写数据类型为 OS_SEM 的变量,同时也作为 ID 标识。p第 2 个参数表示设置的等待时间,范围 0-0 xFFFF,当参数设置为 0-0 xFFFE 时,表示等待这么多个时钟节拍,参数设置为 0 xFFFF 时表示无限等待直到有信号量资源可用。p函数返回 OS_R_SEM 表示函数设置的超时时间范围内收到信号量可用资源。函数返回 OS_R_TMO 表示超时。函数返回 OS_R_OK 表示无需等待,立即获得可用信号量资源。使用举例#include OS_SEM semaphore;_task void AppTaskMs
42、gPro(void) OS_RESULT xResult;const uint16_t usMaxBlockTime = 200; /* 延迟周期 */while (1)xResult = os_sem_wait(&semaphore, usMaxBlockTime);switch (xResult)case OS_R_OK:printf(无需等待接受到信号量同步信号rn);break;case OS_R_SEM:printf(信号量不可用,内收到信号量同步信号rn);break;case OS_R_TMO: /* 超时 */bsp_LedToggle(1);bsp_LedToggle(4);
43、 break; default:break;使用举例os_semsemaphore;os_semsemaphore;_taskvoid task1(_taskvoid task1(voidvoid) ) os_sem_init(semaphore, 0);os_sem_init(semaphore, 0);whilewhile (1) (1) Function1();Function1();os_sem_send(semaphore);os_sem_send(semaphore); _taskvoid task2(_taskvoid task2(voidvoid) ) whilewhile (
44、1) (1) os_sem_wait(semaphore, 0 xFFFF);os_sem_wait(semaphore, 0 xFFFF);Function2();Function2(); 信号量的作用就是确保在每一次调用Function2之前,Function1都有一次完整的调用。使用举例os_semArrived1, Arrived2;os_semArrived1, Arrived2;_task void task1(_task void task1(voidvoid) ) os_sem_init(Arrived1, 0);os_sem_init(Arrived1, 0);os_sem_
45、init(Arrived2, 0);os_sem_init(Arrived2, 0);whilewhile (1) (1) FunctionA1();FunctionA1();os_sem_send(Arrived1);os_sem_send(Arrived1);os_sem_wait(Arrived2, 0 xFFFF);os_sem_wait(Arrived2, 0 xFFFF);FunctionA2();FunctionA2(); _task void task2(_task void task2(voidvoid) ) whilewhile (1) (1) FunctionB1();F
46、unctionB1();os_sem_send(Arrived2);os_sem_send(Arrived2);os_sem_wait(Arrived1, 0 xFFFF);os_sem_wait(Arrived1, 0 xFFFF);FunctionB2();FunctionB2(); 目的是让FunctionA1,functionB1都完成以后,再执行FunctionA2和FunctionB2使用举例os_semMultiplex;os_semMultiplex;_taskvoid task(_taskvoid task(voidvoid) ) os_sem_init(Multiplex,
47、 5);os_sem_init(Multiplex, 5);whilewhile (1) (1) os_sem_wait(Multiplex, 0 xFFFF);os_sem_wait(Multiplex, 0 xFFFF);Function();Function();os_sem_send(Multiplex);os_sem_send(Multiplex); 保证最多只有五个进程能够同时调用Function()。12.4.5 实验例程说明(任务间通信)目的: 学习 RTX 的信号量实验内容: 1. K1 按键按下,串口打印。2. K2 键按下,直接发送信号量同步信号给任务 AppTaskMs
48、gPro。任务 AppTaskMsgPro 接收到消息后进行消息处理。3. 各个任务实现的功能如下: AppTaskUserIF 任务:按键消息处理。 AppTaskLED 任务:LED 闪烁。 AppTaskMsgPro 任务 :消息处理,等待任务 AppTaskUserIF 发来的信号量同步信号。 AppTaskStart 任务 :启动任务,也是最高优先级任务,这里实现按键扫描。 RTX 配置RTX 任务调试信息12.4 .5例程说明(中断方式通信)目的: 学习 RTX 的信号量(中断方式)实验内容: 1. K1 按键按下,串口打印。2. K2 键按下,启动单次定时器中断,50ms 后在定
49、时器中断给任务 AppTaskMsgPro 发送信号量同步信号。任务 AppTaskMsgPro 接收到消息后进行消息处理。 3. 各个任务实现的功能如下: AppTaskUserIF 任务 :按键消息处理。 AppTaskLED 任务:LED 闪烁。 AppTaskMsgPro 任务:消息处理,等待定时器中断发来的信号量同步信号。 AppTaskStart 任务:启动任务,最高优先级任务,实现按键扫描。定时器中断回调函数中发送信号量同步信号:定时器中断的初始化和中断函数在 bsp_timer.c 文件中实现, RTX 的信号量函数在中断服务程序中使用:staticstatic voidvoi
50、d TIM_CallBack1( TIM_CallBack1(voidvoid) ) / /* * 发送信号量同步信号发送信号量同步信号 */isr_sem_send(&semaphore);isr_sem_send(&semaphore); RTX 任务调试信息*嵌入式系统原理与应用56课堂作业建立两个任务 AppTaskPost 和 AppTaskPend。按键按下时任务 AppTaskPost 当每隔 200ms 向等待的任务 AppTaskPend 发送一个任务信号量,并通过串口打印出任务信号量的值。当按键松开时任务AppTaskPend每隔 200ms请求一个任务信号量,红色 LED
51、 闪烁表示请求到一个任务信号量,任务信号量的值减小,任务AppTaskPost通过串口打印任务信号量的值。当任务信号量的值减小到 0时,任务AppTaskPend请求不到任务信号量,红色LED不再闪烁。12.5 互斥信号量p互斥信号量就是信号量的一种特殊形式,也就是信号量初始值为 1 的情况。有些 RTOS 中也将信号量初始值设置为 1 的情况称之为二值信号量。为什么叫二值信号量呢?因为信号量资源被获取了,信号量值就是 0,信号量资源被释放,信号量值就是 1,把这种只有 0 和 1 两种情况的信号量称之为二值信号量。p互斥信号量的主要作用就是对资源实现互斥访问。运行条件p让两个任务 Task1
52、 和 Task2 都有运行串口打印 printf,对函数 printf 通过二值信号量实现互斥访问。如果不对函数 printf 进行互斥访问,串口打印容易出现乱码。p用信号量实现二值信号量只需将信号量的初始值设置为 1 即可。创建二值信号量创建二值信号量OS_SEM semaphore;OS_SEM semaphore;staticstatic voidvoid AppObjCreate( AppObjCreate(voidvoid) ) / /* * 创建二值信号量,实现对互斥资源的独享创建二值信号量,实现对互斥资源的独享 */os_sem_init(semaphore, 0);os_sem
53、_init(semaphore, 0); 二值信号量实现互斥访问_task _task voidvoid AppTaskLED( AppTaskLED(voidvoid) ) constconst uint16_t usFrequency = 1000; uint16_t usFrequency = 1000; os_itv_set(usFrequency);os_itv_set(usFrequency);whilewhile (1) (1) / /* * 永久等待互斥资源可用永久等待互斥资源可用 */os_sem_wait(&semaphore, 0 xffff);os_sem_wait(&
54、semaphore, 0 xffff);printf(printf( 任务任务AppTaskLED正在运行正在运行rn););/ /* * 释放互斥资源释放互斥资源 */os_sem_send(&semaphore);os_sem_send(&semaphore);/ /* * os_itv_wait os_itv_wait是绝对延迟,是绝对延迟,os_dly_wait是相对延迟。是相对延迟。*/os_itv_wait();os_itv_wait(); _task void AppTaskMsgPro(void)const uint16_t usFrequency = 1000; /* 延迟周
55、期 */os_itv_set(usFrequency); /* 设置延迟周期 */while (1)/* 永久等待互斥资源可以 */printf(任务AppTaskMsgPro正在运行rn);/* 释放互斥资源 */os_sem_send(&semaphore);/* os_itv_wait是绝对延迟,os_dly_wait是相对延迟。*/os_itv_wait();12.5.2 优先级翻转问题 p3 个任务 Task1,Task2 和 Task3,优先级分别为 3,2,1。pTask1和Task3采用二值信号实现互斥访问串口打印printf。p起初 Task3 通过二值信号量正在调用 pri
56、ntf,被任务 Task1 抢占,开始执行任务 Task1,也就是图的起始位置。优先级翻转问题pTask1 运行的过程需要调用printf,发现Task3 正在调用,Task1 会被挂起,等待 Task3 释放函数 printf。p在调度器的作用下,Task3 得到运行,Task3 运行的过程中,由于Task2 就绪,抢占了 Task3 的运行。优先级翻转问题就出在这里了,从任务执行的现象上看,高优先级任务 Task1 需要等待低优先级任务 Task2 执行完毕才有机会得到执行,这个与抢占式调度正好反了。pTask2 执行完毕后,Task3 恢复执行,Task3 释放互斥资源后,Task1得到
57、互斥资源,从而可以继续执行。优先级翻转问题(2)考虑三个进程,T1,T2,T3。T3的优先度大于T2,T2的优先度大于T1,采用的调度算法算法是优先度高的可以打断优先度低的进程,临界区等待可以阻断高优先度的进程。问题:进程3被什么阻断了?答:进程3因为要求资源R的临界区,而此时资源R临界区被进程1占用。所以,进程3实际上是被进程2阻断了,低优先度的进程阻断了高优先度的进程,这就是优先度翻转问题。优先度继承优先度继承的意思是,将临界区内的进程的优先度提高为该进程阻断的进程里的优先级最高进程的优先级。当进程3要求进入R临界区执行时,因为被进程1阻断了,进程1此时在临界区内,而且它阻断的是进程3,那
58、么此时它的优先度就会提升为进程3的优先度,也就是说继承了进程3的优先度。当退出临界执行区后,进程1优先度又会降为原先的优先度。这就很好地解决了高优先度进程被无条件阻断的问题死锁优先度继承的意思是,将临界区内的进程的优先度提高为该进程阻断的进程里的优先级最高进程的优先级。T1被创造T1要求进入资源A临界区T2被创建T2要讲求进入资源B临界区T2要求进入资源A临界区T1要求进入资源B临界区当进程2要求资源A临界区时,因为进程1正在资源A临界区内,所以其优先级继承了进程2的优先级,但进程1执行了一段时间后又要求进入资源B临界区以完成当前任务,退出临界区A。两个进程此时都在等待对方退出临界区,而自己却
59、不会主动退出自己占有的临界区,所以死锁。优先度天花板具体说来就是,为每个资源定义优先度天花板,资源的优先度天花板是所有可能要互斥使用该资源的进程的最高优先度。这种策略只允许满足一个条件的进程去进入临界区,该进程的优先度要大于(等于不足够)所有其他进程占用的资源的优先度天花板的最高优先度。同时保留优先度继承策略。当进程2想进入资源B临界区时,被阻断了,因为它优先度并不大于其他进程(进程1)占用资源(A)的优先度天花板(进程2的优先度),被进程1阻断,与此同时,进程1的优先度提升为进程2的优先度,继续执行,直到退出两个临界区。任务任务A申请共享资源申请共享资源S S是否被占用是否被占用任务任务A获
60、得共享资源获得共享资源S S被任务被任务B占用,任务占用,任务A被挂起被挂起 Priority(B)Priority( A)?提升提升B的优先级到的优先级到PIP B释放资源释放资源s并恢复原来的优先级并恢复原来的优先级 等待队列等待队列中优先级最高的任务获得中优先级最高的任务获得s任务任务A继续执行继续执行NYNY优先级继承的主要思想当高优先级任务因申请某共享资源失败被阻塞时,把当前拥有该资源的、且优先级较低的任务的优先级提升,提升的高度等于这个高优先级任务的优先级(可以指定一个PIP) 12.5.3 RTX 互斥信号量的实现RTX 互斥信号量是怎么实现的呢?其实相比二值信号量就是解决了一下
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 2024年度药品研发项目质量监督与医疗器械研发合作合同3篇
- 《湖南师范大学》课件
- 意义未明的单克隆免疫球蛋白病病因介绍
- 新疆警察学院《材料成型工艺与装备》2023-2024学年第一学期期末试卷
- 污水处理厂bot项目特许经营协议示范文本模板
- 梦幻卧室室内设计中的卧室布置技巧
- 浴场劳动合同范例
- 船舶拆解销毁合同范例
- 广州住宅合同范例
- 租摆养护合同范例
- GB/T 1038-2000塑料薄膜和薄片气体透过性试验方法压差法
- 马工程《教育学原理》课后习题讲解
- 茶艺表演费课件
- 创建电力优质工程策划及控制课件
- DBJ61-T 104-2015 陕西省村镇建筑抗震设防技术规程-(高清版)
- 测控电路第7章信号细分与辨向电路
- 外研版(三起)小学英语四年级上册教案(全册)
- 小学生体育学习评价表
- 哈尔滨工业大学信纸模版
- 餐饮店应聘人员面试测评表
- 踝关节扭伤.ppt
评论
0/150
提交评论