NVIC

本课将为大家讲解CKS32F4xx系列产品的中断优先级管理单元NVIC。CM4内核共支持256个中断,其中包含了16个内核中断和240个外部中断,具有256级可编程中断设置。但CKS32F4xx系列只使用了CM4内核的一部分,共有98个中断,包括16个带有FPU核的CM4中断和82个可屏蔽中断,而我们常用的就是这82个可屏蔽中断。

NVIC寄存器简介

MDK为NVIC相关寄存器其定义了如下的结构体:

typedef struct 
{ 
    __IO uint32_t ISER[8]; /*!< Interrupt Set Enable Register */ 
    uint32_t RESERVED0[24]; 
    __IO uint32_t ICER[8]; 
    /*!< Interrupt Clear Enable Register */ 
    uint32_t RSERVED1[24]; 
    __IO uint32_t ISPR[8]; 
    /*!< Interrupt Set Pending Register */ 
    uint32_t RESERVED2[24]; 
    __IO uint32_t ICPR[8]; 
    /*!< Interrupt Clear Pending Register */ 
    uint32_t RESERVED3[24]; 
    __IO uint32_t IABR[8]; 
    /*!< Interrupt Active bit Register */ 
    uint32_t RESERVED4[56]; 
    __IO uint8_t IP[240]; 
    /*!< Interrupt Priority Register, 8Bit wide */ 
    uint32_t RESERVED5[644]; 
    __O uint32_t STIR; 
    /*!< Software Trigger Interrupt Register */ 
} NVIC_Type;

CKS32F4xx系列的中断在这些寄存器控制下有序执行,只有了解这些中断寄存器后,才能方便的使用CKS32F4xx系列中断功能。下面重点介绍这几个寄存器:

ISER[8]:ISER 全称是:Interrupt Set-Enable Registers,这是一个中断使能寄存器组。上面说了CM4内核支持256个中断,这里用8个32位寄存器来控制,每个位控制一个中断。但是CKS32F4xx系列的可屏蔽中断最多只有82个,所以对我们来说,有用的就是三个(ISER[0~2]),ISER[0]的bit 0~31分别对应中断0~31;ISER[1]的bit 0~31对应中断32~63;ISER[2]的bit 0~17对应中断64~81,共82个中断。你要使能某个中断,必须设置相应的ISER位为 1,使该中断被使能,另外还要配合中断分组、屏蔽、IO口映射等设置才算是一个完整的中断设置。具体每一位对应哪个中断,请参考CKS32f4xx.h。

ICER[8]:全称是:Interrupt Clear-Enable Registers,是一个中断除能寄存器组。该寄存器组与 ISER 的作用恰好相反,是用来清除某个中断的使能的。其对应位的功能,也和 ICER 一样。这里要专门设置一个 ICER 来清除中断位,而不是向 ISER 写 0 来清除,是因为 NVIC 的这些寄存器都是写 1 有效的,写 0 是无效的。

ISPR[8]:全称是:Interrupt Set-Pending Registers,是一个中断挂起控制寄存器组。每个位对应的中断和ISER是一样的。通过置1,可以将正在进行的中断挂起,而执行同级或更高级别的中断。写0是无效的。

ICPR[8]:全称是:Interrupt Clear-Pending Registers,是一个中断解挂控制寄存器组。其作用与ISPR相反,对应位也和 ISER是一样的。通过设置1,可以将挂起的中断接挂。写 0 无效。

IABR[8]:全称是:Interrupt Active Bit Registers,是一个中断激活标志位寄存器组。对应位所代表的中断和ISER一样,如果为 1,则表示该位所对应的中断正在被执行。这是一个只读寄存器,通过它可以知道当前在执行的中断是哪一个。在中断执行完了由硬件自动清零。

IP[240]:全称是:Interrupt Priority Registers,是一个中断优先级控制的寄存器组。CKS32F4的中断分组与这个寄存器组密切相关。IP寄存器组由240个8bit的寄存器组成,每个可屏蔽中断占用8bit,这样总共可以表示240个可屏蔽中断。而CKS32F4只用到了其中的82个。IP[81]~IP[0]分别对应中断 81~0。而每个可屏蔽中断占用的 8bit 并没有全部使用,而是只用了高4位。这4位,又分为抢占优先级和响应优先级。抢占优先级在前,响应优先级在后。而这两个优先级各占几个位又要根据 SCB->AIRCR 中的中断分组设置来决定。

