本文基于STM32F407ZGT6
—————————————
滴答定时器(SysTick)的作用:
- 1、作为操作系统时基
- 2、作为精确延时函数时基(delay函数)
滴答定时器是一个 24 位的倒计数定时器,当计到 0 时,将从 RELOAD 寄存器中自动重装载定时器初值,只要不把它在 SysTick 控制寄存器以及状态寄存器中的使能位清零,就将永久不息。
- Cortex‐M3/4 在内核水平上搭载了一个异常响应系统,支持为数众多的系统异常和外部中断。
- 编号为 1-15 的对应系统异常,大于等于 16 的则全是外部中断。
- 有 3 个固定优先级的系统异常:复位,NMI 以及硬 fault,它们的优先级是不可编程的,它们的优先级号是负数,从而高于所有其它异常。
- 其它异常的优先级则都是可编程的(但不能编程为负数)做成芯片后,支持的中断源数目常常不到 240
个,并且优先级的位数也由芯片厂商最终决定。但是16个异常一般都是拥有的,这16个异常都有共同的特性,比如它们的位操作是一样的,这增加了同一内核不同芯片之间代码的可移植性。
SysTick寄存器:(可通过M3/4权威指南查看)
SysTick定时器被捆绑在NVIC中,用于产生SYSTICK异常(异常号:15)。
在以前,大多操作系统需要一个硬件定时器来产生操作系统需要的滴答中断,作为整个系统的时基。
有4个寄存器控制SysTick定时器:
库函数里面的结构体:
typedef struct
{
__IO uint32_t CTRL; /*!< Offset: 0x000 (R/W) SysTick Control and Status Register */
__IO uint32_t LOAD; /*!< Offset: 0x004 (R/W) SysTick Reload Value Register */
__IO uint32_t VAL; /*!< Offset: 0x008 (R/W) SysTick Current Value Register */
__I uint32_t CALIB; /*!< Offset: 0x00C (R/ ) SysTick Calibration Register */
} SysTick_Type;
看后面的注释知道四个结构体成员对应上面四个寄存器。
SysTick定时器除了能服务于操作系统之外,还能用于其它目的:如作为一个闹铃,用于测量时间(delay延时函数)等。
要注意的是,当处理器在调试期间被喊停(halt)时,则SysTick定时器亦将暂停运作。
是个定时器都需要时钟来源,从时钟树来看,SysTick定时器的时钟是AHB/8(也就是HCLK/8)。如果我们外部晶振为 8M,然后倍频到 168M,那么 SysTick 的时钟即为 21Mhz。也就是 SysTick 的计数器 VAL (定时器的CURRENT寄存器)每减 1,就代表时间过了 1/21us ——在21Mhz的频率跳动时,每跳1次所用的时间,1/21us称它为时基:1/(21x10^6hz)=1/21us。(这就是为什么后面程序里面fac_us=SYSCLK/8的原因,在这里SYSCLK=168,所以fac_us=21)
具体代码:
//SYSTICK的时钟固定为AHB时钟的1/8
//SYSCLK:系统时钟频率
void delay_init(u8 SYSCLK)
{
/*定义SysTick的时钟来源,可以是HCLK或者是HCLK/8*/
SysTick_CLKSourceConfig(SysTick_CLKSource_HCLK_Div8);
/*SYSCLK是传进来的参数,这里是STM32F4,所以SYSCLK是168*/
/*HCLK/8定时器时基为1/21us,所以1us跳21次,这就是SYSCLK/8的原因*/
fac_us=SYSCLK/8; //fac_us其实就是定时1us了,这里是一个值21
fac_ms=(u16)fac_us*1000; //代表每个ms需要的systick时钟数
}
那么我们看看us的延时函数:
//延时nus
//nus为要延时的us数.
//注意:nus的值,不要大于798915us(最大值即2^24/fac_us@fac_us=21)
void delay_us(u32 nus)
{
u32 temp;
SysTick->LOAD=nus*fac_us; //时间加载fac_us=21,每个us跳21次
SysTick->VAL=0x00; //清空计数器
SysTick->CTRL|=SysTick_CTRL_ENABLE_Msk ; //SysTick定时器使能,开始倒数,SysTick_CTRL_ENABLE_Msk=1
do
{
/*判断SysTick的控制及状态寄存器(CTRL)第16位是否置1(置1代表倒计数结束)
temp&0x01这个语句是判断SysTick是否已经使能(其实可以省略这个语句)
!(temp&(1<<16)判断CTRL寄存器16位是否置1,置1取反后为0则跳出循环
*/
temp=SysTick->CTRL; //把CTRL寄存器的值赋给temp
}while((temp&0x01)&&!(temp&(1<<16))); //等待时间到达
SysTick->CTRL&=~SysTick_CTRL_ENABLE_Msk; //ysTick定时器使能位清零,关闭计数器
SysTick->VAL =0X00; //清空计数器
}
注释已经在代码中了,十分详细。