信号量
信号量像是一种上锁机制,代码必须获得对应的钥匙才能继续执行,一旦获得了钥匙,也就意味着该任务具有进入被锁部分代码的权限。一旦执行至被锁代码段,则任务一直等待,直到对应被锁部分代码的钥匙被再次释放才能继续执行。
信号量用于控制对共享资源的保护,但是现在基本用来做任务同步用。
要想获取资源的任务必须执行“等待”操作,如果该资源对应的信号量有效值大于1,则任务可以获得该资源,任务继续运行。如果该信号量的有效值为0,则任务加入等待信号量的任务表中。如果等待时间超过某一个设定值,该信号量仍然没有被释放掉,则等待信号量的任务就进入就绪态,如果将等待时间设置为0的话任务就将一直等待该信号量。
这里就涉及到一个请求信号量(Pend)、释放信号量(Post)。
信号量分类
信号量分为两种:二进制信号量与计数型信号量,这是由信号量的取值范围进行区分的。在共享资源中只有任务可以使用信号量,中断服务程序则不能使用。
二进制信号量只能取0和1两个值;计数型信号量的信号量值大于1,计数型信号量的范围由OS_SEM_CTR决定,OS_SEM_CTR可以为8位,16位和32位,取值范围分别为:0~255,0~65535和0~4294967295。
可以很感性地这样认为:信号量的值代表着能够再次申请该信号量的数目;当信号量的值为0的时候,表示该信号量此时不能被申请、被等待了,只有等其他的信号量释放了之后,才能够再次获取。
二值信号量用于那些一次只能一个任务使用的资源,比如I/O设备,打印机计,数型信号量用于某些资源可以同时被几个任务所使用,比如一个缓存池有10个缓存块,那么同时最多可以支持10个任务来使用内存池。
信号量API函数
函数 | 说明 |
OSSemCreate() | 创建一个信号量 |
OSSemDel() | 删除一个信号量 |
OSSemPend() | 等待一个信号量 |
OSSemPendAbort() | 取消等待 |
OSSemPost() | 释放一个信号量 |
OSSemSet() | 强制设置一个信号量的值 |
要想使用信号量,肯定需要先创建一个信号量,我们使用函数OSSemCreate()来创建信号量,函数原型如下:
void OSSemCreate (OS_SEM *p_sem,??????????????????//指向信号量控制块 CPU_CHAR *p_name,?????????????????//指向信号量的名字 OS_SEM_CTR cnt,????????????????????//设置信号量的初始值,1(代表二进制信号量),大于1(代表计数型信号量) OS_ERR *p_err) { CPU_SR_ALLOC(); OS_CRITICAL_ENTER(); p_sem->Type = OS_OBJ_TYPE_SEM; /* Mark the data structure as a semaphore */ p_sem->Ctr = cnt; /* Set semaphore value */ p_sem->TS = (CPU_TS)0; p_sem->NamePtr = p_name; /* Save the name of the semaphore */ OS_PendListInit(&p_sem->PendList); /* Initialize the waiting list */ OSSemQty++; OS_CRITICAL_EXIT_NO_SCHED(); *p_err = OS_ERR_NONE; }
信号量控制块是什么结构呢?
struct os_sem { /* Semaphore */ /* ------------------ GENERIC MEMBERS ------------------ */ OS_OBJ_TYPE Type; /* Should be set to OS_OBJ_TYPE_SEM */ CPU_CHAR *NamePtr; /* Pointer to Semaphore Name (NUL terminated ASCII) */ OS_PEND_LIST PendList; /* List of tasks waiting on semaphore */ #if OS_CFG_DBG_EN > 0u OS_SEM *DbgPrevPtr; OS_SEM *DbgNextPtr; CPU_CHAR *DbgNamePtr; #endif /* ------------------ SPECIFIC MEMBERS ------------------ */ OS_SEM_CTR Ctr;????????????????????????????//信号量当前的取值 CPU_TS TS; };
请求信号量
当一个任务需要独占式的访问某个特定的系统资源时,需要与其他任务或中断服务程序同步,或者需要等待某个事件的发生,应该调用函数OSSemPend(),函数原型如下:
OS_SEM_CTR OSSemPend (OS_SEM *p_sem,????????????????????????//指向信号量控制块 OS_TICK timeout,????????????????????????//指定等待信号量的超时时间(时钟节拍数) OS_OPT opt,????????????????????//用于设置是否使用阻塞模式 CPU_TS *p_ts,????????????//指向一个时间戳,记录接收到信号量的时刻,NULL表示不记录 OS_ERR *p_err) { OS_SEM_CTR ctr; OS_PEND_DATA pend_data; CPU_SR_ALLOC(); if (p_ts != (CPU_TS *)0) { *p_ts = (CPU_TS)0; /* Initialize the returned timestamp */ } CPU_CRITICAL_ENTER(); if (p_sem->Ctr > (OS_SEM_CTR)0) { /* Resource available? */ p_sem->Ctr--; /* Yes, caller may proceed */ if (p_ts != (CPU_TS *)0) { *p_ts = p_sem->TS; /* get timestamp of last post */ } ctr = p_sem->Ctr; CPU_CRITICAL_EXIT(); *p_err = OS_ERR_NONE; return (ctr); } if ((opt & OS_OPT_PEND_NON_BLOCKING) != (OS_OPT)0) { /* Caller wants to block if not available? */ ctr = p_sem->Ctr; /* No */ CPU_CRITICAL_EXIT(); *p_err = OS_ERR_PEND_WOULD_BLOCK; return (ctr); } else { /* Yes */ if (OSSchedLockNestingCtr > (OS_NESTING_CTR)0) { /* Can't pend when the scheduler is locked */ CPU_CRITICAL_EXIT(); *p_err = OS_ERR_SCHED_LOCKED; return ((OS_SEM_CTR)0); } } OS_CRITICAL_ENTER_CPU_CRITICAL_EXIT(); /* Lock the scheduler/re-enable interrupts */ OS_Pend(&pend_data, /* Block task pending on Semaphore */ (OS_PEND_OBJ *)((void *)p_sem), OS_TASK_PEND_ON_SEM, timeout); OS_CRITICAL_EXIT_NO_SCHED(); OSSched(); /* Find the next highest priority task ready to run */ CPU_CRITICAL_ENTER(); switch (OSTCBCurPtr->PendStatus) { case OS_STATUS_PEND_OK: /* We got the semaphore */ if (p_ts != (CPU_TS *)0) { *p_ts = OSTCBCurPtr->TS; } *p_err = OS_ERR_NONE; break; case OS_STATUS_PEND_ABORT: /* Indicate that we aborted */ if (p_ts != (CPU_TS *)0) { *p_ts = OSTCBCurPtr->TS; } *p_err = OS_ERR_PEND_ABORT; break; case OS_STATUS_PEND_TIMEOUT: /* Indicate that we didn't get semaphore within timeout */ if (p_ts != (CPU_TS *)0) { *p_ts = (CPU_TS )0; } *p_err = OS_ERR_TIMEOUT; break; case OS_STATUS_PEND_DEL: /* Indicate that object pended on has been deleted */ if (p_ts != (CPU_TS *)0) { *p_ts = OSTCBCurPtr->TS; } *p_err = OS_ERR_OBJ_DEL; break; default: *p_err = OS_ERR_STATUS_INVALID; CPU_CRITICAL_EXIT(); return ((OS_SEM_CTR)0); } ctr = p_sem->Ctr; CPU_CRITICAL_EXIT(); return (ctr); }
timeout:指定等待信号量的超时时间(时钟节拍数),如果在指定时间内没有等到信号量则允许任务恢复执行。如果指定时间为0的话任务就会一直等待下去,直到等到信号量。
opt:用于设置是否使用阻塞模式,有下面两个选项。OS_OPT_PEND_BLOCKING:指定信号量无效时,任务挂起以等待信号量;OS_OPT_PEND_NON_BLOCKING:信号量无效时,任务直接返回。
任务获得信号量以后就可以访问共享资源了,在任务访问完共享资源以后必须释放信号量,释放信号量也叫发送信号量,使用函数OSSemPost()发送信号量。
如果没有任务在等待该信号量的话则OSSemPost()函数只是简单的将信号量加1,然后返回到调用该函数的任务中继续运行。如果有一个或者多个任务在等待这个信号量,则优先级最高的任务将获得这个信号量,然后由调度器来判定刚获得信号量的任务是否为系统中优先级最高的就绪任务,如果是,则系统将进行任务切换,运行这个就绪任务,OSSemPost()函数原型如下:
OS_SEM_CTR OSSemPost (OS_SEM *p_sem,????????????????????//指向一个信号的指针 OS_OPT opt,????????????????????//用来选择信号量发送的方式 OS_ERR *p_err) { OS_SEM_CTR ctr; CPU_TS ts; ts = OS_TS_GET(); /* Get timestamp */ ctr = OS_SemPost(p_sem, /* Post to semaphore */ opt, ts, p_err); return (ctr); }
opt:用来选择信号量发送的方式。OS_OPT_POST_1:仅向等待该信号量的优先级最高的任务发送信号量;OS_OPT_POST_ALL:向等待该信号量的所有任务发送信号量;OS_OPT_POST_NO_SCHED:该选项禁止在本函数内执行任务调度操作。即使该函数使得更高优先级的任务结束挂起进入就绪状态,也不会执行任务调度,而是会在其他后续函数中完成任务调度。
UCOSIII实际例程
上文说到:信号量用于控制对共享资源的保护,但是现在基本用来做任务同步用。
使用信号量访问共享资源区实验
例程要求:创建3个任务,任务A用于创建其他两个任务,任务A执行一次后就会被删除掉。任务B和任务C都可以访问作为共享资源D,任务B和C对于共享资源D是使用信号量访问的。
例子:
#include "sys.h" #include "delay.h" #include "usart.h" #include "led.h" #include "lcd.h" #include "key.h" #include "includes.h" //UCOSIII中以下优先级用户程序不能使用,ALIENTEK //将这些优先级分配给了UCOSIII的5个系统内部任务 //优先级0:中断服务服务管理任务 OS_IntQTask() //优先级1:时钟节拍任务 OS_TickTask() //优先级2:定时任务 OS_TmrTask() //优先级OS_CFG_PRIO_MAX-2:统计任务 OS_StatTask() //优先级OS_CFG_PRIO_MAX-1:空闲任务 OS_IdleTask() //任务优先级 #define START_TASK_PRIO 3 //任务堆栈大小 #define START_STK_SIZE 128 //任务控制块 OS_TCB StartTaskTCB; //任务堆栈 CPU_STK START_TASK_STK[START_STK_SIZE]; //任务函数 void start_task(void *p_arg); //任务优先级 #define TASK1_TASK_PRIO 4 //任务堆栈大小 #define TASK1_STK_SIZE 128 //任务控制块 OS_TCB Task1_TaskTCB; //任务堆栈 CPU_STK TASK1_TASK_STK[TASK1_STK_SIZE]; void task1_task(void *p_arg); //任务优先级 #define TASK2_TASK_PRIO 5 //任务堆栈大小 #define TASK2_STK_SIZE 128 //任务控制块 OS_TCB Task2_TaskTCB; //任务堆栈 CPU_STK TASK2_TASK_STK[TASK2_STK_SIZE]; void task2_task(void *p_arg); u8 share_resource[30]; ????????????//共享资源区 OS_SEM MY_SEM; ????????????//定义一个信号量,用于访问共享资源 int main(void)????????????????????????//主函数 { OS_ERR err; CPU_SR_ALLOC(); delay_init(); NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); uart_init(115200); LED_Init(); LCD_Init(); KEY_Init(); POINT_COLOR = RED; LCD_ShowString(30,10,200,16,16,"ALIENTEK STM32F1"); LCD_ShowString(30,30,200,16,16,"UCOSIII Examp 10-2"); LCD_ShowString(30,50,200,16,16,"Sem Share Resource"); LCD_ShowString(30,70,200,16,16,"ATOM@ALIENTEK"); LCD_ShowString(30,90,200,16,16,"2013/3/19"); OSInit(&err); ????//初始化UCOSIII OS_CRITICAL_ENTER(); ????????//进入临界区 OSTaskCreate((OS_TCB * )&StartTaskTCB, //任务控制块 (CPU_CHAR * )"start task", //任务名字 (OS_TASK_PTR )start_task, //任务函数 (void * )0, //传递给任务函数的参数 (OS_PRIO )START_TASK_PRIO, //任务优先级 (CPU_STK * )&START_TASK_STK[0], //任务堆栈基地址 (CPU_STK_SIZE)START_STK_SIZE/10, //任务堆栈深度限位 (CPU_STK_SIZE)START_STK_SIZE, //任务堆栈大小 (OS_MSG_QTY )0, //任务内部消息队列能够接收的最大消息数目,为0时禁止接收消息 (OS_TICK )0, //当使能时间片轮转时的时间片长度,为0时为默认长度, (void * )0, //用户补充的存储区 (OS_OPT )OS_OPT_TASK_STK_CHK|OS_OPT_TASK_STK_CLR, //任务选项 (OS_ERR * )&err); //存放该函数错误时的返回值 OS_CRITICAL_EXIT(); //退出临界区 OSStart(&err); //开启UCOSIII } void start_task(void *p_arg)????????????????//开始任务函数 { OS_ERR err; CPU_SR_ALLOC(); p_arg = p_arg; CPU_Init(); #if OS_CFG_STAT_TASK_EN > 0u OSStatTaskCPUUsageInit(&err); //统计任务 #endif #ifdef CPU_CFG_INT_DIS_MEAS_EN //如果使能了测量中断关闭时间 CPU_IntDisMeasMaxCurReset(); #endif #if OS_CFG_SCHED_ROUND_ROBIN_EN //当使用时间片轮转的时候 //使能时间片轮转调度功能,时间片长度为1个系统时钟节拍,既1*5=5ms OSSchedRoundRobinCfg(DEF_ENABLED,1,&err); #endif OS_CRITICAL_ENTER(); //进入临界区 OSSemCreate ((OS_SEM* )&MY_SEM,???????? //创建一个信号量 (CPU_CHAR* )"MY_SEM", (OS_SEM_CTR)1, (OS_ERR* )&err); OSTaskCreate((OS_TCB * )&Task1_TaskTCB, ???????????????? //创建TASK1任务 (CPU_CHAR * )"Task1 task", (OS_TASK_PTR )task1_task, (void * )0, (OS_PRIO )TASK1_TASK_PRIO, (CPU_STK * )&TASK1_TASK_STK[0], (CPU_STK_SIZE)TASK1_STK_SIZE/10, (CPU_STK_SIZE)TASK1_STK_SIZE, (OS_MSG_QTY )0, (OS_TICK )0, (void * )0, (OS_OPT )OS_OPT_TASK_STK_CHK|OS_OPT_TASK_STK_CLR, (OS_ERR * )&err); OSTaskCreate((OS_TCB * )&Task2_TaskTCB, ???????????????????? //创建TASK2任务 (CPU_CHAR * )"Task2 task", (OS_TASK_PTR )task2_task, (void * )0, (OS_PRIO )TASK2_TASK_PRIO, (CPU_STK * )&TASK2_TASK_STK[0], (CPU_STK_SIZE)TASK2_STK_SIZE/10, (CPU_STK_SIZE)TASK2_STK_SIZE, (OS_MSG_QTY )0, (OS_TICK )0, (void * )0, (OS_OPT )OS_OPT_TASK_STK_CHK|OS_OPT_TASK_STK_CLR, (OS_ERR * )&err); OS_CRITICAL_EXIT(); //退出临界区 OSTaskDel((OS_TCB*)0,&err); //删除start_task任务自身 } void task1_task(void *p_arg)????????????????//任务1的任务函数 { OS_ERR err; u8 task1_str[]="First task Running!"; while(1) { printf("\r\n任务1:\r\n"); LCD_Fill(0,110,239,319,CYAN); OSSemPend(&MY_SEM,0,OS_OPT_PEND_BLOCKING,0,&err); //请求信号量 memcpy(share_resource,task1_str,sizeof(task1_str)); ????//向共享资源区拷贝数据 delay_ms(300);????????????????????????????????//任务切换 printf("%s\r\n",share_resource); ????//串口输出共享资源区数据 OSSemPost (&MY_SEM,OS_OPT_POST_1,&err); //发送信号量 LED0 = ~LED0; OSTimeDlyHMSM(0,0,1,0,OS_OPT_TIME_PERIODIC,&err); //延时1s } } void task2_task(void *p_arg)????????????????????//任务2的任务函数 { OS_ERR err; u8 task2_str[]="Second task Running!"; while(1) { printf("\r\n任务2:\r\n"); LCD_Fill(0,110,239,319,BROWN); OSSemPend(&MY_SEM,0,OS_OPT_PEND_BLOCKING,0,&err); //请求信号量 memcpy(share_resource,task2_str,sizeof(task2_str)); //向共享资源区拷贝数据 delay_ms(300); printf("%s\r\n",share_resource); //串口输出共享资源区数据 OSSemPost (&MY_SEM,OS_OPT_POST_1,&err); //发送信号量 LED1 = ~LED1; OSTimeDlyHMSM(0,0,1,0,OS_OPT_TIME_PERIODIC,&err); //延时1s } }
信号量现在更多的被用来实现任务的同步以及任务和ISR间的同步,信号量用于任务同步如下图所示:
上图中用一个小旗子代表信号量,小旗子旁边的数值N为信号量计数值,表示发布信号量的次数累积值,ISR可以多次发布信号量,发布的次数会记录为N。一般情况下,N的初始值是0,表示事件还没有发生过。在初始化时,也可以将N的初值设为大于零的某个值,来表示初始情况下有多少信号量可用。
等待信号量的任务旁边的小沙漏表示等待任务可以设定超时时间。超时的意思是该任务只会等待一定时间的信号量,如果在这段时间内没有等到信号量,UCOSIII就会将任务置于就绪表中,并返回错误码。
例程要求:创建3个任务,任务A用于创建其他两个任务和一个初始值为0的信号量,任务C必须征得任务B的同意才能执行一次操作。
例子:
#include "sys.h" #include "delay.h" #include "usart.h" #include "led.h" #include "lcd.h" #include "key.h" #include "includes.h" //UCOSIII中以下优先级用户程序不能使用,ALIENTEK //将这些优先级分配给了UCOSIII的5个系统内部任务 //优先级0:中断服务服务管理任务 OS_IntQTask() //优先级1:时钟节拍任务 OS_TickTask() //优先级2:定时任务 OS_TmrTask() //优先级OS_CFG_PRIO_MAX-2:统计任务 OS_StatTask() //优先级OS_CFG_PRIO_MAX-1:空闲任务 OS_IdleTask() //任务优先级 #define START_TASK_PRIO 3 //任务堆栈大小 #define START_STK_SIZE 128 //任务控制块 OS_TCB StartTaskTCB; //任务堆栈 CPU_STK START_TASK_STK[START_STK_SIZE]; //任务函数 void start_task(void *p_arg); //任务优先级 #define TASK1_TASK_PRIO 4 //任务堆栈大小 #define TASK1_STK_SIZE 128 //任务控制块 OS_TCB Task1_TaskTCB; //任务堆栈 CPU_STK TASK1_TASK_STK[TASK1_STK_SIZE]; void task1_task(void *p_arg); //任务优先级 #define TASK2_TASK_PRIO 5 //任务堆栈大小 #define TASK2_STK_SIZE 128 //任务控制块 OS_TCB Task2_TaskTCB; //任务堆栈 CPU_STK TASK2_TASK_STK[TASK2_STK_SIZE]; void task2_task(void *p_arg); //LCD刷屏时使用的颜色 int lcd_discolor[14]={ WHITE, BLACK, BLUE, BRED, GRED, GBLUE, RED, MAGENTA, GREEN, CYAN, YELLOW,BROWN, BRRED, GRAY }; OS_SEM SYNC_SEM; //定义一个信号量,用于任务同步 int main(void)????????????????//主函数 { OS_ERR err; CPU_SR_ALLOC(); delay_init(); //时钟初始化 NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);//中断分组配置 uart_init(115200); //串口初始化 LED_Init(); //LED初始化 LCD_Init(); //LCD初始化 KEY_Init(); //按键初始化 POINT_COLOR = RED; LCD_ShowString(30,10,200,16,16,"ALIENTEK STM32F1"); LCD_ShowString(30,30,200,16,16,"UCOSIII Examp 10-3"); LCD_ShowString(30,50,200,16,16,"Sem Sync"); LCD_ShowString(30,70,200,16,16,"ATOM@ALIENTEK"); LCD_ShowString(30,90,200,16,16,"2015/3/19"); POINT_COLOR = BLACK; LCD_DrawRectangle(5,110,234,314); LCD_DrawLine(5,130,234,130); POINT_COLOR = RED; LCD_ShowString(30,111,200,16,16,"SYNC_SEM Value: 0"); POINT_COLOR = BLUE; OSInit(&err); //初始化UCOSIII OS_CRITICAL_ENTER(); //进入临界区 //创建开始任务 OSTaskCreate((OS_TCB * )&StartTaskTCB, //任务控制块 (CPU_CHAR * )"start task", //任务名字 (OS_TASK_PTR )start_task, //任务函数 (void * )0, //传递给任务函数的参数 (OS_PRIO )START_TASK_PRIO, //任务优先级 (CPU_STK * )&START_TASK_STK[0], //任务堆栈基地址 (CPU_STK_SIZE)START_STK_SIZE/10, //任务堆栈深度限位 (CPU_STK_SIZE)START_STK_SIZE, //任务堆栈大小 (OS_MSG_QTY )0, //任务内部消息队列能够接收的最大消息数目,为0时禁止接收消息 (OS_TICK )0, //当使能时间片轮转时的时间片长度,为0时为默认长度, (void * )0, //用户补充的存储区 (OS_OPT )OS_OPT_TASK_STK_CHK|OS_OPT_TASK_STK_CLR, //任务选项 (OS_ERR * )&err); //存放该函数错误时的返回值 OS_CRITICAL_EXIT(); //退出临界区 OSStart(&err); //开启UCOSIII } void start_task(void *p_arg)????????????????//开始任务函数 { OS_ERR err; CPU_SR_ALLOC(); p_arg = p_arg; CPU_Init(); #if OS_CFG_STAT_TASK_EN > 0u OSStatTaskCPUUsageInit(&err); //统计任务 #endif #ifdef CPU_CFG_INT_DIS_MEAS_EN //如果使能了测量中断关闭时间 CPU_IntDisMeasMaxCurReset(); #endif #if OS_CFG_SCHED_ROUND_ROBIN_EN //当使用时间片轮转的时候 //使能时间片轮转调度功能,时间片长度为1个系统时钟节拍,既1*5=5ms OSSchedRoundRobinCfg(DEF_ENABLED,1,&err); #endif OS_CRITICAL_ENTER(); //进入临界区 OSSemCreate ((OS_SEM* )&SYNC_SEM,???????????????????? //创建一个信号量 (CPU_CHAR* )"SYNC_SEM", (OS_SEM_CTR)0, (OS_ERR* )&err); OSTaskCreate((OS_TCB * )&Task1_TaskTCB, ???????????????? //创建TASK1任务 (CPU_CHAR * )"Task1 task", (OS_TASK_PTR )task1_task, (void * )0, (OS_PRIO )TASK1_TASK_PRIO, (CPU_STK * )&TASK1_TASK_STK[0], (CPU_STK_SIZE)TASK1_STK_SIZE/10, (CPU_STK_SIZE)TASK1_STK_SIZE, (OS_MSG_QTY )0, (OS_TICK )0, (void * )0, (OS_OPT )OS_OPT_TASK_STK_CHK|OS_OPT_TASK_STK_CLR, (OS_ERR * )&err); OSTaskCreate((OS_TCB * )&Task2_TaskTCB, ???????? //创建TASK2任务 (CPU_CHAR * )"Task2 task", (OS_TASK_PTR )task2_task, (void * )0, (OS_PRIO )TASK2_TASK_PRIO, (CPU_STK * )&TASK2_TASK_STK[0], (CPU_STK_SIZE)TASK2_STK_SIZE/10, (CPU_STK_SIZE)TASK2_STK_SIZE, (OS_MSG_QTY )0, (OS_TICK )0, (void * )0, (OS_OPT )OS_OPT_TASK_STK_CHK|OS_OPT_TASK_STK_CLR, (OS_ERR * )&err); OS_CRITICAL_EXIT(); //退出临界区 OSTaskDel((OS_TCB*)0,&err); //删除start_task任务自身 } void task1_task(void *p_arg)????????????????//任务1的任务函数 { u8 key; OS_ERR err; while(1) { key = KEY_Scan(0); //扫描按键 if(key==WKUP_PRES) { OSSemPost(&SYNC_SEM,OS_OPT_POST_1,&err);????//发送信号量 LCD_ShowxNum(150,111,SYNC_SEM.Ctr,3,16,0); //显示信号量值 } OSTimeDlyHMSM(0,0,0,10,OS_OPT_TIME_PERIODIC,&err); //延时10ms } } void task2_task(void *p_arg)????????????????//任务2的任务函数 { u8 num; OS_ERR err; while(1) { OSSemPend(&SYNC_SEM,0,OS_OPT_PEND_BLOCKING,0,&err); //请求信号量 num++; LCD_ShowxNum(150,111,SYNC_SEM.Ctr,3,16,0); //显示信号量值 LCD_Fill(6,131,233,313,lcd_discolor[num%14]); //刷屏 LED1 = ~LED1; OSTimeDlyHMSM(0,0,1,0,OS_OPT_TIME_PERIODIC,&err); //延时1s } }