NVIC中断分组

这里简单介绍一下 CKS32F4 的中断分组:CKS32F4将中断分为5个组,组0~4。该分组的设置是由 SCB->AIRCR 寄存器的 bit10~8 来定义的。具体的分配关系如下:

1.jpg

通过这个表,我们就可以清楚的看到组0~4对应的配置关系,例如组设置为3,那么此时所有的82个中断,每个中断的中断优先寄存器的高四位中的最高3位是抢占优先级,低1位是响应优先级。每个中断,你可以设置抢占优先级为0~7,响应优先级为1或0。抢占优先级的级别高于响应优先级。而数值越小所代表的优先级就越高。

这里需要注意两点:

第一,如果两个中断的抢占优先级和响应优先级都是一样的话,则看哪个中断先发生就先执行;

第二,高优先级的抢占优先级是可以打断正在进行的低抢占优先级中断的。而抢占优先级相同的中断,高优先级的响应优先级不可以打断低响应优先级的中断。

结合实例说明一下:假定设置中断优先级组为2,然后设置中断3(RTC_WKUP中断)的抢占优先级为2,响应优先级为1。中断6(外部中断0)的抢占优先级为3,响应优先级为0。中断7(外部中断1)的抢占优先级为2,响应优先级为0。那么这3个中断的优先级顺序为:中断7>中断3>中断6。其中中断3和中断7都可以打断中断6的中断。而中断7和中断3却不可以相互打断!

软件实现

通过以上介绍,我们熟悉了CKS32F4中断设置的大致过程。接下来我们介绍如何使用库函数实现以上中断分组设置以及中断优先级管理,使得我们以后的中断设置简单化。NVIC中断管理函数主要在misc.c文件里面。

首先要讲解的是中断优先级分组函数NVIC_PriorityGroupConfig,其函数申明如下:

void NVIC_PriorityGroupConfig(uint32_t NVIC_PriorityGroup);

这个函数的作用是对中断的优先级进行分组,这个函数在系统中只能被调用一次,一旦分组确定就最好不要更改。这个函数我们可以找到其实现:

void NVIC_PriorityGroupConfig(uint32_t NVIC_PriorityGroup) 
{ 
  assert_param(IS_NVIC_PRIORITY_GROUP(NVIC_PriorityGroup)); 
  SCB->AIRCR = AIRCR_VECTKEY_MASK | NVIC_PriorityGroup; 
}

从函数体可以看出,这个函数唯一目的就是通过设置SCB->AIRCR寄存器来设置中断优先级分组,查看其定义为:

#define IS_NVIC_PRIORITY_GROUP(GROUP) 
(((GROUP) == NVIC_PriorityGroup_0) || 
((GROUP) == NVIC_PriorityGroup_1) || \ 
((GROUP) == NVIC_PriorityGroup_2) || \ 
((GROUP) == NVIC_PriorityGroup_3) || \ 
((GROUP) == NVIC_PriorityGroup_4))

可以看到这个定义对应上表中的分组范围0-4。如果我们需要设置系统的中断优先级分组值为2,那么方法是:NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);这样就确定了一共为“2位抢占优先级,2位响应优先级”。设置好了系统中断分组,接下来设置中断的抢占优先级和响应优先级,这里需要用到一个重要的函数为中断初始化函数NVIC_Init,其函数声明为:voidNVIC_Init(NVIC_InitTypeDef*NVIC_InitStruct),其中NVIC_InitTypeDef是一个结构体,我们可以看看结构体的成员变量:

typedef struct 
{ 
 uint8_t NVIC_IRQChannel; 
 uint8_t NVIC_IRQChannelPreemptionPriority; 
 uint8_t NVIC_IRQChannelSubPriority; 
 FunctionalState NVIC_IRQChannelCmd; 
} NVIC_InitTypeDef;

NVIC_InitTypeDef结构体中间有四个成员变量,这四个成员变量的作用是:

NVIC_IRQChannel:

