从0到1教你写UCOS-III 第六部分:任务时间片运行

       本章在上一章的基础上,加入 SysTick 中断,在 SysTick中断服务函数里面进行任务切换,从而实现双任务的时间片运行,即每个任务运行的时间都是一样的。
 

6.1 SysTick 简介:


       RTOS 需要一个时基来驱动, 系统任务调度的频率等于该时基的频率。 通常该时基由一个定时器来提供,也可以从其它周期性的信号源获得。刚好 Cortex-M 内核中有一个系统定时器 SysTick, 它内嵌在 NVIC 中,是一个 24 位的递减的计数器,计数器每计数一次的时间为 1/SYSCLK。当重装载数值寄存器的值递减到 0 的时候,系统定时器就产生一次中断,以此循环往复。 因为 SysTick 是嵌套在内核中的,所以使得 OS 在 Cortex-M 器件中编写的定时器代码不必修改,使移植工作一下子变得简单很多。 所以 SysTick 是最适合给操作系统提供时基,用于维护系统心跳的定时器。 有关 SysTick 的寄存器汇总见表格 6-1,各个寄存器的用法见表格 6-2、 表格 6-3、 表格 6-4 和表格 6-5。

 
        系统定时器的校准数值寄存器在定时实验中不需要用到。有关各个位的描述这里引用手册里面的英文版本,比较晦涩难懂,暂时不知道这个寄存器用来干什么。有研究过的朋友可以交流,起个抛砖引玉的作用。

 

6.2 初始化 SysTick:


       使用 SysTick 非常简单,只需一个初始化函数搞定, OS_CPU_SysTickInit 函数在os_cpu_c.c 中定义, 具体实现见代码清单 6-1。在这里, SysTick 初始化函数我们没有使用uC/OS-III 官 方 的 , 我 们 是 自 己 另 外 编 写 了 一 个 。 区 别 是 uC/OS-III 官 方 的OS_CPU_SysTickInit 函数里面涉及到 SysTick 寄存器都是重新在 cpu.h中定义,而我们自己编写的则是使用 ARMCM3.h(记得在 os_cpu_c.c 的开头包含 ARMCM3.h 这个头文件) 这个固件库文件里面定义的寄存器,仅此区别而已。
代码清单 6-1 SysTick 初始化

#if 0 /* 不用 uCOS-III 自带的 */
void OS_CPU_SysTickInit (CPU_INT32U cnts)
{
CPU_INT32U prio;
/* 填写 SysTick 的重载计数值 */
CPU_REG_NVIC_ST_RELOAD = cnts - 1u;
/* 设置 SysTick 中断优先级 */
prio = CPU_REG_NVIC_SHPRI3;
prio &= DEF_BIT_FIELD(24, 0);
prio |= DEF_BIT_MASK(OS_CPU_CFG_SYSTICK_PRIO, 24);
CPU_REG_NVIC_SHPRI3 = prio;
/* 使能 SysTick 的时钟源和启动计数器 */
CPU_REG_NVIC_ST_CTRL |= CPU_REG_NVIC_ST_CTRL_CLKSOURCE |
CPU_REG_NVIC_ST_CTRL_ENABLE;
/* 使能 SysTick 的定时中断 */
CPU_REG_NVIC_ST_CTRL |= CPU_REG_NVIC_ST_CTRL_TICKINT;
}
#else /* 直接使用头文件 ARMCM3.h 里面现有的寄存器定义和函数来实现 */
void OS_CPU_SysTickInit (CPU_INT32U ms)
{
/* 设置重装载寄存器的值 */
SysTick->LOAD = ms * SystemCoreClock / 1000 - 1;                  (1)
/* 配置中断优先级为最低 */
NVIC_SetPriority (SysTick_IRQn, (1<<__NVIC_PRIO_BITS) - 1);       (2)
/* 复位当前计数器的值 */
SysTick->VAL = 0;                                                 (3)
/* 选择时钟源、使能中断、使能计数器 */
SysTick->CTRL = SysTick_CTRL_CLKSOURCE_Msk |                      (4)
SysTick_CTRL_TICKINT_Msk |                                        (5)
SysTick_CTRL_ENABLE_Msk;                                          (6)
}
#endif

代码清单 6-1 (1) : 配置重装载寄存器的值,我们配合函数形参 ms 来配置,如果需要配置为 10ms 产生一次中断,形参设置为 10 即可。
代码清单 6-1 (2) :配置 SysTick 的优先级,这里配置为 15,即最低。
代码清单 6-1 (3) :复位当前计数器的值
代码清单 6-1 (4) :选择时钟源,这里选择 SystemCoreClock。
代码清单 6-1 (5) :使能中断
代码清单 6-1 (6) :使能计数器开始计数
 

6.3 编写 SysTick 中断服务函数:


