STM32F104ZET6之ucosⅢ常用函数

一、OS的初始化与启动

  • 1.OS初始化,初始化各种内核对象和全局变量
函数原型:void  OSInit (OS_ERR  *p_err)
参数:p_err,用于返回错误码
返回值:无

它是第一个执行的函数。

  • 2.启动OS,创建任务后调用
函数原型:void  OSStart (OS_ERR  *p_err)
参数:p_err,用于返回错误码
返回值:无

二、任务的管理

  • 1.创建任务
void  OSTaskCreate (OS_TCB        *p_tcb,//类似于线程id,控制任务
                    CPU_CHAR      *p_name,//任务的名字,可以自定义
                    OS_TASK_PTR    p_task,//任务函数,类似线程函数
                    void          *p_arg,//任务传递参数,类似于给线程函数传递参数
                    OS_PRIO        prio,//任务的优先级
                    CPU_STK       *p_stk_base,//任务栈基址,提供一个数组基址(任务栈数组基地址)
                    CPU_STK_SIZE   stk_limit,//空出10%的栈空间给到堆栈检测函数使用,反过来说,当前任务只能使用90%栈空间
                    CPU_STK_SIZE   stk_size,//任务栈的大小,以字(32位)为单位
                    OS_MSG_QTY     q_size,//任务内消息队列的大小,若不使用,写0
                    OS_TICK        time_quanta,//时间片轮转调度算法,若不使用,写0(这是时间片的长度)
                    void          *p_ext,//提供额外存储空间用于存储浮点运算单元寄存器,若不提供,写NULL
                    OS_OPT         opt,//在创建任务的时候,提供额外操作,如果不使用,写OS_OPT_TASK_NONE
                    OS_ERR        *p_err)//返回错误码,没有错误的就返回OS_ERR_NONE

例如,创建任务1

//task control block,任务1控制块
OS_TCB Task1_TCB;
//任务1函数,类似线程函数
void task1(void *parg);
//任务1的任务堆栈,大小为128字,即512字节
CPU_STK task1_stk[128];

//创建任务1
	OSTaskCreate(	(OS_TCB *)&Task1_TCB,//任务控制块,等同于线程id									
					(CPU_CHAR *)"Task1",//任务的名字,可以自定义									
					(OS_TASK_PTR)task1,//任务函数,等同于线程函数									
					(void *)0,//给任务函数传递参数											
					(OS_PRIO)5,//任务优先级,数值越小,优先级越高											 	
					(CPU_STK *)task1_stk,//任务堆栈基地址									
					(CPU_STK_SIZE)128/10,//任务堆栈深度限位,用到这个位置,任务不能再继续使用									
					(CPU_STK_SIZE)128,//任务堆栈大小										
					(OS_MSG_QTY)0,//任务内消息队列的大小,不使用,写0											
					(OS_TICK)0,//时间片轮转调度算法,若不使用,写0(默认时间片长度)											
					(void  *)0,//提供额外存储空间用于存储浮点运算单元寄存器,若不提供,写NULL											
					(OS_OPT)OS_OPT_TASK_NONE,//在创建任务的时候,提供额外操作,如果不使用,写OS_OPT_TASK_NONE								
					&err//返回错误码,没有错误的就返回OS_ERR_NONE												
				);

注意: 创建任务特别检查传递的数组的大小是否空间充足,因为它是作为任务的栈空间使用,若空间不足,会导致程序不能执行,直接跑到HardFault_Handler这个函数。 原因如下: 1)任务里申请大空间的局部变量,例如数组、结构体......,将会占用大量的栈空间 2)任务里包含很多复杂的函数,将会占用大量的栈空间 3)若任务需要用到浮点数运算,要特别注意,出现HardFault_Handler可以尝试分配大一点任务栈,或则提供额外存储空间用于存储浮点运算单元寄存器
  • 2.任务挂起
    暂停任务的执行
void   OSTaskSuspend (OS_TCB  *p_tcb,
                      OS_ERR  *p_err)
