STM32F4X UCOSIII 信号量


在以往的裸机编程中,如果我们需要判断某个事件是否已经发生,通常会使用一个标志位来进行判断,当事件已经发生时,就将该标志位置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__);
		
	}
	
}

在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: 在STM32F4上使用UCOS III操作系统,可以通过串口实现消息的传输。UCOS III是一款基于优先级的实时操作系统,可以帮助我们在嵌入式系统中进行任务的管理和调度。 在使用UCOS III中的消息传输功能时,我们可以选择使用串口进行消息的发送和接收。串口是一种常见的通信方式,可以通过串口通信模块将数据发送给其他设备或从其他设备接收数据。 首先,我们需要配置串口的相关参数,包括波特率、数据位、校验位和停止位等。在STM32F4上,我们可以使用STM32CubeMX工具来生成相应的初始化代码。 接下来,我们需要创建任务来实现消息的发送和接收。在UCOS III中,我们可以使用任务控制块(Task Control Block, TCB)来管理任务。我们可以创建一个发送任务和一个接收任务,分别负责处理发送和接收消息的功能。在发送任务中,我们可以使用串口发送相关的API函数,将消息发送给目标设备。在接收任务中,我们可以使用串口接收相关的API函数,接收其他设备发送的消息。 为了确保消息的正确传输和处理,我们可以使用信号或消息队列来实现同步和互斥。我们可以创建一个信号或消息队列来保护发送和接收任务,在任务完成相应的操作后释放信号或消息队列。 总之,通过配置串口参数并创建相关任务,我们可以在STM32F4上使用UCOS III来实现消息的串口传输。这种方式能够方便地进行设备间的通信,并能够实现消息的发送和接收功能。同时,使用UCOS III操作系统能够帮助我们有效地管理任务,并提供一定的同步和互斥机制,以确保消息的正确性。 ### 回答2: STM32F4是一款强大的32位微控制器,而uC/OS-III是一种实时多任务操作系统。在使用STM32F4和uC/OS-III时,我们可以通过消息队列与串口进行通讯。 UC/OS-III中的任务可以通过消息队列来传递信息和数据。对于串口通讯,我们可以使用消息队列作为数据缓冲区,将要发送或接收的数据存储在队列中。 在发送数据时,任务将要发送的数据写入消息队列,并通知串口任务进行发送。串口任务将从消息队列中读取数据,并通过串口发送出去。 在接收数据时,串口任务将收到的数据存储在消息队列中,并通知接收任务进行处理。接收任务将从消息队列中读取数据,并进行相应的处理,如解析数据、执行相应的操作等。 通过使用消息队列,可以方便地实现任务之间的通讯和数据传递。同时,使用uC/OS-III操作系统能够提供多任务调度和优先级控制的功能,使得串口通讯更加可靠和高效。 需要注意的是,在使用消息队列进行串口通讯时,需要合理地设置消息队列的大小和处理速度,以避免消息丢失或溢出的问题。此外,还需要确保任务之间的协调和正确的处理任务间的同步和互斥关系。 通过以上的方式,我们可以很方便地在STM32F4和uC/OS-III中实现消息队列与串口的通讯,实现数据的发送和接收,并提供高效可靠的通讯方式。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值