每一个支持多进程(线程)的系统都会有一个滴答时钟(系统时钟),这个时钟就好比系统的“心脏”,线程的休眠(延时)和时间片轮转调度都需要用到它。
Cortex-M系列的内核都有一个systick时钟,这个时钟就是设计用来支持操作系统的,是一个24位的自动重装载向下计数器,中断入口就位于中断向量表里面,定义在zephyr-zephyr-v1.13.0\arch\arm\core\cortex_m\vector_table.S:
1 SECTION_SUBSEC_FUNC(exc_vector_table,_vector_table_section,_vector_table) 2 3 /* 4 * setting the _very_ early boot on the main stack allows to use memset 5 * on the interrupt stack when CONFIG_INIT_STACKS is enabled before 6 * switching to the interrupt stack for the rest of the early boot 7 */ 8 .word _main_stack + CONFIG_MAIN_STACK_SIZE 9 10 .word __reset 11 .word __nmi 12 13 .word __hard_fault 14 .word __mpu_fault 15 .word __bus_fault 16 .word __usage_fault 17 .word __reserved 18 .word __reserved 19 .word __reserved 20 .word __reserved 21 .word __svc 22 .word __debug_monitor 23 24 .word __reserved 25 .word __pendsv 26#if defined(CONFIG_CORTEX_M_SYSTICK) 27 .word _timer_int_handler 28#else 29 .word __reserved 30#endif
第27行,_timer_int_handler()就是systick时钟的中断入口函数。
那么问题来了,前面的启动过程随笔里并没有分析到systick时钟是何时被初始化的,事实上systick也是通过设备宏定义的方式进行初始化的,定义在zephyr-zephyr-v1.13.0\drivers\timer\sys_clock_init.c:
SYS_DEVICE_DEFINE("sys_clock", _sys_clock_driver_init, sys_clock_device_ctrl, PRE_KERNEL_2, CONFIG_SYSTEM_CLOCK_INIT_PRIORITY);
可知,系统时钟属于PRE_KERNEL_2类设备,同一类设备也是有分优先级的,优先级高的先初始化,初始化函数为_sys_clock_driver_init(),定义在zephyr-zephyr-v1.13.0\drivers\timer\cortex_m_systick.c:
1 int _sys_clock_driver_init(struct device *device) 2 { 3 /* enable counter, interrupt and set clock src to system clock */ 4 u32_t ctrl = SysTick_CTRL_ENABLE_Msk | SysTick_CTRL_TICKINT_Msk | 5 SysTick_CTRL_CLKSOURCE_Msk; 6 7 ARG_UNUSED(device); 8 9 /* 10 * Determine the reload value to achieve the configured tick rate. 11 */ 12 13 /* systick supports 24-bit H/W counter */ 14 __ASSERT(sys_clock_hw_cycles_per_tick <= (1 << 24), 15 "sys_clock_hw_cycles_per_tick too large"); 16 sysTickReloadSet(sys_clock_hw_cycles_per_tick - 1); 17 18 NVIC_SetPriority(SysTick_IRQn, _IRQ_PRIO_OFFSET); 19 20 SysTick->CTRL = ctrl; 21 22 SysTick->VAL = 0; /* triggers immediate reload of count */ 23 24 return 0; 25}
系统时钟不一定要使用systick,像Nordic的SOC用的是硬件RTC作为系统时钟的,只是不过systick是一个通用的时钟。
第16行,参数sys_clock_hw_cycles_per_tick的含义是多少个systick时钟计数产生一个中断,这里CPU时钟为72MHz(systick时钟源来自CPU),系统时钟中断周期为10ms(100Hz,1秒产生100个中断),所以sys_clock_hw_cycles_per_tick = 72000000 / 100 = 720000。sysTickReloadSet()函数定义在zephyr-zephyr-v1.13.0\drivers\timer\cortex_m_systick.c:
1 static ALWAYS_INLINE void sysTickReloadSet( 2 u32_t count /* count from which timer is to count down */ 3 ) 4 { 5 /* 6 * Write the reload value and clear the current value in preparation 7 * for enabling the timer. 8 * The countflag in the control/status register is also cleared by 9 * this operation. 10 */ 11 SysTick->LOAD = count; 12 SysTick->VAL = 0; /* also clears the countflag */ 13 }
第11行,设置重装载寄存器。
第12行,将计数值置0,在使能systick后就会马上触发中断。
回到_sys_clock_driver_init()函数,第18行,设置systick的中断优先级,这里_IRQ_PRIO_OFFSET的值为1,因此systick的中断优先级就为1。
第20行,使能systick。
第22行,马上触发systick中断,并自动重装计数值。
接下来看systick中断执行函数_timer_int_handler(),定义在zephyr-zephyr-v1.13.0\drivers\timer\cortex_m_systick.c:
1 void _timer_int_han