定义初始化的是哪个中断,这个我们可以在CKS32f4xx.h中定义的枚举类型IRQn的成员变量中可以找到每个中断对应的名字。例如串口1对应USART1_IRQn。

NVIC_IRQChannelPreemptionPriority:

定义这个中断的抢占优先级别;

NVIC_IRQChannelSubPriority:

定义这个中断的响应优先级别。

NVIC_IRQChannelCmd:

该中断通道是否使能。比如我们要使能串口1的中断,同时设置抢占优先级为1,响应优先级位2,初始化的方法是:

NVIC_InitTypeDef NVIC_InitStructure;
NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;//>>串口 1 中断 
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=1 ;//>> 抢占优先级为 1 
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 2;//>> 响应优先级位 2 
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //>>IRQ 通道使能 
NVIC_Init(&NVIC_InitStructure);

这里我们讲解了中断的分组的概念以及设定优先级值的方法,至于每种优先级还有一些关于清除中断,查看中断状态,这在后面课堂中我们讲解每个中断的时候会详细讲解到。

课程总结

中断优先级设置的步骤:

1.系统初始化的时候设置中断分组:

确定组号,也就是确定抢占优先级和响应优先级的分配位数。调用函数为NVIC_PriorityGroupConfig();

2.设置所用到的中断的中断优先级别:

对每个中断调用函数NVIC_Init();确定具体的抢占优先级和响应优先级,并使能通道。

来源:中科芯MCU

免责声明:本文为转载文章,转载此文目的在于传递更多信息,版权归原作者所有。本文所用视频、图片、文字如涉及作品版权问题,请联系小编进行处理(联系邮箱:cathy@eetrend.com)。

围观 38

Cortex-M3内核支持256个中断,其中包含了16个内核中断(异常)和240个外部中断,并且具有256级的可编程中断设置。但是,STM32并没有使用CM3内核的全部东西,而是只用了它的一部分。STM32有84个中断,包括16个内核中断(异常)和68个可屏蔽中断,具有16级可编程的中断优先级。而STM32F103系列上面,16个内核中断(异常)不变,而可屏蔽中断只有60个(在107系列才有68个)。

注意一下:CM3的外部中断和STM32的外部中断不是一个概念。CM3:除了内核异常之外的都是外部中断;STM32:外部中断EXTI只有6个

其实,内核中断也叫内核异常。

  • 中断是指系统停止当前正在运行的程序转而其他服务,可能是程序接收了比自身高优先级的请求,或者是人为设置中断,中断是属于正常现象。
  •  

  • 异常是指由于cpu本身故障、程序故障或者请求服务等引起的错误,异常属于不正常现象。

因此,下面就直接介绍一下STM32F103系列的16个内核中断(异常)、60个中断:



在中断向量表中从优先级7-66(中断号从0-59)代表着STM32F103的60个中断。优先级号越小,优先级越高。当表中的某处异常或中断被触发,程序计数器指针(PC)将跳转到该异常或中断的地址处执行,该地址处存放这一条跳转指令,跳转到该异常或中断的服务函数处执行相应的功能。因此,异常和中断向量表只能用汇编语言编写。

在MDK中,有标准的异常和中断向量表文件可以使用(startup_stm32f10x_hd.s),在其中标明了中断处理函数的名称,不能随意定义。而中断通道NVIC_IRQChannel(即IRQn_Type类型)是在stm32f10x.h文件中进行了宏定义。

什么是NVIC?即嵌套向量中断控制器(Nested Vectored Interrupt Controller)。CM3的中有一个强大而方便的NVIC,它是属于Cortex内核的器件,中断向量表中60个中断都由它来处理。NVIC是Cortex-M3核心的一部分,关于它的资料不在《STM32的技术参考手册》中,应查阅ARM公司的《Cortex-M3技术参考手册》。Cortex-M3的向量中断统一由NVIC管理。

NVIC的核心功能是中断优先级分组、中断优先级的配置、读中断请求标志、清除中断请求标志、使能中断、清除中断等,它控制着STM32中断向量表中中断号为0-59的60个中断!!外部中断信号从核外发出,信号最终要传递到NVIC(嵌套向量中断控制器)。NVIC跟内核紧密耦合,它控制着整个芯片中断的相关功能。

STM32中断优先级分组

中断优先级分组寄存器

这60个中断,怎么管理呢?这就涉及到STM32的中断分组。STM32可以将中断分成5个组,分别为组0-4;同时,对每个中断设置一个抢占优先级和响应优先级。分组配置是由SCB->AIRCR寄存器的bit10-8来定义的。SCB->AIRCR是在哪里的呢?由于这是CM3内核定义的,在文档《Cortex-M3权威指南(中文)》中能查找到。

具体的分配关系如下所示:


其中AIRCR寄存器来确定是用哪种分组,IP寄存器是相对应于那种分组抢占优先级和响应优先级的分配比例。例如组设置成3,那么此时所有的60个中断优先寄存器高4位中的最高3位是抢占优先级,低1位为响应优先级。CM3中定义了8个Bit用于设置中断源的优先级,而STM32只选用其中的4个Bit。

抢占优先级的级别高于响应优先级,而数值越小所代表的的优先级越高。 

介绍一下抢占优先级、响应优先级的区别:

① 高优先级的抢占优先级是可以打断正在进行的低抢占优先级中断的;

② 抢占优先级相同的中断,高响应优先级不可以打断低响应优先级的中断;

③ 抢占优先级相同的中断,当两个中断同时发生的情况下,哪个响应优先级高,哪个先执行;

④ 如果两个中断的抢占优先级和响应优先级都是一样的话,则看哪个中断先发生就先执行;

除此之外有两点需要注意:

① 打断的情况只会与抢占优先级有关, 和响应优先级无关!

② 一般情况下,系统代码执行过程中,只设置一次中断优先级分组,比如分组2,设置好分组之后一般不会再改变分组。随意改变分组会导致中断管理混乱,程序出现意想不到的执行结果。

中断优先级分组库函数

CM3核的优先级分组方式,使用的设置函数NVIC_SetPriorityGrouping()。

接下来介绍STM32的中断优先级分组函数NVIC_PriorityGroupConfig(),用来进行中断分组设置的,此函数是在固件库下misc.c文件中(文件目录是:

STM32F10x_StdPeriph_Lib_V3.5.0\Libraries\STM32F10x_StdPeriph_Driver\src\misc.c):

void NVIC_PriorityGroupConfig(uint32_t NVIC_PriorityGroup)
{
  /* Check the parameters */
  assert_param(IS_NVIC_PRIORITY_GROUP(NVIC_PriorityGroup));
  
  /* Set the PRIGROUP[10:8] bits according to NVIC_PriorityGroup value */
  SCB->AIRCR = AIRCR_VECTKEY_MASK | NVIC_PriorityGroup;
}

函数的参数的取值,是在同文件中进行宏定义的:

#define NVIC_PriorityGroup_0         ((uint32_t)0x700) /*!< 0 bits for pre-emption priority
                                                            4 bits for subpriority */
#define NVIC_PriorityGroup_1         ((uint32_t)0x600) /*!< 1 bits for pre-emption priority
                                                            3 bits for subpriority */
#define NVIC_PriorityGroup_2         ((uint32_t)0x500) /*!< 2 bits for pre-emption priority
                                                            2 bits for subpriority */
#define NVIC_PriorityGroup_3         ((uint32_t)0x400) /*!< 3 bits for pre-emption priority
                                                            1 bits for subpriority */
#define NVIC_PriorityGroup_4         ((uint32_t)0x300) /*!< 4 bits for pre-emption priority
                                                            0 bits for subpriority */

STM32中断优先级管理

中断优先级设置寄存器

分组设置好了之后,怎么设置单个中断的抢占优先级和响应优先级?

MDK为与NVIC相关的寄存器定义了如下的结构体,控制着中断向量表中60个中断(由于与中断内核有关,定义在core_cm3.h文件中):

