详细SysTick定时器(+对寄存器段位的解释)

1。       首先,systick定时器(嘀嗒定时器)隶属于CM4内核的一个外设,内嵌在NVIC中,所以一些函数在core_cm4.c中,系统定时器是一个24bit向下递减的计数器,计数器每次计数时间为1/SYSCLK。        (SYSCLK->系统时钟,通常设定为100MHZ。公式:f=100MHZ,则T=1/f=1/SYSCLK)。                                                          

当重装载数值寄存器的值减到0时,系统定时器就产生一次中断(这个中断需要配置并使能),以此循环往复

频率与周期对照表:(f与T)。 1Hz:1s        1Khz=1ms       1Mhz:1us        100Mhz:10ns                

 对原理图:首先对SysTick->CTRL的第16位置1后,使能LODA(LODA->装载),对SysTick->VAL进行赋值,(VAL当前数值,对应递减的数值,随着数字递减,数值在变化),之后可以选择不分频或者8分频,重装载计数器每1/SYSCLK计数一次,也就是1/100Mhz,10ns计数一次,计数100次就是1us。

2。SysTick定时器寄存器

定时器配置时,只需要配置前三个,最后一个校准寄存器不用配置。 

 

 位段16:计数标志位,意思是:读了该位,他自己清0,如果读过一次,并且他还是0,则该位变成1

 位段2:选择分频

 位段1:置1后,计数器计到0就产生请求,为0没啥作用,

位段0:使能位

段位:把内存分为很多段,每一段有一个段基址,当然段基址也是一个20位的内存地址。不过段寄存器仍然是16位的,它的内容代表了段基址的高16位,这个16位的地址后面再加上4个0就构成20位的段基址。

这个简单,意思是计数到0自己会重新装载数值再倒计时 

 可读可写位。读的时候,就是把寄存器当前倒计的数值进行读取

                        写的时候,意思只是把它置位写状态,这个不是设定数值的寄存器,写不进去实际数值,置写位后,还会清除COUNTFLAG的标志位。

SysTick->VAL寄存器的值每一个时钟周期就会递减1,当他递减到0时候, SysTick->LOAD的值将会进入SysTick->VAL中,并且SysTick->CTRL的COUNTFLAG位将会置1,如果还使能了中断,将还会进入中断。

延时原理就是通过设定SysTick->LOAD的值以及时钟周期的数值(通常设定为100MHz)来实现的

定时器的配置代码如下:(在core_cm4.h中)                                                                                                                                                                                     

__STATIC_INLINE uint32_t SysTick_Config(uint32_t ticks)
{
  if ((ticks - 1UL) > SysTick_LOAD_RELOAD_Msk) { return (1UL); }    /* Reload value impossible */

  SysTick->LOAD  = (uint32_t)(ticks - 1UL);                         /* set reload register */
  NVIC_SetPriority (SysTick_IRQn, (1UL << __NVIC_PRIO_BITS) - 1UL); /* set Priority for Systick Interrupt */
  SysTick->VAL   = 0UL;                                             /* 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 (0UL);                                                     /* Function successful */
}

        对于SysTick_Config(uint32_t ticks);这个函数,要知道他已经将定时器配置为AHB(100MHZ) 时钟为 SysTick 定时器的时钟频率,并且使能了计数中断。也就是说,计数ticks(变量参数)次,就会产生一次SysTick中断。

        因此,我们就可以根据这个进行配置我们想要的ticks参数。例如:我们配置1ms的SysTick中断,我们可以调用SysTick_Config(uint32_t ticks);这个函数,首先,因为我们的计数周期为10ns(1/100MHZ),1ms/10ns=10*-3  /  10*-8=100000,也就是要递减10 0000次,也就是SysTick->VAL递减10 0000次,所以SysTick->LODA是10 0000,也可以说ticks参数必须是10 0000

SysTick_Config(SystemCoreClock/1000);    //设置SysTick定时器1ms中断一次

参数ticks=SystemCoreClock/1000(SystemCoreClock为系统时钟100Mhz,100Mhz/1000=10 0000)。也可以直接填10 0000.