参数: p_tcb,类似于线程id,控制任务
      p_err,返回错误码,没有错误的就返回OS_ERR_NONE
返回值:无
用途:1)如果当前任务不是经常要执行的,可以挂起
     2)保护共享资源,可以挂起
  • 3.任务恢复执行
void  OSTaskResume (OS_TCB  *p_tcb,
                    OS_ERR  *p_err)
参数:p_tcb,类似于线程id,控制任务
     p_err,返回错误码,没有错误的就返回OS_ERR_NONE
返回值:无

4.任务的删除

void  OSTaskDel (OS_TCB  *p_tcb,
                 OS_ERR  *p_err)
参数:p_tcb,类似于线程id,控制任务
     p_err,返回错误码,没有错误的就返回OS_ERR_NONE
返回值:无
用途:专门用于初始化硬件,一般来说,硬件只做一次初始化,完毕后可将该任务删除。

三、中断代码编写

void USART1_IRQHandler(void)                	
{
	uint8_t d=0;

	//进入中断,告诉UCOS,要停止任务调度,因为中断处理是一个原子过程,不可拆分 bug kernel:interrupt atom
    //中断里面不能再有任务调度
	OSIntEnter(); 

	//添加中断处理代码
	.....   

	//退出中断,告诉UCOS,准备可以进行任务调度
	OSIntExit();  
}

四、互斥锁

互斥锁常用于任务之间的共享资源(任务之间访问到相同的函数、相同的全局变量)访问,当某个任务得到互斥锁后,就可以访问共享资源,其他任务等待该任务释放互斥锁才能进行访问。

void task(void *parg)
{
	while(1)
	{
		加锁
		访问共享资源
		解锁(立即)

		.....
		加锁
		访问共享资源
		解锁(立即)
		....
	}
}
  • 1.创建互斥锁
void  OSMutexCreate (OS_MUTEX  *p_mutex,
                     CPU_CHAR  *p_name,
                     OS_ERR    *p_err)
参数:p_mutex,互斥锁对象
     p_name,互斥锁名字
     p_err,返回错误码,没有错误的就返回OS_ERR_NONE
返回值:无
  • 2.等待互斥锁,若等待成功,则锁定共享资源
void  OSMutexPend (OS_MUTEX  *p_mutex,
                   OS_TICK    timeout,
                   OS_OPT     opt,
                   CPU_TS    *p_ts,
                   OS_ERR    *p_err)
参数:p_mutex,互斥锁对象
     timeout,超时时间,默认写0,一直等待
     opt,设置当前等待互斥锁的阻塞方式,默认写OS_OPT_PEND_BLOCKING,阻塞等待
     p_ts,用于记录等待互斥锁花了多长时间,默认写NULL,不记录。
     p_err,返回错误码,没有错误的就返回OS_ERR_NONE
  • 3.释放互斥锁,解锁
void  OSMutexPost (OS_MUTEX  *p_mutex,
                   OS_OPT     opt,
                   OS_ERR    *p_err)
参数:p_mutex,互斥锁对象
     opt,释放互斥锁后希望其他等待锁的任务(最高优先级就绪)得到立即执行,可以填写这个参数OS_OPT_POST_NONE。
         若使用了OS_OPT_POST_NO_SCHED这个参数,得到互斥锁的任务不会立即执行,会等到当前任务让出cpu才会执行。
     p_err,返回错误码,没有错误的就返回OS_ERR_NONE

五、信号量

  • 信号量常用于任务的同步,通过该信号,就能够控制某个任务的执行,这个信号具有计数值,因此,可以称为计数信号量。

  • 信号量的P、V操作,P表示申请一个资源,每次P操作使信号量减1,V是释放一个资源,每次V操作使信号量加1。

  • 信号量表示的是当前可用的资源个数,当信号量为负时,申请资源的进程就只能等待了。
    所以,信号量是负的多少,就表明有多少个进程申请了资源但无资源可用只能处于等待状态。

  • 1.创建信号量

void  OSSemCreate (OS_SEM      *p_sem,
                   CPU_CHAR    *p_name,
                   OS_SEM_CTR   cnt,
                   OS_ERR      *p_err)
