今日继续学习使用嘉立创购买的 立创梁山派天空星,芯片是 STM32F407VET6
主要是在学习移植UC/OS III系统时,需要我掌握好内核定时器SystemTick,以掌控整个实时操作系统的节拍,但我之前并未过多研究单片机的内核定时器,因此同步学习一下
文章目的是理解内核定时器SystemTick初始化,并能够自己添加上SystemTick的中断功能
文章提供测试代码讲解、完整工程下载、测试效果图
【立创·天空星STM32F407VET6】入门手册:
目录
系统定时器SysTick:
SysTick定时器可用作标准的下行计数器,是一个24位向下计数器,有自动重新装载能力,可屏蔽系统中断发生器。Cortex-M4处理器内部包含了一个简单的定时器,所有基于M4内核的控制器都带有SysTick定时器,这样就方便了程序在不同的器件之间的移植。SysTick定时器可用于操作系统,提供系统必要的时钟节拍,为操作系统的任务调度提供一个有节奏的“心跳”。正因为所有的M4内核的芯片都有Systick定时器,这在移植的时候不像普通定时器那样难以移植。
RCU 通过 AHB 时钟(HCLK)8 分频后作为 Cortex 系统定时器(SysTick)的外部时钟。通过对 SysTick 控制和状态寄存器的设置,可选择上述时钟或 AHB(HCLK)时钟作为 SysTick 时钟。
SysTick定时器设定初值并使能之后,每经过1个系统时钟周期,计数值就减1,减到0时,SysTick计数器自动重新装载初值并继续计数,同时内部的COUNTFLAG标志位被置位,触发中断(前提开启中断)。
SysTick相关寄存器:
SysTick Control and Status Register (SysTick_CTRL): 用于控制SysTick定时器的启动、停止,选择时钟源,以及读取当前定时器的状态。
SysTick Reload Value Register (SysTick_LOAD): 设置定时器的重载值,即定时器从加载值递减到0所需的时钟周期数。
SysTick Current Value Register (SysTick_VAL): 读取或清除定时器的当前值。
SysTick Calibration Value Register (SysTick_CALIB): 提供一个校准值,用于实现与实际时钟频率无关的固定周期中断。
STK_CTRL 控制寄存器:
寄存器内有 4 个位具有意义
第 0 位:ENABLE,Systick 使能位(0:关闭 Systick 功能;1:开启 Systick 功能)
第 1 位:TICKINT,Systick 中断使能位(0:关闭 Systick 中断;1:开启 Systick 中断)
第 2 位:CLK SOURCE,Systick 时钟源选择(0:使用 HCLK/8 作为 Systick 时钟;1:使用 HCLK 作为 Systick 时钟)
第 16 位:COUNT FLAG,Systick 计数比较标志,当计数到0时,置1;定时器开始重新计数时(CLK_LOAD重新写入数值)自动清零。
STK_LOAD 重载寄存器:
Systick 是一个递减的定时器,当定时器递减至0 时,重载寄存器中的值就会被重装载,继续开始递减。STK_LOAD 重载寄存器是个24 位的寄存器最大计数0xFFFFFF。
STK_VAL 当前值寄存器:
24 位的寄存器,读取时返回当前倒计数的值,写它则使之清零,同时还会清除在 SysTick 控制及状态寄存器中的 COUNTFLAG 标志。
STK_CALRB 校准值寄存器:
用于存储或调整时钟的校准值。这在需要精确时间计数的应用中非常重要,例如实时操作系统(RTOS)或需要精确时间戳的应用。
SysTick相关函数:
SysTick定时器的使用主要有:
SysTick_Config()函数
SysTick_Config()函数主要用来设置定时时间
void SysTick_CLKSourceConfig(uint32_t SysTick_CLKSource)函数
systick_clksource_set()函数用来选择SysTick时钟源
SysTick定时周期计算方法:
通过对初始化函数的解读可以了解到SysTick 的 时钟源来自于HCLK(不分频)、频率168M:
SysTick_CLKSourceConfig(SysTick_CLKSource_HCLK); //Systick 时钟源频率168M
由此我们知道了SystemCoreClock =168 000 000
带入计算式:
SysTick_LOAD = (SystemCoreClock / TickRate) - 1
其中:
SysTick_LOAD 是SysTick的 计数寄存器重载值
TickRate 就是你希望的周期,如果是1000Hz(即1ms),就填1000即可
这部分初始化函数调用书写大致如下:
uint32_t reload = SystemCoreClock / 1000 - 1; // 1ms中断频率 SysTick->LOAD = reload;
配置系统时钟新增中断功能:
配置SysTick 定时器步骤大致如下:
1、设置时钟源
2、设置 SysTick_LOAD 重载值(需要中断就打开中断)
3、清零当前计数器值
4、打开SysTick 定时器
5、如果需要还可配置定时器的NVIC中断优先级
本文这部分实践内容主要是打开 SysTick中断,频率1ms
并借此中断服务函数与标志位组合达到每1s串口发送数据的程序效果:
SysTick初始化:
/**
* This function will initial stm32 board.
*/
void board_init(void)
{
/* NVIC Configuration */
#define NVIC_VTOR_MASK 0x3FFFFF80
#ifdef VECT_TAB_RAM
/* Set the Vector Table base location at 0x10000000 */
SCB->VTOR = (0x10000000 & NVIC_VTOR_MASK);
#else /* VECT_TAB_FLASH */
/* Set the Vector Table base location at 0x08000000 */
SCB->VTOR = (0x08000000 & NVIC_VTOR_MASK);
#endif
SysTick_CLKSourceConfig(SysTick_CLKSource_HCLK); //Systick 时钟源频率168M
// 计算SysTick重装载值(SystemCoreClock为168MHz,希望SysTick中断频率为1ms(1000 Hz))
//SysTick_LOAD = (SystemCoreClock / TickRate) - 1
uint32_t reload = SystemCoreClock / 1000 - 1; // 1ms中断频率
SysTick->LOAD = reload;
// 清除SysTick当前值并启动SysTick,同时使能中断
SysTick->VAL = 0; // 清空当前值
SysTick->CTRL = SysTick_CTRL_CLKSOURCE_Msk | SysTick_CTRL_TICKINT_Msk | SysTick_CTRL_ENABLE_Msk;
/* 设置NVIC优先级分组 */
// 4位抢占优先级和0位子优先级
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4);
/* 设置SysTick中断的优先级 */
// 设置SysTick的抢占优先级为3(最低),没有子优先级(因为分组4没有子优先级位)
NVIC_SetPriority(SysTick_IRQn, 3); // 注意:优先级值根据分组设置可能有所不同
}
SysTick中断服务函数:
首先我们要注释掉:#include "stm32f4xx_it.h" 文件中的 中断服务函数定义的代码:
然后在我们选定的位置编写中断服务函数:
我是选择在主函数下面编写了,这样可以更直白观察:这个函数里面包括了一个打印函数,用于串口测试:
其中的 i 与 m 只是定义的int类型的全局变量,计数测试用的:
//SysTick中断服务函数:
void SysTick_Handler(void)
{
// 在这里处理SysTick中断
// 例如,可以更新一个时间戳,或者触发某个周期性任务
// 清除SysTick的中断标志位(如果需要的话,某些STM32库会自动处理)
SysTick->CTRL &= ~SysTick_CTRL_COUNTFLAG_Msk;
i++;
if(i==1000)
{
i=0;m++;
UsartPrintf(USART1," %d seconds past \r\n",m); //打印测试字符串
}
// ... 其他中断处理代码 ...
}
测试效果图:
通过串口助手时间戳对比看出,确实是1s发送了一次串口数据:
整体测试工程下载:
网上学习资料网址贴出: