STM32(从库函数到寄存器)——SysTick

本章知识的总结大部分来自于《M3--权威指南》

SysTick属于NVIC的一部分(即属于内核的一部分),可以产生SysTick异常,为简单的24位向下计数器,可以使用外部参考时钟或内部时钟。SysTick有两个重要的特征:一,它是为OS服务的,作为一个周期性的中断来定期触发OS(这需要后续的探索);二是处理器设计要确保运行在非特权等级的应用无法禁止该定时器,否则任务会禁止SysTick定时器并锁定整个系统。

SysTick定时器存在四个寄存器。CMSIS-Core头文件中定义了一个名为SysTick的结构体。

若系统中不需要OS,SysTick定时器可用作定时器的简单外设,用以产生周期性的中断,延时和时间测量。(这三个方面是本篇的重点)若想产生周期性的SysTIck中断,最简单的方法是使用CMSIS-Core的函数SysTick_Config(),下面的例程就使用了该函数。

下面来看程序。

#include "stm32f10x.h"
#include "bsp_led.h"
#include "bsp_SysTick.h"

int main(void)
{
	LED_GPIO_Config ();
	SysTick_Init();
	while(1)
	{
							
	  LED1 (ON);
	  Delay_us (10000);           //10000*10us=100ms
	  LED1 (OFF);
							
}

依旧是在一个简单的led灯点亮的实验基础上,增添了定时器的内容。在主函数中,出现了SysTick的配置函数,消失了以往的用while(i--)的简单方式进行的延时函数。进入SysTick_Init(),观察配置的细节。

void SysTick_Init(void)
{
	

	if (SysTick_Config(SystemCoreClock / 100000))	
	{ 
		/* Capture error */ 
		while (1);
	}

调用库函数SysTick_Config(),如果为真,就出现错误,进入无限循环。为了搞清楚这个,我们进入这个库函数。

static __INLINE uint32_t SysTick_Config(uint32_t ticks)
{ 
  if (ticks > SysTick_LOAD_RELOAD_Msk)  return (1);            /* Reload value impossible */
                                                               
  SysTick->LOAD  = (ticks & SysTick_LOAD_RELOAD_Msk) - 1;      /* set reload register */
  NVIC_SetPriority (SysTick_IRQn, (1<<__NVIC_PRIO_BITS) - 1);  /* set Priority for Cortex-M0 System Interrupts */
  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 */
}

首先注意声明函数的关键字static(static的三种用法会详细学习介绍,简单来说static函数可以很好地解决不同原文件中函数同名的问题,因为一个源文件对于其他源文件中的static函数是不可见的。  当你的程序中有很多个源文件的时候,你肯定会让某个源文件只提供一些外界需要的接口,其他的函数可能是为了实现这些接口而编写,这些其他的函数你可能并不希望被外界(非本源文件)所看到,这时候就可以用static修饰这些“其他的函数”。)ticks为中断间隔,即多少个时钟周期中断一次,若大于SysTick_LOAD_RELOAD_MSK,就返回1,即为真

/* SysTick Reload Register Definitions */
#define SysTick_LOAD_RELOAD_Pos             0                                             /*!< SysTick LOAD: RELOAD Position */
#define SysTick_LOAD_RELOAD_Msk            (0xFFFFFFul << SysTick_LOAD_RELOAD_Pos)        /*!< SysTick LOAD: RELOAD Mask */

SysTick_LOAD_RELOAD_MSK是装载和重装载寄存器内的值,为0xFFFFFF(24)位,末尾的“ul”表示强制转换为unsigned long。

当ticks数满足要求后,ticks与重加载寄存器数值位与,放入重装载寄存器,数值应该减1,因为要向下计数到0.

设置中断优先级,研究一下NVIC_SetPriority()函数,设置优先级为15.

static __INLINE void NVIC_SetPriority(IRQn_Type IRQn, uint32_t priority)
{
  if(IRQn < 0) {
    SCB->SHP[((uint32_t)(IRQn) & 0xF)-4] = ((priority << (8 - __NVIC_PRIO_BITS)) & 0xff); } /* set Priority for Cortex-M3 System Interrupts */
  else {
    NVIC->IP[(uint32_t)(IRQn)] = ((priority << (8 - __NVIC_PRIO_BITS)) & 0xff);    }        /* set Priority for device specific Interrupts  */
}

如果优先级编号小于0,设置系统中断;反之设置设备中断。在CMSIS-Core中SysTick的枚举值为-1,所以当然是前者。SCB是系统控制块,追踪SCB可以发现

#define SCB                 ((SCB_Type *)           SCB_BASE)         /*!< SCB configuration struct          */

SCB_BASE为0xE000E000,SCB_Type是SCB的数据的数据结构

typedef struct
{
  __I  uint32_t CPUID;                        /*!< Offset: 0x00  CPU ID Base Register                                  */
  __IO uint32_t ICSR;                         /*!< Offset: 0x04  Interrupt Control State Register                      */
  __IO uint32_t VTOR;                         /*!< Offset: 0x08  Vector Table Offset Register                          */
  __IO uint32_t AIRCR;                        /*!< Offset: 0x0C  Application Interrupt / Reset Control Register        */
  __IO uint32_t SCR;                          /*!< Offset: 0x10  System Control Register                               */
  __IO uint32_t CCR;                          /*!< Offset: 0x14  Configuration Control Register                        */
  __IO uint8_t  SHP[12];                      /*!< Offset: 0x18  System Handlers Priority Registers (4-7, 8-11, 12-15) */
  __IO uint32_t SHCSR;                        /*!< Offset: 0x24  System Handler Control and State Register             */
  __IO uint32_t CFSR;                         /*!< Offset: 0x28  Configurable Fault Status Register                    */
  __IO uint32_t HFSR;                         /*!< Offset: 0x2C  Hard Fault Status Register                            */
  __IO uint32_t DFSR;                         /*!< Offset: 0x30  Debug Fault Status Register                           */
  __IO uint32_t MMFAR;                        /*!< Offset: 0x34  Mem Manage Address Register                           */
  __IO uint32_t BFAR;                         /*!< Offset: 0x38  Bus Fault Address Register                            */
  __IO uint32_t AFSR;                         /*!< Offset: 0x3C  Auxiliary Fault Status Register                       */
  __I  uint32_t PFR[2];                       /*!< Offset: 0x40  Processor Feature Register                            */
  __I  uint32_t DFR;                          /*!< Offset: 0x48  Debug Feature Register                                */
  __I  uint32_t ADR;                          /*!< Offset: 0x4C  Auxiliary Feature Register                            */
  __I  uint32_t MMFR[4];                      /*!< Offset: 0x50  Memory Model Feature Register                         */
  __I  uint32_t ISAR[5];                      /*!< Offset: 0x60  ISA Feature Register                                  */
} SCB_Type;                                                

里面有y一堆寄存器。SCB->SHP[12]是系统处理优先级寄存器,SCB在低功耗方面发挥了巨大作用。

设置完优先级后,清除当前值和计数标志。

 SysTick->CTRL  = SysTick_CTRL_CLKSOURCE_Msk | 
                   SysTick_CTRL_TICKINT_Msk   | 
                   SysTick_CTRL_ENABLE_Msk;

设置SysTick的控制和状态寄存器:使用内核时钟(第2位),滴答计时器减至0时产生异常(第1位),定时器使能(第0位)。

至此,SysTick_Config结束。如果调用此函数返回1,就出错。反之,关闭计时器。配置计时器完成。

让我们复盘整个过程。

当每次调用延时函数时,传参数10000给TimingDelay,同时使计数器使能SysTick->CTRL |=  SysTick_CTRL_ENABLE_Msk;打开中断,进入中断函数。在中断函数中,调用一个外部函数TimingDelay_Decrement()。

void TimingDelay_Decrement(void)
{
	if (TimingDelay != 0x00)
	{ 
		TimingDelay--;
	}
}

每完成一次中断周期,即t=ticks(设定的中断周期数)*系统的时钟周期(1/f)= (72000000/100000) * (1/72000000) = 10us ,TimingDelay自减一次,直到减完10000次10ms,最后一次退出中断,退出函数,完成一次延时。

猜你喜欢

转载自blog.csdn.net/weixin_41080288/article/details/82531665