typedef struct
{
  __IO uint32_t ISER[8];                      /*!< Offset: 0x000  Interrupt Set Enable Register           */
       uint32_t RESERVED0[24];                                   
  __IO uint32_t ICER[8];                      /*!< Offset: 0x080  Interrupt Clear Enable Register         */
       uint32_t RSERVED1[24];                                    
  __IO uint32_t ISPR[8];                      /*!< Offset: 0x100  Interrupt Set Pending Register          */
       uint32_t RESERVED2[24];                                   
  __IO uint32_t ICPR[8];                      /*!< Offset: 0x180  Interrupt Clear Pending Register        */
       uint32_t RESERVED3[24];                                   
  __IO uint32_t IABR[8];                      /*!< Offset: 0x200  Interrupt Active bit Register           */
       uint32_t RESERVED4[56];                                   
  __IO uint8_t  IP[240];                      /*!< Offset: 0x300  Interrupt Priority Register (8Bit wide) */
       uint32_t RESERVED5[644];                                  
  __O  uint32_t STIR;                         /*!< Offset: 0xE00  Software Trigger Interrupt Register     */
}  NVIC_Type;     

我们依次介绍一下这些寄存器:

先介绍几个寄存器组长度为8,这些寄存器是32位寄存器。由于STM32只有60个可屏蔽中断,8个32位寄存器中只需要2个就有64位了,每1位控制一个中断。

  • ISER[8](Interrupt Set-Enable Registers):中断使能寄存器。其中只使用到了ISER[0]和ISER[1],ISER[0]的bit0~bit31分别对应中断0~31。ISER[1]的bit0~27对应中断32~59。要使能某个中断,就必须设置相应的ISER位为1,使该中断被使能(这仅仅是使能,还要配合中断分组、屏蔽、I/O口映射等设置才算完整)。具体每一位对应哪个中断参考stm32f103x.h里面第140行。
  • ICER[8](Interrupt Clear-Enable Registers):中断移除寄存器。该寄存器的作用于ISER相反。这里专门设置一个ICER来清除中断位,而不是向ISER位写0,是因为NVIC的寄存器写1有效,写0无效。
  • ISPR[8](Interrupt Set-Pending Registers):中断挂起控制寄存器。通过置1可以将正在进行的中断挂起,执行同级或者更高级别的中断。写0无效。
  • ICPR[8](Interrupt Clear-Pending Registers):中断解挂控制寄存器。通过置1可以将正在挂起的中断解挂。写0无效。
  • IABR[8](Interrupt Active-Bit Registers):中断激活标志位寄存器。这是一个只读寄存器,可以知道当前在执行的中断是哪一个(为1),在中断执行完后硬件自动清零。

最后,介绍一个寄存器组长度为240,这个寄存器为8位寄存器。240个8位寄存器,每个中断使用一个寄存器来确定优先级。由于CM3由240个外部中断,所以这个寄存器组的数目就是240(注意与上面寄存器的区别,一个是一个寄存器控制一个,一个是一位控制一个)。

  • IP[240](Interrupt Priority Registers):中断优先级控制的寄存器。这是用来控制每个中断的优先级。由于STM32F10x系列一共60个可屏蔽中断,故使用IP[59]~IP[0]。其中每个IP寄存器的高4位[7:4]用来设置抢占和响应优先级(根据分组),低4位没有用到。而两个优先级各占几个位又要由上面讲到的中断优先级分组决定。

中断优先级设置库函数

接下来介绍如何使用库函数实现中断优先级管理,这里使用NVIC_Init()函数来进行对每个中断优先级的设置(misc.c文件中):

