完善资料让更多小伙伴认识你,还能领取20积分哦, 立即完善>
转rtx操作系统
本章节开始讲解RTX的另一个重要的资源共享机制---互斥信号量(Mutex,即Mutual Exclusion的缩写)。注意,建议初学者学习完上个章节的信号量后再学习本章节的互斥信号量。 本章教程配套的例子含Cortex-M3内核的STM32F103和Cortex-M4内核的STM32F407。 15.1 互斥信号量 15.2 互斥信号量API函数 15.3 实验例程说明 15.4 总结 15.1 互斥信号量 15.1.1 互斥信号量的概念及其作用 互斥信号量就是信号量的一种特殊形式,也就是信号量初始值为1的情况。有些RTOS中也将信号量初始值设置为1的情况称之为二值信号量。为什么叫二值信号量呢?因为信号量资源被获取了,信号量值就是0,信号量资源被释放,信号量值就是1,把这种只有0和1两种情况的信号量称之为二值信号量。互斥信号量的主要作用就是对资源实现互斥访问。下面举一个通过二值信号量实现资源独享,即互斥访问的例子,让大家有一个形象的认识 |
|
相关推荐
|
|
运行条件:
u 让两个任务Task1和Task2都有运行串口打印printf,这里我们就对函数printf通过二值信号量实现互斥访问。如果不对函数printf进行互斥访问,串口打印容易出现乱码。 u 用信号量实现二值信号量只需将信号量的初始值设置为1即可。 代码实现: u 创建二值信号量 复制代码 OS_SEM semaphore; /* ********************************************************************************************************* * 函 数 名: AppObjCreate * 功能说明: 创建任务通信机制 * 形 参: 无 * 返 回 值: 无 ********************************************************************************************************* */ static void AppObjCreate (void) { /* 创建二值信号量,实现对互斥资源的独享 */ os_sem_init (semaphore, 0); } |
|
|
|
|
|
通过二值信号量实现对printf函数互斥访问的两个任务
复制代码 /* ********************************************************************************************************* * 函 数 名: AppTaskLED * 功能说明: 串口打印 * 形 参: 无 * 返 回 值: 无 * 优 先 级: 2 ********************************************************************************************************* */ __task void AppTaskLED(void) { const uint16_t usFrequency = 1000; /* 延迟周期 */ /* 设置延迟周期 */ os_itv_set(usFrequency); while(1) { /* 永久等待互斥资源可用 */ os_sem_wait (&semaphore, 0xffff); printf("任务AppTaskLED正在运行rn"); /* 释放互斥资源 */ os_sem_send (&semaphore); /* os_itv_wait是绝对延迟,os_dly_wait是相对延迟。*/ os_itv_wait(); } } /* ********************************************************************************************************* * 函 数 名: AppTaskMsgPro * 功能说明: 串口打印 * 形 参: 无 * 返 回 值: 无 * 优 先 级: 3 ********************************************************************************************************* */ __task void AppTaskMsgPro(void) { const uint16_t usFrequency = 1000; /* 延迟周期 */ /* 设置延迟周期 */ os_itv_set(usFrequency); while(1) { /* 永久等待互斥资源可以 */ os_sem_wait (&semaphore, 0xffff); printf("任务AppTaskMsgPro正在运行rn"); /* 释放互斥资源 */ os_sem_send (&semaphore); /* os_itv_wait是绝对延迟,os_dly_wait是相对延迟。*/ os_itv_wait(); } } 有了上面二值信号量的认识之后,互斥信号量跟二值信号量又有什么区别呢?互斥信号量可以防止优先级翻转,而二值信号量不支持,下面我们就讲解一下优先级翻转问题。 |
|
|
|
|
|
运行条件:
u 创建3个任务Task1,Task2和Task3,优先级分别为3,2,1。也就是Task1的优先级最高 u 任务Task1和Task3互斥访问串口打印printf,采用二值信号实现互斥访问。 u 起初Task3通过二值信号量正在调用printf,被任务Task1抢占,开始执行任务Task1,也就是上图的起始位置。 运行过程描述如下: u 任务Task1运行的过程需要调用函数printf,发现任务Task3正在调用,任务Task1会被挂起,等待Task3释放函数printf。 u 在调度器的作用下,任务Task3得到运行,Task3运行的过程中,由于任务Task2就绪,抢占了Task3的运行。优先级翻转问题就出在这里了,从任务执行的现象上看,任务Task1需要等待Task2执行完毕才有机会得到执行,这个与抢占式调度正好反了,正常情况下应该是高优先级任务抢占低优先级任务的执行,这里成了高优先级任务Task1等待低优先级任务Task2完成。所以这种情况被称之为优先级翻转问题。 u 任务Task2执行完毕后,任务Task3恢复执行,Task3释放互斥资源后,任务Task1得到互斥资源,从而可以继续执行。 上面就是一个产生优先级翻转问题的现象。 |
|
|
|
|
|
15.1.3 RTX互斥信号量的实现
RTX互斥信号量是怎么实现的呢?其实相比二值信号量就是解决了一下优先级翻转的问题。下面我们通过如下的框图来说明一下RTX互斥信号量的实现,让大家有一个形象的认识。 |
|
|
|
|
|
运行条件:
u 创建2个任务Task1和Task2,优先级分别为1和3,也就是任务Task2的优先级最高 u 任务Task1和Task2互斥访问串口打印printf。 u 使用RTX的互斥信号量实现串口打印printf的互斥访问。 运行过程描述如下: u 低优先级任务Task1执行过程中先获得互斥资源printf的执行。此时任务Task2抢占了任务Task1的执行,任务Task1被挂起。任务Task2得到执行。 u 任务Task2执行过程中也需要调用互斥资源,但是发现任务Task1正在访问,此时任务Task1的优先级会被提升到跟Task2同一个优先级,也就是优先级3,这个就是所谓的优先级继承(Priorityinheritance),这样就有效的防止了优先级翻转问题。任务Task2被挂起,任务Task1有新的优先级继续执行。 u 任务Task1执行完毕并释放互斥资源后,优先级恢复到原来的水平。由于互斥资源可以使用,任务Task2获得互斥资源后开始执行。 上面就是一个简单RTX互斥信号量的实现过程。 15.1.4 RTX中断方式互斥信号量的实现 互斥信号量仅支持用在RTX的任务中,中断函数中不可使用。 |
|
|
|
|
|
15.2 互斥信号量API函数
使用如下3个函数可以实现RTX的互斥信号量: u os_mut_init u os_mut_release u os_mut_wait 关于这3个函数的讲解及其使用方法可以看教程第3章3.3小节里面说的参考资料rlarm.chm文件 |
|
|
|
|
|
下面我们也对这3个函数依次进行讲解:
15.2.1 函数os_mut_init 函数原型: 复制代码 void os_mut_init ( OS_ID mutex ); /* OS_MUT类型变量 */ 函数描述: 函数os_mut_init用于互斥信号量的初始化并设置初始值。 u 第1个参数填写数据类型为OS_MUT的变量,同时也作为ID标识 使用这个函数要注意以下问题: 1. 函数的参数必须是OS_MUT类型的。 使用举例: 复制代码 #include static OS_MUT mutex; /* ********************************************************************************************************* * 函 数 名: AppObjCreate * 功能说明: 创建任务通信机制 * 形 参: 无 * 返 回 值: 无 ********************************************************************************************************* */ static void AppObjCreate (void) { /* 创建互斥信号量 */ os_mut_init (&mutex); } |
|
|
|
|
|
15.2.2 函数os_mut_wait
函数原型: 复制代码 OS_RESULT os_mut_wait ( OS_ID mutex, /* OS_MUT类型变量 */ U16 timeout ); /* 超时时间 */ 函数描述: 函数os_mut_wait用于获取互斥信号量资源,如果互斥资源可用,那么调用函数os_mut_wait后可以成功获取互斥资源,在此函数的源码将计数值加1(互斥信号量源码的实现上跟信号量不同)。如果互斥资源不可用,调用此函数的任务将由运行态转到挂起态,等待信号量资源可用,也就是计数值为0的时候。 如果一个低优先级的任务通过互斥信号量正在访问互斥资源,那么当一个高优先级的任务也通过互斥信号量访问这个互斥资源的话,会将这个低优先级任务的优先级提升到和高优先级任务一样的优先级,这就是所谓的优先级继承,通过优先级继承可以有效防止优先级翻转问题。当低优先级任务释放了互斥资源之后,重新恢复到原来的优先级。 u 第1个参数填写数据类型为OS_MUT的变量,同时也作为ID标识 u 第2个参数表示设在的等待时间,范围0-0xFFFF,当参数设置为0-0xFFFE时,表示等待这么多个时钟节拍,参数设置为0xFFFF时表示无限等待直到互斥资源可用。 u 函数返回OS_R_MUT表示函数设置的超时时间范围内收到互斥信号量可用资源。 函数返回OS_R_TMO表示超时。 函数返回OS_R_OK表示无需等待,立即获得互斥资源。 |
|
|
|
|
|
使用这个函数要注意以下问题:
1. 使用此函数前一定要调用函数os_mut_init进行初始化。 使用举例: 复制代码 #include static OS_MUT mutex; /* ********************************************************************************************************* * 函 数 名: AppTaskLED * 功能说明: LED闪烁,并串口打印任务正在运行 * 形 参: 无 * 返 回 值: 无 * 优 先 级: 2 ********************************************************************************************************* */ __task void AppTaskLED(void) { const uint16_t usFrequency = 1000; /* 延迟周期 */ /* 设置延迟周期 */ os_itv_set(usFrequency); while(1) { bsp_LedToggle(2); bsp_LedToggle(3); /* 永久等待互斥资源可以 */ os_mut_wait (&mutex, 0xffff); printf("任务AppTaskLED正在运行rn"); /* 释放互斥资源 */ os_mut_release (&mutex); /* os_itv_wait是绝对延迟,os_dly_wait是相对延迟。*/ os_itv_wait(); } } |
|
|
|
|
|
15.2.3 函数os_mut_release
函数原型: 复制代码 OS_RESULT os_mut_release ( OS_ID mutex ); /* OS_MUT类型变量 */ 函数描述: 函数os_mut_release用于释放互斥资源,调用此函数会将计数值减1。只有当计数值减到0的时候其它的任务才可以获取互斥资源。也就是说如果用户调用os_mut_wait和os_mut_release,需要配套使用。通过函数os_mut_wait实现互斥信号量计数值加1,通过函数os_mut_release实现互斥信号量计数值减1操作,这样的话,这两个函数可以实现嵌套调用,但是一定要保证成对调用,要不会造成互斥资源无法正确释放。 如果拥有互斥资源的任务的优先级被提升了,那么此函数会恢复任务以前的优先级。 u 第1个参数参数填写数据类型为OS_MUT的变量,同时也作为ID标识。 u 返回值OS_R_OK,表示互斥信号量成功释放。 返回值OS_R_NOR,表示互斥信号量的内部计数值已经是0或者调用此函数的任务不是互斥资源的拥有者。 |
|
|
|
|
|
使用这个函数要注意以下问题:
1. 使用此函数前一定要调用函数os_mut_init进行初始化。 使用举例: 复制代码 #include static OS_MUT mutex; /* ********************************************************************************************************* * 函 数 名: AppTaskLED * 功能说明: LED闪烁,并串口打印任务正在运行 * 形 参: 无 * 返 回 值: 无 * 优 先 级: 2 ********************************************************************************************************* */ __task void AppTaskLED(void) { const uint16_t usFrequency = 1000; /* 延迟周期 */ /* 设置延迟周期 */ os_itv_set(usFrequency); while(1) { bsp_LedToggle(2); bsp_LedToggle(3); /* 永久等待互斥资源可以 */ os_mut_wait (&mutex, 0xffff); printf("任务AppTaskLED正在运行rn"); /* 释放互斥资源 */ os_mut_release (&mutex); /* os_itv_wait是绝对延迟,os_dly_wait是相对延迟。*/ os_itv_wait(); } } |
|
|
|
|
|
15.3 实验例程说明
15.3.1 STM32F103开发板实验 配套例子: V4-413_RTX实验_互斥信号量 实验目的: 1. 学习RTX的互斥信号量 实验内容: 1. K1按键按下,串口打印。 2. 在调用printf函数的地方都加上互斥信号量,防止多个任务调用此函数造成冲突,以至于串口打印出现乱码。 3. 各个任务实现的功能如下: AppTaskUserIF任务 :按键消息处理。 AppTaskLED任务 :LED闪烁和串口打印。 AppTaskMsgPro任务 :消息处理,这里是用作LED闪烁和串口打印。 AppTaskStart任务 :启动任务,也是最高优先级任务,这里实现按键扫描。 |
|
|
|
|
|
RTX配置:
RTX配置向导详情如下: u Task Configuration l Number of concurrent running tasks 允许创建4个任务,实际创建了如下四个任务 AppTaskUserIF任务 :按键消息处理。 AppTaskLED任务 :LED闪烁和串口打印 AppTaskMsgPro任务 :消息处理,这里是用作LED闪烁和串口打印。 AppTaskStart任务 :启动任务,也是最高优先级任务,这里实现按键扫描。 l Number of tasks with user-provided stack 创建的4个任务都是采用自定义堆栈方式。 |
|
|
|
|
|
程序设计:
u 任务栈大小分配: staticuint64_t AppTaskUserIFStk[512/8]; /* 任务栈*/ staticuint64_t AppTaskLEDStk[256/8]; /* 任务栈 */ staticuint64_t AppTaskMsgProStk[512/8]; /* 任务栈 */ staticuint64_t AppTaskStartStk[512/8]; /* 任务栈 */ 将任务栈定义成uint64_t类型可以保证任务栈是8字节对齐的,8字节对齐的含义就是数组的首地址对8求余等于0。如果不做8字节对齐的话,部分C语言库函数,浮点运算和uint64_t类型数据运算会出问题。 u 系统栈大小分配: |
|
|
|
|
|
RTX初始化:
复制代码 /* ********************************************************************************************************* * 函 数 名: main * 功能说明: 标准c程序入口。 * 形 参: 无 * 返 回 值: 无 ********************************************************************************************************* */ int main (void) { /* 初始化外设 */ bsp_Init(); /* 创建启动任务 */ os_sys_init_user (AppTaskStart, /* 任务函数 */ 4, /* 任务优先级 */ &AppTaskStartStk, /* 任务栈 */ sizeof(AppTaskStartStk)); /* 任务栈大小,单位字节数 */ while(1); } |
|
|
|
|
|
RTX任务创建:
复制代码 /* ********************************************************************************************************* * 函 数 名: AppTaskCreate * 功能说明: 创建应用任务 * 形 参: 无 * 返 回 值: 无 ********************************************************************************************************* */ static void AppTaskCreate (void) { HandleTaskUserIF = os_tsk_create_user(AppTaskUserIF, /* 任务函数 */ 1, /* 任务优先级 */ &AppTaskUserIFStk, /* 任务栈 */ sizeof(AppTaskUserIFStk)); /* 任务栈大小,单位字节数 */ HandleTaskLED = os_tsk_create_user(AppTaskLED, /* 任务函数 */ 2, /* 任务优先级 */ &AppTaskLEDStk, /* 任务栈 */ sizeof(AppTaskLEDStk)); /* 任务栈大小,单位字节数 */ HandleTaskMsgPro = os_tsk_create_user(AppTaskMsgPro, /* 任务函数 */ 3, /* 任务优先级 */ &AppTaskMsgProStk, /* 任务栈 */ sizeof(AppTaskMsgProStk)); /* 任务栈大小,单位字节数 */ } |
|
|
|
|
|
互斥信号量创建
复制代码 static OS_MUT mutex; /* ********************************************************************************************************* * 函 数 名: AppTaskLED * 功能说明: LED闪烁,并串口打印任务正在运行 * 形 参: 无 * 返 回 值: 无 * 优 先 级: 2 ********************************************************************************************************* */ |
|
|
|
|
|
【敏矽微ME32G070开发板免费体验】使用coremark测试敏矽微ME32G070 跑分
250 浏览 0 评论
【敏矽微ME32G070开发板免费体验】开箱+点灯+点亮OLED
473 浏览 2 评论
571 浏览 0 评论
嵌入式学习-飞凌嵌入式ElfBoard ELF 1板卡-内核编译之初次编译
260 浏览 0 评论
400 浏览 0 评论
【youyeetoo X1 windows 开发板体验】少儿AI智能STEAM积木平台
11976 浏览 31 评论
小黑屋| 手机版| Archiver| 电子发烧友 ( 湘ICP备2023018690号 )
GMT+8, 2024-12-20 09:02 , Processed in 0.926253 second(s), Total 101, Slave 84 queries .
Powered by 电子发烧友网
© 2015 bbs.elecfans.com
关注我们的微信
下载发烧友APP
电子发烧友观察
版权所有 © 湖南华秋数字科技有限公司
电子发烧友 (电路图) 湘公网安备 43011202000918 号 电信与信息服务业务经营许可证:合字B2-20210191 工商网监 湘ICP备2023018690号