3。利用SysTick定时器设计程序,运行计时器

        在此,我们定义一个全局变量volatile uint32_t sysTickUptime=0;在之后,每一次产生中断sysTickUptime++;(volatile修饰符表示sysTickUptime变量是一个易变的变量,它告诉编译器不要优化内容,直接读取变量内容)。

        SysTick定时器中断在文件stm32f4xx_it.c,它中不仅有SysTick定时器中断,其他所有中断都在其中。

         SysTick定时器中断代码如下:

extern unit32_t sysTickUptime;
void SysTick_Handler(void)
{
    sysTickUptime++;
}

        通过这段简单的代码可知:每1ms产生中断后sysTickUptime会进行加1。就是说,如果我们想知道一段代码运行了多长时间,可以在代码前面,后面分别读一下sysTickUptime的值:StartTime=sysTickUptime; 代码运行时间;EndTime=sysTickUptime;最终代码运行的时间为EndTime-StartTime(ms)

        但是,在32系统时钟中,都是以兆级别的,所以一个程序运行时间很短,一般以微妙级,所以毫秒对于一些代码,可能太大,不适合,不满足要求,所以也有微妙的计数器,以下是微妙级函数:

//延时初始化
void Delay_init(void)
{
    RCC_ClocksTypeDef clocks;
    //选择系统时钟HCLK作为Systick定时器的时钟,开Systick中断1ms一次
    SysTick_Config(SystemCoreClock/1000);
    RCC_GetClocksFreq(&clocks);//获取当前时钟
    usTicks = clocks.SYSCLK_Frequency/1000000;//100M/1M=100次 也就是计数100次为1us
}

//微妙计数器
uint32_t micros(void)
{
//register请求编译器尽可能的将变量存在CPU内部寄存器,而不是通过内存寻址访问。
    register uint32_t ms, cycle_cnt;
    do
    {
        ms = sysTickUptime;
        cycle_cnt = SysTick->VAL;//SysTick->VAL 的计数范围10 0000~0就是1ms,递减计数器
    }while (ms != sysTickUptime);
return (ms*1000)+ (usTicks * 1000 - cycle_cnt)/usTicks; 

}

        我们配置SysTick定时器的计数时钟为系统时钟,也就是100Mhz,也可以说SysTick->VAL每10ns递减1,1us也就是递减100次,1ms也就是10 0000次,其实我们也可以把上面的程序改为微妙级的中断,但是这样比较消耗资源,中断频率太高对程序运行也不太友好 。因此,我们可以进行毫秒中断微秒计时的方法。usTicks表示1微秒的计数次数,usTicks=100Mhz/1Mhz=100次,那么,在我们没进毫秒中断前进行循环读取sysTickUptime和SysTick->VAL的值(毫秒级别大嘛,没产生中断前可以读很多次),SysTick->VAL是从10 0000递减到0进行一次中断sysTickUptime才加1。

        所以在函数micros(void);在调用的时候,do{},while()里面的函数先进行一次执行,把两个值分别赋给ms,cycle_cnt,因为刚赋完,ms肯定=sysTickUptime,所以条件不满足,立即返回微妙计数值。等待sysTickUptime更新后,就再进入函数。就是说:do{},while()​​​​​​​里面的循环体只有在sysTickUptime更新时才执行两次,其他情况执行一次就返回了微妙计数。

4。​​​​​​​利用SysTick定时器设置精准延时

        上面我们写出了毫秒计数器millis();i和微妙计数器micros();也知道了怎么计算程序运行时间,其实延时函数就简单为:延时多长时间计多少次数。

//毫秒延时
void delay_ms(unit32_t nms)
{
    uint32_t mStart = 0;
    mStart = mills();//毫秒开始值
    while(mills()-mStart<nms)
    {}
}

//微妙延时
void delay_us(uint32_t nus)
{
    uint32_t uStart =0;
    uStart = micros();
    while(micros()-uStart<nus)
    {}
}

  • 2
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值