参数:p_sem,信号量对象
     p_name,信号量名字
     cnt,信号量的初值
     p_err,返回错误码,没有错误的就返回OS_ERR_NONE
  • 2.等待信号量
OS_SEM_CTR  OSSemPend (OS_SEM   *p_sem,
                       OS_TICK   timeout,
                       OS_OPT    opt,
                       CPU_TS   *p_ts,
                       OS_ERR   *p_err)
参数:p_sem,信号量对象
     timeout,超时时间,默认写0,一直等待
     opt,设置当前等待互斥锁的阻塞方式,默认写OS_OPT_PEND_BLOCKING,阻塞等待
     p_ts,用于记录等待互斥锁花了多长时间,默认写NULL,不记录。
     p_err,返回错误码,没有错误的就返回OS_ERR_NONE
  • 3.释放信号量
OS_SEM_CTR  OSSemPost (OS_SEM  *p_sem,
                       OS_OPT   opt,
                       OS_ERR  *p_err)
参数:p_sem,信号量对象
     opt,信号量接收方 
        OS_OPT_POST_1,只释放信号量给最高优先级且就绪的任务,类似于UDP的点播
        OS_OPT_POST_ALL,释放给所有等待信号量的任务,类似于UDP的广播
     p_err,返回错误码,没有错误的就返回OS_ERR_NONE

六、消息队列

消息队列常用于数据的传输,等待消息队列成功后,得到的是消息内容指针(也就是数据的首地址)。

  • 1.创建消息队列
void  OSQCreate (OS_Q        *p_q,
                 CPU_CHAR    *p_name,
                 OS_MSG_QTY   max_qty,
                 OS_ERR      *p_err)
参数:p_q,消息队列对象
     p_name,消息队列的名字
     max_qty,消息队列支持多少条消息
     p_err,返回错误码,没有错误的就返回OS_ERR_NONE
  • 2.等待消息
void  *OSQPend (OS_Q         *p_q,
                OS_TICK       timeout,
                OS_OPT        opt,
                OS_MSG_SIZE  *p_msg_size,
                CPU_TS       *p_ts,
                OS_ERR       *p_err)
参数:p_q,消息队列对象
     timeout,超时时间,默认写0,一直等待
     opt,设置当前等待互斥锁的阻塞方式,默认写OS_OPT_PEND_BLOCKING,阻塞等待
     p_msg_size,消息的大小(用于获取消息的带下)
     p_ts,用于记录等待消息花了多长时间,默认写NULL,不记录。
     p_err,返回错误码,没有错误的就返回OS_ERR_NONE

返回值:NULL,没有接收到消息
       非NULL,指向消息内容

注意:等待消息且使用完毕后,需将消息内容清空,否则在后续的使用可能出现问题。

  • 3.发送消息
void  OSQPost (OS_Q         *p_q,
               void         *p_void,
               OS_MSG_SIZE   msg_size,
               OS_OPT        opt,
               OS_ERR       *p_err)
参数:p_q,消息队列对象
     p_void,消息的内容
     msg_size,消息的大小
     opt,OS_OPT_POST_FIFO+OS_OPT_POST_ALL(发送给所有等待消息的任务)或许OS_OPT_POST_FIFO
     p_err,返回错误码,没有错误的就返回OS_ERR_NONE

七、事件标志组

事件标志组是专门管理标志位,一个事件标志组可以管理32个标志位。
在前后台系统(裸机模式下),经常会用到标志位,查询标志位是否置位,有明显的缺点,CPU得不到休息,会一直工作查询,增加CPU的功耗。

int main(void)
{
     while(1)
     {
          if(flag1)
          {

          }

          if(flag2)
          {

          }

          if(flag3)
          {

          }
     }
}
  • 1.创建事件标志组
void  OSFlagCreate (OS_FLAG_GRP  *p_grp,
                    CPU_CHAR     *p_name,
                    OS_FLAGS      flags,
                    OS_ERR       *p_err)
