系统初始化

前提是你 提交于 2020-03-31 13:05:08

RTOS系统的启动

在系统上电时,第一个执行的启动文件是汇编语言编写的复位函数Reset_Handler,复位函数最后会调用C库函数_ _main,__main的主要工作是初始化系统的堆和栈,最后调用C中的main()函数,从而进入C的世界。

1 系统初始化

系统初始化是根据我们配置宏定义进行的,有一些则是系统必要的初始化,如空闲任务、时钟节拍任务等。

注意:在系统初始化中,空闲任务的初始化和时钟节拍任务的初始化是必须存在的任务,否则系统无法正常运行。

1.1 空闲任务初始化

空闲任务是一个无限的死循环,其优先级是最低的,所以任何优先级比它高的任务都能抢占它,从而取得CPU的使用权。

?? 为什么系统要有空闲任务呢?

因为CPU是不会停下来的,此时系统就必须保证有一个随时处于就绪态的任务,而且这个任务不会抢占其他任务的资源。当且仅当系统的其他任务处于阻塞态时,系统才会运行空闲任务。

1.1.1 空闲任务的作用

空闲任务可以做很多事情,如任务统计;钩入用户自定义的钩子函数,实现用户自定义的功能等。

注意:在钩子函数中,用户不容许调入任何可以使空闲任务阻塞的接口函数,空闲任务是不容许被阻塞的


1.2 时钟节拍任务

2 CPU初始化

在main()函数中,除了需要对扳级硬件进行初始化,还需要进行一些系统相关的初始化,如CPU初始化。

?? 为什么进行CPU初始化?

在uC/OS中,有一个很好的功能就是时间戳,它的精度高达纳秒级别,是CPU内核的一个资源,所以使用时要对CPU进行相关的初始化。

2.1 时间戳

在uC/OS-III中,很多代码中都加入了时间测量的功能,比如任务关中断的时间,关调度器的时间等。

2.1.1 时间戳的实现

选择:通常执行一条代码是需要多个时钟周期的,为纳秒级别。要想准确的测量代码的运行时间,时间戳的精度很重要。通常单片机中的硬件定时器的精度都是微秒级别,远达不到测量几条代码运行时间所需要的精度。

​ 在ARM Cortex-M系列内核中,有一个DWT的外设,该外设有一个32位的寄存器,叫做CYCCNT,它是一个向上的计数器,记录的是内核时钟HCLK的运行个数。当CYCCNT溢出之后,会清零重新开始向上计数。

​ 该计数器在uC/OS-III中恰好被用来实现时间戳的功能。


在STM32F103系列的单片机中,HCLK时钟频率最高为72MHz,单个时钟的周期为1/72us = 0.0139us = 14ns,CYCCNT总共能记录的时间为2 32 *14 = 60s。

2.1.1.1 CPU初始化函数

/* CPU初始化函数 */
void CPU_Init(void)
{
    /* CPU初始化函数中总共做了3件事
     * 1)初始化时间戳
     * 2)初始化中断禁用时间测量
     * 3)初始化CPU名字 */
     
#if ((CPU_CFG_TS_EN		== DEF_ENABLED) || \
    (CPU_CFG_TS_TMR_EN	== DEF_ENABLED))
    CPU_TS_Init();
#endif
    
}

2.1.1.2 时间戳初始化函数

