UcosII移植、调度、功能、运行流程解析

吐槽下csdn的编辑功能,真的菜的抠脚。

接下来,陆陆续续会将语雀的个人笔记搬移到csdn上。

该篇文档过长,建议目录查找关键位置。

UcosII是剥夺内核(每个系统节拍会切换到最高就绪优先级任务),系统运行取决于任务的优先级。任务优先级的关系:优先级越小,数值越大;优先级越大,数值越小。

移植

1、到ucos官方下载相应mcu型号的源码文件。
2、得到
在这里插入图片描述

EvalBoards 文件夹里面包含评估板相关文件,提取部分,
在这里插入图片描述在这里插入图片描述
APP.c就不需要了,钩子函数在
在这里插入图片描述

BSP.c文件,跟板子相关的函数可以删除了,特别注意时间戳所需要的函数实现要保留。
BSP_CPU_ClkFreq();
CPU_TS_TmrInit();
CPU_TS_TmrRd();
CPU_TS32_to_uSec();

3、直接复制LIB跟CPU文件夹
在这里插入图片描述

4、在3个文件夹下删除不需要的编译器环境,RealView为Keil环境
在这里插入图片描述

5、修改编译器asm汇编文件 OS_CPU_A.ASM:
在这里插入图片描述

keil环境下,查看函数定义前面是否是extern如果不是,改成extern。
这样修改的目的,是因为MDK编程环境不认识PUBLIC,要用EXPORT.

6、最重要的2个函数的连接:
PendSV_Handler() 和 SysTick_Handler();
在asm文件里面将 OS_CPU_PendSVHandler
在这里插入图片描述
更改为MCU启动文件中断向量表中的PendSV_Handler:
在这里插入图片描述
7、配置SysTick心跳
UcosII的心跳时间是由SysTick系统滴答来实现,具体心跳的间隔由配置文件OS_TICKS_PER_SEC宏来决定,关于时钟节拍的时间,如果中断时间越短,它的实时性越好,但是CPU做的无用功越多,同样,定时的时间越长,虽然CPU做的无用功越少,但是,它的实时性越差,根据CPU性能推荐10-100毫秒。

/*********************************************************************
* 函数名称: Delay_init
* 函数描述: 延时及ucosII心跳初始化
* 入		参:  ucSysclk 系统时钟频率
* 出    参:无
* 返 回 值: 无
**********************************************************************/
void Delay_init(UINT8 ucSysclk)
{
#if SYSTEM_SUPPORT_OS 									//如果需要支持OS.
	UINT32 ulReload;
#endif
 	SysTick->CTRL&=~(1<<2);								//SYSTICK使用外部时钟源	 
	fac_us=ucSysclk/8;										//不论是否使用OS,fac_us都需要使用
#if SYSTEM_SUPPORT_OS 									//如果需要支持OS.
	ulReload=ucSysclk/8;									//每秒钟的计数次数 单位为M	   
	ulReload*=1000000/OS_DISPATCH_NUM;		//根据delay_ostickspersec设定溢出时间
																				//reload为24位寄存器,最大值:16777216,在180M下,约合0.699s左右	
	fac_ms=1000/OS_DISPATCH_NUM;					//代表OS可以延时的最少单位	   
	SysTick->CTRL|=1<<1;   								//开启SYSTICK中断
	SysTick->LOAD=ulReload; 							//每1/OS_DISPATCH_NUM秒中断一次	
	SysTick->CTRL|=1<<0;   								//开启SYSTICK    
#else
	fac_ms=((u32)SYSCLK*1000)/8;					//非OS下,代表每个ms需要的systick时钟数   
#endif
}	

8、修改SysTick中断
在it(中断)文件中修改SysTick_Handler()函数,添加UcosII的调度程序。

void SysTick_Handler(void)
{
		if( OS_RUN == Valid )				
		{
			OSIntEnter();								//进入中断
			OSTimeTick();       				//调用ucos的时钟服务程序               
			OSIntExit();       	 				//触发任务切换软中断
		}
}

配置文件

所有移植完成后,开始配置UcosII功能,配置文件为os_cfg.h:

                                       /* ---------------------- MISCELLANEOUS ----------------------- */
#define OS_APP_HOOKS_EN           0u   /* Application-defined hooks are called from the uC/OS-II hooks 从uC/OS-II钩子调用应用程序定义的钩子*/
#define OS_ARG_CHK_EN             0u   /* Enable (1) or Disable (0) argument checking                  启用(1)或禁用(0)参数检查*/
#define OS_CPU_HOOKS_EN           1u   /* uC/OS-II hooks are found in the processor port files         uC/OS-II钩子可以在处理器端口文件中找到*/

#define OS_DEBUG_EN               0u   /* Enable(1) debug variables                                    启用debug*/

#define OS_EVENT_MULTI_EN         0u   /* Include code for OSEventPendMulti()                          启用OSEventPendMulti()函数*/
#define OS_EVENT_NAME_EN          0u   /* Enable names for Sem, Mutex, Mbox and Q                      使能事件名称*/

#define OS_LOWEST_PRIO           63u   /* Defines the lowest priority that can be assigned ...         最低优先级*/
                                       /* ... MUST NEVER be higher than 254!                           */

#define OS_MAX_EVENTS            20u   /* Max. number of event control blocks in your application      最大应用程序中事件控制块的数量*/
#define OS_MAX_FLAGS              5u   /* Max. number of Event Flag Groups    in your application      最大应用程序中的事件标志组数目*/
#define OS_MAX_MEM_PART           0u   /* Max. number of memory partitions                             最大内存分区数*/
#define OS_MAX_QS                 5u   /* Max. number of queue control blocks in your application      最大应用程序中队列控制块的数量*/
#define OS_MAX_TASKS             20u   /* Max. number of tasks in your application, MUST be >= 2       最大应用程序中的任务数,必须是>= 2*/

#define OS_SCHED_LOCK_EN          1u   /* Include code for OSSchedLock() and OSSchedUnlock()           */

#define OS_TICK_STEP_EN           1u   /* Enable tick stepping feature for uC/OS-View                  */
#define OS_TICKS_PER_SEC        200u   /* Set the number of ticks in one second                        设置一秒内的节拍数*/
 
#define OS_TLS_TBL_SIZE           0u   /* Size of Thread-Local Storage Table                           线程本地存储表的大小*/


                                       /* --------------------- TASK STACK SIZE ---------------------- 任务堆栈大小*/
