UC/OS-III 信号量

news/2024/11/7 22:38:28/

信号量

  • 一、信号量
  • 二、二值信号量
    • 2.1 、二值信号量的应用场景
    • 2.2、二值信号量的运作机制
  • 三、计数信号量
    • 3.1、计数信号量的运作机制
  • 四、信号量创建流程
  • 五、信号量接口函数
    • 1、创建信号量函数 OSSemCreate()
    • 2、信号量删除函数 OSSemDel()
    • 3、信号量释放函数 OSSemPost()
    • 4、信号量获取函数 OSSemPend()
  • 六、例程
    • 1、二值信号量
    • 2、计数信号量

一、信号量

**信号量(Semaphore)**是一种实现任务间通信的机制,可以实现任务之间同步或临界资源的互斥访问, 常用于协助一组相互竞争的任务来访问临界资源(初学者可类似的将它看作裸机系统中的标记变量)。二值信号量:在 uCOS 中我们用信号量用于同步,任务与任务的同步,中断与任务的同步,可以大大提高效率。计数信号量:在实际的使用中,我们常将计数信号量用于事件计数与资源管理。
注:UC/OS-III中的信号量不具备传递数据的功能,而消息队列却有。

二、二值信号量

二值信号量可以用于临界资源访问与同步功能。不过由于二值信号量没有优先级继承机制,所以更偏向于同步功能的应用。
用作同步时,信号量在创建后应被置为空,任务 1 获取信号量而进入阻塞,任务 2 在某种条件发生后,释放信号量,于是任务 1 获得信号量得以进入就绪态,如果任务 1 的优先级是最高的,那么就会立即切换任务,从而达到了两个任务间的同步。同样的,在中断服务函数中释放信号量,任务 1 也会得到信号量,从而达到任务与中断间的同步。还记得我们经常说的中断要快进快出吗,在裸机开发中我们经常是在中断中做一个标记,然后在退出的时候进行轮询处理,这个就是类似我们使用信号量进行同步的,当标记发生了,我们再做其他事情。在 uCOS 中我们用信号量用于同步,任务与任务的同步,中断与任务的同步,可以大大提高效率。

2.1 、二值信号量的应用场景

在嵌入式操作系统中二值信号量是任务间、任务与中断间同步的重要手段,信号量使用最多的一般都是二值信号量与互斥量(下章讲解)。二值信号量是哪二值呢? 0值:信号量资源被获取;1值:信号量资源被释放。把这种只有 0 和 1 两种情况的信号量称之为二值信号量。在多任务系统中,我们经常会使用这个二值信号量。
例:二值信号量在任务与任务中同步的应用场景:假设我们有一个温湿度的传感器,假设是 1s 采集一次数据,那么我们让他在液晶屏中显示数据出来,这个周期也是要 1s 一次的。但如何不阻塞的让液晶屏准时刷新,这就需要同步协调工作,在温湿度采集完毕之后,进行液晶屏数据的刷新,这样子,才是最准确的,并且不会浪费 CPU的资源。

2.2、二值信号量的运作机制

创建信号量时,系统会为创建的信号量对象分配内存,并把可用信号量初始化为用户自定义的个数, 二值信号量的最大可用信号量个数为 1。二值信号量获取,任何任务都可以从创建的二值信号量资源中获取一个二值信号量,获取成功则返回正确,否则任务会根据用户指定的阻塞超时时间来等待其它任务/中断释放信号量。在等待这段时间,系统将任务变成阻塞态,任务将被挂到该信号量的阻塞等待列表中。在二值信号量无效的时候,假如此时有任务获取该信号量的话,那么任务将进入阻塞状态。

三、计数信号量

