信号量是RTOS中一种用于任务间同步的机制,可以实现任务间同步或互斥。简单来说,信号量就相当于是裸机中的标志位,只是在RTOS中信号量会比裸机中的标志位使用更加安全。信号量是一个非负的整数,当有任务获取信号量时,信号量的个数会减1,当有任务释放信号量时,信号量的个数会加1。当信号量个数为0时,就代表当前系统中的信号量已经使用完毕,没有多余的信号量。UCOSIII中的信号量不会传递数据
我们首先以一个日常生活中常见的问题进行类比。
假设有一个停车场,里面有3个停车位,刚开始停车位是空的。这时候A车进入停车场,此时停车场的停车位还有2个,过一会B车也进入了停车场,此时停车位还有1个,过一会C车进入停车场,此时停车位已经被占满了,没有空闲的停车位。这时候D车也想进入停车场,但是因为已经没有停车位了,所以D车只能在外面等,过一会A车离开了停车场,此时停车位有1个,这时D车因为有了空闲的停车位,就可以进入停车场。
在上面的场景中,共享资源是停车场里面的3个停车位,相当于是3个信号量,ABCD四辆车相当于是4个任务,车进入停车位相当于是获取信号量,信号量减1,车离开停车位相当于是释放信号量,信号量加1。D车等待停车位相当于是阻塞。
上面的那个停车场问题用上图来进行表达,上图中定义了信号量个数为3,则代表系统中有3个可用的信号量,任务1、任务2、任务3依次获取了3个信号量,此时如果任务4想获取信号量,就只能阻塞等待或者超过等待时间自动退出,除非此时任务1/2/3中的其中一个任务释放信号量,否则任务4永远都不会得到信号量。
在信号量创建时,用户需要指定信号量的个数。
/*
*p_sem:信号量对象
*p_name:信号量名字
*cnt:信号量个数
*p_err:错误代码
*/
void OSSemCreate (OS_SEM *p_sem,
CPU_CHAR *p_name,
OS_SEM_CTR cnt,
OS_ERR *p_err)
当信号量被删除后,系统将不能继续该信号量
/*
*p_sem:信号量对象
*opt:用户选择
*p_err:错误代码
返回值: >0 有等待信号量的任务个数
==0 没有任务等待信号量
*/
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
/*
*p_sem:信号量对象
*opt:用户选择
*p_err:错误代码
返回值: 当前剩余的信号量
*/
OS_SEM_CTR OSSemPost (OS_SEM *p_sem,
OS_OPT opt,
OS_ERR *p_err)
opt选项可以选择OS_OPT_POST_1、OS_OPT_POST_ALL和OS_OPT_POST_NO_SCHED
/*
*p_sem:信号量对象
*timeout:超时等待时间
*opt:用户选择
*p_ts:时间戳
*p_err:错误代码
返回值: 当前剩余的信号量
*/
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
改例程是任务1每隔1秒释放一次信号量,任务2则阻塞等待信号量
/*
*********************************************************************************************************
* EXAMPLE CODE
*
* (c) Copyright 2013; Micrium, Inc.; Weston, FL
*
* All rights reserved. Protected by international copyright laws.
* Knowledge of the source code may not be used to write a similar
* product. This file may only be used in accordance with a license
* and should not be redistributed in any way.
*********************************************************************************************************
*/
/*
*********************************************************************************************************
*
* EXAMPLE CODE
*
* IAR Development Kits
* on the
*
* STM32F429II-SK KICKSTART KIT
*
* Filename : app.c
* Version : V1.00
* Programmer(s) : YS
*********************************************************************************************************
*/
/*
*********************************************************************************************************
* INCLUDE FILES
*********************************************************************************************************
*/
#include
/*
*********************************************************************************************************
* LOCAL DEFINES
*********************************************************************************************************
*/
/*
*********************************************************************************************************
* LOCAL GLOBAL VARIABLES
*********************************************************************************************************
*/
/* ----------------- APPLICATION GLOBALS -------------- */
static OS_TCB AppTaskStartTCB;
static CPU_STK AppTaskStartStk[APP_CFG_TASK_START_STK_SIZE];
#define APPTASK1NAME "App Task1"
#define APP_TASK1_PRIO 4
#define APP_TASK1_STK_SIZE 1024
static OS_TCB AppTask1TCB;
static void AppTask1 (void *p_arg);
static CPU_STK AppTask1Stk[APP_TASK1_STK_SIZE];
#define APPTASK2NAME "App Task2"
#define APP_TASK2_PRIO 5
#define APP_TASK2_STK_SIZE 1024
static OS_TCB AppTask2TCB;
static void AppTask2 (void *p_arg);
static CPU_STK AppTask2Stk[APP_TASK2_STK_SIZE];
static OS_SEM sem;
/*
*********************************************************************************************************
* FUNCTION PROTOTYPES
*********************************************************************************************************
*/
static void AppTaskStart (void *p_arg);
/*
*********************************************************************************************************
* main()
*
* Description : This is the standard entry point for C code. It is assumed that your code will call
* main() once you have performed all necessary initialization.
*
* Arguments : none
*
* Returns : none
*********************************************************************************************************
*/
int main(void)
{
OS_ERR err;
OSInit(&err); /* Init uC/OS-III. */
OSTaskCreate((OS_TCB *)&AppTaskStartTCB, /* Create the start task */
(CPU_CHAR *)"App Task Start",
(OS_TASK_PTR )AppTaskStart,
(void *)0u,
(OS_PRIO )APP_CFG_TASK_START_PRIO,
(CPU_STK *)&AppTaskStartStk[0u],
(CPU_STK_SIZE )AppTaskStartStk[APP_CFG_TASK_START_STK_SIZE / 10u],
(CPU_STK_SIZE )APP_CFG_TASK_START_STK_SIZE,
(OS_MSG_QTY )0u,
(OS_TICK )0u,
(void *)0u,
(OS_OPT )(OS_OPT_TASK_STK_CHK | OS_OPT_TASK_STK_CLR),
(OS_ERR *)&err);
OSStart(&err); /* Start multitasking (i.e. give control to uC/OS-III). */
}
/*
*********************************************************************************************************
* STARTUP TASK
*
* Description : This is an example of a startup task. As mentioned in the book's text, you MUST
* initialize the ticker only once multitasking has started.
*
* Arguments : p_arg is the argument passed to 'AppTaskStart()' by 'OSTaskCreate()'.
*
* Returns : none
*
* Notes : 1) The first line of code is used to prevent a compiler warning because 'p_arg' is not
* used. The compiler should not generate any code for this statement.
*********************************************************************************************************
*/
static void AppTaskStart (void *p_arg)
{
CPU_INT32U cpu_clk_freq;
CPU_INT32U cnts;
OS_ERR err;
(void)p_arg;
BSP_Init();
CPU_Init(); /* Initialize the uC/CPU services */
cpu_clk_freq = BSP_CPU_ClkFreq(); /* Determine SysTick reference freq. */
cnts = cpu_clk_freq /* Determine nbr SysTick increments */
/ (CPU_INT32U)OSCfg_TickRate_Hz;
OS_CPU_SysTickInit(cnts); /* Init uC/OS periodic time src (SysTick). */
Mem_Init(); /* Initialize memory managment module */
Math_Init(); /* Initialize mathematical module */
#if OS_CFG_STAT_TASK_EN > 0u
OSStatTaskCPUUsageInit(&err); /* Compute CPU capacity with no task running */
#endif
#ifdef CPU_CFG_INT_DIS_MEAS_EN
CPU_IntDisMeasMaxCurReset();
#endif
#if (APP_CFG_SERIAL_EN == DEF_ENABLED)
App_SerialInit(); /* Initialize Serial communication for application ... */
#endif
OSTaskCreate((OS_TCB *)&AppTask1TCB, // 线程TCB
(CPU_CHAR *)APPTASK1NAME, // 线程名字
(OS_TASK_PTR ) AppTask1, // 线程入口函数
(void *) "TASK1", // 线程参数
(OS_PRIO ) APP_TASK1_PRIO, // 线程优先级
(CPU_STK *)&AppTask1Stk[0], // 线程栈起始地址
(CPU_STK_SIZE) APP_TASK1_STK_SIZE / 10, // 栈深度的限制位置
(CPU_STK_SIZE) APP_TASK1_STK_SIZE, // 栈大小
(OS_MSG_QTY ) 20u, // 最大的消息个数
(OS_TICK ) 0u, // 时间片
(void *) 0, // 向用户提供的内存位置的指针
(OS_OPT )(OS_OPT_TASK_STK_CHK | OS_OPT_TASK_STK_CLR), // 线程特定选项
(OS_ERR *)&err); // 错误标志
if(OS_ERR_NONE == err)
printf("%s Create Success\r\n",APPTASK1NAME);
else
printf("%s Create Error\r\n",APPTASK1NAME);
OSTaskCreate((OS_TCB *)&AppTask2TCB, // 线程TCB
(CPU_CHAR *)APPTASK2NAME, // 线程名字
(OS_TASK_PTR ) AppTask2, // 线程入口函数
(void *) "TASK2", // 线程参数
(OS_PRIO ) APP_TASK2_PRIO, // 线程优先级
(CPU_STK *)&AppTask2Stk[0], // 线程栈起始地址
(CPU_STK_SIZE) APP_TASK2_STK_SIZE / 10, // 栈深度的限制位置
(CPU_STK_SIZE) APP_TASK2_STK_SIZE, // 栈大小
(OS_MSG_QTY ) 20u, // 最大的消息个数
(OS_TICK ) 0u, // 时间片
(void *) 0, // 向用户提供的内存位置的指针
(OS_OPT )(OS_OPT_TASK_STK_CHK | OS_OPT_TASK_STK_CLR), // 线程特定选项
(OS_ERR *)&err); // 错误标志
if(OS_ERR_NONE == err)
printf("%s Create Success\r\n",APPTASK2NAME);
else
printf("%s Create Error\r\n",APPTASK2NAME);
OSSemCreate(&sem,(CPU_CHAR*)"sem",(OS_SEM_CTR)2,&err);
if(OS_ERR_NONE == err)
printf("Create Sem Success\r\n");
else
printf("Create Sem Error\r\n");
OSTaskDel ( & AppTaskStartTCB, & err );
}
static void AppTask1 (void *p_arg)
{
OS_ERR err;
while(DEF_TRUE)
{
OSTimeDly ( 1000, OS_OPT_TIME_DLY, & err ); // 1000ms释放一次信号量
OSSemPost(&sem,OS_OPT_POST_ALL,&err);
if(OS_ERR_NONE == err)
printf("%s send post success\r\n",__func__);
else
printf("%s send post error\r\n",__func__);
}
}
static void AppTask2 (void *p_arg)
{
OS_ERR err;
while(DEF_TRUE)
{
OSSemPend (&sem,0,OS_OPT_PEND_BLOCKING,0,&err); // 获取信号量
if(OS_ERR_NONE == err)
printf("%s send pend success\r\n",__func__);
else
printf("%s send pend error\r\n",__func__);
}
}