STM32F4X UCOSIII 信号量

2023-09-21 14:23:06


在以往的裸机编程中,如果我们需要判断某个事件是否已经发生,通常会使用一个标志位来进行判断,当事件已经发生时,就将该标志位置1,否则就将该标志位置0。但是有了RTOS之后,我们可以用信号量来代替裸机中的标志位。

信号量概念

信号量是RTOS中一种用于任务间同步的机制,可以实现任务间同步或互斥。简单来说,信号量就相当于是裸机中的标志位,只是在RTOS中信号量会比裸机中的标志位使用更加安全。信号量是一个非负的整数,当有任务获取信号量时,信号量的个数会减1,当有任务释放信号量时,信号量的个数会加1。当信号量个数为0时,就代表当前系统中的信号量已经使用完毕,没有多余的信号量。UCOSIII中的信号量不会传递数据

信号量工作机制

停车场问题

我们首先以一个日常生活中常见的问题进行类比。
假设有一个停车场,里面有3个停车位,刚开始停车位是空的。这时候A车进入停车场,此时停车场的停车位还有2个,过一会B车也进入了停车场,此时停车位还有1个,过一会C车进入停车场,此时停车位已经被占满了,没有空闲的停车位。这时候D车也想进入停车场,但是因为已经没有停车位了,所以D车只能在外面等,过一会A车离开了停车场,此时停车位有1个,这时D车因为有了空闲的停车位,就可以进入停车场。
在上面的场景中,共享资源是停车场里面的3个停车位,相当于是3个信号量,ABCD四辆车相当于是4个任务,车进入停车位相当于是获取信号量,信号量减1,车离开停车位相当于是释放信号量,信号量加1。D车等待停车位相当于是阻塞。

UCOSIII信号量工作机制

在这里插入图片描述
上面的那个停车场问题用上图来进行表达,上图中定义了信号量个数为3,则代表系统中有3个可用的信号量,任务1、任务2、任务3依次获取了3个信号量,此时如果任务4想获取信号量,就只能阻塞等待或者超过等待时间自动退出,除非此时任务1/2/3中的其中一个任务释放信号量,否则任务4永远都不会得到信号量。

信号量常用API

信号量创建

在信号量创建时,用户需要指定信号量的个数。

/*
*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

  • 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

  • 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

  • OS_OPT_PEND_BLOCKING:阻塞等待信号量,除非有信号量,否则任务不会恢复
  • OS_OPT_PEND_NON_BLOCKING:不阻塞等待信号量,如果任务等待时间超过设定的超时时间,任务会恢复并返回一个错误代码

UCOSIII 信号量例程

改例程是任务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  <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          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__);
		
	}
	
}

在这里插入图片描述

更多推荐

什么是媒体邀约?邀请媒体的流程

传媒如春雨,润物细无声,大家好,我是51媒体网胡老师。媒体邀约,简单来说,是企业或组织为了某个特定的事件、活动或新闻,主动邀请媒体进行报道或参与。这个过程旨在确保媒体的出席并进一步传播相关信息,从而达到更广泛的受众。邀请媒体的流程大致可以分为以下几个步骤:媒体媒体宣传的目的:明确为何需要邀请媒体,是为了推广新产品、发布

1_图神经网络GNN基础知识学习

文章目录安装PyTorchGeometric安装工具包在KarateClub数据集上使用图卷积网络(GCN)进行节点分类两个画图函数GraphNeuralNetworks数据集:Zachary'skarateclubnetwork.PyTorchGeometric数据集介绍edge_index使用networkx可视化

Bash脚本学习 - 条件句、数组、for循环,函数

1.条件测试[和]是一个用于执行条件测试的命令。它们必须用空格分隔开,并且在[后面和]前面必须有空格。-eq是一个比较运算符,表示等于(equal)。它用于比较两个值是否相等。2.条件句在ifelseifelse.sh文件中,#!/bin/bashif[${1,,}=herbert];thenecho"Oh,you'r

利用gpt进行GMV变化数据分析

prompt:现在已知男性GMV从800降至600,女性GMV从1200至1300,请计算男女GMV变动对整体GMV变动的贡献度output:在这个问题中,我们要计算男性和女性的GMV(总销售额)变动对整体GMV变动的贡献度。首先,计算男性和女性的GMV变动量:男性GMV变动量:600-800=-200600−800=

排序算法-----快速排序(递归)

目录前言快速排序步骤原理大致思路流程动态图代码实现算法分析空间复杂度时间复杂度稳定性前言今天我们开始学习排序算法中的快速排序算法,既然叫快速排序,那肯定是体现在快这方面,相较于前面所学习过的排序算法,快速排序是比这些算法的速度要快的,将来很多时候我们都会用到快速排序来去做排序的,下面就一起来学习吧!快速排序快速排序(Q

第 363 场 LeetCode 周赛题解

A计算K置位下标对应元素的和模拟classSolution{public:intpop_cnt(intx){//求x的二进制表示中的1的位数intres=0;for(;x;x>>=1)if(x&1)res++;returnres;}intsumIndicesWithKSetBits(vector<int>&nums,i

Git概述

目录一、什么是Git二、什么是版本控制系统三、Git和SVN对比SVN集中式SVN优缺点Git分布式Git优缺点四、Git工作流程四个工作区域工作流程五、Git下载与安装一、什么是Git很多人都知道,林纳斯·托瓦兹在1991年创建了开源的Linux,从此,Linux系统不断发展,已经成为最大的服务器系统软件了。Git是

漏刻有时数据可视化Echarts组件开发(28):异形柱图、pictorialBar和dataZoom组件的使用

构建容器vardom=document.getElementById('container');varmyChart=echarts.init(dom,null,{renderer:'canvas',useDirtyRect:false});模拟数据vardataList=[{name:'班级一',value:120,

【redis总结】

文章目录1、redis简介2、为什么要选择redis做缓存3、数据结构4、redis多线程模型redis6.0的变化5、redis持久化AOF的实现过程RDB的实现过程6、redis集群的搭建7、redis过期删除和淘汰策略8、redis的内存淘汰策略1、redis简介Redis(RemoteDictionarySer

全国职业技能大赛云计算--高职组赛题卷④(容器云)

全国职业技能大赛云计算--高职组赛题卷④(容器云)第二场次题目:容器云平台部署与运维任务1DockerCE及私有仓库安装任务(5分)任务2基于容器的web应用系统部署任务(15分)任务3基于容器的持续集成部署任务(15分)任务4Kubernetes容器云平台部署与运维(15分,本任务只公布考试范围,不公布赛题)需要环境

Maven常见面试题总结

Maven简介Maven是一个项目管理和整合工具。Maven包含了一个项目对象模型(ProjectObjectModel),一组标准集合,一个项目生命周期管理系统(ProjectLifecycleManagementSystem),一个依赖管理系统(DependencyManagementSystem),和用来运行定义

热文推荐