#define OS_TASK_TMR_STK_SIZE    128u   /* Timer      task stack size (# of OS_STK wide entries)        计时器任务栈大小(OS_STK范围条目的#)*/
#define OS_TASK_STAT_STK_SIZE   128u   /* Statistics task stack size (# of OS_STK wide entries)        统计任务堆栈大小(OS_STK范围条目的#)*/
#define OS_TASK_IDLE_STK_SIZE   128u   /* Idle       task stack size (# of OS_STK wide entries)        空闲任务栈大小(OS_STK宽度条目的#)*/


                                       /* --------------------- TASK MANAGEMENT ---------------------- 任务管理*/
#define OS_TASK_CHANGE_PRIO_EN    1u   /*     Include code for OSTaskChangePrio()                      */
#define OS_TASK_CREATE_EN         1u   /*     Include code for OSTaskCreate()                          */
#define OS_TASK_CREATE_EXT_EN     1u   /*     Include code for OSTaskCreateExt()                       */
#define OS_TASK_DEL_EN            1u   /*     Include code for OSTaskDel()                             */
#define OS_TASK_NAME_EN           1u   /*     Enable task names                                        启用任务名称*/
#define OS_TASK_PROFILE_EN        1u   /*     Include variables in OS_TCB for profiling                在OS_TCB中包含用于分析的变量*/
#define OS_TASK_QUERY_EN          1u   /*     Include code for OSTaskQuery()                           */
#define OS_TASK_REG_TBL_SIZE      1u   /*     Size of task variables array (#of INT32U entries)        任务变量数组的大小(INT32U项的#)*/
#define OS_TASK_STAT_EN           1u   /*     Enable (1) or Disable(0) the statistics task             启用(1)或禁用(0)统计任务*/
#define OS_TASK_STAT_STK_CHK_EN   1u   /*     Check task stacks from statistic task                    从统计任务检查任务堆栈*/
#define OS_TASK_SUSPEND_EN        1u   /*     Include code for OSTaskSuspend() and OSTaskResume()      */
#define OS_TASK_SW_HOOK_EN        1u   /*     Include code for OSTaskSwHook()                          */


                                       /* ----------------------- EVENT FLAGS ------------------------ 事件标志*/
#define OS_FLAG_EN                1u   /* Enable (1) or Disable (0) code generation for EVENT FLAGS    为事件标志启用(1)或禁用(0)代码生成*/
#define OS_FLAG_ACCEPT_EN         1u   /*     Include code for OSFlagAccept()                          */
#define OS_FLAG_DEL_EN            1u   /*     Include code for OSFlagDel()                             */
#define OS_FLAG_NAME_EN           1u   /*     Enable names for event flag group                        */
#define OS_FLAG_QUERY_EN          1u   /*     Include code for OSFlagQuery()                           */
#define OS_FLAG_WAIT_CLR_EN       1u   /* Include code for Wait on Clear EVENT FLAGS                   包括等待清除事件标志的代码*/
#define OS_FLAGS_NBITS           16u   /* Size in #bits of OS_FLAGS data type (8, 16 or 32)            OS_FLAGS数据类型的大小(8、16或32*/


                                       /* -------------------- MESSAGE MAILBOXES --------------------- 消息邮箱*/
#define OS_MBOX_EN                1u   /* Enable (1) or Disable (0) code generation for MAILBOXES      为邮箱启用(1)或禁用(0)代码生成*/
#define OS_MBOX_ACCEPT_EN         1u   /*     Include code for OSMboxAccept()                          */
#define OS_MBOX_DEL_EN            1u   /*     Include code for OSMboxDel()                             */
#define OS_MBOX_PEND_ABORT_EN     1u   /*     Include code for OSMboxPendAbort()                       */
#define OS_MBOX_POST_EN           1u   /*     Include code for OSMboxPost()                            */
#define OS_MBOX_POST_OPT_EN       1u   /*     Include code for OSMboxPostOpt()                         */
#define OS_MBOX_QUERY_EN          1u   /*     Include code for OSMboxQuery()                           */


                                       /* --------------------- MEMORY MANAGEMENT -------------------- 内存管理*/
#define OS_MEM_EN                 0u   /* Enable (1) or Disable (0) code generation for MEMORY MANAGER 为内存管理器启用(1)或禁用(0)代码生成*/
#define OS_MEM_NAME_EN            1u   /*     Enable memory partition names                            启用内存分区名称*/
#define OS_MEM_QUERY_EN           1u   /*     Include code for OSMemQuery()                            */


                                       /* ---------------- MUTUAL EXCLUSION SEMAPHORES --------------- 互斥信号量*/
#define OS_MUTEX_EN               1u   /* Enable (1) or Disable (0) code generation for MUTEX          启用(1)或禁用(0)互斥的代码生成*/
#define OS_MUTEX_ACCEPT_EN        1u   /*     Include code for OSMutexAccept()                         */
#define OS_MUTEX_DEL_EN           1u   /*     Include code for OSMutexDel()                            */
#define OS_MUTEX_QUERY_EN         1u   /*     Include code for OSMutexQuery()                          */


                                       /* ---------------------- MESSAGE QUEUES ---------------------- 消息队列*/
#define OS_Q_EN                   1u   /* Enable (1) or Disable (0) code generation for QUEUES         为队列启用(1)或禁用(0)代码生成*/
#define OS_Q_ACCEPT_EN            1u   /*     Include code for OSQAccept()                             */
#define OS_Q_DEL_EN               1u   /*     Include code for OSQDel()                                */
#define OS_Q_FLUSH_EN             1u   /*     Include code for OSQFlush()                              */
#define OS_Q_PEND_ABORT_EN        1u   /*     Include code for OSQPendAbort()                          */
#define OS_Q_POST_EN              1u   /*     Include code for OSQPost()                               */
#define OS_Q_POST_FRONT_EN        1u   /*     Include code for OSQPostFront()                          */
#define OS_Q_POST_OPT_EN          1u   /*     Include code for OSQPostOpt()                            */
#define OS_Q_QUERY_EN             1u   /*     Include code for OSQQuery()                              */


                                       /* ------------------------ SEMAPHORES ------------------------ 信号量*/
#define OS_SEM_EN                 1u   /* Enable (1) or Disable (0) code generation for SEMAPHORES     为信号量启用(1)或禁用(0)代码生成*/
#define OS_SEM_ACCEPT_EN          1u   /*    Include code for OSSemAccept()                            */
#define OS_SEM_DEL_EN             1u   /*    Include code for OSSemDel()                               */
#define OS_SEM_PEND_ABORT_EN      1u   /*    Include code for OSSemPendAbort()                         */
#define OS_SEM_QUERY_EN           1u   /*    Include code for OSSemQuery()                             */
#define OS_SEM_SET_EN             1u   /*    Include code for OSSemSet()                               */


                                       /* --------------------- TIME MANAGEMENT ---------------------- 时间管理*/