SysTick 中断服务函数也是在 os_cpu_c.c 中定义,具体实现见代码清单 6-2。
代码清单 6-2 SysTick 中断服务函数

/* SysTick 中断服务函数 */
void SysTick_Handler(void)
{
OSTimeTick();
}

        SysTick 中断服务函数很简单,里面仅调用了函数 OSTimeTick()。 OSTimeTick()是与时间相关的函数,在 os_time.c(os_time.c 第一次使用需要自行在文件夹 uCOS-III\Source 中新建并添加到工程的 uC/OS-III Source 组)文件中定义,具体实现见代码清单 6-3。
代码清单 6-3 OSTimeTick()函数

void OSTimeTick (void)
{
/* 任务调度 */
OSSched();
}

       OSTimeTick()很简单,里面仅调用了函数 OSSched, OSSched 函数暂时没有修改,与上一章一样,具体见代码清单 6-4。
代码清单 6-4 OSSched 函数

void OSSched (void)
{
if ( OSTCBCurPtr == OSRdyList[0].HeadPtr ) {
OSTCBHighRdyPtr = OSRdyList[1].HeadPtr;
} else {
OSTCBHighRdyPtr = OSRdyList[0].HeadPtr;
}
OS_TASK_SW();
}

6.4 main 函数:


        main 函数与上一章区别不大,仅仅是加入了 SysTick 相关的内容,具体见代码清单 6-5。
代码清单 6-5 main 函数和任务代码

int main(void)
{
OS_ERR err;
/* 关闭中断 */
CPU_IntDis();                           (1)
/* 配置 SysTick 10ms 中断一次 */
OS_CPU_SysTickInit (10);                (2)
/* 初始化相关的全局变量 */
OSInit(&err);
/* 创建任务 */
OSTaskCreate ((OS_TCB*) &Task1TCB,
(OS_TASK_PTR ) Task1,
(void *) 0,
(CPU_STK*) &Task1Stk[0],
(CPU_STK_SIZE) TASK1_STK_SIZE,
(OS_ERR *) &err);
OSTaskCreate ((OS_TCB*) &Task2TCB,
(OS_TASK_PTR ) Task2,
(void *) 0,
(CPU_STK*) &Task2Stk[0],
(CPU_STK_SIZE) TASK2_STK_SIZE,
(OS_ERR *) &err);
/* 将任务加入到就绪列表 */
OSRdyList[0].HeadPtr = &Task1TCB;
OSRdyList[1].HeadPtr = &Task2TCB;
/* 启动 OS,将不再返回 */
OSStart(&err);
}
/* 任务 1 */
void Task1( void *p_arg )
{
for ( ;; ) {
flag1 = 1;
delay( 100 );
flag1 = 0;
delay( 100 );
/* 任务切换,这里是手动切换 */
//OSSched();                             (3)
}
}
/* 任务 2 */
void Task2( void *p_arg )
{
for ( ;; ) {
flag2 = 1;
delay( 100 );
flag2 = 0;
delay( 100 );
/* 任务切换,这里是手动切换 */
//OSSched();                              (4)
}
}

        代码清单 6-5 (1) : 关闭中断。因为在 OS 系统初始化之前我们使能了 SysTick 定时器产生 10ms 的中断,在中断里面触发任务调度,如果一开始我们不关闭中断,就会在 OS还有启动之前就进入 SysTick 中断,然后发生任务调度,既然 OS都还没启动,那调度是不允许发生的,所以先关闭中断。系统启动后,中断由 OSStart()函数里面的 OSStartHighRdy()重新开启。
       代码清单 6-5 (2) : 配置 SysTick 为 10ms 中断一次。任务的调度是在 SysTick 的中断服务函数中完成的,中断的频率越高就意味着 OS 的调度越高,系统的负荷就越重,一直在不断的进入中断,则执行任务的时间就减小。选择合适的 SysTick 中断频率会提供系统的运行效率, uC/OS-III官方推荐为 10ms,或者高点也行。
       代码清单 6-5 (3)、(4): 任务调度将不再在各自的任务里面实现,而是放到了SysTick 中断服务函数中。从而实现每个任务都运行相同的时间片,平等的享有 CPU。


6.5 实验想像:


进入软件调试,点击全速运行按钮就可看到实验波形,具体见图 6-1。

       从图 6-1 我们可以看到,两个任务轮流的占有 CPU,享有相同的时间片。其实目前的实验现象与上一章的实验现象还没有本质上的区别, 加入 SysTick 只是为了后续章节做准备。 上一章两个任务也是轮流的占有 CPU,也是享有相同的时间片,该时间片是任务单次运行的时间。不同的是本章任务的时间片等于 SysTick 定时器的时基,是很多个 任务单次运行时间的综合。即在这个时间片里面任务运行了非常多次,如果我们把波形放大, 就会发现大波形里面包含了很多小波形,具体见图 6-2。

 

 

 

 

 

 

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值