/* 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的系统时钟,具体大小与硬件有关
    CPU_TS_Tmrinit();
#endif
    
}
#endif

2.1.1.3 时间戳定时器初始化函数

/* 时间戳定时器初始化函数 */
#if ((CPU_CFG_TS_TMR_EN	== DEF_ENABLED))
void CPU_TS_TmrInit(void)
{
    CPU_INT32U		fclk_freq;
    fclk_freq	=	BSP_CPU_ClkFreq(); //获取CPU的HCLK时钟,与硬件有关
    
    /* 启用 DWT 外设,由另外的内核调试寄存器DEMCR的位24控制,写1启动 */
    BSP_REG_DEM_CR		|= (CPU_INT32U)BSP_BIT_DEM_CR_TRCENA;
    /* DWT CYCCNT 寄存器启用之前先清零 */
    BSP_REG_DWT_CYCCNT	 = (CPU_INT32U)0u;
    /* 启用 ortex-M3 DWT CYCCNT寄存器
     * DWT_CTRL的位0控制,写1启动
     * 将CPU的HCLK时钟赋值给全局变量CPU_TS_TmrFreq_Hz */
    BSP_REG_DWT_CR		|= (CPU_INT32U)BSP_BIT_DWT_CR_CYCCNTENA;
    CPU_TS_TmrFreqSet((CPU_TS_TMR_FREQ)fclk_freq;
}

3 SysTick初始化

时钟节拍需要依赖于硬件定时器,在STM32的SysTick时钟内嵌在NVIC中,是MCU的内核定时器,是一个24位的递减的计数器,计数器每计数一次的时间为 1/SYSCLK 。当重装载数值寄存器(LOAD)的值递减到0时,系统定时器就产生一次中断,按此循环。通常都使用该定时器产生操作系统的时钟节拍。

时钟节拍:是系统以固定的频率产生中断,并在中断中处理与时间相关的事件,推动所有任务向前运行。

时钟节拍的频率:表示操作系统每秒产生多少个tick。

tick:即操作系统节拍的时钟周期。

/* OS_CPU_SysTickInit() 初始化SysTick */

/* 1)配置重装载计数器(LOAD)的值
 * 2)配置SysTick的优先级,这里配置为15,即最低
 * 3)复位当前计数器(VAL)的值
 * 4)配置控制及状态寄存器(CTRL)选择时钟源,启用中断,启用计数器开始计数 */

 1 int main(void)
 2 {
 3 OS_ERR err;
 4
 5 /* 关闭中断 */
 6 CPU_IntDis(); 								(1)
 7
 8 /* 配置 SysTick 10ms 中断一次 */
 9 OS_CPU_SysTickInit (10); 					(2)
10
11 /* 初始化相关的全局变量 */
12 OSInit(&err);
13
14 /* 创建任务 */
15 OSTaskCreate ((OS_TCB*) &Task1TCB,
16 				(OS_TASK_PTR ) Task1,
17 				(void *) 0,
18 				(CPU_STK*) &Task1Stk[0],
19 				(CPU_STK_SIZE) TASK1_STK_SIZE,
20 				(OS_ERR *) &err);
21
22 OSTaskCreate ((OS_TCB*) &Task2TCB,
23 				(OS_TASK_PTR ) Task2,
24 				(void *) 0,
25 				(CPU_STK*) &Task2Stk[0],
26 				(CPU_STK_SIZE) TASK2_STK_SIZE,
27 				(OS_ERR *) &err);
28
29 /* 将任务加入到就绪列表 */
30 OSRdyList[0].HeadPtr = &Task1TCB;
31 OSRdyList[1].HeadPtr = &Task2TCB;
32
33 /* 启动 OS,将不再返回 */
34 OSStart(&err);
35 }
36
37
38
39 /* 任务 1 */
40 void Task1( void *p_arg )
41 {
42 		for ( ;; ) {
43 		flag1 = 1;
44 		delay( 100 );
45 		flag1 = 0;
46 		delay( 100 );
47
48 		/* 任务切换,这里是手动切换 */
49 		//OSSched(); 							(3)
50 		}
51 }
52
53 /* 任务 2 */
54 void Task2( void *p_arg )
55 {
56 		for ( ;; ) {
57 		flag2 = 1;
58 		delay( 100 );							(5)
59 		flag2 = 0;
60 		delay( 100 );
61
62 		/* 任务切换,这里是手动切换 */
63 		//OSSched(); 							(4)
64 		}
65 }

中断服务函数SysTick_Handler()调用任务调度函数,进行任务切换,实现每个任务都运行相同的时间片,平等的享有CPU。但时间是按时间流的顺序显示的,两个任务轮流占用CPU。

由此可见,任务中的延时使用的是软件延时,即还是让CPU空等来达到延时的效果。使用RTOS的优势就是充分发挥CPU的性能,永远不让它闲着。任务如果需要延时,也就不能再让CPU空等来实现延时的效果。
RTOS中的延时叫阻塞延时,即当任务需要延时时,会放弃CPU的使用权,CPU可以去做其他的事情,当任务延时时间到,将重新获得CPU的使用权继续运行,这样就可以充分利用CPU,而不是空等。

4 内存初始化

系统是软件,必须为其分配一块内存,所以在系统创建任务之前,必须将系统必要的内容进行初始化。uC/OS采用一块连续的大数组作为系统管理的内存,在使用前需要先将管理的内存进行初始化。

/* 初始化内存管理模块 */
Mem_Init();

5 OSStart()

在创建完任务的时候,我们需要开启调度器,因为创建仅仅是把任务添加到系统中, 还没真正调度,我们使用 OSStart()函数就能让系统开始运行。

OSStart()是uCOS 为我们提供一个系统启动的函数接口。

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!