#define OS_TIME_DLY_HMSM_EN       1u   /*     Include code for OSTimeDlyHMSM()                         */
#define OS_TIME_DLY_RESUME_EN     1u   /*     Include code for OSTimeDlyResume()                       */
#define OS_TIME_GET_SET_EN        1u   /*     Include code for OSTimeGet() and OSTimeSet()             */
#define OS_TIME_TICK_HOOK_EN      1u   /*     Include code for OSTimeTickHook()                        */


                                       /* --------------------- TIMER MANAGEMENT --------------------- 软件定时器*/
#define OS_TMR_EN                 1u   /* Enable (1) or Disable (0) code generation for TIMERS         启用(1)或禁用(0)软件定时器代码生成*/
#define OS_TMR_CFG_MAX           16u   /*     Maximum number of timers                                 最大定时器数*/
#define OS_TMR_CFG_NAME_EN        1u   /*     Determine timer names                                    确定计时器的名字*/
#define OS_TMR_CFG_WHEEL_SIZE     8u   /*     Size of timer wheel (#Spokes)                            计时器轮尺寸(辐条数)*/
#define OS_TMR_CFG_TICKS_PER_SEC 100u  /*     Rate at which timer management task runs (Hz)            计时器管理任务运行的速率(Hz)*/
#define OS_TASK_TMR_PRIO		  		0u   /* 														 																 软件定时器的优先级,设置为最高		*/

初始化

UcosII的初始化函数用于初始化配置,在os_cfg.h文件配置的功能会通过宏编译来选择性初始化。
在这里插入图片描述

核心调度函数

SysTick_Handler

函数用于根据时钟节拍来寻找延时完毕的任务,并将其设置成就绪状态,然后在就绪任务中获取最高优先级任务并调用OSIntExit函数来切换至该任务,在任何中断中,要用OSIntEnter()和OSIntExit()函数来表面进入和退出中断,OSIntExit函数会检查中断嵌套深度,并始终会在无中断嵌套后,才开始任务的调度。
在这里插入图片描述

OSIntEnter

这个函数用于通知uC/OS-II您将要为一个中断服务服务程序(ISR)。这允许uC/OS-II跟踪中断嵌套,从而只在最后一个嵌套ISR执行调度:

void  OSIntEnter (void)
{
    if (OSRunning == OS_TRUE) {
        if (OSIntNesting < 255u) {
            OSIntNesting++;                      /* Increment ISR nesting level                        */
        }
    }
}

OSTimeTick

函数通过while轮询TCB块任务有OSTCBDly延时的,发生1次心跳进行减1操作,并且判断延时是否到达,到达后会将相应的任务从新设置为就绪状态。
在这里插入图片描述

OSIntExit

通知uCOSII已经完成了对ISR的服务。当最后一个嵌套ISR已经完成,uC/OS-II将调用调度器来确定是否一个新的高优先级任务准备运行,最后OS_SchedNew(),查找当前最高优先级就绪任务,通过OSIntCtxSw中断级调度函数触发PendSV异常来进行调度:

void  OSIntExit (void)
{
#if OS_CRITICAL_METHOD == 3u                               /* Allocate storage for CPU status register */
    OS_CPU_SR  cpu_sr = 0u;
#endif

    if (OSRunning == OS_TRUE) {
        OS_ENTER_CRITICAL();
        if (OSIntNesting > 0u) {                           /* Prevent OSIntNesting from wrapping       */
            OSIntNesting--;
        }
        if (OSIntNesting == 0u) {                          /* Reschedule only if all ISRs complete ... */
            if (OSLockNesting == 0u) {                     /* ... and not locked.                      */
                OS_SchedNew();
                OSTCBHighRdy = OSTCBPrioTbl[OSPrioHighRdy];
                if (OSPrioHighRdy != OSPrioCur) {          /* No Ctx Sw if current task is highest rdy */
#if OS_TASK_PROFILE_EN > 0u
                    OSTCBHighRdy->OSTCBCtxSwCtr++;         /* Inc. # of context switches to this task  */
#endif
                    OSCtxSwCtr++;                          /* Keep track of the number of ctx switches */

#if OS_TASK_CREATE_EXT_EN > 0u
#if defined(OS_TLS_TBL_SIZE) && (OS_TLS_TBL_SIZE > 0u)
                    OS_TLS_TaskSw();
#endif
#endif
                    OSIntCtxSw();                          /* Perform interrupt level ctx switch       */
                }
            }
        }
        OS_EXIT_CRITICAL();
    }
}

OS_SchedNew

在这里插入图片描述
在UcosII中,为了节约CPU任务调度时间,不可能每次轮询查找所有任务那些就绪了,于是用了任务就绪组跟任务就绪位表,可以很方便的直接找出当前最高优先级的任务。
如当前有4个组有任务就绪,OSRdyGrp表示为0xF,将0xF代入OSUnMapTbl[OSRdyGrp]表,OSUnMapTbl数组内下标15的值为0,既找出了当前最高优先级的任务组,并赋值给y,再将y代入OSUnMapTbl[OSRdyTbl[y]]表中找出最高优先级任务的位位置(OSRdyTbl[y]里面保存了任务的位位置的偏移,创建任务确定的)最终得到OSPrioHighRdy(包含了任务在8*8的矩阵中的位置信息,高2位空,中3位为Y,低3位为X),合成最高优先级且已经就绪的任务横坐标和纵坐标。
OSRdyGrp就绪组信息和OSRdyTbl就绪位信息会在任务创建、等待完成、调度的时候填充进去,同样,也会在任务挂起、删除、等待、调度中将自己删除。

OSRdyGrp(1字节)表示任务就绪组,OSRdyGrp每一位代表1组,1组表示8个任务,Y是OSRdyGrp任务就绪组中的组位置,表示任务在0-8个组中那个组有任务就绪了,X是任务在OSRdyTbl[ptcb->OSTCBY]就绪位表中的位位置,表示任务在0-8位中那个位有任务就绪了,这个数组是按照就绪组序号存放的。
在这里插入图片描述
OSUnMapTbl[]:就绪表数组:数组中每一项是一个字节,第N项代表了第N个就绪组,字节数据代表了该优先级组中的优先级偏移(范围是从0~7,就是低三位),此时OSRdyGrp就绪组可能多个位被置位,表示可能多个优先级组有就绪任务;
OSRdyTbl[]就绪表数组某个元素代表了某组中多个任务中的某个任务就绪,也是最低位0为最高优先级,OSRdyTbl这个要特别注意的是该变量为数组,参数是通过任务的优先级右移的值来决定下标位置,其实也就是按8个任务分为1组。即:OSRdyTbl[0]装的任务0-7,OSRdyTbl[1]装的任务8-15。
OSUnMapTbl[]表将2的8次方种可能全部算好了,为了节约时间,所以用查表的方式进行高就绪优先级任务的选择:

