第14章 信号量
本章节开始讲解RTX的另一个重要的任务间的同步和资源共享机制,信号量。
本章教程配套的例子含Cortex-M3内核的STM32F103和Cortex-M4内核的STM32F407。
14.1 信号量
14.2 信号量API函数
14.3 实验例程说明(任务间通信)
14.4 实验例程说明(中断方式通信)
14.5 总结
14.1 信号量
14.1.1 信号量的概念及其作用
信号量(semaphores)是20世纪60年代中期EdgserDijkstra发明的。使用信号量的最初目的是为了给共享资源建立一个标志,该标志表示该共享资源被占用情况。这样,当一个任务在访问共享资源之前,就可以先对这个标志进行查询,从而在了解资源被占用的情况之后,再来决定自己的行为。
实际的应用中,信号量的作用又该如何体现呢?比如有个30人的电脑机房,我们就可以创建信号量的初始化值是30,表示30个可用资源,不理解的初学者表示信号量还有初始值?是的,信号量说白了就是共享资源的数量。另外我们要求一个同学使用一台电脑,这样每有一个同学使用一台电脑,那么信号量的数值就减一,直到30台电脑都被占用,此时信号量的数值就是0。如果此时还有几个同学没有电脑可以使用,那么这几个同学就得等待,直到有同学离开,有一个同学离开,那么信号量的数值就加1,有两个就加2,依次类推。刚才没有电脑用的同学此时就有电脑可以用了,有几个同学用,信号量就减几,直到再次没有电脑可以用,这么一个过程就是使用信号量来管理共享资源的过程。
平时使用信号量主要实现以下两个功能:
1. 两个任务或者中断函数跟任务之间的同步功能,这个和上章节讲解的事件标志组是类似的。其实就是共享资源为1的时候。
2. 多个共享资源的管理,就像上面举的机房上机的例子。
实际上信号量还有很多其它用法,而且极具挑战性,可以大大的开拓大家的视野,有兴趣的同学可以阅读一下《The Little Book Of Semaphores》,作者是Allen B.Downy。
14.1.2 RTX任务间信号量的实现
任务间信号量的实现是指各个任务之间使用信号量实现任务的同步或者资源共享功能。
下面我们通过如下的框图来说明一下RTX信号量的实现,让大家有一个形象的认识。
运行条件:
1. 创建2个任务Task1和Task2。
2. 创建信号量可用资源为1。
运行过程描述如下:
1. 任务Task1运行过程中调用函数os_sem_wait获取信号量资源,如果信号量没有被任务Task2占用,Task1将直接获取资源。如果信号被Task2占用,任务Task1将由运行态转到挂起状态,等待资源可用。一旦获取了资源并使用完毕后会通过函数os_sem_send释放掉资源。
2、 任务Task2运行过程中调用函数os_sem_wait获取信号量资源,如果信号量没有被任务Task2占用,Task1将直接获取资源。如果信号被Task2占用,任务Task1将由运行态转到挂起状态,等待资源可以。一旦获取了资源并使用完毕后会通过函数os_sem_send释放掉资源。
上面就是一个简单RTX任务间信号量的使用过程。
14.1.3 RTX中断方式信号量的实现
RTX中断方式信号量的实现是指中断函数和RTX任务之间使用信号量。信号量的中断方式主要是用于实现任务同步,与上个章节讲解事件标志组中断方式是一样的。
下面我们通过如下的框图来说明一下RTX中断方式信号量的实现,让大家有一个形象的认识。
运行条件:
1. 创建1个任务Task1和一个串口接收中断。
2. 信号量的初始值为0,串口中断调用函数isr_sem_send释放信号量,任务Task1调用函数os_sem_wait获取信号量资源。
运行过程描述如下:
1. 任务Task1运行过程中调用函数os_sem_wait,由于信号量的初始值是0,没有信号量资源可用,任务Task1由运行态进入到挂起态。
2. Task1挂起的情况下,串口接收到数据进入到了串口中断服务程序,在串口中断服务程序中调用函数isr_sem_send释放信号量资源,信号量数值加1,此时信号量计数值为1,任务Task1由挂起态进入到就绪态,在调度器的作用下由就绪态又进入到运行态,任务Task1获得信号量后,信号量数值减1,此时信号量计数值又变成了0。
3. 再次循环执行时,任务Task1调用函数os_sem_wait由于没有资源可用再次进入到挂起态,等待串口释放信号量资源,如此往复循环。
上面就是一个简单RTX中断方式信号量同步过程。实际应用中,中断方式的消息机制切记注意以下四个问题:
1. 中断函数的执行时间越短越好,防止其它低于这个中断优先级的异常不能得到及时响应。
2. 实际应用中,建议不要在中断中实现消息处理,用户可以在中断服务程序里面发送消息通知任务,在任务中实现消息处理,这样可以有效的保证中断服务程序的实时响应。同时此任务也需要设置为高优先级,以便退出中断函数后任务可以得到及时执行。
3. 中断服务程序中一定要调用专用于中断的信号量设置函数isr_sem_send。
4. 在RTX操作系统中实现中断函数和裸机编程是一样的。
(1)另外强烈推荐用户将Cortex-M3内核的STM32F103和Cortex-M4内核的STM32F407的NVIC优先级分组设置为4,即:NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4);这样中断优先级的管理将非常方便。
(2)用户要在RTX多任务开启前就设置好优先级分组,一旦设置好切记不可再修改。
14.2 信号量API函数
使用如下4个函数可以实现RTX的信号量:
os_sem_init
os_sem_send
isr_sem_send
os_sem_wait
关于这4个函数的讲解及其使用方法可以看教程第3章3.3小节里面说的参考资料rlarm.chm文件
下面我们对这四个函数依次进行讲解:
14.2.1 函数os_sem_init
函数原型:
- void os_sem_init (
- OS_ID semaphore, /* os_sem类型变量 */
- U16 token_count ); /* 信号量初始值 */
函数描述:
函数os_sem_init用于信号量的初始化并设置初始值
1. 第1个参数填写数据类型为OS_SEM的变量,同时也作为ID标识
2. 第2个参数是信号量初始值,也就是可用资源个数。
使用举例:
- #include <rtl.h>
-
- OS_SEM semaphore;
-
- static void AppObjCreate (void)
- {
- /* 创建信号量计数值是0, 用于任务同步 */
- os_sem_init (&semaphore, 0);
- }
细心的读者可能发现,上面的初始化函数为什么不是这么写:os_sem_init(semaphore, 0),而是在变量semaphore前面加一个取地址符&,实际上这两种写法都是可以的,RTX的手册里面默认都是前面加上个取地址符,大家使用的时候也可以都加上,方便区分。
为什么两种写法都可以呢,因为OS_SEM是这么定义的:typedef U32 OS_SEM[2],对应上面的函数举例就是 U32 semaphore[2],定义了一个32位数组,数组里面有两个元素。所以os_sem_init的第一个参数填semaphore或者&semaphore都是这个数组的首地址。
14.2.2 函数os_sem_send
函数原型:
- OS_RESULT os_sem_send (
- OS_ID semaphore ); /* OS_SEM类型变量 */
函数描述:
函数os_sem_send用于释放信号量,调用后信号量计数值加1。
1. 第1个参数参数填写数据类型为OS_SEM的变量,同时也作为ID标识。
2. 返回值永远是OS_R_OK。
使用这个函数要注意以下问题:
1. 使用此函数前一定要调用函数os_sem_init进行初始化。
使用举例:
- #include <rtl.h>
-
- OS_SEM semaphore;
-
- __task void task1 (void) {
- ..
- os_sem_init (&semaphore, 0);
- os_sem_send (&semaphore);
- ..
- }
14.2.3 函数isr_sem_send
函数原型:
- void isr_sem_send (
- OS_ID semaphore ); /* OS_SEM类型变量 */
函数描述:
函数isr_sem_send用于释放信号量,调用后信号量计数值加1。
1. 第1个参数参数填写数据类型为OS_SEM的变量,同时也作为ID标识。
使用这个函数要注意以下问题:
1. 使用此函数前一定要调用函数os_sem_init进行初始化。
使用举例:
- #include <rtl.h>
-
- OS_SEM semaphore;
-
- void EXTI0_IRQHandler (void){
- ..
- isr_sem_send (&semaphore1);
- ..
- }
14.2.4 函数os_sem_wait
函数原型:
- OS_RESULT os_sem_wait (
- OS_ID semaphore, /* OS_SEM类型变量 */
- U16 timeout ); /* 超时时间设置 */
函数描述:
函数os_sem_wait用于获取信号量,如果当前的信号量计数值大于0,那么调用函数os_sem_wait后可以成功获取信号量,并将信号量的计数值减1。如果信号量计数值等于0,调用此函数的任务将由运行态转到挂起态,等待信号量资源可用,也就是等待信号量计数值大于0。
1. 第1个参数参数填写数据类型为OS_SEM的变量,同时也作为ID标识。
2. 第2个参数表示设置的等待时间,范围0-0xFFFF,当参数设置为0-0xFFFE时,表示等待这么多个时钟节拍,参数设置为0xFFFF时表示无限等待直到有信号量资源可用。
3. 函数返回OS_R_SEM表示函数设置的超时时间范围内收到信号量可用资源。
函数返回OS_R_TMO表示超时。
函数返回OS_R_OK表示无需等待,立即获得可用信号量资源。
使用这个函数要注意以下问题:
1. 使用此函数前一定要调用函数os_sem_init进行初始化。
使用举例:
- #include <rtl.h>
-
- OS_SEM semaphore;
-
- __task void AppTaskMsgPro(void)
- {
- OS_RESULT xResult;
- const uint16_t usMaxBlockTime = 200; /* 延迟周期 */
-
- while(1)
- {
- xResult = os_sem_wait (&semaphore, usMaxBlockTime);
-
- switch (xResult)
- {
- /* 无需等待接受到信号量同步信号 */
- case OS_R_OK:
- printf("无需等待接受到信号量同步信号\r\n");
- break;
-
- /* 信号量不可用,usMaxBlockTime等待时间内收到信号量同步信号 */
- case OS_R_SEM:
- printf("信号量不可用,usMaxBlockTime等待时间内收到信号量同步信号\r\n");
- break;
-
- /* 超时 */
- case OS_R_TMO:
- bsp_LedToggle(1);
- bsp_LedToggle(4);
- break;
-
- /* 其他值不处理 */
- default:
- break;
-
- }
- }
- }
14.3 实验例程说明(任务间通信)
14.3.1 STM32F103开发板实验
配套例子:
V4-411_RTX实验_信号量
实验目的:
1. 学习RTX的信号量
实验内容:
1. K1按键按下,串口打印。
2. K2键按下,直接发送信号量同步信号给任务AppTaskMsgPro。任务AppTaskMsgPro接收到消息后进行消息处理。
3. 各个任务实现的功能如下:
AppTaskUserIF任务 :按键消息处理。
AppTaskLED任务 :LED闪烁。
AppTaskMsgPro任务 :消息处理,等待任务AppTaskUserIF发来的信号量同步信号。
AppTaskStart任务 :启动任务,也是最高优先级任务,这里实现按键扫描。
RTX配置:
RTX配置向导详情如下:
Task Configuration
Number of concurrent running tasks
允许创建4个任务,实际创建了如下四个任务
AppTaskUserIF任务 :按键消息处理。
AppTaskLED任务 :LED闪烁。
AppTaskMsgPro任务 :消息处理,等待任务AppTaskUserIF发来的信号量同步信号。
AppTaskStart任务 :启动任务,也是最高优先级任务,这里实现按键扫描。
Number of tasks with user-provided stack
创建的4个任务都是采用自定义堆栈方式。
RTX任务调试信息:
程序设计:
任务栈大小分配:
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类型数据运算会出问题。
系统栈大小分配:
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_SEM semaphore;
- /*
- *********************************************************************************************************
- * 函 数 名: AppObjCreate
- * 功能说明: 创建任务通信机制
- * 形 参: 无
- * 返 回 值: 无
- *********************************************************************************************************
- */
- static void AppObjCreate (void)
- {
- /* 创建信号量计数值是0, 用于任务同步 */
- os_sem_init (semaphore, 0);
- }
四个RTX任务的实现:
- /*
- *********************************************************************************************************
- * 函 数 名: AppTaskUserIF
- * 功能说明: 按键消息处理
- * 形 参: 无
- * 返 回 值: 无
- * 优 先 级: 1 (数值越小优先级越低,这个跟uCOS相反)
- *********************************************************************************************************
- */
- __task void AppTaskUserIF(void)
- {
- uint8_t ucKeyCode;
-
- while(1)
- {
- ucKeyCode = bsp_GetKey();
-
- if (ucKeyCode != KEY_NONE)
- {
- switch (ucKeyCode)
- {
- /* K1键按下,打印调试说明 */
- case KEY_DOWN_K1:
- printf("K1键按下,使用MDK中自带的RTX调试组件,请务必使用MDK4.74版本进行调试\r\n");
- break;
-
- /* K2键按下,直接发送信号量同步信号给任务AppTaskMsgPro */
- case KEY_DOWN_K2:
- printf("K2键按下,直接发送信号量同步信号给任务AppTaskMsgPro\r\n");
- os_sem_send (&semaphore);
- break;
-
- /* 其他的键值不处理 */
- default:
- break;
- }
- }
-
- os_dly_wait(20);
- }
- }
-
- /*
- *********************************************************************************************************
- * 函 数 名: AppTaskLED
- * 功能说明: LED闪烁。
- * 形 参: 无
- * 返 回 值: 无
- * 优 先 级: 2
- *********************************************************************************************************
- */
- __task void AppTaskLED(void)
- {
- const uint16_t usFrequency = 200; /* 延迟周期 */
-
- /* 设置延迟周期 */
- os_itv_set(usFrequency);
-
- while(1)
- {
- bsp_LedToggle(2);
- bsp_LedToggle(3);
-
- /* os_itv_wait是绝对延迟,os_dly_wait是相对延迟。*/
- os_itv_wait();
- }
- }
-
- /*
- *********************************************************************************************************
- * 函 数 名: AppTaskMsgPro
- * 功能说明: 消息处理,等待任务AppTaskUserIF发来的信号量同步信号
- * 形 参: 无
- * 返 回 值: 无
- * 优 先 级: 3
- *********************************************************************************************************
- */
- __task void AppTaskMsgPro(void)
- {
- OS_RESULT xResult;
- const uint16_t usMaxBlockTime = 200; /* 延迟周期 */
-
- while(1)
- {
- xResult = os_sem_wait (&semaphore, usMaxBlockTime);
-
- switch (xResult)
- {
- /* 无需等待接受到信号量同步信号 */
- case OS_R_OK:
- printf("无需等待接受到信号量同步信号\r\n");
- break;
-
- /* 信号量不可用,usMaxBlockTime等待时间内收到信号量同步信号 */
- case OS_R_SEM:
- printf("信号量不可用,usMaxBlockTime等待时间内收到信号量同步信号\r\n");
- break;
-
- /* 超时 */
- case OS_R_TMO:
- bsp_LedToggle(1);
- bsp_LedToggle(4);
- break;
-
- /* 其他值不处理 */
- default:
- break;
- }
- }
- }
-
- /*
- *********************************************************************************************************
- * 函 数 名: AppTaskStart
- * 功能说明: 启动任务,也就是最高优先级任务。这里实现按键扫描。
- * 形 参: 无
- * 返 回 值: 无
- * 优 先 级: 4
- *********************************************************************************************************
- */
- __task void AppTaskStart(void)
- {
- /* 创建任务 */
- AppTaskCreate();
-
- /* 创建任务通信机制 */
- AppObjCreate();
-
- while(1)
- {
- /* 按键扫描 */
- bsp_KeyScan();
- os_dly_wait(10);
- }
- }
14.3.2 STM32F407开发板实验
配套例子:
V5-411_RTX实验_信号量
实验目的:
1. 学习RTX的信号量
实验内容:
1. K1按键按下,串口打印。
2. K2键按下,直接发送信号量同步信号给任务AppTaskMsgPro。
任务AppTaskMsgPro接收到消息后进行消息处理。
3. 各个任务实现的功能如下:
AppTaskUserIF任务 :按键消息处理。
AppTaskLED任务 :LED闪烁。
AppTaskMsgPro任务 :消息处理,等待任务AppTaskUserIF发来的信号量同步信号。
AppTaskStart任务 :启动任务,也是最高优先级任务,这里实现按键扫描。
RTX配置:
RTX配置向导详情如下:
Task Configuration
Number of concurrent running tasks
允许创建4个任务,实际创建了如下四个任务
AppTaskUserIF任务 :按键消息处理。
AppTaskLED任务 :LED闪烁。
AppTaskMsgPro任务 :消息处理,等待任务AppTaskUserIF发来的信号量同步信号。
AppTaskStart任务 :启动任务,也是最高优先级任务,这里实现按键扫描。
Number of tasks with user-provided stack
创建的4个任务都是采用自定义堆栈方式。
RTX任务调试信息:
程序设计:
任务栈大小分配:
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类型数据运算会出问题。
系统栈大小分配:
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_SEM semaphore;
- /*
- *********************************************************************************************************
- * 函 数 名: AppObjCreate
- * 功能说明: 创建任务通信机制
- * 形 参: 无
- * 返 回 值: 无
- *********************************************************************************************************
- */
- static void AppObjCreate (void)
- {
- /* 创建信号量计数值是0, 用于任务同步 */
- os_sem_init (semaphore, 0);
- }
四个RTX任务的实现:
- /*
- *********************************************************************************************************
- * 函 数 名: AppTaskUserIF
- * 功能说明: 按键消息处理
- * 形 参: 无
- * 返 回 值: 无
- * 优 先 级: 1 (数值越小优先级越低,这个跟uCOS相反)
- *********************************************************************************************************
- */
- __task void AppTaskUserIF(void)
- {
- uint8_t ucKeyCode;
-
- while(1)
- {
- ucKeyCode = bsp_GetKey();
-
- if (ucKeyCode != KEY_NONE)
- {
- switch (ucKeyCode)
- {
- /* K1键按下,打印调试说明 */
- case KEY_DOWN_K1:
- printf("K1键按下,使用MDK中自带的RTX调试组件,请务必使用MDK4.74版本进行调试\r\n");
- break;
-
- /* K2键按下,直接发送信号量同步信号给任务AppTaskMsgPro */
- case KEY_DOWN_K2:
- printf("K2键按下,直接发送信号量同步信号给任务AppTaskMsgPro\r\n");
- os_sem_send (&semaphore);
- break;
-
- /* 其他的键值不处理 */
- default:
- break;
- }
- }
-
- os_dly_wait(20);
- }
- }
-
- /*
- *********************************************************************************************************
- * 函 数 名: AppTaskLED
- * 功能说明: LED闪烁。
- * 形 参: 无
- * 返 回 值: 无
- * 优 先 级: 2
- *********************************************************************************************************
- */
- __task void AppTaskLED(void)
- {
- const uint16_t usFrequency = 200; /* 延迟周期 */
-
- /* 设置延迟周期 */
- os_itv_set(usFrequency);
-
- while(1)
- {
- bsp_LedToggle(2);
- bsp_LedToggle(3);
-
- /* os_itv_wait是绝对延迟,os_dly_wait是相对延迟。*/
- os_itv_wait();
- }
- }
-
- /*
- *********************************************************************************************************
- * 函 数 名: AppTaskMsgPro
- * 功能说明: 消息处理,等待任务AppTaskUserIF发来的信号量同步信号
- * 形 参: 无
- * 返 回 值: 无
- * 优 先 级: 3
- *********************************************************************************************************
- */
- __task void AppTaskMsgPro(void)
- {
- OS_RESULT xResult;
- const uint16_t usMaxBlockTime = 200; /* 延迟周期 */
-
- while(1)
- {
- xResult = os_sem_wait (&semaphore, usMaxBlockTime);
-
- switch (xResult)
- {
- /* 无需等待接受到信号量同步信号 */
- case OS_R_OK:
- printf("无需等待接受到信号量同步信号\r\n");
- break;
-
- /* 信号量不可用,usMaxBlockTime等待时间内收到信号量同步信号 */
- case OS_R_SEM:
- printf("信号量不可用,usMaxBlockTime等待时间内收到信号量同步信号\r\n");
- break;
-
- /* 超时 */
- case OS_R_TMO:
- bsp_LedToggle(1);
- bsp_LedToggle(4);
- break;
-
- /* 其他值不处理 */
- default:
- break;
- }
- }
- }
-
- /*
- *********************************************************************************************************
- * 函 数 名: AppTaskStart
- * 功能说明: 启动任务,也就是最高优先级任务。这里实现按键扫描。
- * 形 参: 无
- * 返 回 值: 无
- * 优 先 级: 4
- *********************************************************************************************************
- */
- __task void AppTaskStart(void)
- {
- /* 创建任务 */
- AppTaskCreate();
-
- /* 创建任务通信机制 */
- AppObjCreate();
-
- while(1)
- {
- /* 按键扫描 */
- bsp_KeyScan();
- os_dly_wait(10);
- }
- }
14.4 实验例程说明(中断方式通信)
14.4.1 STM32F103开发板实验
配套例子:
V4-412_RTX实验_信号量(中断方式)
实验目的:
1. 学习RTX的信号量(中断方式)
实验内容:
1. K1按键按下,串口打印。
2. K2键按下,启动单次定时器中断,50ms后在定时器中断给任务AppTaskMsgPro发送信号量同步信号。任务AppTaskMsgPro接收到消息后进行消息处理。
3. 各个任务实现的功能如下:
AppTaskUserIF任务 :按键消息处理。
AppTaskLED任务 :LED闪烁。
AppTaskMsgPro任务 :消息处理,等待定时器中断发来的信号量同步信号。
AppTaskStart任务 :启动任务,也是最高优先级任务,这里实现按键扫描。
RTX配置:
RTX配置向导详情如下:
Task Configuration
Number of concurrent running tasks
允许创建4个任务,实际创建了如下四个任务
AppTaskUserIF任务 :按键消息处理。
AppTaskLED任务 :LED闪烁。
AppTaskMsgPro任务 :消息处理,等待定时器中断发来的信号量同步信号。
AppTaskStart任务 :启动任务,也是最高优先级任务,这里实现按键扫描。
Number of tasks with user-provided stack
创建的4个任务都是采用自定义堆栈方式。
RTX任务调试信息:
程序设计:
任务栈大小分配:
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类型数据运算会出问题。
系统栈大小分配:
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_SEM semaphore;
- /*
- *********************************************************************************************************
- * 函 数 名: AppObjCreate
- * 功能说明: 创建任务通信机制
- * 形 参: 无
- * 返 回 值: 无
- *********************************************************************************************************
- */
- static void AppObjCreate (void)
- {
- /* 创建信号量计数值是0, 用于任务同步 */
- os_sem_init (semaphore, 0);
- }
四个RTX任务的实现:
- /*
- /*
- *********************************************************************************************************
- * 函 数 名: AppTaskUserIF
- * 功能说明: 按键消息处理
- * 形 参: 无
- * 返 回 值: 无
- * 优 先 级: 1 (数值越小优先级越低,这个跟uCOS相反)
- *********************************************************************************************************
- */
- __task void AppTaskUserIF(void)
- {
- uint8_t ucKeyCode;
-
- while(1)
- {
- ucKeyCode = bsp_GetKey();
-
- if (ucKeyCode != KEY_NONE)
- {
- switch (ucKeyCode)
- {
- /* K1键按下,打印调试说明 */
- case KEY_DOWN_K1:
- printf("K1键按下,使用MDK中自带的RTX调试组件,请务必使用MDK4.74版本进行调试\r\n");
- break;
-
- /* K2键按下,启动单次定时器中断,50ms后在定时器中断给任务AppTaskMsgPro发送信号量同步信号 */
- case KEY_DOWN_K2:
- printf("K2键按下,启动单次定时器中断,50ms后在定时器中断给任务AppTaskMsgPro发送信
- 号量同步信号\r\n");
- bsp_StartHardTimer(1 ,50000, (void *)TIM_CallBack1);
- break;
-
- /* 其他的键值不处理 */
- default:
- break;
- }
- }
-
- os_dly_wait(20);
- }
- }
-
- /*
- *********************************************************************************************************
- * 函 数 名: AppTaskLED
- * 功能说明: LED闪烁。
- * 形 参: 无
- * 返 回 值: 无
- * 优 先 级: 2
- *********************************************************************************************************
- */
- __task void AppTaskLED(void)
- {
- const uint16_t usFrequency = 200; /* 延迟周期 */
-
- /* 设置延迟周期 */
- os_itv_set(usFrequency);
-
- while(1)
- {
- bsp_LedToggle(2);
- bsp_LedToggle(3);
-
- /* os_itv_wait是绝对延迟,os_dly_wait是相对延迟。*/
- os_itv_wait();
- }
- }
-
- /*
- *********************************************************************************************************
- * 函 数 名: AppTaskMsgPro
- * 功能说明: 消息处理,等待定时器中断发来的信号量同步信号
- * 形 参: 无
- * 返 回 值: 无
- * 优 先 级: 3
- *********************************************************************************************************
- */
- __task void AppTaskMsgPro(void)
- {
- OS_RESULT xResult;
- const uint16_t usMaxBlockTime = 200; /* 延迟周期 */
-
- while(1)
- {
- xResult = os_sem_wait (&semaphore, usMaxBlockTime);
-
- switch (xResult)
- {
- /* 无需等待接受到信号量同步信号 */
- case OS_R_OK:
- printf("无需等待接受到信号量同步信号\r\n");
- &n