STM32F407ZG Systick定时器及应用
Systick是系统定时器,对于CORTEX M3和M4的内核芯片,都有一个Systick定时器。
Systick定时器可以认为是系统主频(如STM32F407ZG为168M的时钟滴答)的实时计数。
Systick定时器的用法
Systick定时器常用来做延时,或者实时系统的心跳时钟。这样可以节省MCU资源,不用浪费一个定时器。比如UCOS中,分时复用,需要一个最小的时间戳,一般在STM32+UCOS系统中,都采用Systick做UCOS心跳时钟。
Systick定时器的用法
Systick定时器就是系统滴答定时器,一个24 位的倒计数定时器,计到0时,将从RELOAD 寄存器中自动重装载定时初值。只要不把它在SysTick 控制及状态寄存器中的使能位清除,就永不停息(循环进行重载计数,计数到0然后再重载计数),即使在睡眠模式下也能工作。
Systick定时器会将从RELOAD寄存器中装置初始值后,会一直按系统时钟定时减1,直到减为0.然后重新从RELOAD寄存器中装置初始值继续计数。
SysTick定时器被捆绑在NVIC中,用于产生SYSTICK异常(异常号:15)。
- Systick中断优先级也可以设置。
Systick相关寄存器
CTRL:SysTick控制和状态寄存器
SysTick初始化、控制、状态寄存器ENABLE:SYSTICK定时器的使能位
- TICKINT:计数到0后是否产生中断
- CLKSOURCE:时钟源选择,使用SysTick_CLKSourceConfig();函数配置
- 外部时钟源是 HCLK(AHB总线时钟)的1/8,即168M的1/8=21M
- 内核时钟是 HCLK时钟,即168M.
- COUNTFLAG:状态位,是否读取过本寄存器。
RELOAD: SysTick重装载数值寄存器。
预设的重载置,即我们要设置的定时值CURRENT:SysTick 当前值寄存器
Systick相关函数
固件库中的Systick相关函数:
SysTick_CLKSourceConfig() //Systick时钟源选择 misc.c文件中
void SysTick_CLKSourceConfig(uint32_t SysTick_CLKSource) { /* Check the parameters */ assert_param(IS_SYSTICK_CLK_SOURCE(SysTick_CLKSource)); if (SysTick_CLKSource == SysTick_CLKSource_HCLK) { SysTick->CTRL |= SysTick_CLKSource_HCLK; } else { SysTick->CTRL &= SysTick_CLKSource_HCLK_Div8; } }
SysTick_Config(uint32_t ticks) //初始化systick,时钟为HCLK,并开启中断
static __INLINE uint32_t SysTick_Config(uint32_t ticks) { if (ticks > SysTick_LOAD_RELOAD_Msk) return (1); /* Reload value impossible */ /* set reload register */ SysTick->LOAD = (ticks & SysTick_LOAD_RELOAD_Msk) - 1; /* set Priority for Cortex-M0 System Interrupts */ NVIC_SetPriority (SysTick_IRQn, (1<<__NVIC_PRIO_BITS) - 1); SysTick->VAL = 0; /* Load the SysTick Counter Value */ 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中断服务函数:
- void SysTick_Handler(void);
Systick使用示例
这里实现的是每隔500ms两上LED灯的交规点亮,并且扬声器每500ms发出声音
#include "stm32f4xx.h"
static u8 tick_us=0;
static u16 tick_ms=0;
void InitSysClck(u8 sysclk)
{
SysTick_CLKSourceConfig(SysTick_CLKSource_HCLK_Div8); //168/8=21M
tick_us = sysclk/8;
tick_ms = (u16)tick_us*1000;
}
void delay_xms(u16 nms)
{
u32 temp;
SysTick->LOAD=(u32)nms*tick_ms;
SysTick->VAL =0x00;
SysTick->CTRL|=SysTick_CTRL_ENABLE_Msk ;
do
{
temp=SysTick->CTRL;
}while((temp&0x01)&&!(temp&(1<<16)));
SysTick->CTRL&=~SysTick_CTRL_ENABLE_Msk; //关闭计数器
SysTick->VAL =0X00;
}
void delay_ms(u16 nms)
{
u8 repeat=nms/540;
u16 remain=nms%540;
while(repeat)
{
delay_xms(540);
repeat--;
}
if(remain)delay_xms(remain);
}
int main(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOF, ENABLE);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9 | GPIO_Pin_10|GPIO_Pin_8;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;
GPIO_Init(GPIOF, &GPIO_InitStructure);
InitSysClck(168);
while(1)
{
GPIO_SetBits(GPIOF,GPIO_Pin_9);
GPIO_ResetBits(GPIOF,GPIO_Pin_10);
GPIO_ResetBits(GPIOF,GPIO_Pin_8);
//Delay(0x7FFFFF);
delay_ms(500);
GPIO_SetBits(GPIOF,GPIO_Pin_10);
GPIO_SetBits(GPIOF,GPIO_Pin_8);
GPIO_ResetBits(GPIOF,GPIO_Pin_9);
delay_ms(500);
//Delay(0x7FFFfF);
}
}
问题
在实际的过程中,发现延迟并不准确,变长了,发现我使用了系统默认的PLL参数配置,导致时钟配置不正确。
- 如 system_stm32f4xx.c 文件中PLL_M原值为255,这里应改为8,因为我们使用的是8M的外频时钟,而并非25M的时钟,导致系统时钟变低引起的。
- 另一个地方是stm32f4xx.h文件 中的HSE_VALUE也需要从25000000改为8000000。