同样还是由主函数入手,当然不是说的用户层的主函数,而是
int $Sub$$main(void)
{
rt_hw_interrupt_disable();
rtthread_startup();
return 0;
}
然后进入rtthread_startup();函数,时钟肯定是第一批需要处理的事儿,在进入rt_hw_board_init();函数,便能看见对系统时钟的初始化
/* System clock initialization */
SystemClock_Config();
rt_hw_systick_init();
第一句便是所以STM32代码都会有的函数,对硬件时钟进行初始化,选择内部还是外部,分频之类的操作。
第二句便和系统相关了
该函数用于系统时钟的硬件初始化,内部初始化了滴答定时器
void rt_hw_systick_init(void)
{
HAL_SYSTICK_Config(HAL_RCC_GetHCLKFreq() / RT_TICK_PER_SECOND);
HAL_SYSTICK_CLKSourceConfig(SYSTICK_CLKSOURCE_HCLK);
HAL_NVIC_SetPriority(SysTick_IRQn, 0, 0);
}
HAL_RCC_GetHCLKFreq() 函数返回系统时钟,我L151工程里面是32M,RT_TICK_PER_SECOND宏在rt_config.h中被定义。HAL_SYSTICK_Config()该函数实际就是设定一个滴答定时器的中断间隔,假设RT_TICK_PER_SECOND被定义为1,那么每32M个节拍中断一次则等于1秒,系统的最小时间间隔就为1s。我工程里面设定的是1000,则表示我设定的定时器最小时间间隔为1ms。
第二句和第三句分别是设定定时器的分频和中断等级。
执行完毕则滴答定时器开始不断发生中断
下列函数即是滴答定时器的中断函数
void SysTick_Handler(void)
{
/* enter interrupt */
rt_interrupt_enter();
HAL_IncTick();
rt_tick_increase();
/* enter interrupt */
rt_interrupt_leave();
}
HAL_IncTick();是库函数层面的累加,在系统中实际是没有意义的。
rt_tick_increase()则进行了和系统相关的处理,实现如下:
void rt_tick_increase(void)
{
struct rt_thread *thread;
/* increase the global tick */
#ifdef RT_USING_SMP
rt_cpu_self()->tick ++;
#else
++ rt_tick;
#endif
/* check time slice */
thread = rt_thread_self();
-- thread->remaining_tick;
if (thread->remaining_tick == 0)
{
/* change to initialized tick */
thread->remaining_tick = thread->init_tick;
/* yield */
rt_thread_yield();
}
/* check timer */
rt_timer_check();
}
由于没有定义RT_USING_SMP宏,所以即是单纯的系统时间累计加到rt_tick变量上,并且rt_thread_self函数也是之间返回了当前运行的线程,之后将线程时间片减1,将减尽线程可用事件片后就使该线程放出执行权。线程部分具体还是在之后研究该部分的时候说明,最后rt_timer_check函数是在检查有没有发生定时器超时事件,同样具体放到定时器研究去详细说明。
综上其实和硬件相关的仅仅是void rt_hw_systick_init(void)函数和硬件中断回调void SysTick_Handler(void)。如果相关更换系统时钟的类型,例如使用RTC,则只需要在初始化中配置RTC按照RT_TICK_PER_SECOND宏周期中断,并在RTC的中断回调函数中执行rt_tick_increase()即可。
和系统时钟相关的函数存放于Kennel/clock.c中都是一些给时钟变量rt_tick服务的函数。注意的是tick已经表示的是系统节拍,也就是1/RT_TICK_PER_SECOND秒,可不是晶振分配得到的32M时钟。
网友评论