关于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),通知操作系统已经过去一个系统时钟
先看这段代码:
- void SysTick_Handler(void)
- {
- /* enter interrupt */
- rt_interrupt_enter();
- rt_tick_increase();
- /* leave interrupt */
- rt_interrupt_leave();
- }
其中函数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();
- }
若系统的硬件是外置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中:
- /* Tick per Second */
- #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()中有如下一段代码(关键部份为橙色):
- /* Get SYSCLK source -------------------------------------------------------*/
- switch (RCC->CFGR & RCC_CFGR_SWS)
- {
- case 0x00: /* MSI used as system clock source */
- SystemCoreClock = msirange;
- break;
- case 0x04: /* HSI used as system clock source */
- SystemCoreClock = HSI_VALUE;
- break;
- case 0x08: /* HSE used as system clock source */
- SystemCoreClock = HSE_VALUE;
- break;
- case 0x0C: /* PLL used as system clock source */
- /* PLL_VCO = (HSE_VALUE or HSI_VALUE or MSI_VALUE/ PLLM) * PLLN
- SYSCLK = PLL_VCO / PLLR
- */
- pllsource = (RCC->PLLCFGR & RCC_PLLCFGR_PLLSRC);
- pllm = ((RCC->PLLCFGR & RCC_PLLCFGR_PLLM) >> 4U) + 1U ;
- switch (pllsource)
- {
- case 0x02: /* HSI used as PLL clock source */
- pllvco = (HSI_VALUE / pllm);
- break;
- case 0x03: /* HSE used as PLL clock source */
- pllvco = (HSE_VALUE / pllm);
- break;
- default: /* MSI used as PLL clock source */
- pllvco = (msirange / pllm);
- break;
- }
- pllvco = pllvco * ((RCC->PLLCFGR & RCC_PLLCFGR_PLLN) >> 8U);
- pllr = (((RCC->PLLCFGR & RCC_PLLCFGR_PLLR) >> 25U) + 1U) * 2U;
- SystemCoreClock = pllvco/pllr;
- break;
- default:
- SystemCoreClock = msirange;
- break;
- }
- /* Compute HCLK clock frequency --------------------------------------------*/
- /* Get HCLK prescaler */
- tmp = AHBPrescTable[((RCC->CFGR & RCC_CFGR_HPRE) >> 4U)];
- /* HCLK clock frequency */
- SystemCoreClock >>= tmp;
在代码中,switch (RCC->CFGR & RCC_CFGR_SWS)显然就是硬件的时钟寄存器,一般STM32F103系列电路板上用的是外部晶振,所以就是0x08的值在起作用。
- case 0x08: /* HSE used as system clock source */
- SystemCoreClock = HSE_VALUE;
查找HSE_VALUE的定义,果然能找到
#define HSE_VALUE ((uint32_t)8000000U) /*!< Value of the External oscillator in Hz */
一般电路板上这个外部晶振都是8MHz,所以此处HSE_VALUE为8000000U
同时,看这段代码:
- /* Get HCLK prescaler */
- tmp = AHBPrescTable[((RCC->CFGR & RCC_CFGR_HPRE) >> 4U)];
- /* HCLK clock frequency */
- SystemCoreClock >>= tmp;
注释Get HCLK prescaler,意思是取得AHB的预分频;
注释HCLK clock frequency,意思是HCLK的频率。
一般我们都会将HCLK调整为72MHz,由此可知,SystemCoreClock算出来的值是72000000。
资料一: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中断。
- /**
- * SysTick是一个简单的递减24位计数器
- * 以下是对SysTick进行重定义,用于初始化OS系统的系统滴答(又称心跳)
- **/
- #define _SCB_BASE (0xE000E010UL)
- #define _SYSTICK_CTRL (*(rt_uint32_t *)(_SCB_BASE + 0x0)) //SysTick->CTRL, 地址 0xE000E010 -- 状态和控制寄存器
- #define _SYSTICK_LOAD (*(rt_uint32_t *)(_SCB_BASE + 0x4)) //SysTick->LOAD, 地址 0xE000E014 -- 重装载值寄存器
- #define _SYSTICK_VAL (*(rt_uint32_t *)(_SCB_BASE + 0x8)) //SysTick->VAL, 地址 0xE000E018 -- 当前值寄存器
- #define _SYSTICK_CALIB (*(rt_uint32_t *)(_SCB_BASE + 0xC)) //SysTick->CALRB, 地址 0xE000E01C -- 校准值寄存器
- #define _SYSTICK_PRI (*(rt_uint8_t *)(0xE000ED23UL)) //SCB->SHP[11], 地址 0xE000ED23 -- ARM内核部件优先级设置寄存器的第11个字节:SysTick Priority level ,
- // 用于配置SysTick(系统定时器)的优先级,0x00为最高,0xFF为最低
- static uint32_t _SysTick_Config(rt_uint32_t ticks)
- {
- if ((ticks - 1) > 0xFFFFFF)
- {
- return 1;
- }
- _SYSTICK_LOAD = ticks - 1;
- _SYSTICK_PRI = 0xFF;
- _SYSTICK_VAL = 0;
- _SYSTICK_CTRL = 0x07; //使能了SysTick工作;产生中断;时钟来源:HCLK;
- return 0;
- }
资料二:关于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位可以使能计数器,current value 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时,自动将重装载值重装到当前值计数器,重装载值的大小需要自己设置
2.3 SysTick->VAL当前值寄存器
可读可写,当计数器使能时,这个寄存器的值开始递减,使用前后注意清零
三、SysTick逻辑图
四、时钟频率与延时
4.1时钟选择
采用参考8分频的参考时钟(168M / 8 = 21M)比较准,所以此处SysTick计数器选择21M的时钟
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
等待计数器计数完毕
当前值清零
关闭计数器
六、示例代码
- #include "delay.h"
- #define Value_us 21
- #define Value_ms 21000
- void delay_init(void)
- {
- SysTick->CTRL &= (1 << 2); //控制寄存器位2置0,选择8分频时钟
- SysTick->CTRL &= ~(1 << 0); //关闭计数器
- }
- void delay_us(u32 num)
- {
- SysTick->LOAD = num * Value_us;
- SysTick->VAL = 0;
- SysTick->CTRL |= (1 << 0); //使能计数器
- while (!(SysTick->CTRL & 1<<16)); //判断是否计数完毕
- SysTick->VAL = 0;
- SysTick->CTRL &= ~(1 << 0); //关闭计数器
- }
- void delay_ms(u32 num)
- {
- SysTick->LOAD = num * Value_ms;
- SysTick->VAL = 0;
- SysTick->CTRL |= (1 << 0); //使能计数器
- while (!(SysTick->CTRL & 1<<16));
- SysTick->VAL = 0;
- SysTick->CTRL &= ~(1 << 0); //关闭计数器
- }
详细资料请参考《Cortex M3与M4权威指南》
关于RT thread系统节拍时钟的配置相关推荐
- rt thread系统下添加wiznet软件包后,不插网线CPU利用率100%问题
rt thread系统下添加wiznet软件包后如果不插网线的话其他任务运行很卡,使用ps命令发现优先级低的任务很多都超时了 rt thread线程错误码 添加了一个可以查看CPU利用率的软件包CPU ...
- stm32f407单片机rt thread 片外spi flash OTA升级配置示例
参考地址https://www.rt-thread.org/document/site/application-note/system/rtboot/an0028-rtboot/ 第一步,生成Boot ...
- RT Thread根据开发板制作BSP方法
之前一直不懂怎么使用RT Thread的软件包,感谢网上的大神,看了你们的博客后大概了解一些,在此做下记录.用RT Thread软件包需要RT Thread的系统,但是RT Thread和RT Thr ...
- 使用RT Thread设备框架封装一个I2C设备——DS3231
使用RT Thread设备框架封装一个I2C设备--DS3231 前言 ENV配置 I2C测试 将ds3231封装成一个字符设备 结语 前言 学习rt thread的I2C的时候,恰巧手上的板子留了d ...
- GPS北斗时钟同步系统(网络时钟系统)组成及配置
GPS北斗时钟同步系统(网络时钟系统)组成及配置 GPS北斗时钟同步系统(网络时钟系统)组成及配置 随着计算机和网络通信技术的飞速发展,火电厂热工自动化系统数字化.网络化的时代已经到来.一方面它为控制 ...
- 【AT91SAM7X-EK开发板】系统时钟的配置
前言 [AT91SAM7X-EK]基于AT91SAM7X256,系统的外部晶振是:18.432MHz,而不是8MHz.16MHz这样常见的普通晶振,是怎么生成48MHz MCK(主时钟)的呢? 了解A ...
- RT Thread Studio 配置IIC并读取AS5600角度
RT Thread Studio 配置IIC并读取AS5600角度 一.RT Thread Studio 配置IIC 1.在RT Thread Seting 中开启IIC功能 并保存 一定要保存才能更 ...
- RCC——系统时钟函数分析时钟的配置流程和自己动手写时钟配置函数配置时钟,实现超频
下面是SystemInit(void)函数的源代码:重点和时钟配置有关的是SetSysClock()函数 void SystemInit (void) {/* Reset the RCC clock ...
- STM32系统时钟及配置方法
什么是时钟 单片机如果要正常运行,时钟信号是必不可少的.作为CPU的脉搏,时钟的快慢决定了CPU的运行速率,执行指令的速度.一般时钟源会被分频器或倍频器分成多种频率的时钟,以满足系统的不同应用. 那么 ...
最新文章
- 搞定Go单元测试(三)—— 断言(testify)
- linux5支持32,Red Hat发布RHEL5.3 可支持32颗虚拟CPU
- HDU - 6315 Naive Operations(线段树+思维)
- 大剑无锋之HashMap全考点
- Android开发杂谈更新中
- 又一款5G手机获进网许可 开售在即 你会尝鲜吗?
- 设计灵感|独具中国韵味的海报设计
- @configuration注解_Spring注解@Configuration
- 使用未初始化的内存是什么意思_活动板房是什么意思?怎么装修?使用年限+价格知识点合集...
- JSP中的坑(二):使用include包含jsp文件时contentType中charset的值区分大小写
- RestTemplate 发送文件
- 乘风破浪,遇见未来新能源汽车(Electric Vehicle)之特斯拉提车必须知道的十个流程
- 精密单点定位/PPP软件GAMP学习之一
- 防止sql拼接的Java方法_JAVA程序防止SQL注入的方法
- iOS 图片编辑——缩放剪切
- 通过requests获取网络上图片的大小
- php+条形码在线怎么生成,[条形码生成器]php实现在线生成条形码例子分享
- telegram自动发信息_创建telegram 机器人 并发送消息
- 【金猿技术展】视频矫正技术——基于参数估计的自由几何变换算法
- 升级版的冒泡排序Java