void NVIC_Init(NVIC_InitTypeDef* NVIC_InitStruct)
{
  uint32_t tmppriority = 0x00, tmppre = 0x00, tmpsub = 0x0F;
  
  /* Check the parameters */
  assert_param(IS_FUNCTIONAL_STATE(NVIC_InitStruct->NVIC_IRQChannelCmd));
  assert_param(IS_NVIC_PREEMPTION_PRIORITY(NVIC_InitStruct->NVIC_IRQChannelPreemptionPriority));  
  assert_param(IS_NVIC_SUB_PRIORITY(NVIC_InitStruct->NVIC_IRQChannelSubPriority));
    
  if (NVIC_InitStruct->NVIC_IRQChannelCmd != DISABLE)
  {
    /* Compute the Corresponding IRQ Priority --------------------------------*/    
    tmppriority = (0x700 - ((SCB->AIRCR) & (uint32_t)0x700))> 0x08;
    tmppre = (0x4 - tmppriority);
    tmpsub = tmpsub >> tmppriority;
 
    tmppriority = (uint32_t)NVIC_InitStruct->NVIC_IRQChannelPreemptionPriority << tmppre;
    tmppriority |=  NVIC_InitStruct->NVIC_IRQChannelSubPriority & tmpsub;
    tmppriority = tmppriority << 0x04;
        
    NVIC->IP[NVIC_InitStruct->NVIC_IRQChannel] = tmppriority;
    
    /* Enable the Selected IRQ Channels --------------------------------------*/
    NVIC->ISER[NVIC_InitStruct->NVIC_IRQChannel >> 0x05] =
      (uint32_t)0x01 << (NVIC_InitStruct->NVIC_IRQChannel & (uint8_t)0x1F);
  }
  else
  {
    /* Disable the Selected IRQ Channels -------------------------------------*/
    NVIC->ICER[NVIC_InitStruct->NVIC_IRQChannel >> 0x05] =
      (uint32_t)0x01 << (NVIC_InitStruct->NVIC_IRQChannel & (uint8_t)0x1F);
  }
}

其中,NVIC_InitTypeDef为一个结构体,它的成员变量为:

typedef struct
{
  uint8_t NVIC_IRQChannel;                    /*!< Specifies the IRQ channel to be enabled or disabled.
                                                   This parameter can be a value of @ref IRQn_Type 
                                                   (For the complete STM32 Devices IRQ Channels list, please
                                                    refer to stm32f10x.h file) */
 
  uint8_t NVIC_IRQChannelPreemptionPriority;  /*!< Specifies the pre-emption priority for the IRQ channel
                                                   specified in NVIC_IRQChannel. This parameter can be a value
                                                   between 0 and 15 as described in the table @ref NVIC_Priority_Table */
 
  uint8_t NVIC_IRQChannelSubPriority;         /*!< Specifies the subpriority level for the IRQ channel specified
                                                   in NVIC_IRQChannel. This parameter can be a value
                                                   between 0 and 15 as described in the table @ref NVIC_Priority_Table */
 
  FunctionalState NVIC_IRQChannelCmd;         /*!< Specifies whether the IRQ channel defined in NVIC_IRQChannel
                                                   will be enabled or disabled. 
                                                   This parameter can be set either to ENABLE or DISABLE */   
} NVIC_InitTypeDef;

NVIC_InitTypeDef结构体有4个成员变量:

① NVIC_IRQChannel:定义初始化的是哪一个中断,这个可以在stm32f10x.h文件中查到每个中断对应的名字,如USART1_IRQn;

② NVIC_IRQChannelPreemptionPriority:定义此中断的抢占优先级别;

③ NVIC_IRQChannelSubPriority:定义此中断的响应优先级别;

④ NVIC_IRQChannelCmd:该中断是否使能。

其实我们看NVIC_Init()函数内部使能中断,也是通过ISER寄存器配置的。这与我么之前的内容并不矛盾。函数内部使用NVIC->ISER,而NVIC是一个宏定义,

#define NVIC     ((NVIC_Type *) NVIC_BASE)     /*!< NVIC configuration struct         */

也就是直接操作结构体来实现操作ISER寄存器。

比如,使能串口1中断,抢占优先级为1,响应优先级为2,初始化的方法为:

NVIC_InitTypeDef   NVIC_InitStructure;
NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;//串口1中断
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=1 ;// 抢占优先级为1
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 2;// 子优先级位2
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;//IRQ通道使能
NVIC_Init(&NVIC_InitStructure);	//根据上面指定的参数初始化NVIC寄存器

总结与分析

最后总结一下中断优先级设置的步骤:

1、系统运行后先设置中断优先级分组。调用函数:

void NVIC_PriorityGroupConfig(uint32_t NVIC_PriorityGroup);
整个系统执行过程中,只设置一次中断分组;

2、针对每个中断,设置对应的抢占优先级和响应优先级:

void NVIC_Init(NVIC_InitTypeDef* NVIC_InitStruct);

来源:CSDN,作者:Yngz_Miao
原文:
https://blog.csdn.net/qq_38410730/article/details/79829983
版权声明:本文为博主原创文章,转载请附上博文链接!