INT8U  const  OSUnMapTbl[256] = {
    0u, 0u, 1u, 0u, 2u, 0u, 1u, 0u, 3u, 0u, 1u, 0u, 2u, 0u, 1u, 0u, /* 0x00 to 0x0F                   */
    4u, 0u, 1u, 0u, 2u, 0u, 1u, 0u, 3u, 0u, 1u, 0u, 2u, 0u, 1u, 0u, /* 0x10 to 0x1F                   */
    5u, 0u, 1u, 0u, 2u, 0u, 1u, 0u, 3u, 0u, 1u, 0u, 2u, 0u, 1u, 0u, /* 0x20 to 0x2F                   */
    4u, 0u, 1u, 0u, 2u, 0u, 1u, 0u, 3u, 0u, 1u, 0u, 2u, 0u, 1u, 0u, /* 0x30 to 0x3F                   */
    6u, 0u, 1u, 0u, 2u, 0u, 1u, 0u, 3u, 0u, 1u, 0u, 2u, 0u, 1u, 0u, /* 0x40 to 0x4F                   */
    4u, 0u, 1u, 0u, 2u, 0u, 1u, 0u, 3u, 0u, 1u, 0u, 2u, 0u, 1u, 0u, /* 0x50 to 0x5F                   */
    5u, 0u, 1u, 0u, 2u, 0u, 1u, 0u, 3u, 0u, 1u, 0u, 2u, 0u, 1u, 0u, /* 0x60 to 0x6F                   */
    4u, 0u, 1u, 0u, 2u, 0u, 1u, 0u, 3u, 0u, 1u, 0u, 2u, 0u, 1u, 0u, /* 0x70 to 0x7F                   */
    7u, 0u, 1u, 0u, 2u, 0u, 1u, 0u, 3u, 0u, 1u, 0u, 2u, 0u, 1u, 0u, /* 0x80 to 0x8F                   */
    4u, 0u, 1u, 0u, 2u, 0u, 1u, 0u, 3u, 0u, 1u, 0u, 2u, 0u, 1u, 0u, /* 0x90 to 0x9F                   */
    5u, 0u, 1u, 0u, 2u, 0u, 1u, 0u, 3u, 0u, 1u, 0u, 2u, 0u, 1u, 0u, /* 0xA0 to 0xAF                   */
    4u, 0u, 1u, 0u, 2u, 0u, 1u, 0u, 3u, 0u, 1u, 0u, 2u, 0u, 1u, 0u, /* 0xB0 to 0xBF                   */
    6u, 0u, 1u, 0u, 2u, 0u, 1u, 0u, 3u, 0u, 1u, 0u, 2u, 0u, 1u, 0u, /* 0xC0 to 0xCF                   */
    4u, 0u, 1u, 0u, 2u, 0u, 1u, 0u, 3u, 0u, 1u, 0u, 2u, 0u, 1u, 0u, /* 0xD0 to 0xDF                   */
    5u, 0u, 1u, 0u, 2u, 0u, 1u, 0u, 3u, 0u, 1u, 0u, 2u, 0u, 1u, 0u, /* 0xE0 to 0xEF                   */
    4u, 0u, 1u, 0u, 2u, 0u, 1u, 0u, 3u, 0u, 1u, 0u, 2u, 0u, 1u, 0u  /* 0xF0 to 0xFF                   */
};

如prio优先级为12的任务:

ptcb->OSTCBY             = (INT8U)(prio >> 3u);         				//Y 01
ptcb->OSTCBX             = (INT8U)(prio & 0x07u);   						//X 04
ptcb->OSTCBBitY          = (OS_PRIO)(1uL <<  ptcb->OSTCBY);     //TCB_Y 02
ptcb->OSTCBBitX          = (OS_PRIO)(1uL <<  ptcb->OSTCBX);     //TCB_X 10
OSRdyGrp                |= ptcb->OSTCBBitY;        							//02
OSRdyTbl[ptcb->OSTCBY]  |= ptcb->OSTCBBitX;        							//10

代入查表:

y=OSUnMapTbl[OSRdyGrp];                //OSRdyGrp值为02,OSUnMapTbl表中的02值为01,所以y值为01
x=OSUnMapTbl[OSRdyTbl[y]]);            //OSRdyTbl[y]为OSRdyTbl[1],这里面存的是ptcb->OSTCBBitX的值0x10,OSUnMapTbl表中0x10的值为04。所以x等于04

任务优先级OSPrioHighRdy的组成为:1个字节,高2为为0,因为只有64个,中间3位为Y,低3位为X。
最后 OSPrioHighRdy = y <<3 + x; //OSPrioHighRdy = 0x0C。
得到的Y就是第几行,X就是第几位,任务优先级12的任务在第1行第4个。从0开始数。
找到最高优先级的就绪任务后,通过OSTCBPrioTbl[OSPrioHighRdy]会将相应的优先级任务的TCB块指针赋值给OSTCBHighRdy,然后调用OSIntCtxSw。

OSIntCtxSw

该函数为汇编函数,用于在中断结束时进行任务调度的函数,触发PendSV异常

;********************************************************************************************************
;                   PERFORM A CONTEXT SWITCH (From interrupt level) - OSIntCtxSw()
;
; Note(s) : 1)当OSIntExit()确定需要进行上下文切换时,OSIntCtxSw()被OSIntExit()调用
;中断的结果。这个函数只会触发一个PendSV异常
;当不再有活动中断且中断已启用时处理。
;********************************************************************************************************

OSIntCtxSw
    LDR     R0, =NVIC_INT_CTRL                                  ; Trigger the PendSV exception (causes context switch)
    LDR     R1, =NVIC_PENDSVSET
    STR     R1, [R0]
    BX      LR

PendSV_Handler

PendSV是可悬挂任务,当有任务触发PendSV异常后,如果此时被更高异常中断打断,则PendSV异常挂起,当所有高于PendSV的中断执行完毕后,PendSV才执行。
在这里插入图片描述

PendSV异常主要用来执行上下文切换,保存当前任务的堆栈,加载即将执行任务的堆栈至系统寄存器,然后跳转至要执行任务的函数中,一般从加载的任务堆栈中将LR赋值给PC。

