关于RT thread系统节拍时钟的配置

关于RT thread系统节拍时钟的配置

                 -----本文基于rt-thread-3.1.3版本编写

首先,使用RTthread OS时,要配置(或者明白)它的系统节拍rt_tick(划重点)。

系统节拍

  • 系统节拍是特定的周期中断,可以看是系统心跳,中断之间的时间间隔取决于不同的应用,一般是 1ms–100ms,系统节拍率越快,系统的额外开销就越大,从系统启动开始计数的时钟节拍数称为系统时间。
  • RT-Thread 中,系统节拍的长度可以根据 RT_TICK_PER_SECOND 的定义来调整,等于 1/RT_TICK_PER_SECOND 秒。

系统节拍实现方式

  • 系统节拍由配置为中断触发模式的硬件定时器产生,当中断到来时,将调用一次:void rt_tick_increase(void),通知操作系统已经过去一个系统时钟

 

先看这段代码:

  1. void SysTick_Handler(void)  
  2. {  
  3.     /* enter interrupt */  
  4.     rt_interrupt_enter();  
  5.   
  6.     rt_tick_increase();  
  7.   
  8.     /* leave interrupt */  
  9.     rt_interrupt_leave();  
  10. }  

 

其中函数rt_tick_increase()的代码是:

 

  1. void rt_tick_increase(void)  
  2. {  
  3.     struct rt_thread *thread;  
  4.   
  5.     /* increase the global tick */  
  6. #ifdef RT_USING_SMP  
  7.     rt_cpu_self()->tick ++;  
  8. #else  
  9.     ++ rt_tick;  
  10. #endif  
  11.   
  12.     /* check time slice */  
  13.     thread = rt_thread_self();  
  14.   
  15.     -- thread->remaining_tick;  
  16.     if (thread->remaining_tick == 0)  
  17.     {  
  18.         /* change to initialized tick */  
  19.         thread->remaining_tick = thread->init_tick;  
  20.   
  21.         /* yield */  
  22.         rt_thread_yield();  
  23.     }  
  24.   
  25.     /* check timer */  
  26.     rt_timer_check();  
  27. }  

 

若系统的硬件是外置8MHz晶振,系统时钟每秒节拍数1000表示1s内rt_tick增加1000,即时钟节拍为1ms;若设为100,则rt_tick每隔10ms加1,时钟节拍为10ms。

所以如果想做成时钟节拍为5ms的,那就把RT_TICK_PER_SECOND设置为200;如果想把时钟节拍做成2ms的,那就把RT_TICK_PER_SECOND设置为500,以此类推。

 

SysTick_Handler是STM32的硬件滴答时钟的中断库函数,既然是一个时钟,那么类似定时器,它应该有一个时间间隔才产生一次中断。这个时间间隔就由以下这个函数来设置:

 

HAL_SYSTICK_Config(HAL_RCC_GetHCLKFreq() / RT_TICK_PER_SECOND);

 

其中RT_TICK_PER_SECOND定义在rtconfig.h中:

  1. /* Tick per Second */  
  2. #define RT_TICK_PER_SECOND 1000  

RT thread对于此处代码的解释是,等于 1/RT_TICK_PER_SECOND 秒,RT_TICK_PER_SECOND默认是1000。

 

又因HAL_RCC_GetHCLKFreq()的返回值是SystemCoreClock,所以就要查找SystemCoreClock在哪里赋值的。所以继续查看代码,按以下顺序找到:

rtthread_startup() -> rt_hw_board_init() -> SystemClock_Config() -> SystemCoreClockUpdate ()

 

在函数SystemCoreClockUpdate()中有如下一段代码(关键部份为橙色):

  1. /* Get SYSCLK source -------------------------------------------------------*/  
  2.   switch (RCC->CFGR & RCC_CFGR_SWS)  
  3.   {  
  4.     case 0x00:  /* MSI used as system clock source */  
  5.       SystemCoreClock = msirange;  
  6.       break;  
  7.   
  8.     case 0x04:  /* HSI used as system clock source */  
  9.       SystemCoreClock = HSI_VALUE;  
  10.       break;  
  11.   
  12.     case 0x08:  /* HSE used as system clock source */  
  13.       SystemCoreClock = HSE_VALUE;  
  14.       break;  
  15.   
  16.     case 0x0C:  /* PLL used as system clock  source */  
  17.       /* PLL_VCO = (HSE_VALUE or HSI_VALUE or MSI_VALUE/ PLLM) * PLLN 
  18.          SYSCLK = PLL_VCO / PLLR 
  19.          */  
  20.       pllsource = (RCC->PLLCFGR & RCC_PLLCFGR_PLLSRC);  
  21.       pllm = ((RCC->PLLCFGR & RCC_PLLCFGR_PLLM) >> 4U) + 1U ;  
  22.   
  23.       switch (pllsource)  
  24.       {  
  25.         case 0x02:  /* HSI used as PLL clock source */  
  26.           pllvco = (HSI_VALUE / pllm);  
  27.           break;  
  28.   
  29.         case 0x03:  /* HSE used as PLL clock source */  
  30.           pllvco = (HSE_VALUE / pllm);  
  31.           break;  
  32.   
  33.         default:    /* MSI used as PLL clock source */  
  34.           pllvco = (msirange / pllm);  
  35.           break;  
  36.       }  
  37.       pllvco = pllvco * ((RCC->PLLCFGR & RCC_PLLCFGR_PLLN) >> 8U);  
  38.       pllr = (((RCC->PLLCFGR & RCC_PLLCFGR_PLLR) >> 25U) + 1U) * 2U;  
  39.       SystemCoreClock = pllvco/pllr;  
  40.       break;  
  41.   
  42.     default:  
  43.       SystemCoreClock = msirange;  
  44.       break;  
  45.   }  
  46.     
  47. /* Compute HCLK clock frequency --------------------------------------------*/  
  48.   /* Get HCLK prescaler */  
  49.   tmp = AHBPrescTable[((RCC->CFGR & RCC_CFGR_HPRE) >> 4U)];  
  50.   /* HCLK clock frequency */  
  51.   SystemCoreClock >>= tmp;  

 

在代码中,switch (RCC->CFGR & RCC_CFGR_SWS)显然就是硬件的时钟寄存器,一般STM32F103系列电路板上用的是外部晶振,所以就是0x08的值在起作用。

  1. case 0x08:  /* HSE used as system clock source */  
  2.       SystemCoreClock = HSE_VALUE;  

 

查找HSE_VALUE的定义,果然能找到

#define HSE_VALUE    ((uint32_t)8000000U) /*!< Value of the External oscillator in Hz */  

一般电路板上这个外部晶振都是8MHz,所以此处HSE_VALUE为8000000U

 

同时,看这段代码:

  1. /* Get HCLK prescaler */  
  2.   tmp = AHBPrescTable[((RCC->CFGR & RCC_CFGR_HPRE) >> 4U)];  
  3.   /* HCLK clock frequency */  
  4. SystemCoreClock >>= tmp;  

注释Get HCLK prescaler,意思是取得AHB的预分频;

注释HCLK clock frequency,意思是HCLK的频率。

一般我们都会将HCLK调整为72MHz,由此可知,SystemCoreClock算出来的值是72000000。

关于RT thread系统节拍时钟的配置_第1张图片

 

资料一:RT thread中的SysTick

在版本为3.1.3的RT thread OS的board.c源码中,有关于OS系统滴答(心跳)的初始化的代码。

此处要先说明一个知识点,STM32芯片中,分为ARM内核(如SysTick)和普通外设(如IIC,USART,TIM)。ARM内核的部件也是具有中断的,这些中断当然也具有优先级。

RT thread OS通过重定义和配置,来使能SysTick计时,并产生SysTick_Handler中断。

 

  1. /** 
  2. *   SysTick是一个简单的递减24位计数器 
  3. *   以下是对SysTick进行重定义,用于初始化OS系统的系统滴答(又称心跳) 
  4. **/  
  5. #define _SCB_BASE       (0xE000E010UL)  
  6. #define _SYSTICK_CTRL   (*(rt_uint32_t *)(_SCB_BASE + 0x0))     //SysTick->CTRL,  地址 0xE000E010 --  状态和控制寄存器   
  7. #define _SYSTICK_LOAD   (*(rt_uint32_t *)(_SCB_BASE + 0x4))     //SysTick->LOAD,  地址 0xE000E014 --  重装载值寄存器  
  8. #define _SYSTICK_VAL    (*(rt_uint32_t *)(_SCB_BASE + 0x8))     //SysTick->VAL,   地址 0xE000E018 --  当前值寄存器  
  9. #define _SYSTICK_CALIB  (*(rt_uint32_t *)(_SCB_BASE + 0xC))     //SysTick->CALRB, 地址 0xE000E01C --  校准值寄存器  
  10. #define _SYSTICK_PRI    (*(rt_uint8_t  *)(0xE000ED23UL))        //SCB->SHP[11],   地址 0xE000ED23 --  ARM内核部件优先级设置寄存器的第11个字节:SysTick Priority level   
  11.                                     //   用于配置SysTick(系统定时器)的优先级,0x00为最高,0xFF为最低  
  1. static uint32_t _SysTick_Config(rt_uint32_t ticks)  
  2. {  
  3.     if ((ticks - 1) > 0xFFFFFF)  
  4.     {  
  5.         return 1;  
  6.     }  
  7.       
  8.     _SYSTICK_LOAD = ticks - 1;   
  9.     _SYSTICK_PRI = 0xFF;  
  10.     _SYSTICK_VAL  = 0;  
  11. _SYSTICK_CTRL = 0x07;   //使能了SysTick工作;产生中断;时钟来源:HCLK  
  12.    
  13.     return 0;  
  14. }  

 

资料二:关于STM32的中断优先级设置—systick

项目中需要将systick中断的优先级调低,遇到的问题总结如下:

1. STM32中断优先级是使用4-bit来表示的,即总共有16个级别。

