μC/OSIII学习day2
介绍
阻塞延时与空闲任务,时间戳
# 阻塞延时与空闲任务
RTOS中的演示叫阻塞延时,即任务需要演示的时候,任务会放弃CPU的使用权,CPU可以执行其他的任务,可以更加充分的利用CPU资源.
如果在延时的时候CPU实在没有任务可以运行,RTOS会为CPU创建一个空闲任务去执行.
空闲任务是系统在初始化的时候创建的优先级最低的任务.
空闲任务是对全局变量
OSIdleTaskCtr
进行加操作.在实际应用中,当系统进入空闲任务的时候,可在空闲任务中让单片机进入休眠或者低功耗等.
# 空闲任务
空闲任务栈
- 空闲任务的栈是已经定义好的,数组的大小由
OS_CFG_IDLE_TASK_STK_SIZE
这个宏控制. - 空闲任务的栈起始地址被定义成一个常量.
- os.h文件中存在具有全局属性的变量
OSCfg_IdleTaskStkBasePtr
和OSCfg_IdleTaskStkSize
,可以在其他文件里被使用
os_cfg_app.c中有空闲任务栈的定义
CPU_STK OSCfg_IdleTaskStk[OS_CFG_IDLE_TASK_STK_SIZE]; /* 空闲任务栈起始地址 */ CPU_STK * const OSCfg_IdleTaskStkBasePtr = (CPU_STK *)&OSCfg_IdleTaskStk[0]; /* 空闲任务栈大小 */ CPU_STK_SIZE const OSCfg_IdleTaskStkSize = (CPU_STK_SIZE)OS_CFG_IDLE_TASK_STK_SIZE;
1
2
3
4
5
6- 空闲任务的栈是已经定义好的,数组的大小由
空闲任务TCB
空闲任务块的TCB在os.h中定义,是一个全局变量.
OS_EXT OS_TCB OSIdleTaskTCB;
1空闲任务函数
空闲任务定义在os_core.c文件中
#if (OS_CFG_TASK_IDLE_EN == DEF_ENABLED) void OS_IdleTask (void *p_arg) { #if ((OS_CFG_DBG_EN == DEF_ENABLED) || (OS_CFG_STAT_TASK_EN == DEF_ENABLED)) CPU_SR_ALLOC(); #endif (void)p_arg; /* Prevent compiler warning for not using 'p_arg' */ while (DEF_ON) { #if ((OS_CFG_DBG_EN == DEF_ENABLED) || (OS_CFG_STAT_TASK_EN == DEF_ENABLED)) CPU_CRITICAL_ENTER(); #if (OS_CFG_DBG_EN == DEF_ENABLED) OSIdleTaskCtr++; #endif #if (OS_CFG_STAT_TASK_EN == DEF_ENABLED) OSStatTaskCtr++; #endif CPU_CRITICAL_EXIT(); #endif #if (OS_CFG_APP_HOOKS_EN == DEF_ENABLED) OSIdleTaskHook(); /* Call user definable HOOK */ #endif } } #endif
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28空闲任务初始化
空闲任务的初始化再
OSInit()
函数中完成void OSInit(OS_ERR *p_err){ ... OS_IdleTaskInit(p_err); ... } ... #if (OS_CFG_TASK_IDLE_EN == DEF_ENABLED) void OS_IdleTaskInit (OS_ERR *p_err) { #if (OS_CFG_DBG_EN == DEF_ENABLED) OSIdleTaskCtr = 0u; #endif /* --------------- CREATE THE IDLE TASK --------------- */ OSTaskCreate(&OSIdleTaskTCB, (CPU_CHAR *)((void *)"uC/OS-III Idle Task"), OS_IdleTask, DEF_NULL, (OS_CFG_PRIO_MAX - 1u), OSCfg_IdleTaskStkBasePtr, OSCfg_IdleTaskStkLimit, OSCfg_IdleTaskStkSize, 0u, 0u, DEF_NULL, (OS_OPT_TASK_STK_CHK | OS_OPT_TASK_STK_CLR | OS_OPT_TASK_NO_TLS), p_err); } #endif
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
# 阻塞延时
阻塞延时的阻塞是指任务调用该延时函数后,任务会被剥离CPU的使用权,然后进入阻塞状态,直至延时结束.
阻塞延时函数在os_time.c中定义
void OSTimeDly(OS_TICK dly){ ... //设置延时时间 OSTCBCurPtr->TaskDelayTicks = dly; OSSched(); } struct os_tcb { CPU_STK *StkPtr; CPU_STK_SIZE StkSize; /* 任务延时周期个数 */ OS_TICK TaskDelayTicks; }OSTCBCurPtr;
1
2
3
4
5
6
7
8
9
10
11
12
13
14TaskDelayTick
是任务控制块的一个成员,用于记录任务需要延时的时间(单位为SysTick的中断周期).
# 时间戳
时间戳使用的是ARM Cortex-M系列内核中的DWT外设.
# 时间戳简介
在μC/OS中,很多地方的代码都加入了时间测量的功能,比如任务关中断的时间,关调度器的时间等.知道了某段代码的运行时间,就明显地知道该代码的执行效率如果时间过长就可以优化或者调整代码策略.
# 时间戳的实现
在ARM Cortex-M系列内核中.有一个 DWT 的外设,该外设有一个32位的寄存器叫CYCCNT,它是一个向上的计数器,记录的是内核时钟HCLK运行的个数,当CYCCNT溢出之后,会清零重新开始向上计数.该计数器在μC/OS中正好被用来实现时间戳的功能.
# 时间戳代码
CPU_Init
函数void CPU_Init(void){ /* CPU 初始化函数中总共做了三件事 1,初始化时间戳 2,初始化中断禁用时间测量 3,初始化 CPU 名字 此处为时间戳功能*/ #if ((CPU_CFG_TS_EN == DEF_ENABLED)||(CPU_CFG_TS_TMR_EN == DEF_ENABLED)) CPU_TS_Init();(2) #endif }
1
2
3
4
5
6
7
8
9
10
11
12CPU_CFG_TS_EN
和CPU_CFG_TS_TMR_EN
这两个宏在cpu_core.h中定义,用于控制时间戳相关功能代码.CPU_CFG_TS_32_EN
和CPU_CFG_TS_64_EN
这两个宏在cpu_cfg.h中,用于控制时间戳是32还是64为的,默认使用的是32位.CPU_TS_Init()
函数#if ((CPU_CFG_TS_EN == DEF_ENABLED)||(CPU_CFG_TS_TMR_EN == DEF_ENABLED)) static void CPU_TS_Init(void){ #if (CPU_CFG_TS_TMR_EN == DEF_ENABLED) CPU_TS_TmrFreq_Hz = 0u; CPU_TS_TmrInit(); #endif } #endif
1
2
3
4
5
6
7
8
9CPU_TS_TmrFreq_Hz
是一个全局变量,表示CPU的具体时钟,具体大小跟硬件相关.CPU_TS_TmrInit()
函数//时间戳定时器初始化 #if (CPU_CFG_TS_TMR_EN == DEF_ENABLED) void CPU_TS_TmrInit(void){ CPU_INT32U fclk_freq; fclk_freq = BSP_CPU_ClkFreq(); /* 启用 DWT 外设 */ BSP_REG_DEM_CR |= (CPU_INT32U)BSP_BIT_DEM_CR_TRCENA; /* DWT CYCCNT 寄存器计数清零 */ BSP_REG_DWT_CYCCNT = (CPU_INT32U)0u; /* 启用 Cortex-M3 DWT CYCCNT 寄存器 */ BSP_REG_DWT_CR |= (CPU_INT32U)BSP_BIT_DWT_CR_CYCCNTENA; CPU_TS_TmrFreqSet((CPU_TS_TMR_FREQ)fclk_freq); } #endif
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15BSP_CPU_ClkFreq()
函数个用于获取CPU的HCLK时钟的BSP函数,具体跟硬件相关.
CPU_TS_TmrFreqSet()
函数把函数
BSP_CPU_ClkFreq()
获取到的CPU的HCLK时钟赋值给全局变量CPU_TS_TmrFreq_Hz
.#if (CPU_CFG_TS_TMR_EN == DEF_ENABLED) void CPU_TS_TmrFreqSet (CPU_TS_TMR_FREQ freq_hz) { CPU_TS_TmrFreq_Hz = freq_hz; } #endif
1
2
3
4
5
6
7CPU_TS_TmrRd()
函数用于获取CYCNNT计数器的值.
#if (CPU_CFG_TS_TMR_EN == DEF_ENABLED) CPU_TS_TMR CPU_TS_TmrRd(void){ CPU_TS_TMR ts_tmr_cnts; ts_tmr_cnts = (CPU_TS_TMR)BSP_REG_DWT_CYCCNT; return (ts_tmr_cnts); } #endif
1
2
3
4
5
6
7
8OS_TS_GET()
函数用于获取CYCNNT计数器的值,实际上是一个宏定义,将CPU底层的函
CPU_TS_TmrRd()
重新取个名字封装,供内核和用户函数使用.
# 时间戳的使用
在os_cfg.h中使能时间戳
#define OS_CFG_TS_EN 1u
1在cpu_cfg.h中使能宏定义
CPU_CFG_TS_32_EN
或CPU_CFG_TS_64_EN
#define CPU_CFG_TS_32_EN DEF_ENABLED #define CPU_CFG_TS_64_EN DEF_DISABLED
1
2定义三个全局变量
uint32_t TimeStart; uint32_t TimeEnd; uint32_t TimeUse
1
2
3在
main()
函数中加入CPU_Init()
函数int main(){ OS_ERR err; CPU_Init(); BSP_Init(); OSInit(&err); ... OSStart(&err); }
1
2
3
4
5
6
7
8
9
10
11
12在任务函数中对函数的执行时间进行测量
void Relays1_Task(void *p_arg){ OS_ERR err; while(1){ TimeStart = OS_TS_GET(); Relays_State(RELAYS1,RELAYS_TOGGLE); OSTimeDly(2000,OS_OPT_TIME_DLY,&err); TimeEnd = OS_TS_GET; TimeUse = TimeEnd - TimeStart; } }
1
2
3
4
5
6
7
8
9
10
11
12若所用μC/OS代码代码中缺少
CPU_TS_TmrInit
和CPU_TS_TmrRd
函数,添加即可#if (CPU_CFG_TS_TMR_EN == DEF_ENABLED) #define BSP_REG_DEM_CR (*(CPU_REG32 *)0xE000EDFC) #define BSP_REG_DWT_CR (*(CPU_REG32 *)0xE0001000) #define BSP_REG_DWT_CYCCNT (*(CPU_REG32 *)0xE0001004) #define BSP_REG_DBGMCU_CR (*(CPU_REG32 *)0xE0042004) #define BSP_BIT_DEM_CR_TRCENA (1<<24) #define BSP_BIT_DWT_CR_CYCCNTENA (1<<0) void CPU_TS_TmrInit(void) { CPU_INT32U fclk_freq; fclk_freq = BSP_CPU_ClkFreq(); /* 使能DWT外设 */ BSP_REG_DEM_CR |= (CPU_INT32U)BSP_BIT_DEM_CR_TRCENA; /* DWT CYCCNT寄存器计数清0 */ BSP_REG_DWT_CYCCNT = (CPU_INT32U)0u; /* 使能Cortex-M3 DWT CYCCNT寄存器 */ BSP_REG_DWT_CR |= (CPU_INT32U)BSP_BIT_DWT_CR_CYCCNTENA; CPU_TS_TmrFreqSet((CPU_TS_TMR_FREQ)fclk_freq); } CPU_TS_TMR CPU_TS_TmrRd(void) { CPU_TS_TMR ts_tmr_cnts; ts_tmr_cnts = (CPU_TS_TMR)BSP_REG_DWT_CYCCNT; return (ts_tmr_cnts); } #endif
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37