PendSV_Handler
    CPSID   I                                                   ; Prevent interruption during context switch	防止上下文切换期间的中断
    MRS     R0, PSP                                             ; PSP is process stack pointer  							PSP是进程栈指针
    CBZ     R0, PendSV_Handler_nosave                           ; Skip register save the first time						第一次跳过寄存器保存

    SUBS    R0, R0, #0x20                                       ; Save remaining regs r4-11 on process stack	将其余的regs r4-11保存在进程堆栈上
    STM     R0, {R4-R11}

    LDR     R1, =OSTCBCur                                       ; OSTCBCur->OSTCBStkPtr = SP;
    LDR     R1, [R1]
    STR     R0, [R1]                                            ; R0 is SP of process being switched out			R0是被切换的进程的SP

                                                                ; At this point, entire context of process has been saved	此时,进程的整个上下文已经保存
PendSV_Handler_nosave
    PUSH    {R14}                                               ; Save LR exc_return value		保存LR exc_return值
    LDR     R0, =OSTaskSwHook                                   ; OSTaskSwHook();							调用钩子函数
    BLX     R0
    POP     {R14}

    LDR     R0, =OSPrioCur                                      ; OSPrioCur = OSPrioHighRdy;	赋值最高优先级任务
    LDR     R1, =OSPrioHighRdy
    LDRB    R2, [R1]
    STRB    R2, [R0]

    LDR     R0, =OSTCBCur                                       ; OSTCBCur  = OSTCBHighRdy;		赋值最高优先级任务的TCB块指针
    LDR     R1, =OSTCBHighRdy
    LDR     R2, [R1]
    STR     R2, [R0]

    LDR     R0, [R2]                                            ; R0 is new process SP; SP = OSTCBHighRdy->OSTCBStkPtr;从新任务中将栈顶赋值给SP
    LDM     R0, {R4-R11}                                        ; Restore r4-11 from new process stack									从新的进程堆栈恢复r4-11
    ADDS    R0, R0, #0x20
    MSR     PSP, R0                                             ; Load PSP with new process SP												加载新进程SP的PSP
    ORR     LR, LR, #0xF4                                       ; Ensure exception return uses process stack					确保异常返回使用进程堆栈
    CPSIE   I
    BX      LR                                                  ; Exception return will restore remaining context			异常返回将恢复剩余的上下文

    END

该汇编函数主要完成:
1、将CPU的通用寄存器保存压入当前任务堆栈(每一个任务都有自己的堆栈,含SP变量等)。
2、将堆栈指针SP赋值给当前任务控制块中的OSTCBStkPtr中。
3、调用钩子函数(如果有)。
4、获得最高优先级的任务块,OSPrioCur = OSPrioHighRdy; OSTCBCur(当前TCB运行指针) = OSTCBHighRdy;
5、将新任务块中的OSTCBStkPtr(PSP栈顶指针)赋值给 SP;
6、将新的任务块中的R4-R11值出栈给CPU通用寄存器;
7、中断返回IRET(RETI),使PC指向当前TCB运行指针,完成调度。

OSTimeDly

UcOSII的任务调度除了SysTick,还用OSTimeDly延时函数来进行调度。
在这里插入图片描述
该函数进入后首先判读有无中断嵌套和加锁,此函数首先将自己的就绪组及就绪位状态清除,目的是让自身任务挂起,并且将延时的时间放进当前任务的OSTCBDly里面(延时的时间通过SysTick心跳来进行减操作),然后进入**OS_Sched()**函数。

OS_Sched

在这里插入图片描述

该函数也会检查有无中断嵌套和加锁,然后调用OS_SchedNew函数来寻找当前最高优先级的就绪任务,然后将找到的高优先级TCB指针赋值给OSTCBHighRdy,然后判断是不是当前正在运行的,如果不是,将调用OS_TASK_SW宏,来调度任务。

OS_TASK_SW

该宏实际是调用了OSCtxSw汇编函数,该汇编函数跟OSIntCtxSw内容一致,都是用于触发PengSV异常任务A切换到B时需要保存cpu的”现场数据“使系统能够返回A;同时需要加载B的”现场数据“到cpu中,以使B回到被切换时的状态。而只有cpu的特权模式允许上述操作,由用户级进入特权级的惟一途径是触发异常,包括中断)

OSCtxSw
    LDR     R0, =NVIC_INT_CTRL                                  ; Trigger the PendSV exception (causes context switch)
    LDR     R1, =NVIC_PENDSVSET
    STR     R1, [R0]
    BX      LR

OSStartHighRdy

该汇编函数用于UcosII启动时调用:

;********************************************************************************************************
;                                         START MULTITASKING
;                                      void OSStartHighRdy(void)
;
; Note(s) : 1) This function triggers a PendSV exception (essentially, causes a context switch) to cause
;              the first task to start.
;
;           2) OSStartHighRdy() MUST:
;              a) 设置PendSV异常优先级最低
;              b) 设置初始PSP为0,告诉上下文切换器这是第一次运行
;              c) 将主堆栈设置为OS_CPU_ExceptStkBase
;              d) 设置OSRunning(系统开始运行标志)为真;
;              e) 触发PendSV;
;              f) 启用中断,开始调度;
;********************************************************************************************************

OSStartHighRdy
    LDR     R0, =NVIC_SYSPRI14                                  ; Set the PendSV exception priority
    LDR     R1, =NVIC_PENDSV_PRI
    STRB    R1, [R0]

    MOVS    R0, #0                                              ; Set the PSP to 0 for initial context switch call
    MSR     PSP, R0

    LDR     R0, =OS_CPU_ExceptStkBase                           ; Initialize the MSP to the OS_CPU_ExceptStkBase
    LDR     R1, [R0]
    MSR     MSP, R1    

    LDR     R0, =OSRunning                                      ; OSRunning = TRUE
    MOVS    R1, #1
    STRB    R1, [R0]

    LDR     R0, =NVIC_INT_CTRL                                  ; Trigger the PendSV exception (causes context switch)
    LDR     R1, =NVIC_PENDSVSET
    STR     R1, [R0]

    CPSIE   I                                                   ; Enable interrupts at processor level

OSStartHang
    B       OSStartHang                                         ; Should never get here

任务运行流程

任务创建

任务创建通过OSTaskCreateExt()函数进行,主要初始化任务堆栈和初始化TCB块的信息:

	OSTaskCreateExt((void(*)(void*) )start_task,                						//任务函数
                    (void*          )0,                         					//传递给任务函数的参数
                    (OS_STK*        )&START_TASK_STK[START_STK_SIZE-1],		//任务堆栈栈顶
                    (INT8U          )START_TASK_PRIO,           					//任务优先级
                    (INT16U         )START_TASK_PRIO,           					//任务ID
                    (OS_STK*        )&START_TASK_STK[0],        					//任务堆栈栈底
                    (INT32U         )START_STK_SIZE,            					//任务堆栈大小
                    (void*          )0,                         					//用户补充的存储区
                    (INT16U         )OS_TASK_OPT_STK_CHK|OS_TASK_OPT_STK_CLR|OS_TASK_OPT_SAVE_FP);//保存浮点寄存器的值