计数信号量用于计数的,在实际的使用中,我们常将计数信号量用于事件计数与资源管理。每当某个事件发生时,任务或者中断将释放一个信号量(信号量计数值加 1),当处理被事件时(一般在任务中处理),处理任务会取走该信号量(信号量计数值减 1),信号量的计数值则表示还有多少个事件没被处理。此外,系统还有很多资源,我们也可以使用计数信号量进行资源管理,信号量的计数值表示系统中可用的资源数目,任务必须先获取到信号量才能获取资源访问权,当信号量的计数值为零时表示系统没有可用的资源,但是要注意,在使用完资源的时候必须归还信号量,否则当计数值为0的时候任务就无法访问该资源了。计数型信号量允许多个任务对其进行操作,但限制了任务的数量。
**例:**比如有一个停车场,里面只有 100 个车位,那么能停的车只有 100 辆,也相当于我们的信号量有 100 个,假如一开始停车场的车位还有 100 个,那么每进去一辆车就要消耗一个停车位,车位的数量就要减一,对应的,我们的信号量在使用之后也需要减一,当停车场停满了 100 辆车的时候,此时的停车位为 0,再来的车就不能停进去了,否则将造成事故,也相当于我们的信号量为 0,后面的任务对这个停车场资源的访问也无法进行,当有车从停车场离开的时候,车位又空余出来了,那么,后面的车就能停进去了,我们信号量的操作也是一样的,当我们释放了这个资源,后面的任务才能对这个资源进行访问。

3.1、计数信号量的运作机制

计数信号量可以用于资源管理,允许多个任务获取信号量访问共享资源,但会限制任务的最大数目。访问的任务数达到可支持的最大数目时,会阻塞其他试图获取该信号量的任务,直到有任务释放了信号量。这就是计数型信号量的运作机制,虽然计数信号量允许多个任务访问同一个资源,但是也有限定,比如某个资源限定只能有 3 个任务访问,那么第 4 个任务访问的时候,会因为获取不到信号量而进入阻塞,等到有任务(比如任务 1)释放掉该资源的时候,第 4 个任务才能获取到信号量从而进行资源的访问。

四、信号量创建流程

1、在app.c文件中定义一个信号量。
例:标志KEY1是否被单击的多值信号量

OS_SEM SemOfKey;          //标志KEY1是否被单击的多值信号量

2、在起始任务AppTaskStart()中创建信号量