参数:p_grp,事件标志组对象
     p_name,事件标志组的名字
     flags,事件标志组里所有标志位的初值,默认写0
     p_err,返回错误码,没有错误的就返回OS_ERR_NONE
  • 2.等待事件标志组
OS_FLAGS  OSFlagPend (OS_FLAG_GRP  *p_grp,
                      OS_FLAGS      flags,
                      OS_TICK       timeout,
                      OS_OPT        opt,
                      CPU_TS       *p_ts,
                      OS_ERR       *p_err)
参数:p_grp,事件标志组对象
     flags,要等待哪些标志位;0x01<==>0000 0001,则等待bit0;0x05<==>0000 0101,则等待bit0和bit2;0x83<==>1000 0011,则等待bit0、bit1、bit7。
     timeout,超时时间,默认写0,一直等待
     opt,默认写以下格式
        OS_OPT_PEND_FLAG_SET_ANY + OS_OPT_PEND_FLAG_CONSUME+OS_OPT_PEND_BLOCKING
        OS_OPT_PEND_FLAG_SET_ANY,等待任意一个标志位置位
        OS_OPT_PEND_FLAG_CONSUME,等待任意一个标志位成功后,就自动将其清零
        OS_OPT_PEND_BLOCKING,阻塞等待
     p_ts,用于记录等待事件花了多长时间,默认写NULL,不记录。
     p_err,返回错误码,没有错误的就返回OS_ERR_NONE
返回值:返回等待成功的标志组的所有标志位。
  • 3.设置事件标志组
OS_FLAGS  OSFlagPost (OS_FLAG_GRP  *p_grp,
                      OS_FLAGS      flags,
                      OS_OPT        opt,
                      OS_ERR       *p_err)
参数:p_grp,事件标志组对象
     flags,结合opt参数一起使用。设置/清零哪些标志位。同上flags
     opt,
        OS_OPT_POST_FLAG_SET,对应的bit置位
        OS_OPT_POST_FLAG_CLR,对应的bit清零
     p_err,返回错误码,没有错误的就返回OS_ERR_NONE

八、软件定时器

内核提供了一个模拟定时器的机制,类似于任务,但是占用资源少,只能做一些简单的定时控制。
在软件定时器,不能添加时间管理函数、阻塞等待函数(等待互斥锁/信号量/事件标志组/消息队列)。

  • 1.创建软件定时器
void  OSTmrCreate (OS_TMR               *p_tmr,
                   CPU_CHAR             *p_name,
                   OS_TICK               dly,
                   OS_TICK               period,
                   OS_OPT                opt,
                   OS_TMR_CALLBACK_PTR   p_callback,
                   void                 *p_callback_arg,
                   OS_ERR               *p_err)

参数:p_tmr,软件定时器对象
     p_name,软件定时器的名字
     dly,启动定时器后,延迟多长时间执行,默认隐含dly*10ms
     period,定时周期,默认隐含period*10ms
     opt
        OS_OPT_TMR_ONE_SHOT,软件定时器执行一遍
        OS_OPT_TMR_PERIODIC,软件定时器周期性执行
     p_callback,软件定时器执行的回调函数
         void  p_callback (OS_TMR *p_tmr, void *p_arg);
     p_callback_arg,传递参数给软件定时器的回调函数
     p_err,返回错误码,没有错误的就返回OS_ERR_NONE
  • 2.启动软件定时器
CPU_BOOLEAN  OSTmrStart (OS_TMR  *p_tmr,
                         OS_ERR  *p_err)
参数:p_tmr,软件定时器对象
     p_err,返回错误码,没有错误的就返回OS_ERR_NONE

返回值:DEF_TRUE is the timer was started
       DEF_FALSE if not or upon an error

注意:当前p_callback会1秒执行一遍,当调用睡眠2秒和等待事件标志组,该函数还是1秒被调用一次。

