当前位置:   article > 正文

(学习日记)2024.03.05:UCOSIII第七节:SysTick+任务时间片

(学习日记)2024.03.05:UCOSIII第七节:SysTick+任务时间片

写在前面:
由于时间的不足与学习的碎片化,写博客变得有些奢侈。
但是对于记录学习(忘了以后能快速复习)的渴望一天天变得强烈。
既然如此
不如以天为单位,以时间为顺序,仅仅将博客当做一个知识学习的目录,记录笔者认为最通俗、最有帮助的资料,并尽量总结几句话指明本质,以便于日后搜索起来更加容易。


标题的结构如下:“类型”:“知识点”——“简短的解释”
部分内容由于保密协议无法上传。


点击此处进入学习日记的总目录

十六、UCOSIII:介绍SysTick

1、SysTick简介

RTOS需要一个时基来驱动,系统任务调度的频率等于该时基的频率。
通常该时基由一个定时器来提供,也可以从其他周期性的信号源获得。 刚好Cortex-M内核中有一个系统定时器SysTick,它内嵌在NVIC中,是一个24位的递减的计数器,计数器每计数一次的时间为1/SYSCLK。
当重装载数值寄存器的值递减到0的时候,系统定时器就产生一次中断,以此循环往复。

因为SysTick是嵌套在内核中的, 所以使得OS在Cortex-M器件中编写的定时器代码不必修改,使移植工作一下子变得简单很多。
所以SysTick是最适合给操作系统提供时基, 用于维护系统心跳的定时器。

SysTick寄存器汇总如下:

寄存器名称寄存器描述
CTRLSysTick控制及状态寄存器
LOADSysTick重装载数值寄存器
VALSysTick当前数值寄存器

SysTick控制及状态寄存器如下:

位段名称类型复位值描述
16COUNTFLAGR/W0如果在上次读取本寄存器后, SysTick 已经计到了 0,则该位为 1。
2CLKSOURCER/W0时钟源选择位,0=AHB/8,1=处理器时钟AHB
1TICKINTR/W01=SysTick倒数计数到 0时产生 SysTick异常请求,0=数到 0 时无动作。也可以通过读取COUNTFLAG标志位来确定计数器是否递减到0
0ENABLER/W0SysTick 定时器的启用位

SysTick 重装载数值寄存器如下:

位段名称类型复位值描述
23:0RELOADR/W0当倒数计数至零时,将被重装载的值

SysTick当前数值寄存器如下:

位段名称类型复位值描述
23:0CURRENTR/W0读取时返回当前倒计数的值,写它则使之清零,同时还会清除在SysTick控制及状态寄存器中的COUNTFLAG 标志

2、初始化SysTick

使用SysTick非常简单,只需一个初始化函数搞定,OS_CPU_SysTickInit函数在os_cpu_c.c中定义

我使用的是野火的教材,SysTick初始化函数野火没有使用μC/OS-III官方的,野火是自己另外编写了一个。
区别是uC/OS-III官方的OS_CPU_SysTickInit函数里面涉及 SysTick寄存器都是重新在cpu.h中定义,
而野火自己编写的则是使用ARMCM3.h(记得在os_cpu_c.c的开头包含ARMCM3.h这个头文件) 这个固件库文件里面定义的寄存器,仅此区别而已。

#include "ARMCM3.h"


#if 0 /* 不用μC/OS-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;

    /* 配置中断优先级为最低 */
    NVIC_SetPriority (SysTick_IRQn, (1<<__NVIC_PRIO_BITS) - 1);

    /* 复位当前计数器的值 */
    SysTick->VAL   = 0;

    /* 选择时钟源、启用中断、启用计数器 */
    SysTick->CTRL  = SysTick_CTRL_CLKSOURCE_Msk |
                    SysTick_CTRL_TICKINT_Msk   |
                    SysTick_CTRL_ENABLE_Msk;
}
#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
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • SysTick->LOAD = ms * SystemCoreClock / 1000 - 1;
    配置重装载寄存器的值,我们配合函数形参ms来配置,如果需要配置为10ms产生一次中断,形参设置为10即可。

  • NVIC_SetPriority (SysTick_IRQn, (1<<__NVIC_PRIO_BITS) - 1);
    配置SysTick的优先级,这里配置为15,即最低。