围观 929

1、SysTick的介绍

SysTick定时器被捆绑在NVIC中,用于产生SYSTICK异常(异常号:15)。在以前,大多操作系统需要一个硬件定时器来产生操作系统需要的滴答中断,作为整个系统的时基。例如,为多个任务许以不同数目的时间片,确保没有一个任务能霸占系统;或者把每个定时器周期的某个时间范围赐予特定的任务等,还有操作系统提供的各种定时功能,都与这个滴答定时器有关。因此,需要一个定时器来产生周期性的中断,而且最好还让用户程序不能随意访问它的寄存器,以维持操作系统“心跳”的节律。

Cortex‐M3处理器内部包含了一个简单的定时器。因为所有的CM3芯片都带有这个定时器,软件在不同 CM3器件间的移植工作得以化简。该定时器的时钟源可以是内部时钟(FCLK,CM3上的自由运行时钟),或者是外部时钟( CM3处理器上的STCLK信号)。不过,STCLK的具体来源则由芯片设计者决定,因此不同产品之间的时钟频率可能会大不相同,你需要检视芯片的器件手册来决定选择什么作为时钟源。

SysTick定时器能产生中断,CM3为它专门开出一个异常类型,并且在向量表中有它的一席之地。它使操作系统和其它系统软件在CM3器件间的移植变得简单多了,因为在所有CM3产品间对其处理都是相同的。

2、SysTick寄存器

STM32中的systick,Systick 部分内容属于NVIC控制部分,一共有4个寄存器,名称和地址分别是:

STK_CSR, 0xE000E010 -- 控制寄存器
STK_LOAD, 0xE000E014 -- 重载寄存器
STK_VAL, 0xE000E018 -- 当前值寄存器
STK_CALRB, 0xE000E01C -- 校准值寄存器

(1)STK_CSR控制寄存器

stm32之SysTick的理解(NVIC)

第0位:ENABLE,Systick 使能位(0:关闭Systick功能;1:开启Systick功能)

第1位:TICKINT,Systick 中断使能位(0:关闭Systick中断;1:开启Systick中断)

第2位:CLKSOURCE,Systick时钟源选择(0:使用HCLK/8 作为Systick时钟;1:使用HCLK作为Systick时钟)

第16位:COUNTFLAG,Systick计数比较标志,如果在上次读取本寄存器后,SysTick 已经数到了0,则该位为1。如果读取该位,该位将自动清零.

(2) STK_LOAD 重载寄存器

stm32之SysTick的理解(NVIC)

Systick是一个递减的定时器,当定时器递减至0时,重载寄存器中的值就会被重装载,继续开始递减。STK_LOAD 重载寄存器是个24位的寄存器最大计数0xFFFFFF。

(3)STK_VAL当前值寄存器

stm32之SysTick的理解(NVIC)

也是个24位的寄存器,读取时返回当前倒计数的值,写它则使之清零,同时还会清除在SysTick 控制及状态寄存器中的COUNTFLAG 标志。

(4)STK_CALRB校准值寄存器

stm32之SysTick的理解(NVIC)

不知到怎么使用,有兴趣的请参考CM3内核参考手册。

3、程序中的理解和使用

RT-Thread系统bsp的stm32F10x中关于Systick的初始化:在启动文件中其实已经一定了SystemInit();其中就已经定义了32的AHB时钟、APB1时钟和APB2时钟的大小。

RTT中

/* Configure the SysTick */  
    SysTick_Config( SystemCoreClock / RT_TICK_PER_SECOND ); //<span style="font-family: Arial, Helvetica, sans-serif;">RT_TICK_PER_SECOND =100</span>  
__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 Systick Interrupt */  
     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->CTRL寄存器的表示使用HCLK作为Systick的时钟也就是72Mhz,Systick中断使能,并启动Systick定时器。

按照这个函数来算的话,写入SysTick->LOAD的值为72000000/100;这样算的话(1/72M)*SysTick->LOAD=10ms;故该RTT系统的最小调度时间就为10ms。

以上资料来自CM3中文参考手册

来源:梦想着破灭一切    
围观 1362
订阅 RSS - NVIC