STM32F4X UCOSIII 互斥量
- 互斥量的概念
- 互斥量的工作机制
- 洗手间问题
- 互斥量优先级继承
- 没有优先级继承
- 优先级继承
- UCOSIII互斥量API
- 互斥量创建函数
- 互斥量删除函数
- 互斥量申请函数
- 互斥量释放函数
- UCOSIII 互斥量例程
互斥量的概念
UCOSIII中的互斥量是一种特殊的信号量,它的本质也是一种信号量,不具备信息传递的功能。互斥量的主要作用是可以实现共享资源的互斥访问,提供优先级翻转机制。当一个任务持有互斥量时,除非该任务主动释放互斥量,否则其他任务都申请不到该互斥量。
互斥量的工作机制
洗手间问题
我们可以用一个实际的例子来说明一下互斥量的应用场景。假设有一个洗手间,洗手间里面有一把锁,假如现在游客A要进入洗手间,进入洗手间之后把锁锁上了,这时游客B也想进入洗手间,但是因为此时已经有人在洗手间,所以游客B无法进入,所以此时游客B只能在门外等,一直等待游客A上完洗手间之后,把锁打开,游客B才可以进入。
在上面的例子中,洗手间是共享资源,洗手间里面的锁是互斥量,游客A和游客B是两个任务。游客A上锁相当于是任务A申请互斥量,游客B等待相当于是任务B阻塞,游客A打开锁相当于是任务A解锁。
互斥量优先级继承
互斥量跟信号量非常类似,但是互斥量比信号量多了一个优先级继承的机制,下面可以通过一个例子来进行说明。
没有优先级继承
假设系统中有3个任务,分别是低优先级任务L,中优先级任务M和高优先级任务H。
- 任务L在使用共享资源,此时任务H被唤醒,开始执行,但是任务L还没释放共享资源。
- 任务H也要使用共享资源,但是任务L还没释放,所以任务H在阻塞等待。
- 任务M被唤醒,任务M优先级比任务L要高,任务M被执行,任务L还没有释放资源
- 任务M执行完毕,归还CPU资源,任务L继续执行
- 任务L执行完毕,释放资源,此时任务H获取资源,开始执行。
从上面的例子可以看到,任务H是高优先级任务,但是等待时间过长,这对系统的实时性会造成一定的影响。
优先级继承
下面来看一下有优先级继承的效果,同样地也是假设系统中有3个任务,分别是低优先级任务L,中优先级任务M和高优先级任务H。
- 任务L在使用共享资源,此时任务H被唤醒,开始执行,但是任务L还没释放共享资源。
- 任务H也要使用共享资源,但是任务L还没释放,此时因为互斥量的优先级继承机制,任务L的优先级会被临时提升到跟任务H相同的优先级。
- 任务M被唤醒,但是因为任务M的优先级要比任务L的低,所以任务M处于就绪状态,没有被执行。
- 任务L运行完毕,释放资源,此时任务L的优先级会变成原来的优先级,任务H获取资源,开始运行。
- 任务H运行完成,释放资源,任务M开始运行。
- 任务M运行完毕,系统正常运行,按照设定好的优先级运行。
可以看到,当使用互斥量时,任务L的优先级会被临时提高,这样就可以保证一些高优先级的任务能够尽快运行,尽可能提高系统的实时性。
UCOSIII互斥量API
互斥量创建函数
/*
* p_mutex:互斥量对象
* p_name:互斥量名字
* p_err:错误代码
*/
void OSMutexCreate (OS_MUTEX *p_mutex,
CPU_CHAR *p_name,
OS_ERR *p_err)
互斥量删除函数
/*
* p_mutex:要删除的互斥量对象
* opt:用户选项
* p_err:错误代码
返回值: 等于0:删除成功
大于0:互斥量上有等待的任务
*/
OS_OBJ_QTY OSMutexDel (OS_MUTEX *p_mutex,
OS_OPT opt,
OS_ERR *p_err)
其中opt可以选择OS_OPT_DEL_NO_PEND和OS_OPT_DEL_ALWAYS。
-
OS_OPT_DEL_NO_PEND:删除互斥量如果该互斥量上有挂起的任务,则等待挂起的任务恢复才删除。
-
OS_OPT_DEL_ALWAYS:不管该互斥量上是否有挂起的任务,直接删除互斥量
互斥量申请函数
/*
* p_mutex:互斥量对象
* timeout:超时时间
* opt:用户选项
* p_ts:时间戳
* p_err:错误代码
*/
void OSMutexPend (OS_MUTEX *p_mutex,
OS_TICK timeout,
OS_OPT opt,
CPU_TS *p_ts,
OS_ERR *p_err)
opt选项可以选择OS_OPT_PEND_BLOCKING、OS_OPT_PEND_NON_BLOCKING
- OS_OPT_PEND_BLOCKING:阻塞等待互斥量。
- OS_OPT_PEND_NON_BLOCKING:不阻塞等待互斥量,如果任务等待时间超过设定的超时时间,任务会恢复并返回一个错误代码。
互斥量释放函数
/*
* p_mutex:互斥量对象
* opt:用户选项
* p_err:错误代码
*/
void OSMutexPost (OS_MUTEX *p_mutex,
OS_OPT opt,
OS_ERR *p_err)
opt选项可以选择OS_OPT_POST_NONE和OS_OPT_POST_NO_SCHED。
- OS_OPT_POST_NONE:释放互斥量不任何操作
- OS_OPT_POST_NO_SCHED:释放互斥量的同时不进行调度
UCOSIII 互斥量例程
/*
*********************************************************************************************************
* 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 <includes.h>
/*
*********************************************************************************************************
* 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 3
#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 4
#define APP_TASK2_STK_SIZE 1024
static OS_TCB AppTask2TCB;
static void AppTask2 (void *p_arg);
static CPU_STK AppTask2Stk[APP_TASK2_STK_SIZE];
#define APPTASK3NAME "App Task3"
#define APP_TASK3_PRIO 5
#define APP_TASK3_STK_SIZE 1024
static OS_TCB AppTask3TCB;
static void AppTask3 (void *p_arg);
static CPU_STK AppTask3Stk[APP_TASK3_STK_SIZE];
static OS_MUTEX mutex;
/*
*********************************************************************************************************
* 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 ) 5u, // 最大的消息个数
(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 ) 5u, // 最大的消息个数
(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);
OSTaskCreate((OS_TCB *)&AppTask3TCB, // 线程TCB
(CPU_CHAR *)APPTASK3NAME, // 线程名字
(OS_TASK_PTR ) AppTask3, // 线程入口函数
(void *) "TASK3", // 线程参数
(OS_PRIO ) APP_TASK3_PRIO, // 线程优先级
(CPU_STK *)&AppTask3Stk[0], // 线程栈起始地址
(CPU_STK_SIZE) APP_TASK3_STK_SIZE / 10, // 栈深度的限制位置
(CPU_STK_SIZE) APP_TASK3_STK_SIZE, // 栈大小
(OS_MSG_QTY ) 5u, // 最大的消息个数
(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",APPTASK3NAME);
else
printf("%s Create Error\r\n",APPTASK3NAME);
OSMutexCreate(&mutex,"mutex",&err);
if(OS_ERR_NONE == err)
printf("Create Mutex Success\r\n");
else
printf("Create Mutex Error\r\n");
OSTaskDel ( & AppTaskStartTCB, & err );
}
static void AppTask1 (void *p_arg)
{
OS_ERR err;
while(DEF_TRUE)
{
printf("APP1 获取互斥量 \r\n");
OSMutexPend(&mutex,0,OS_OPT_PEND_BLOCKING,0,&err);
printf("APP1 释放互斥量 \r\n");
OSMutexPost(&mutex,OS_OPT_POST_NONE,&err);
OSTimeDly ( 1000, OS_OPT_TIME_DLY, & err ); // 1s运行一次
}
}
static void AppTask2 (void *p_arg)
{
OS_ERR err;
while(DEF_TRUE)
{
printf("Task1 Runing Task2 Arg %s\r\n",p_arg);
OSTimeDly ( 200, OS_OPT_TIME_DLY, & err ); // 200ms运行一次
}
}
static void AppTask3 (void *p_arg)
{
OS_ERR err;
static CPU_INT32U i;
while(DEF_TRUE)
{
printf("APP3 获取互斥量 \r\n");
OSMutexPend(&mutex,0,OS_OPT_PEND_BLOCKING,0,&err);
for (i=0; i<600000; i++) //模拟低优先级任务占用信号量
{
OSSched();//发起任务调度
}
printf("APP3 释放互斥量 \r\n");
OSMutexPost(&mutex,OS_OPT_POST_NONE,&err);
OSTimeDly ( 1000, OS_OPT_TIME_DLY, & err ); // 1s运行一次
}
}