3、SysTick中断服务函数

SysTick中断服务函数也是在os_cpu_c.c中定义

/* SysTick 中断服务函数 */
void SysTick_Handler(void)
{
    OSTimeTick();
}
  • 1
  • 2
  • 3
  • 4
  • 5

SysTick中断服务函数很简单,里面仅调用了函数OSTimeTick()。
OSTimeTick()是与时间相关的函数, 在os_time.c文件中定义。

#include "os.h"

void  OSTimeTick (void)
{
	/* 任务调度 */
	OSSched();
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

OSTimeTick()很简单,里面仅调用了函数OSSched,OSSched函数暂时没有修改,还是手动切换任务
在这里插入图片描述

十七、UCOSIII:任务时间片运行

1、对main()函数修改

main()函数与之前区别不大

  1. 加入了SysTick相关的内容
    在这里插入图片描述
  2. 注释掉任务里的OSSched()函数
    在这里插入图片描述
/*
************************************************************************************************************************
*                                                 包含的头文件
************************************************************************************************************************
*/
#include "os.h"
#include "ARMCM3.h"

/*
************************************************************************************************************************
*                                                   宏定义
************************************************************************************************************************
*/


/*
************************************************************************************************************************
*                                                  全局变量
************************************************************************************************************************
*/

uint32_t flag1;
uint32_t flag2;

/*
************************************************************************************************************************
*                                                  TCB & STACK & 任务声明
************************************************************************************************************************
*/
#define  TASK1_STK_SIZE       20
#define  TASK2_STK_SIZE       20

static   CPU_STK   Task1Stk[TASK1_STK_SIZE];
static   CPU_STK   Task2Stk[TASK2_STK_SIZE];

static   OS_TCB    Task1TCB;
static   OS_TCB    Task2TCB;

void     Task1( void *p_arg );
void     Task2( void *p_arg );

/*
************************************************************************************************************************
*                                                  函数声明
************************************************************************************************************************
*/
void delay(uint32_t count);

/*
************************************************************************************************************************
*                                                    main函数
************************************************************************************************************************
*/
/*
* 注意事项:1、该工程使用软件仿真,debug需选择 Ude Simulator
*           2、在Target选项卡里面把晶振Xtal(Mhz)的值改为25,默认是12,
*              改成25是为了跟system_ARMCM3.c中定义的__SYSTEM_CLOCK相同,确保仿真的时候时钟一致
*/
int main(void)
{	
	OS_ERR err;
	
	/* 关闭中断 */
    CPU_IntDis();

  /* 配置SysTick 10ms 中断一次 */
  OS_CPU_SysTickInit(10);
	
	/* 初始化相关的全局变量 */
	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);
}

/*
************************************************************************************************************************
*                                                    函数实现
************************************************************************************************************************
*/
/* 软件延时 */
void delay (volatile uint32_t count)
{
	for(; count!=0; count--);
}



/* 任务1 */
void Task1( void *p_arg )
{
	for( ;; )
	{
		flag1 = 1;
		delay( 100 );		
		flag1 = 0;
		delay( 100 );
		
		/* 任务切换,这里是手动切换 */		
		//OSSched();
	}
}

/* 任务2 */
void Task2( void *p_arg )
{
	for( ;; )
	{
		flag2 = 1;
		delay( 100 );		
		flag2 = 0;
		delay( 100 );
		
		/* 任务切换,这里是手动切换 */
		//OSSched();
	}
}
  • 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
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134
  • 135
  • 136

2、关闭中断函数CPU_IntDis()

在这里插入图片描述
关闭中断。
因为在OS系统初始化之前我们启用了SysTick定时器产生10ms的中断,在中断里面触发任务调度。
如果一开始我们不关闭中断,就会在OS还有启动之前就进入SysTick中断,然后发生任务调度,既然OS都还没启动,那调度是不允许发生的, 所以先关闭中断。
系统启动后,中断由OSStart()函数里面的OSStartHighRdy()重新开启。

CPU_IntDis()在cpu.c中被声明
在这里插入图片描述
一个关闭中断,一个开启中断
代码在cpu_a.asm中被定义
在这里插入图片描述

3、仿真测试

在这里插入图片描述

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

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/AllinToyou/article/detail/216344
推荐阅读
相关标签
  

闽ICP备14008679号