SysTick定时器基础知识
SysTick是一个24位定时器,计数器以递减的方式工作,递减到0,硬件自动重装计数值,如果使能中断,则产生中断。且所有的Cortex M器件都有这个定时器,所有的CM芯片的SysTick都是相同的。
SysTick定时器被捆绑在NVIC中,用于产生Sysick异常(异常号:15)。
SysTick中断的优先级也可以设置。
SysTick定时器的计数频率为系统时钟频率或者1/8系统时钟频率,HAL库默认为系统时钟频率。
SysTick寄存器
SysTick控制寄存器
SysTick重装寄存器
SysTick的递减计数器
寄存器代码
#include "delay.h"
static u8 fac_us=0;//us延时倍数
static u16 fac_ms=0;//ms延时倍数
void delay_init(u8 SYSCLK)
{
SysTick->CTRL&=~(1<<2);
//控制寄存器位2置0,选择8分频时钟
fac_us=SYSCLK/8;
//采用参考8分频的参考时钟
fac_ms=(u16)fac_us*1000;
}
void delay_us(u32 nus)
{
u32 temp;
SysTick->LOAD=nus*fac_us;
//时间加载
SysTick->VAL=0x00;
//清空计数器
SysTick->CTRL=0x01 ;
//开始倒数
do
{
temp=SysTick->CTRL;
}while((temp&0x01)&&!(temp&(1<<16)));
//等待时间到达
SysTick->CTRL=0x00;
//关闭计数器
SysTick->VAL =0X00;
//清空计数器
}
//同理
void delay_ms(u16 nms)
{
u32 temp;
SysTick->LOAD=(u32)nms*fac_ms;
SysTick->VAL =0x00;
SysTick->CTRL=0x01 ;
do
{
temp=SysTick->CTRL;
}while((temp&0x01)&&!(temp&(1<<16)));
SysTick->CTRL=0x00;
SysTick->VAL =0X00;
}
HAL库重要函数
1个计数器函数,中断中调用:
__weak void HAL_IncTick(void)
{
uwTick++;
}
1个获取节拍计数器函数
__weak uint32_t HAL_GetTick(void)
{
return uwTick;
}
1个延时函数
__weak void HAL_Delay(__IO uint32_t Delay)
{
uint32_t tickstart = 0;
tickstart = HAL_GetTick();
while((HAL_GetTick() - tickstart) < Delay)
{
}
}
挂起SysTick定时器函数
__weak void HAL_SuspendTick(void)
{
/* Disable SysTick Interrupt */
CLEAR_BIT(SysTick->CTRL,SysTick_CTRL_TICKINT_Msk);
}
恢复SysTick定时器函数
__weak void HAL_ResumeTick(void)
{
/* Enable SysTick Interrupt */
SET_BIT(SysTick->CTRL,SysTick_CTRL_TICKINT_Msk);
}
补充
1.进行实验时应将
stm32f1xx.it.c(h)里的注释掉
否则会出现多重定义(multiply defined)这个问题
标题
main.c
#include "MyIncludes.h"
u16 sys_cnt = 0;
void systick_isr(void)
{
if(sys_cnt < 1000 )
{
sys_cnt++;//每运行一次时钟,sys_cnt++;
}
else
{
sys_cnt = 0;
HAL_GPIO_TogglePin(GPIOC,GPIO_PIN_4|GPIO_PIN_5);
//超过规定的sys_cnt,归零sys_cnt,并反转PC4 PC5灯
}
}
int main()
{
System_Init();
LED_Init();
SysTick_Init(systick_isr);//这里运用到了函数指针;
while(1)
{
}
}
Systick.h
#ifndef __SYSTICK_H_
#define __SYSTICK_H_
#include "stm32f1xx.h"
#include "stm32_types.h"
#include "stm32f1xx_hal.h"
typedef struct
{
void(*Operation)(void);//函数指针
}_SysTick_Info;
//extern _SysTick_Info SysTick_Info;
/*可有可无,貌似没用到*/
void SysTick_Init(void (*ISR)(void));
#endif
Systick.c
#include "Systick.h"
_sysTick_Info SysTick_Info;
void SysTick_Handler(void)
{
HAL_IncTick();
if(SysTick_Info.Operation != NULL)
SysTick_Info.Operation();
}
void SysTick_Init(void(*ISR)(void))
{
SysTick_Info.Operation = ISR;
SysTick_Config(HALL_RCC_GetHCLKFreq()/1000);
}
代码补充说明
学习这个的时候遇到了一些问题,或许是很简单的问题就是懵在Sys Tick滴答定时器是如何运行的。就是刚开始懵到两个函数指针到底指向哪里,现在我的理解是这样的。
其实很简单: 先在main.c中进行 SysTick_Init(systick_Isr) SysTick滴答定时器的初始化函数,同时看systick.h中的声明
void SysTick_Init(void (*ISR)(void));也就是函数指针ISR指向systick_isr()这个函数。然后就很清晰了,然后看systick.c.
void SysTick_Init(void(*ISR)(void))
{
SysTick_Info.Operation = ISR;
/*又用一个函数指针,指向ISR,但最终还是
systick_isr()这个函数*/
SysTick_Config(HAL_RCC_GetHCLKFreq()/1000);
/*这个是设置时间HAL_RCC_GetHCLKFreq()可以得知
uint32_t SystemCoreClock = 72000000;
系统核心时钟
然后进入这个函数
__STATIC_INLINE uint32_t SysTick_Config(uint32_t ticks)
{
if ((ticks - 1UL) > SysTick_LOAD_RELOAD_Msk)
{
return (1UL);
无法重新加载值 也就是时间过大,
}
SysTick->LOAD = (uint32_t)(ticks - 1UL);
设置重新加载寄存器
NVIC_SetPriority (SysTick_IRQn, (1UL << __NVIC_PRIO_BITS) - 1UL);
设置中断的优先级
SysTick->VAL = 0UL;
加载计数器值
SysTick->CTRL = SysTick_CTRL_CLKSOURCE_Msk |
SysTick_CTRL_TICKINT_Msk |
SysTick_CTRL_ENABLE_Msk;
启用 SysTick IRQ and SysTick 定时器
return (0UL);
返回0 也就是成功
}*/
}
然后是
void SysTick_Handler(void)
{
HAL_IncTick();
/*这个是计数,也就是计时函数
内部有
__weak void HAL_IncTick(void)
{
uwTick++;
}这个函数*/
if(SysTick_Info.Operation !=NULL)
SysTick_Info.Operation();
/*这个就是判断是否使用了systick定时器,也就是main.c中的
systick_isr()函数。
}