目录
一、结构图与寄存器
二、SysTick定时时间计算
三、Systick-系统定时器配置程序
如何更改systick中断优先级:
四、实验设计
(1) 通过查询标志位来写延时函数
(2)通过使能中断来写延时函数
SysTick:系统定时器,24位,只能递减,存在于内核,嵌套在NVIC中。
所有的Cortex-M内核的单片机都具有这个定时器。通过系统定时器,我们可以实现精准的软件延时(毫秒、微秒级)。
重装载寄存器:存放初始值 STK_CLK:时钟
counter在时钟的驱动下,从reload初值开始往下递减计数到0(这样为一个循环),产生中断和置位COUNTFLAG标志。然后又从reload值开始重新递减计数,如此循环。
其相关寄存器在M3编程手册里面:
1.STK_CTRL寄存器(控制及状态寄存器):第2位:AHB指的是高速总线时钟—HCLK,为72M,这一位置0,时钟就是9M,这一位置1,时钟就是72M。第1位:置0,不产生中断;置1,产生中断。
2. STK_LOAD寄存器(重装载数值寄存器),存放一个24位的初始值。
3. STK_VAL寄存器(当前数值寄存器)。
首先,我们要确定3个变量。
1-t:一个计数循环的时间(从初始值递减到0所用的时间),跟reload(存放初始值的寄存器)和CLK有关
2-CLK:72M或者9M,由STK_CTRL寄存器配置
3-RELOAD(初始值):24位,用户自己配置
t = reload * ( 1/clk )
Clk = 72M时,t = (72) *(1/ 72 M )= 1US
Clk = 72M时,t = (72000) *(1/ 72 M )= 1MS
SysTick寄存器结构体:在固件库文件:core_cm3.h中定义
SysTick配置库函数:在固件库文件:core_cm3.h中定义
SysTick配置中断优先级:在固件库文件:core_cm3.h中定义
SysTick配置的过程(此程序已经写好):
步骤如下:
1.判断自己规定的初始值是否超过了最大值。
2.初始化reload寄存器的值(确定初始值)。
3.配置systick的中断优先级。
static __INLINE uint32_t SysTick_Config(uint32_t ticks)
{
if (ticks > SysTick_LOAD_RELOAD_Msk) return (1); /* Reload value impossible */
/*ticks;自己规定的初始值
SysTick_LOAD_RELOAD_Msk:2的24次方 初始值超过了最大值,肯定是不行的
*/
/*初始化reload寄存器的值*/
SysTick->LOAD = (ticks & SysTick_LOAD_RELOAD_Msk) - 1; /* set reload register */
/*配置systick的中断优先级*/
NVIC_SetPriority (SysTick_IRQn, (1<<__NVIC_PRIO_BITS) - 1); /* set Priority for Cortex-M0 System Interrupts */
/*读取当前计数的值,计数器值清0*/
SysTick->VAL = 0; /* Load the SysTick Counter Value */
/*配置systick的时钟为AHB时钟,使能systick的中断,使能systick*/
SysTick->CTRL = SysTick_CTRL_CLKSOURCE_Msk |
SysTick_CTRL_TICKINT_Msk |
SysTick_CTRL_ENABLE_Msk; /* Enable SysTick IRQ and SysTick Timer */
return (0); /* Function successful */
}
systick中断优先级配置的是scb->shprx寄存器(内核的);而外设的中断优先级配置的是nvic->iprx,有优先级分组,有抢占优先级和子优先级的说法。
我们看一下中断优先级的第二个形参;:__NVIC_PRIO_BITS,官网给的备注是:STM32使用4位作为优先级,所以这个形参的官网给的值是4。在这个函数中,1左移4位,就是16,16-1=15。总共有4个位来表示优先级,15就是“1111”,也就是说,systick中断这个优先级永远是最低的。
比较内核和外设的中断优先级:自己先确定好外设的NVIC分组,systick就套用相同的分组方法。内核外设的中断优先级的四个位按照外设的中断优先级来分组来解析,即人为的分出抢占优先级和子优先级。例如:前面这个15,解析成4个位就为:1111。如果NVIC分组选择的是组1,即第一个1代表着主优先级,后面三个1代表着子优先级。这个时候,如果你想提高systick中断的优先级的话,就更改主/子优先级的大小,比外设的主/子优先级高就行(也就是上面4位2进制的数变低)。
所以:我们只需要更改“__NVIC_PRIO_BITS”这个变量的大小即可。
4.读取当前计数的值,计数器值清0(VAL寄存器)。
5.配置systick的时钟为AHB时钟,使能systick的中断,使能systick。
1-编写一个微秒延时函数 2-编写一个毫秒延时函数
方法有两种:通过查询标志位来写延时函数,通过使能中断来写延时函数
先定义一个变量,循环一轮(1ms/us),然后在for循环里面等待到了我们规定的延时的时间(CTRL寄存器寄存器第16位为1),就完成了延时。
/*通过查询标志位来写延时函数*/
void SysTick_Delayms(uint32_t ms)//1毫秒延时函数,延时多少毫秒更改形参即可
{
uint32_t i;
SysTick_Config(72000);//循环1轮就是1毫秒
for(i=0;iCTRL) & (1<<16)) != 1);
//判断CTRL这个寄存器的16位不为1,就继续在while循环里面等待
}
SysTick->CTRL &= ~SysTick_CTRL_ENABLE_Msk;//把这位清0,失能systick
}
void SysTick_Delayus(uint32_t us)//1微秒延时函数,延时多少微秒更改形参即可
{
uint32_t i;
SysTick_Config(72);//循环1轮就是1微秒
for(i=0;iCTRL) & (1<<16)) != 1);
//判断CTRL这个寄存器的16位不为1,就继续在while循环里面等待
}
SysTick->CTRL &= ~SysTick_CTRL_ENABLE_Msk;//把这位清0,失能systick
}
定义一个全局变量(毫秒和微秒要分开用),先把我们要延时的这个数值赋给这个全局变量,然后走一轮循环;在中断服务函数里面,找到systick的中断服务函数,每进一次中断这个全局变量就减一;建立while循环,减到了0之后失能systick。
中断函数文件内:
extern volatile uint32_t isr_ms;//这个全局变量在bsp_systick.c文件中
extern volatile uint32_t isr_us;//这个全局变量在bsp_systick.c文件中
void SysTick_Handler(void)//systick 的中断服务函数
{
isr_ms--;
isr_us--;
}
延时函数文件内:
/*通过使能中断来写延时函数*/
volatile uint32_t isr_ms;//定义一个全局变量
void SysTick_Delayms_INT(uint32_t ms)//1毫秒延时函数,延时多少毫秒更改形参即可
{
isr_ms = ms;
SysTick_Config(72000);//循环1轮就是1毫秒
while(isr_ms);
//把这位清0,失能systick
SysTick->CTRL &= ~SysTick_CTRL_ENABLE_Msk;
}
volatile uint32_t isr_us;//定义一个全局变量
void SysTick_Delayus_INT(uint32_t us)//1毫秒延时函数,延时多少毫秒更改形参即可
{
isr_us = us;
SysTick_Config(72);//循环1轮就是1毫秒
while(isr_us);
//把这位清0,失能systick
SysTick->CTRL &= ~SysTick_CTRL_ENABLE_Msk;
}