2. 优先级分为2个部分:抢先优先级和子优先级,上述的4bit可以灵活分配给抢先优先级和子优先级,比如,1bit表示抢先优先级,则剩余3bit表示子优先级。固件库中对应的函数是:NVIC_PriorityGroupConfig()。

3. 中断分为内核中断和芯片(STM32)中断,配置的寄存器位置是不同的,芯片中断的配置使用NVIC,内核中断(比如systick)的配置使用SCB

4. 内核中断优先级设置使用函数:NVIC_SetPriority(),此函数需要一个表示优先级的参数,可以利用NVIC_EncodePriority()这个函数生成,比如:

  NVIC_SetPriority(SysTick_IRQn, NVIC_EncodePriority(NVIC_GetPriorityGrouping(), 0, 1));//抢先优先级:0(最高);子优先级:1()

 

资料三:SysTick的总结(寄存器操作)

我是新手,最近用STM32的SYSTick做了延时

编程思路:

选择时钟源

关闭计数器

设置重装载值

当前值清零

使能SysTick

等待计数器计数完毕

当前值清零

关闭计数器

 

一、概述:

SysTick是一个简单的递减24位计数器

如果你不需要再应用程序中嵌入操作系统,SysTick可以作为简单的延时和产生周期性的中断;

状态控制寄存器的第0位可以使能计数器,current value register(当前值寄存器)随着时钟一直递减,当他减到0的时候,重装载寄存器(reload value register)就会重新装载这只的值,计数器继续从这个值递减

二、相关寄存器:

2.1  SysTick->CTRL 状态和控制寄存器

控制和状态寄存器CTRL(复位值0x00000000)

位段

名称

类型

描述

16

COUNTFLAG

只读

计数到0时置1;读取该位将清0

2

CLKSOURCE

可读可写

时钟来源?: 0--HCLK/81--HCLK

1

TICKINT

可读可写

1计数到0时产生SysTick异常请求(即中断)

0:不产生异常请求(即中断)

0

ENABLE

可读可写

使能位,即定时器的开关,1有效

 

 

2.2  SysTick->LOAD 重装载值寄存器

http://c.51hei.com/a/huq/a/a/a/61/61.002.jpg

当前值寄存器为0时,自动将重装载值重装到当前值计数器,重装载值的大小需要自己设置

 

2.3  SysTick->VAL当前值寄存器

http://c.51hei.com/a/huq/a/a/a/61/61.003.jpg

可读可写,当计数器使能时,这个寄存器的值开始递减,使用前后注意清零

三、SysTick逻辑图

http://c.51hei.com/a/huq/a/a/a/61/61.004.jpg

 

http://c.51hei.com/a/huq/a/a/a/61/61.005.jpg

四、时钟频率与延时

4.1时钟选择

采用参考8分频的参考时钟(168M / 8 = 21M)比较准,所以此处SysTick计数器选择21M的时钟

http://c.51hei.com/a/huq/a/a/a/61/61.006.jpg

4.2  如何延时1us

时钟频率为21M,也就是1s的时间技术21M次。

由此可知计数一次用了 (1/21000000) s ,用了(1/21000) ms, 用了(1/21) us

所以,1us计数21次。

4.3  如何延时1ms

因为1ms = 1000us,所以综上所述,1ms计数1000*21次计数,1ms也就是21000次计数

4.3最大延时

24位计数器能保存的最大值 : 16777215。

最大延时时间 =:16777215 / 21 = 798915 us = 798.915ms

五、编程思路

5.1  Delay初始化

选择时钟源

关闭计数器

5.2  延时函数

设置重装载值

当前值清零

使能SysTick

等待计数器计数完毕

当前值清零

关闭计数器

六、示例代码

  1. #include "delay.h"  
  2. #define    Value_us    21  
  3. #define    Value_ms    21000  
  4. void delay_init(void)  
  5. {  
  6.               SysTick->CTRL &= (1 << 2); //控制寄存器位20,选择8分频时钟  
  7.               SysTick->CTRL &= ~(1 << 0); //关闭计数器  
  8. }  
  9. void delay_us(u32 num)  
  10. {              
  11.               SysTick->LOAD = num * Value_us;  
  12.               SysTick->VAL = 0;  
  13.               SysTick->CTRL |= (1 << 0); //使能计数器  
  14.               while (!(SysTick->CTRL & 1<<16));              //判断是否计数完毕  
  15.               SysTick->VAL = 0;  
  16.               SysTick->CTRL &= ~(1 << 0); //关闭计数器              
  17. }  
  18. void delay_ms(u32 num)  
  19. {                             
  20.               SysTick->LOAD = num * Value_ms;  
  21.               SysTick->VAL = 0;  
  22.               SysTick->CTRL |= (1 << 0); //使能计数器  
  23.               while (!(SysTick->CTRL & 1<<16));  
  24.               SysTick->VAL = 0;  
  25.               SysTick->CTRL &= ~(1 << 0); //关闭计数器              
  26. }  

详细资料请参考《Cortex M3M4权威指南》

 

你可能感兴趣的:(C代码,stm32,RTOS)