/* 创建多值信号量 SemOfKey */OSSemCreate((OS_SEM      *)&SemOfKey,    //指向信号量变量的指针(CPU_CHAR    *)"SemOfKey",    //信号量的名字(OS_SEM_CTR   )0,             //信号量这里是指示事件发生,所以赋值为0,表示事件还没有发生(OS_ERR      *)&err);         //错误类型

五、信号量接口函数

1、创建信号量函数 OSSemCreate()

 void OSSemCreate (OS_SEM *p_sem,  //信号量控制块指针CPU_CHAR *p_name,  //信号量名称OS_SEM_CTR cnt,  //资源数目或事件是否发生标志OS_ERR *p_err)  //返回错误类型

注:cnt参数:表示初始化时候资源的个数或事件是否发生标志,二值信号量的时候,这个值一般为 0 或者为 1,而如果信号量作为计数信号量的时候,这个值一般定义为初始资源的个数。

2、信号量删除函数 OSSemDel()

OSSemDel()用于删除一个信号量,信号量删除函数是根据信号量结构(信号量句柄)直接删除的,删除之后这个信号量的所有信息都会被系统清空,而且不能再次使用这个信号量了,但如果某个信号量没有被定义,那也是无法被删除的,如果有任务阻塞在该信号量上,那么尽量不要删除该信号量。想要使用信号量删除函数就必须将OS_CFG_SEM_DEL_EN 宏定义配置为 1。该宏位于os_cfg.h文件中。

 OS_OBJ_QTY OSSemDel (OS_SEM *p_sem,  //信号量指针OS_OPT opt,  //选项OS_ERR *p_err) //返回错误类型
opt含义
OS_OPT_DEL_NO_PEND需要在没有任务等待信号量的条件下删除信号量。判断目前等待列表中是否有任务,没有则删除,有则返回有任务在等待的错误
OS_OPT_DEL_ALWAYS如果等待列表有任务,则要把等待列表中的任务恢复为就绪态,再删除;如果没有,直接删除

3、信号量释放函数 OSSemPost()

 OS_SEM_CTR OSSemPost (OS_SEM *p_sem,  //信号量控制块指针OS_OPT opt,    //选项OS_ERR *p_err) //返回错误类型
opt含义
OS_OPT_POST_FIFO默认采用 FIFO 方式发布信号量
OS_OPT_POST_LIFO采用 LIFO 方式发布信号量
OS_OPT_POST_1当前任务只想给一个任务(最高优先级的)发信号
OS_OPT_POST_ALL发布给所有等待的任务,也叫广播信号量

4、信号量获取函数 OSSemPend()

 OS_SEM_CTR OSSemPend (OS_SEM *p_sem,      //信号量指针OS_TICK timeout,    //等待超时时间OS_OPT opt,         //选项CPU_TS *p_ts,       //等到信号量时的时间戳OS_ERR *p_err)      //返回错误类型
opt含义
OS_OPT_PEND_BLOCKING阻塞调用方式,直到信号量可用或发生超时
OS_OPT_PEND_NON_BLOCKING不阻塞调用方式,如果信号量不可用,则ossempende()不会阻塞,而是返回给调用者使用适当的错误代码

六、例程

1、二值信号量

释放信号量任务在检测按键是否按下,如果按下则释放信号量,此时释放信号量会唤醒获取任务,获取任务开始运行,然后形成两个任务间的同步,LED 进行翻转,因为如果没按下按键,那么信号量就不会释放,只有当信号量释放的时候,获取信号量的任务才会被唤醒,如此一来就达到任务与任务的同步,同时程序的运行会在串口打印出相关信息。

#include <includes.h>
/*
***************************************************************************
*                             LOCAL DEFINES
***************************************************************************
*/
OS_SEM SemOfKey;          //标志KEY1是否被单击的多值信号量
/*
**************************************************************************
*                                TCB
**************************************************************************
*/
static  OS_TCB   AppTaskStartTCB;                                //任务控制块static  OS_TCB   AppTaskKeyTCB;
static  OS_TCB   AppTaskLed1TCB;
/*
***************************************************************************
*                                                STACKS
*************************************************************************
*/
static  CPU_STK  AppTaskStartStk[APP_TASK_START_STK_SIZE];       //任务堆栈static  CPU_STK  AppTaskKeyStk [ APP_TASK_KEY_STK_SIZE ];
static  CPU_STK  AppTaskLed1Stk [ APP_TASK_LED1_STK_SIZE ];
/*
*************************************************************************
*                                         FUNCTION PROTOTYPES
*************************************************************************
*/static  void  AppTaskStart  (void *p_arg);                       //任务函数声明static  void  AppTaskKey  ( void * p_arg );
static  void  AppTaskLed1 ( void * p_arg );int  main (void)
{OS_ERR  err;OSInit(&err);                                                           //初始化 uC/OS-III/* 创建起始任务 */OSTaskCreate((OS_TCB     *)&AppTaskStartTCB,                            //任务控制块地址(CPU_CHAR   *)"App Task Start",                            //任务名称(OS_TASK_PTR ) AppTaskStart,                               //任务函数(void       *) 0,                                          //传递给任务函数(形参p_arg)的实参(OS_PRIO     ) APP_TASK_START_PRIO,                        //任务的优先级(CPU_STK    *)&AppTaskStartStk[0],                         //任务堆栈的基地址(CPU_STK_SIZE) APP_TASK_START_STK_SIZE / 10,               //任务堆栈空间剩下1/10时限制其增长(CPU_STK_SIZE) APP_TASK_START_STK_SIZE,                    //任务堆栈空间(单位:sizeof(CPU_STK))(OS_MSG_QTY  ) 5u,                                         //任务可接收的最大消息数(OS_TICK     ) 0u,                                         //任务的时间片节拍数(0表默认值OSCfg_TickRate_Hz/10)(void       *) 0,                                          //任务扩展(0表不扩展)(OS_OPT      )(OS_OPT_TASK_STK_CHK | OS_OPT_TASK_STK_CLR), //任务选项(OS_ERR     *)&err);                                       //返回错误类型OSStart(&err);                                                          //启动多任务管理(交由uC/OS-III控制)}static  void  AppTaskStart (void *p_arg)
{CPU_INT32U  cpu_clk_freq;CPU_INT32U  cnts;OS_ERR      err;(void)p_arg;BSP_Init();                                                 //板级初始化CPU_Init();                                                 //初始化 CPU 组件(时间戳、关中断时间测量和主机名)cpu_clk_freq = BSP_CPU_ClkFreq();                           //获取 CPU 内核时钟频率(SysTick 工作时钟)cnts = cpu_clk_freq / (CPU_INT32U)OSCfg_TickRate_Hz;        //根据用户设定的时钟节拍频率计算 SysTick 定时器的计数值OS_CPU_SysTickInit(cnts);                                   //调用 SysTick 初始化函数,设置定时器计数值和启动定时器Mem_Init();                                                 //初始化内存管理组件(堆内存池和内存池表)#if OS_CFG_STAT_TASK_EN > 0u                                    //如果使能(默认使能)了统计任务OSStatTaskCPUUsageInit(&err);                               //计算没有应用任务(只有空闲任务)运行时 CPU 的(最大)
#endif                                                          //容量(决定 OS_Stat_IdleCtrMax 的值,为后面计算 CPU //使用率使用)。CPU_IntDisMeasMaxCurReset();                                //复位(清零)当前最大关中断时间/* 创建多值信号量 SemOfKey */OSSemCreate((OS_SEM      *)&SemOfKey,    //指向信号量变量的指针(CPU_CHAR    *)"SemOfKey",    //信号量的名字(OS_SEM_CTR   )0,             //信号量这里是指示事件发生,所以赋值为0,表示事件还没有发生(OS_ERR      *)&err);         //错误类型/* 创建 AppTaskKey 任务 */OSTaskCreate((OS_TCB     *)&AppTaskKeyTCB,                              //任务控制块地址(CPU_CHAR   *)"App Task Key",                              //任务名称(OS_TASK_PTR ) AppTaskKey,                                 //任务函数(void       *) 0,                                          //传递给任务函数(形参p_arg)的实参(OS_PRIO     ) APP_TASK_KEY_PRIO,                          //任务的优先级(CPU_STK    *)&AppTaskKeyStk[0],                           //任务堆栈的基地址(CPU_STK_SIZE) APP_TASK_KEY_STK_SIZE / 10,                 //任务堆栈空间剩下1/10时限制其增长(CPU_STK_SIZE) APP_TASK_KEY_STK_SIZE,                      //任务堆栈空间(单位:sizeof(CPU_STK))(OS_MSG_QTY  ) 5u,                                         //任务可接收的最大消息数(OS_TICK     ) 0u,                                         //任务的时间片节拍数(0表默认值OSCfg_TickRate_Hz/10)(void       *) 0,                                          //任务扩展(0表不扩展)(OS_OPT      )(OS_OPT_TASK_STK_CHK | OS_OPT_TASK_STK_CLR), //任务选项(OS_ERR     *)&err);                                       //返回错误类型/* 创建 LED1 任务 */OSTaskCreate((OS_TCB     *)&AppTaskLed1TCB,                             //任务控制块地址(CPU_CHAR   *)"App Task Led1",                             //任务名称(OS_TASK_PTR ) AppTaskLed1,                                //任务函数(void       *) 0,                                          //传递给任务函数(形参p_arg)的实参(OS_PRIO     ) APP_TASK_LED1_PRIO,                         //任务的优先级(CPU_STK    *)&AppTaskLed1Stk[0],                          //任务堆栈的基地址(CPU_STK_SIZE) APP_TASK_LED1_STK_SIZE / 10,                //任务堆栈空间剩下1/10时限制其增长(CPU_STK_SIZE) APP_TASK_LED1_STK_SIZE,                     //任务堆栈空间(单位:sizeof(CPU_STK))(OS_MSG_QTY  ) 5u,                                         //任务可接收的最大消息数(OS_TICK     ) 0u,                                         //任务的时间片节拍数(0表默认值OSCfg_TickRate_Hz/10)(void       *) 0,                                          //任务扩展(0表不扩展)(OS_OPT      )(OS_OPT_TASK_STK_CHK | OS_OPT_TASK_STK_CLR), //任务选项(OS_ERR     *)&err);                                       //返回错误类型OSTaskDel ( & AppTaskStartTCB, & err );                     //删除起始任务本身,该任务不再运行}static  void  AppTaskKey ( void * p_arg )
{OS_ERR      err;(void)p_arg;while (DEF_TRUE) {                                                         //任务体if( Key_Scan(KEY1_GPIO_PORT,KEY1_GPIO_PIN) == KEY_ON ) //如果KEY1被单击OSSemPost((OS_SEM  *)&SemOfKey,                                        //发布SemOfKey(OS_OPT   )OS_OPT_POST_ALL,                                   //发布给所有等待任务(OS_ERR  *)&err);                                             //返回错误类型OSTimeDlyHMSM ( 0, 0, 0, 20, OS_OPT_TIME_DLY, & err );                   //每20ms扫描一次}}static  void  AppTaskLed1 ( void * p_arg )
{OS_ERR         err;CPU_INT32U     cpu_clk_freq;CPU_TS         ts_sem_post, ts_sem_get;CPU_SR_ALLOC();  //使用到临界段(在关/开中断时)时必需该宏,该宏声明和定义一个局部变//量,用于保存关中断前的 CPU 状态寄存器 SR(临界段关中断只需保存SR)//,开中断时将该值还原。(void)p_arg;cpu_clk_freq = BSP_CPU_ClkFreq();               //获取CPU时钟,时间戳是以该时钟计数while (DEF_TRUE) {                              //任务体OSSemPend ((OS_SEM   *)&SemOfKey,             //等待该信号量被发布(OS_TICK   )0,                     //无期限等待(OS_OPT    )OS_OPT_PEND_BLOCKING,  //如果没有信号量可用就等待(CPU_TS   *)&ts_sem_post,          //获取信号量最后一次被发布的时间戳(OS_ERR   *)&err);                 //返回错误类型ts_sem_get = OS_TS_GET();                     //获取解除等待时的时间戳LED1_TOGGLE;                            //切换LED1的亮灭状态				OS_CRITICAL_ENTER();                          //进入临界段,不希望下面串口打印遭到中断			printf ( "\r\n发布信号量的时间戳是%d", ts_sem_post );printf ( "\r\n解除等待状态的时间戳是%d", ts_sem_get );printf ( "\r\n接收到信号量与发布信号量的时间相差%dus\r\n", ( ts_sem_get - ts_sem_post ) / ( cpu_clk_freq / 1000000 ) );			OS_CRITICAL_EXIT(); }	
}

2、计数信号量

实例是模拟停车场工作运行。在创建信号量的时候初始化 5 个可用的信号量,并且创建了两个任务:一个是获取信号量任务,一个是释放信号量任务,两个任务独立运行,获取信号量任务是通过按下 KEY1 按键进行信号量的获取,模拟停车场停车操作,其等待时间是 0,在串口调试助手输出相应信息。释放信号量任务则是信号量的释放,释放信号量任务也是通过按下 KEY2 按键进行信号量的释放,模拟停车场取车操作,在串口调试助手输出相应信息。

#include <includes.h>
/*
*********************************************************************************************************
*                                            LOCAL DEFINES
*********************************************************************************************************
*/OS_SEM SemOfKey;          //标志KEY1是否被按下的多值信号量/*
*********************************************************************************************************
*                                                 TCB
*********************************************************************************************************
*/static  OS_TCB   AppTaskStartTCB;                                //任务控制块static  OS_TCB   AppTaskKey1TCB;
static  OS_TCB   AppTaskKey2TCB;/*
*********************************************************************************************************
*                                                STACKS
*********************************************************************************************************
*/static  CPU_STK  AppTaskStartStk[APP_TASK_START_STK_SIZE];       //任务堆栈static  CPU_STK  AppTaskKey1Stk [ APP_TASK_KEY1_STK_SIZE ];
static  CPU_STK  AppTaskKey2Stk [ APP_TASK_KEY2_STK_SIZE ];/*
*********************************************************************************************************
*                                         FUNCTION PROTOTYPES
*********************************************************************************************************
*/static  void  AppTaskStart  (void *p_arg);                       //任务函数声明static  void  AppTaskKey1  ( void * p_arg );
static  void  AppTaskKey2  ( void * p_arg );int  main (void)
{OS_ERR  err;OSInit(&err);                                                           //初始化 uC/OS-III/* 创建起始任务 */OSTaskCreate((OS_TCB     *)&AppTaskStartTCB,                            //任务控制块地址(CPU_CHAR   *)"App Task Start",                            //任务名称(OS_TASK_PTR ) AppTaskStart,                               //任务函数(void       *) 0,                                          //传递给任务函数(形参p_arg)的实参(OS_PRIO     ) APP_TASK_START_PRIO,                        //任务的优先级(CPU_STK    *)&AppTaskStartStk[0],                         //任务堆栈的基地址(CPU_STK_SIZE) APP_TASK_START_STK_SIZE / 10,               //任务堆栈空间剩下1/10时限制其增长(CPU_STK_SIZE) APP_TASK_START_STK_SIZE,                    //任务堆栈空间(单位:sizeof(CPU_STK))(OS_MSG_QTY  ) 5u,                                         //任务可接收的最大消息数(OS_TICK     ) 0u,                                         //任务的时间片节拍数(0表默认值OSCfg_TickRate_Hz/10)(void       *) 0,                                          //任务扩展(0表不扩展)(OS_OPT      )(OS_OPT_TASK_STK_CHK | OS_OPT_TASK_STK_CLR), //任务选项(OS_ERR     *)&err);                                       //返回错误类型OSStart(&err);                                                          //启动多任务管理(交由uC/OS-III控制)}static  void  AppTaskStart (void *p_arg)
{CPU_INT32U  cpu_clk_freq;CPU_INT32U  cnts;OS_ERR      err;(void)p_arg;BSP_Init();                                                 //板级初始化CPU_Init();                                                 //初始化 CPU 组件(时间戳、关中断时间测量和主机名)cpu_clk_freq = BSP_CPU_ClkFreq();                           //获取 CPU 内核时钟频率(SysTick 工作时钟)cnts = cpu_clk_freq / (CPU_INT32U)OSCfg_TickRate_Hz;        //根据用户设定的时钟节拍频率计算 SysTick 定时器的计数值OS_CPU_SysTickInit(cnts);                                   //调用 SysTick 初始化函数,设置定时器计数值和启动定时器Mem_Init();                                                 //初始化内存管理组件(堆内存池和内存池表)#if OS_CFG_STAT_TASK_EN > 0u                                    //如果使能(默认使能)了统计任务OSStatTaskCPUUsageInit(&err);                               //计算没有应用任务(只有空闲任务)运行时 CPU 的(最大)
#endif                                                          //容量(决定 OS_Stat_IdleCtrMax 的值,为后面计算 CPU //使用率使用)。CPU_IntDisMeasMaxCurReset();                                //复位(清零)当前最大关中断时间/* 创建多值信号量 SemOfKey */OSSemCreate((OS_SEM      *)&SemOfKey,    //指向信号量变量的指针(CPU_CHAR    *)"SemOfKey",    //信号量的名字(OS_SEM_CTR   )5,             //表示现有资源数目(OS_ERR      *)&err);         //错误类型							 /* 创建 AppTaskKey1 任务 */OSTaskCreate((OS_TCB     *)&AppTaskKey1TCB,                             //任务控制块地址(CPU_CHAR   *)"App Task Key1",                             //任务名称(OS_TASK_PTR ) AppTaskKey1,                                //任务函数(void       *) 0,                                          //传递给任务函数(形参p_arg)的实参(OS_PRIO     ) APP_TASK_KEY1_PRIO,                         //任务的优先级(CPU_STK    *)&AppTaskKey1Stk[0],                          //任务堆栈的基地址(CPU_STK_SIZE) APP_TASK_KEY1_STK_SIZE / 10,                //任务堆栈空间剩下1/10时限制其增长(CPU_STK_SIZE) APP_TASK_KEY1_STK_SIZE,                     //任务堆栈空间(单位:sizeof(CPU_STK))(OS_MSG_QTY  ) 5u,                                         //任务可接收的最大消息数(OS_TICK     ) 0u,                                         //任务的时间片节拍数(0表默认值OSCfg_TickRate_Hz/10)(void       *) 0,                                          //任务扩展(0表不扩展)(OS_OPT      )(OS_OPT_TASK_STK_CHK | OS_OPT_TASK_STK_CLR), //任务选项(OS_ERR     *)&err);                                       //返回错误类型/* 创建 AppTaskKey2 任务 */OSTaskCreate((OS_TCB     *)&AppTaskKey2TCB,                             //任务控制块地址(CPU_CHAR   *)"App Task Key2",                             //任务名称(OS_TASK_PTR ) AppTaskKey2,                                //任务函数(void       *) 0,                                          //传递给任务函数(形参p_arg)的实参(OS_PRIO     ) APP_TASK_KEY2_PRIO,                         //任务的优先级(CPU_STK    *)&AppTaskKey2Stk[0],                          //任务堆栈的基地址(CPU_STK_SIZE) APP_TASK_KEY2_STK_SIZE / 10,                //任务堆栈空间剩下1/10时限制其增长(CPU_STK_SIZE) APP_TASK_KEY2_STK_SIZE,                     //任务堆栈空间(单位:sizeof(CPU_STK))(OS_MSG_QTY  ) 5u,                                         //任务可接收的最大消息数(OS_TICK     ) 0u,                                         //任务的时间片节拍数(0表默认值OSCfg_TickRate_Hz/10)(void       *) 0,                                          //任务扩展(0表不扩展)(OS_OPT      )(OS_OPT_TASK_STK_CHK | OS_OPT_TASK_STK_CLR), //任务选项(OS_ERR     *)&err);                                       //返回错误类型OSTaskDel ( & AppTaskStartTCB, & err );                     //删除起始任务本身,该任务不再运行		
}static  void  AppTaskKey1 ( void * p_arg )
{OS_ERR      err;OS_SEM_CTR  ctr;CPU_SR_ALLOC();  //使用到临界段(在关/开中断时)时必需该宏,该宏声明和定义一个局部变//量,用于保存关中断前的 CPU 状态寄存器 SR(临界段关中断只需保存SR)//,开中断时将该值还原。		(void)p_arg;while (DEF_TRUE) {                                                         //任务体if( Key_Scan(KEY1_GPIO_PORT,KEY1_GPIO_PIN) == KEY_ON ) //如果KEY1被按下{ctr = OSSemPend ((OS_SEM   *)&SemOfKey,               //等待该信号量 SemOfKey(OS_TICK   )0,                       //下面选择不等待,该参无效(OS_OPT    )OS_OPT_PEND_NON_BLOCKING,//如果没信号量可用不等待(CPU_TS   *)0,                       //不获取时间戳(OS_ERR   *)&err);                   //返回错误类型OS_CRITICAL_ENTER();                                  //进入临界段if ( err == OS_ERR_NONE )                      printf ( "\r\nKEY1被按下:成功申请到停车位,剩下%d个停车位。\r\n", ctr );else if ( err == OS_ERR_PEND_WOULD_BLOCK )printf ( "\r\nKEY1被按下:不好意思,现在停车场已满,请等待!\r\n" );OS_CRITICAL_EXIT(); }OSTimeDlyHMSM ( 0, 0, 0, 20, OS_OPT_TIME_DLY, & err );  //每20ms扫描一次}}static  void  AppTaskKey2 ( void * p_arg )
{OS_ERR      err;OS_SEM_CTR  ctr;CPU_SR_ALLOC();  //使用到临界段(在关/开中断时)时必需该宏,该宏声明和定义一个局部变//量,用于保存关中断前的 CPU 状态寄存器 SR(临界段关中断只需保存SR)//,开中断时将该值还原。	(void)p_arg;while (DEF_TRUE) {                                                         //任务体if( Key_Scan(KEY2_GPIO_PORT,KEY2_GPIO_PIN) == KEY_ON ) //如果KEY2被按下{ctr = OSSemPost((OS_SEM  *)&SemOfKey,                                  //发布SemOfKey(OS_OPT   )OS_OPT_POST_ALL,                            //发布给所有等待任务(OS_ERR  *)&err);                                      //返回错误类型OS_CRITICAL_ENTER();                                                   //进入临界段printf ( "\r\nKEY2被按下:释放1个停车位,剩下%d个停车位。\r\n", ctr );OS_CRITICAL_EXIT();}OSTimeDlyHMSM ( 0, 0, 0, 20, OS_OPT_TIME_DLY, & err );                    //每20ms扫描一次}
}

http://www.ppmy.cn/news/147238.html

相关文章

UCOS-III

一、UCOSIII 简介 UCOSIII 是一个可裁剪、可固化、可剥夺的多任务系统&#xff0c;没有任务数目的限制&#xff0c;是 UCOS 的第三代内核&#xff0c;UCOSIII 有以下几个重要的特性&#xff1a; 可剥夺多任务管理&#xff1a;UCOSIII 和 UCOSII 一样都属于可剥夺的多任务内核…

手机通讯原理的工作原理

一、手机的工作原理&#xff1a; 一般来说&#xff0c;我们普通用户只要学会如何使用好手机就可以了&#xff0c;对于其具体的工作原理不必仔细深究&#xff1b;然而在使用手机的过程中&#xff0c;由于各种因素的影响&#xff0c;手机不可避免地要出现故障&#xff0c…

UCOSII

文章目录 UCOSII提出者是什么开发及特性 UCOSII结构UCOSII并发 泉水 UCOSII 提出者 UCOSII的前身是UCOS&#xff0c;最早出自于1992 年美国嵌入式系统专家Jean J.Labrosse 在《嵌入式系统编程》杂志的5月和6月刊上刊登的文章连载&#xff0c;并把UCOS 的源码发布在该杂志的 …

剑指 Offer 17. 打印从1到最大的n位数解题思路

文章目录 题目解题思路 题目 输入数字 n&#xff0c;按顺序打印出从 1 到最大的 n 位十进制数。比如输入 3&#xff0c;则打印出 1、2、3 一直到最大的 3 位数 999。 示例 1: 输入: n 1 输出: [1,2,3,4,5,6,7,8,9] 说明&#xff1a; 用返回一个整数列表来代替打印 n 为正…

如何将Linux命令默认的提示结果不显示或者不输出?以及在脚本中如何替代Ctrl + C等关闭命令的终端窗口?

一、问题描述&#xff1a; 当我们想要获取命令的输出结果时&#xff0c;有时候命令会带出一些默认的提示信息输出到屏幕上&#xff0c;如何过滤掉这个提示信息呢&#xff1f; 例如&#xff0c;telnet命令退出时会默认提示“Connection closed by foreign host.” 例如&#x…

网络原理入门知识小结

文章目录 1 模型介绍2 各层功能3 PDU4 以太网和无线网的差异5 IP地址5.1 IP地址中的特殊地址 6 OSI模型中各层涉及协议7 ARP和RARP 1 模型介绍 OSI七层模型&#xff0c;从下至上分别为物理层&#xff0c;数据链路层&#xff0c;网络层&#xff0c;传输层&#xff0c;会话层&am…

获TÜV莱茵认可,美的冰箱在节能和静音方面已达全球领先水平

近日&#xff0c;美的冰箱&#xff08;型号&#xff1a;HD-500RWEN(A)&#xff09;获得德国莱茵TV大中华区&#xff08;以下简称“TV莱茵”&#xff09;Quality-mark能效Class A等级验证和噪音Class A等级验证认证证书。此次TV莱茵从新欧标能效及噪音两个维度对美的冰箱进行专业…

新电途与华为AITO达成充电合作;SGW和摩托罗拉完成家庭音频产品许可交易 | 美通企业日报...

美通社要闻摘要&#xff1a; 喜来登酒店及度假村继续在华东地区拓展。新电途与华为AITO达成充电合作。"安进生物技术体验项目"中国站正式启动。博世灵活7系吸尘器全新上市。SGW和摩托罗拉完成家庭音频产品许可交易。美的工业技术旗下GMCC美芝、Welling威灵亮相泰国暖…