void p_callback(OS_TMR *p_tmr, void *p_arg)
{
	OS_ERR  err;
	
	OS_FLAGS  flags;
	
	printf("timer_callback ...\r\n");
	
	//无效
	OSFlagPend(&g_flag_grp,0x03,0, OS_OPT_PEND_FLAG_SET_ANY+OS_OPT_PEND_FLAG_CONSUME+OS_OPT_PEND_BLOCKING,NULL,&err);
	
	//无效
	delay_ms(2000);
}

九、等待多个内核对象

内核对象,可以是信号量、消息队列、互斥锁、事件标志组等。

  • 等待多个内核对象,只能等待信号量和消息队列。
//This function only allows you to pend on semaphores and/or message queues.
OS_OBJ_QTY  OSPendMulti (OS_PEND_DATA  *p_pend_data_tbl,
                         OS_OBJ_QTY     tbl_size,
                         OS_TICK        timeout,
                         OS_OPT         opt,
                         OS_ERR        *p_err)
参数:p_pend_data_tbl,内核对象的数组
     tbl_size,内核对象的数目
     timeout,超时时间,默认写0,一直等待
     opt,设置当前等待多个内核对象的阻塞方式,默认写OS_OPT_PEND_BLOCKING,阻塞等待
     p_err,返回错误码,没有错误的就返回OS_ERR_NONE

返回值:>0,就绪内核对象的数目
       =0,超时或发生错误

注意:使用上述函数,确保OS_CFG_Q_EN、OS_CFG_SEM_EN、OS_CFG_PEND_MULTI_EN宏定义开关有效,并可在os_cfg.h文件找到。

  • 等待多个内核对象示例:
//1.修改os_cfg.h文件中如下宏,使用等待多个内核对象
//#define OS_CFG_PEND_MULTI_EN            0u   /* Enable (1) or Disable (0) code generation for multi-pend feature      */
#define OS_CFG_PEND_MULTI_EN            1u   /* Enable (1) or Disable (0) code generation for multi-pend feature      */

//2.创建任务块
......

//3.创建信号量对象、消息队列对象
static OS_SEM g_sem;
OS_Q g_queue;
//创建内核对象数组
OS_PEND_DATA g_pend_tb1[2];

//4.创建任务,用于测试
......

//5.创建信号量、创建消息队列
OSSemCreate(&g_sem, "sem", 0, &err);
OSQCreate(&g_queue, "queue", 32, &err);

void task1(void *parg)
{
	OS_ERR err;
	OS_OBJ_QTY obj_num = 0;
	//6.初始化等待多个内核对象数组
	g_pend_tb1[0].PendObjPtr = (OS_PEND_OBJ *)&g_sem;
	g_pend_tb1[1].PendObjPtr = (OS_PEND_OBJ *)&g_queue;
	
	printf("task1 is create ok\r\n");
	
	while(1)
	{	
        //7.等待多个内核对象,并进行分析
		obj_num = OSPendMulti(g_pend_tb1,/
        2,//等待两个内核对象
        0,//一直等待
        OS_OPT_PEND_BLOCKING,//阻塞等待
        &err);
		
        if(obj_num)
		{
			if(g_pend_tb1[0].RdyObjPtr == (OS_PEND_OBJ *)&g_sem)
			{
				printf("sem get.\r\n");
			}
			
			if(g_pend_tb1[1].RdyObjPtr == (OS_PEND_OBJ *)&g_queue)
			{
				printf("message: %s Length: %d\r\n", (char *)g_pend_tb1[1].RdyMsgPtr, g_pend_tb1[1].RdyMsgSize);
                //不需要了,则清空已经读取的数据
                memset(g_pend_tb1[1].RdyMsgPtr, 0 , g_pend_tb1[1].RdyMsgSize);
			}
		}

		//delay_ms(1000);	
	} 
}

理想代码框架:

  • 1.一个任务管理一个硬件
  • 2.任务间数据传输使用消息队列
  • 3.任务间共享资源访问使用互斥锁
  • 4.任务的同步使用信号量
  • 5.标志位的管理使用事件标志组
posted @ 2019-11-27 12:38  pxysource  阅读(337)  评论(0编辑  收藏  举报