[uCOS/RTOS] 关于RT thread系统节拍时钟的配置

[复制链接]
318|1
 楼主 | 2020-1-1 18:26 | 显示全部楼层 |阅读模式
本帖最后由 xyzjacky 于 2020-1-8 12:52 编辑

首先,使用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。

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

同时,看这段代码:
52. /* Get HCLK prescaler */  
53.   tmp = AHBPrescTable[((RCC->CFGR & RCC_CFGR_HPRE) >> 4U)];  
54.   /* HCLK clock frequency */  
55. SystemCoreClock >>= tmp;  

注释Get HCLK prescaler,意思是取得AHB的预分频;
注释HCLK clock frequency,意思是HCLK的频率。
一般我们都会将HCLK调整为72MHz,由此可知,SystemCoreClock算出来的值是72000000。
人体.png

使用特权

评论回复
 楼主 | 2020-1-1 18:29 | 显示全部楼层
本帖最后由 xyzjacky 于 2020-1-1 18:43 编辑

资料一: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个部分:抢先优先级和子优先级,上述的4个bit可以灵活分配给抢先优先级和子优先级,比如,1个bit表示抢先优先级,则剩余3个bit表示子优先级。固件库中对应的函数是: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位可以使能计数器,currentvalue register(当前值寄存器)随着时钟一直递减,当他减到0的时候,重装载寄存器(reload value register)就会重新装载这只的值,计数器继续从这个值递减
二、相关寄存器:
2.1  SysTick->CTRL 状态和控制寄存器
控制和状态寄存器CTRL(复位值0x00000000)
  
位段
  
名称
类型
描述
16
COUNTFLAG
只读
计数到0时置1;读取该位将清0
2
CLKSOURCE
可读可写
时钟来源?: 0--HCLK/8;1--HCLK
1
TICKINT
可读可写
1:计数到0时产生SysTick异常请求(即中断)
  
0:不产生异常请求(即中断)
0
ENABLE
可读可写
使能位,即定时器的开关,1有效




2.2  SysTick->LOAD 重装载值寄存器
当前值寄存器为0时,自动将重装载值重装到当前值计数器,重装载值的大小需要自己设置
1.jpg
2.3  SysTick->VAL当前值寄存器
可读可写,当计数器使能时,这个寄存器的值开始递减,使用前后注意清零
2.jpg

三、SysTick逻辑图
3.jpg
4.jpg
file:///C:/Users/XYZJAC~2/AppData/Local/Temp/msohtmlclip1/01/clip_image005.jpg
file:///C:/Users/XYZJAC~2/AppData/Local/Temp/msohtmlclip1/01/clip_image006.jpg
四、时钟频率与延时
4.1时钟选择
采用参考8分频的参考时钟(168M / 8 =21M)比较准,所以此处SysTick计数器选择21M的时钟
5.jpg


file:///C:/Users/XYZJAC~2/AppData/Local/Temp/msohtmlclip1/01/clip_image007.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); //控制寄存器位2置0,选择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权威指南》




(待续……)

(PS:21ic论坛的 帖子编辑功能始终让人头疼。)

使用特权

评论回复
扫描二维码,随时随地手机跟帖
您需要登录后才可以回帖 登录 | 注册

本版积分规则

我要发帖 投诉建议 创建版块 申请版主

快速回复

您需要登录后才可以回帖
登录 | 注册
高级模式

论坛热帖

关闭

热门推荐上一条 /2 下一条

在线客服 快速回复 返回顶部 返回列表