• 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 ...
    ");
    	
    	//无效
    	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
    ");
    	
    	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.
    ");
    			}
    			
    			if(g_pend_tb1[1].RdyObjPtr == (OS_PEND_OBJ *)&g_queue)
    			{
    				printf("message: %s Length: %d
    ", (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.标志位的管理使用事件标志组
  • 相关阅读:
    Python_装饰器复习_30
    Python_每日习题_0002_个税计算
    linux-文件流4种读取方式
    Max Sum Plus Plus
    棋盘问题
    noip数学
    P3384 【模板】树链剖分
    P2419 [USACO08JAN]牛大赛Cow Contest
    poj3159 Candies(差分约束)
    小K的农场(差分约束)
  • 原文地址:https://www.cnblogs.com/risesource/p/11941451.html
Copyright © 2020-2023  润新知