初始化任务堆栈

设置堆栈,任务函数,初始化ro-r15及系统xPSR状态寄存器,如使能FPU,还需初始化FPU寄存器。初始化ro-r15及系统xPSR状态寄存器。
在这里插入图片描述

添加进TCB任务控制块

OS_TCBInit(),该函数首先从空闲TCB指针申请了一块控制块(地址),依次在该申请的TCB块中加入了任务的堆栈指针(栈顶、栈底)、更新了空闲TCB指针、任务优先级、任务选项、并清除了任务的挂起状态并置为就绪状态,计算出了任务的X和Y(横坐标和纵坐标),然后将这个新申请的TCB块地址加到 OSTCBPrioTbl[Prio] TCB指针表中(基于优先级存放),然后再加入到OSTCBList双向链表中,更新OSRdyGrp任务就绪表,更新OSRdyTbl任务的就绪表、更新OSTaskCtr数量任务计数器:
![](https://img-blog.csdnimg.cn/img_convert/2467577be157c5d3b0801b5ddc46e247.png#align=left&display=inline&height=639&margin=[object Object]&originHeight=865&originWidth=1326&status=done&style=none&width=979)在这里插入图片描述
OS_TCBInit()函数初始化完毕后,会在OSTaskCreateExt()函数内部调用OS_Sched()函数进行任务调度,但由于os还未运行,此时是无法调度的。

开始任务

ucosII通过开始任务来启动其他应用任务,开始任务初始化完毕后,会调用OSStart()函数来启动任务调度。
该函数会判断OSStart标志是否为0,也就是os系统没有启动。
然后调用OS_SchedNew()函数找出最高优先级的任务优先级。
将当前的最高优先级就绪任务的TCB快指针确定后赋值给OSTCBCur(当前运行的TCB指针)。
最后调用OSStartHighRdy来启动调度。
在这里插入图片描述

功能说明

信号量(二值、计数型)

信号量总体运行规则是:创建1个事件控制块, 有任务请求信号量,就把该任务放入等待事件列表中,然后其他任务会发送信号量,就是把等待信号量的任务从挂起态设置为就绪态。
二值信号量就是独占任务,只能1个任务占用。
计数型或多值信号量,就是在初始化信号量的时候传入的共享资源cnt数(>1),表示该资源可以同时被cnt个任务同时占用,与二值信号量的区别是,使用后需要释放。

OSSemCreate()

信号量创建函数:
在这里插入图片描述
该函数创建1个具有信号量事件的类型OS_EVENT的控制块,并返回,用于请求和发送。

OSSemPend()

请求信号量:
在这里插入图片描述
在这里插入图片描述
该请求函数有3个参数,1个是创建信号量时返回的事件控制块指针,1个是超时时间(0是一直等待),1个是错误返回。
1、函数进入后,判断信号量计数是否大于1,如果是说明有信号量发送了,进行减一后,此时函数将在此返回,执行**OSSemPend(temp)**后面的逻辑代码。
在这里插入图片描述
2、再次进入这函数后,因为没有信号量发送或已经发送了,所以当前任务没有得到可用信号量,不会返回,此时当前运行的任务状态将被与等为信号量待定状态,同时将超时时间传入(超时时间是系统节拍,如果传入的值为真,则当前任务在系统滴答中断里面递减满足超时时间后,会将此任务恢复就绪,并再次执行)。
在这里插入图片描述
3、将事件控制块信息保存到当前任务的事件控制块指针中,将当前任务放入等候名单,然后清除当前任务的就绪组和就绪位信息。
在这里插入图片描述

4、执行调度函数,进行调度。
在这里插入图片描述
5、该信号量被发送信号量发送信号或超时恢复的时候,任务将从调度函数OS_Sched()的下一条指令执行(因为在进行系统调度的时候,入栈保存的LR寄存器指向下一条指令的地址)。
在这里插入图片描述
恢复任务后,检查状态,并将任务状态更改为准备运行。

OSSemPost()

发送信号量:
在这里插入图片描述
函数只有1个参数,即事件控制块指针。

1、验证事件控制块是否有等待事件。
在这里插入图片描述
2、通过OS_EventTaskRdy函数得到等待任务的X和Y,获取任务的优先级,取得TCB块指针,将等待任务的TCB块内的延时清0,清除任务的信号量待定状态,将任务从新加入到就绪组中,最后从事件等待列表中删除此任务。
在这里插入图片描述
3、信号量值加1。
在这里插入图片描述
4、执行调度函数,进行任务调度。

互斥信号量

互斥信号量用于降解优先级翻转的问题,当ABC的优先级分别为123时,C在某个时刻获得了信号量的使用权,在执行逻辑代码的时候,系统掉调度发生,因为B任务准备就绪,且优先级大于C,此时线程切换至B,在执行B代码的时候,A准备就绪,线程切换至A,A请求信号量,因为C被B打断,但C的信号量这个时候还没有释放,故A任务挂起,返回执行B,等B执行完后,才执行C,等C释放信号量后,A才得以运行。这一过程就是典型的发生了优先级翻转问题:B先于A运行。
解决这个问题就引入了互斥信号量,还是ABC,优先级为123,C在某个时刻获得了信号量的使用权,在执行逻辑代码的时候,系统掉调度发生,因为B任务准备就绪,且优先级大于C,此时线程切换至B,在执行B代码的时候,A准备就绪,线程切换至A,A请求信号量,在请求的过程中,发现优先级比C高,为了C不被其他任务打断,故将C的优先级提高到天花板,此时线程切换回C,等C执行完,释放信号量,恢复优先级后,线程切换至A,A执行完后,再执行B。
只要低优先级任务拿到信号量,高优先级任务再请求时,都会直接把低优先级的任务的优先级提升到天花板,无论有没有其他任务打断这个低优先级任务,直接提升。

OSMutexCreate()

互斥信号量创建函数参数1为需要继承的天花板优先级,参数2为错误返回。
在这里插入图片描述在这里插入图片描述
函数进入后,判断了天花板优先级是否已经有任务占用了,没有的话,就开辟1个事件块,并将事件的参数进行初始化,将OSEventCnt置为可用的。
在这里插入图片描述
函数返回事件控制块首地址。

OSMutexPend()

请求互斥信号量函数,参数1为请求的信号量事件块,参数2超时时间,参数3为错误返回。
在这里插入图片描述在这里插入图片描述
在这里插入图片描述
函数进来获取继承优先级
在这里插入图片描述
判断信号量是否已经被占用,没有占用就占用该信号量,将当前任务的TCB,优先级与继承优先级位或存入事件结构体后函数正常返回。
在这里插入图片描述
当发生互斥(信号量被已经被独占没有被释放,这个时候又有其他任务申请)后,函数经过②不会返回,表示信号量已经被其他任务占用了,此时先获取占用信号量任务的优先级及TCB块指针:
在这里插入图片描述
比较占有该信号量的任务的优先级比继承优先级小时才会进行反转: 在这里插入图片描述
随后会比较占用信号量任务的优先级是否比当前任务小。
在这里插入图片描述
如果是,从占用信号量任务的就绪列表和位列表将其删除,更改占用信号量任务的优先级为天花板,重新计算XY,放入就绪列表和就绪位表。
将当前任务添加到等候名单(挂起),并将当前这个任务的任务状态更改为信号量待定,然后调用线程切换到获取到天花板优先级任务中去。
在这里插入图片描述

OSMutexPost()

互斥信号量发送函数:

INT8U OSMutexPost (OS_EVENT *pevent)
{
    INT8U pip;
    INT8U prio;
#if OS_CRITICAL_METHOD == 3
    OS_CPU_SR cpu_sr = 0;													//方式3将把cpsr状态寄存器推入临时堆栈cpu_sr中,可以安全返回之前的中断状态
#endif
    if (OSIntNesting > 0) {
        return (OS_ERR_POST_ISR);
    }
#if OS_ARG_CHK_EN > 0
    if (pevent == (OS_EVENT *)0) {
        return (OS_ERR_PEVENT_NULL);
    }
#endif
    if (pevent->OSEventType != OS_EVENT_TYPE_MUTEX) {
        return (OS_ERR_EVENT_TYPE);
    }
    OS_ENTER_CRITICAL();
    pip = (INT8U)(pevent->OSEventCnt >> 8);									//变相置顶值pip
    prio = (INT8U)(pevent->OSEventCnt & OS_MUTEX_KEEP_LOWER_8);				//持有mutex资源的task原始优先级
    if (OSTCBCur != (OS_TCB *)pevent->OSEventPtr) {							//因为解决了局部优先级翻转问题,所以OSTCBCur肯定要等于pevent->OSEventPtr,否则发生了不知名的异常
        OS_EXIT_CRITICAL();
        return (OS_ERR_NOT_MUTEX_OWNER);
    }
    if (OSTCBCur->OSTCBPrio == pip) {			//task被提升了优先级到pip置顶值,也就是一个比该task优先级高,比pip低的进程需要访问互斥资源,即:存在B角色进程,那么使用OSMutex_RdyAtPrio()把本task从就绪控制矩阵中摘下来,同时将自己还原到prio优先级		
        OSMutex_RdyAtPrio(OSTCBCur, prio);
    }
    OSTCBPrioTbl[pip] = OS_TCB_RESERVED;
    if (pevent->OSEventGrp != 0) {
        
        /*OS_EventTaskRdy()函数将摘掉等待在pevent事件控制矩阵上的task中优先级最高的task
        如果该task仅仅等待该pevent事件,那么将该task添加到就绪控制矩阵中
        OSRdyGrp |= bity;
        OSRdyTbl[y] |= bitx;这样调度程序就会根据情况调度OS_Sched()该task了*/
        prio = OS_EventTaskRdy(pevent, (void *)0, OS_STAT_MUTEX, OS_STAT_PEND_OK);
   
        /*保持处于高8位的pip置顶优先级值同时清除低8位数据*/
        pevent->OSEventCnt &= OS_MUTEX_KEEP_UPPER_8;
        pevent->OSEventCnt |= prio;									//获得执行权的task充当优先级翻转计算中的C角色
        pevent->OSEventPtr = OSTCBPrioTbl[prio];					//task的控制块传给OSEventPtr指针,供优先级翻转计算使用
        if (prio <= pip) {
            
            /*prio比pip小,那么说明比置顶值pip优先级还要高的进程竟然访问了共享资源,
            那么这时可能会出现优先级翻转,因为这时mutex机制已经不起作用,
            所以应该保证"变相置顶的方式"初始化时,自己内定的最高优先级pip务必大于所有能访问互斥资源的进程优先级[gliethttp]*/
            OS_EXIT_CRITICAL();
            OS_Sched();	
            return (OS_ERR_PIP_LOWER);
        } else {
            OS_EXIT_CRITICAL();
            OS_Sched();
            return (OS_ERR_NONE);
        }
    }
    
    //没有任何一个task悬停在本event事件控制矩阵上[gliethttp]
    pevent->OSEventCnt |= OS_MUTEX_AVAILABLE;					//还原为初始值
    pevent->OSEventPtr = (void *)0;								//现在本task不悬停在任何event事件上
    OS_EXIT_CRITICAL();
    return (OS_ERR_NONE);
}

该函数还是首先检查了中断嵌套和加锁,然后获取了继承优先级和任务原优先级,然后会判断当前任务的优先级是否被提升过,如果是,将通过函数OSMutex_RdyAtPrio恢复任务的优先级,然后判断有无任务正在等待互斥信号。
如果有的话就通过OS_EventTaskRdy获取等待任务的优先级,在获取优先级的同时会把获取的任务通过OS_EventTaskRemove函数从event等待列表中删除,并把等待的任务加入到就绪表中,如果没有event mutex的等待任务,则会直接设置event的OSEventCnt位OS_MUTEX_AVAILABLE和OSEventPtr的清0操作,然后调用OS_Sched来调度任务。
如果没有等待任务在event的等待列表中,则需要当前任务自己释放自己,也就是第三部分最后的4行操作。

消息邮箱

没有特别的,跟信号量唯一的区别是发送信号量时可以携带1个信息给等待信号量的任务**。**

OSMboxCreate

邮箱任务创建,该函数返回1个OS_EVENT事件控制块首地址。

OSMboxPend

邮箱等待函数,该函数参数1为等待邮箱的事件块,参数2为超时时间,参数3为错误返回,进入函数后先判断是否在中断或加锁中,首次进入后,肯定没人给他发消息,所以该函数通过OS_EventTaskWait函数将自己挂起,如果是第二次或以上进入,一定是有人发消息了,所以再次进入后,会判断消息指针是否为空,如果不为空,则将消息指针清零,同时返回消息指针。
在这里插入图片描述

OSMboxPost

邮箱发送函数,该函数参数1为发送邮箱的事件块,参数2为要传递的消息,进入函数后先判断是否在中断或加锁中,然后判断有无正在等待的任务,如果有,同样通过OS_EventTaskRdy函数将等待任务中挂起态放入运行列表中,并且将消息赋值给等待任务的OSTCBMsg,随后将自身任务从就绪列表中删除,最后通过OS_Sched寻找高优先级就绪任务进行调度。

特别注意的是,该函数二次进入后是从OS_Sched函数后开始执行的,可以看到将直接得到消息,并将消息指针返回。

消息队列

可以存放N个信息的指针,当请求消息的任务得到消息了,这时候又有任务给他发,就会在消息的环形缓冲区累加地址,数据得以保存,不会丢失。

OSQCreate

消息队列创建函数,参数1为放消息的buff,参数2为消息个数,返回OS_EVENT类型的事件块指针。

OSQPend

消息队列请求函数,参数1为发送消息队列的事件块,参数2为超时时间,参数3为错误返回,进入函数后先判断是否在中断或加锁中,首次进入后,肯定没人给他发消息,所以该函数通过OS_EventTaskWait函数将自己挂起,如果是第二次或以上进入,一定是有人发消息了,所以再次进入后,会判断消息指针是否为空,如果不为空,则将消息指针清零,同时返回消息指针,跟消息邮箱不同的是,在返回之前会将存放消息队列的读指针进行加1,消息记录减1,并会判断读指针越界没有,如果越界将重新指向消息队列队头。

OSQPost

邮箱发送函数,该函数参数1为发送消息队列的事件块,参数2为要传递的消息,进入函数后先判断是否在中断或加锁中,然后判断有无正在等待的任务,如果有,同样通过OS_EventTaskRdy函数将等待任务中挂起态放入运行列表中,并且将消息赋值给等待任务的OSTCBMsg,随后将自身任务从就绪列表中删除,最后通过OS_Sched寻找高优先级就绪任务进行调度,再次进入后是在OS_Sched后,这里会维护一个消息队列的写指针,并且会判断写指针超出消息队列没,超出了将重新指向队列队头。

事件标志组

跟信号量、消息邮箱、队列的区别就是可以设置等待多项事件的标志,等所有标志都立起来的时候,才会生效。

软件定时器

软件定时器在功能开启后,会在ucos初始化时由OSTmr_Init函数创建1个软件定时器任务OSTmr_Task。

OSTmr_Task

软件定时器的任务执行函数,该函数是由SysTick心跳里面的OSTmrSignal函数中的OSTimeTickHook钩子函数来调用的,钩子函数会根据节拍来调用OSTmrSignal函数,OSTmrSignal函数里面会对OSTmr_Task函数发送信号量,OSTmr_Task便得以执行:

static  void  OSTmr_Task (void *p_arg)
{
    INT8U            err;
    OS_TMR          *ptmr;
    OS_TMR          *ptmr_next;
    OS_TMR_CALLBACK  pfnct;
    OS_TMR_WHEEL    *pspoke;
    INT16U           spoke;


    p_arg = p_arg;                                               /* Prevent compiler warning for not using 'p_arg'    */
    for (;;) {
        OSSemPend(OSTmrSemSignal, 0u, &err);                     /* Wait for signal indicating time to update timers  */
        OSSchedLock();
        OSTmrTime++;                                             /* Increment the current time                        */
        spoke  = (INT16U)(OSTmrTime % OS_TMR_CFG_WHEEL_SIZE);    /* Position on current timer wheel entry             */
        pspoke = &OSTmrWheelTbl[spoke];
        ptmr   = pspoke->OSTmrFirst;
        while (ptmr != (OS_TMR *)0) {
            ptmr_next = (OS_TMR *)ptmr->OSTmrNext;               /* Point to next timer to update because current ... */
                                                                 /* ... timer could get unlinked from the wheel.      */
            if (OSTmrTime == ptmr->OSTmrMatch) {                 /* Process each timer that expires                   */
                OSTmr_Unlink(ptmr);                              /* Remove from current wheel spoke                   */
                if (ptmr->OSTmrOpt == OS_TMR_OPT_PERIODIC) {
                    OSTmr_Link(ptmr, OS_TMR_LINK_PERIODIC);      /* Recalculate new position of timer in wheel        */
                } else {
                    ptmr->OSTmrState = OS_TMR_STATE_COMPLETED;   /* Indicate that the timer has completed             */
                }
                pfnct = ptmr->OSTmrCallback;                     /* Execute callback function if available            */
                if (pfnct != (OS_TMR_CALLBACK)0) {
                    (*pfnct)((void *)ptmr, ptmr->OSTmrCallbackArg);
                }
            }
            ptmr = ptmr_next;
        }
        OSSchedUnlock();
    }
}

函数首次进入后,将自己挂起,根据时钟节拍接收信号量来执行。
当函数得到信号量的时候,首先上锁,并将软件定时器的计数自增,然后获取定时器条轮的入口地址,从条轮任务列表中取出任务的首地址,然后判断任务是否存在,存在的话先取出下个条轮任务的首地址,然后根据软件定时器的计时变量是否等于当前定时任务的定时时间,如果是,解锁,然后判断任务的定时器是否是需要重新装载,如果是,调用OSTmr_Link函数重新放入定时条轮,如不是,将指示该软件定时器为完成。然后调用回调执行任务,最后条轮指针指向下一个软件定时器入口。

OSTmrCreate

软件定时器创建函数,参数1为延时时间、参数2为周期还是单次、参数3是触发后手动添加时间还是自动重装,参数4是回调函数,参数5是回调参数,参数6是任务名字,参数7错误返回,函数将任务的参数加入时间队列后,返回OS_TMR类型的块指针。

OSTmrStart

软件定时器开始函数,参数1为要启动的OS_TMR类型的块指针,参数2为错误返回,函数首先进入后判断是否在中断中,然后上锁,根据定时器状态来执行,首次启动定时器为停止状态,调用OSTmr_Link函数将要启动的任务连接到时间条轮上。然后通过OSTmr_Task函数来执行任务。
定时器装载进定时器轮时就会确定其对应的OSTmrMacth值,这个值在定时器停止或重新装载之前保证不会变化,因此就能根据定时器控制块的OSTmrMacth字段值计算出这个定时器是在哪个轮辐之上,从而把大量的定时器分散于不同的轮辐之上,加速对定时器的操作。
当定时器轮需要接收一个定时器时,会先计算此定时器应该放在哪个轮辐之上,然后将其插入轮辐链表的第一个,如果此轮辐上还有其他定时器,则还会将原来第一个定时器的Prev指针指向新插入的这个定时器。
而当定时器轮卸下一个定时器时,则如果不是在轮辐的第一个,则可以通过Prev指针找到前一个定时器,这样就把删除定时器的时间控制在O(1)了。
在这里插入图片描述

任务同步

禁止任何中断和任务调度
OS_ENTER_CRITICAL()
OS_EXIT_CRITICAL()

禁止任务调度
OSSchedLock()
OSSchedUnlock()

  • 6
    点赞
  • 26
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值