文章目录
- NVIC
- NVIC是什么
- NVIC寄存器
- NVIC 结构体
- NVIC 相关固件库函数
- 如何定义优先级
- 中断编程
- 外部中断 EXTI
- EXIT 外部中断/事件控制器
- EXIT的使用
- EXTI内部寄存器分析
- GPIO触发中断例程
- 为什么中断后要清除中断标志位
- SysTick的使用
- SysTick分析
NVIC
NVIC是什么
待补充.........
NVIC寄存器
/*文件core_cm4.h*/
/** \ingroup CMSIS_core_register
\defgroup CMSIS_NVIC Nested Vectored Interrupt Controller (NVIC)
\brief Type definitions for the NVIC Registers
@{
*/
/** \brief Structure type to access the Nested Vectored Interrupt Controller (NVIC).
*/
typedef struct
{
__IO uint32_t ISER[8]; /*!< Offset: 0x000 (R/W) Interrupt Set Enable Register */
uint32_t RESERVED0[24];
__IO uint32_t ICER[8]; /*!< Offset: 0x080 (R/W) Interrupt Clear Enable Register */
uint32_t RSERVED1[24];
__IO uint32_t ISPR[8]; /*!< Offset: 0x100 (R/W) Interrupt Set Pending Register */
uint32_t RESERVED2[24];
__IO uint32_t ICPR[8]; /*!< Offset: 0x180 (R/W) Interrupt Clear Pending Register */
uint32_t RESERVED3[24];
__IO uint32_t IABR[8]; /*!< Offset: 0x200 (R/W) Interrupt Active bit Register */
uint32_t RESERVED4[56];
__IO uint8_t IP[240]; /*!< Offset: 0x300 (R/W) Interrupt Priority Register (8Bit wide) */
uint32_t RESERVED5[644];
__O uint32_t STIR; /*!< Offset: 0xE00 ( /W) Software Trigger Interrupt Register */
} NVIC_Type;
一般只使用 ISER(使能中断)、ICER(失能中断)、和IP(设置中断优先级 )这三个寄存器。
NVIC 结构体
/**
* @brief NVIC Init Structure definition
*/
typedef struct
{
uint8_t NVIC_IRQChannel; /*!< Specifies the IRQ channel to be enabled or disabled.
This parameter can be an enumerator of @ref IRQn_Type
enumeration (For the complete STM32 Devices IRQ Channels
list, please refer to stm32f4xx.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 MISC_NVIC_Priority_Table
A lower priority value indicates a higher priority */
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 MISC_NVIC_Priority_Table
A lower priority value indicates a higher priority */
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;
成员 | 描述 |
---|---|
uint8_t NVIC_IRQChannel | 中断源,IRQn_Type类型 |
uint8_t NVIC_IRQChannelPreemptionPriorityuint8_t NVIC_IRQChannelPreemptionPriority | 抢占优先级,由优先级分组确定 |
uint8_t NVIC_IRQChannelSubPriority | 子优先级,操作NVIC_ISER和NVIC_ICER这两个寄存器 |
FunctionalState NVIC_IRQChannelCmd | 中断使能或失能 |
NVIC 相关固件库函数
** 相关固件库函数由core_cm4.h和misc.h提供 **
函数名 | 描述 |
---|---|
void NVIC_EnableIRQ(IRQn_Type IRQn) | 使能中断 |
void NVIC_DisableIRQ(IRQn_Type IRQn) | 失能中断 |
void NVIC_SetPendingIRQ(IRQn_Type IRQn) | 设置外部中断挂起位 |
void NVIC_ClearPendingIRQ(IRQn_Type IRQn) | 清除外部中断挂起位 |
uint32_t NVIC_GetPriority(IRQn_Type IRQn) | 获取挂起的外部中断的编号 |
void NVIC_SetPriority(IRQn_Type IRQn, uint32_t priority) | 设置中断优先级 |
void NVIC_SetPriorityGrouping(uint32_t PriorityGroup) | 设置中断优先级组 |
uint32_t NVIC_GetPriority(IRQn_Type IRQn) | 获取中断优先级 |
uint32_t NVIC_GetPriorityGrouping(void) | 获取中断优先级组号 |
void NVIC_SystemReset(void) | 系统复位 |
上述固件库函数是在core_cm4.h中提供的,含有一部分在misc.h中提供,misc.h中的函数调用core_cm4.h中提供的函数设置中断 |
void NVIC_PriorityGroupConfig(uint32_t NVIC_PriorityGroup);
void NVIC_Init(NVIC_InitTypeDef* NVIC_InitStruct);
void NVIC_SetVectorTable(uint32_t NVIC_VectTab, uint32_t Offset);
void NVIC_SystemLPConfig(uint8_t LowPowerMode, FunctionalState NewState);
void SysTick_CLKSourceConfig(uint32_t SysTick_CLKSource);
** 上述函数参数说明 **
参数名 | 描述 |
---|---|
IRQn_Type | 是一个枚举类型,在core_cm4中定义,内容为stm32对应为设备的中断编号(stm32f407中有十个系统异常和82个外部中断) |
PriorityGroup | 优先级组号 0~7 |
如何定义优先级
优先级分组:不同系列的单片机优先级分组有多种,一般来说中断优先级分组在整个程序中只需用设置一次就可以了,重复设置只是覆盖以前设置的值。设置中断优先级分组使用的是SCB->AIRCR寄存器的PRIGROUP的[10:8]位,那么设置中断优先级分组的意义是什么呢?设置中断优先级分组实际上是确定了中断抢占优先级(主优先级)和子优先级所占的位数,core_cm4.h提供的** NVIC_SetPriorityGrouping(uint32_t PriorityGroup)函数设置中断优先级分组 **
该函数内容如下:
_STATIC_INLINE void NVIC_SetPriorityGrouping(uint32_t PriorityGroup)
{
uint32_t reg_value;
uint32_t PriorityGroupTmp = (PriorityGroup & (uint32_t)0x07UL); /* only values 0..7 are used */
reg_value = SCB->AIRCR; /* read old register configuration */
reg_value &= ~((uint32_t)(SCB_AIRCR_VECTKEY_Msk | SCB_AIRCR_PRIGROUP_Msk)); /* clear bits to change */
reg_value = (reg_value |
((uint32_t)0x5FAUL << SCB_AIRCR_VECTKEY_Pos) |
(PriorityGroupTmp << 8) ); /* Insert write key and priorty group */
SCB->AIRCR = reg_value;
}
// 0x5FUL 的具体含义是指 将 0x5F 指定为UL类型,即usigned long (无符号长整型)
再来看一下设置中断优先级的函数
__STATIC_INLINE void NVIC_SetPriority(IRQn_Type IRQn, uint32_t priority)
{
if((int32_t)IRQn < 0) {
SCB->SHP[(((uint32_t)(int32_t)IRQn) & 0xFUL)-4UL] = (uint8_t)((priority << (8 - __NVIC_PRIO_BITS)) & (uint32_t)0xFFUL);
}
else {
NVIC->IP[((uint32_t)(int32_t)IRQn)] = (uint8_t)((priority << (8 - __NVIC_PRIO_BITS)) & (uint32_t)0xFFUL);
}
}
可以看到stm32提供的函数在设置中断优先级时形参为中断号和要设置的中断优先级,中断号小于0时使用的是SCB-SHP寄存器,首先看一看SHP寄存器和IP寄存器是什么
typedef struct
{
__I uint32_t CPUID; /*!< Offset: 0x000 (R/ ) CPUID Base Register */
__IO uint32_t ICSR; /*!< Offset: 0x004 (R/W) Interrupt Control and State Register */
__IO uint32_t VTOR; /*!< Offset: 0x008 (R/W) Vector Table Offset Register */
__IO uint32_t AIRCR; /*!< Offset: 0x00C (R/W) Application Interrupt and Reset Control Register */
__IO uint32_t SCR; /*!< Offset: 0x010 (R/W) System Control Register */
__IO uint32_t CCR; /*!< Offset: 0x014 (R/W) Configuration Control Register */
__IO uint8_t SHP[12]; /*!< Offset: 0x018 (R/W) System Handlers Priority Registers (4-7, 8-11, 12-15) */
__IO uint32_t SHCSR; /*!< Offset: 0x024 (R/W) System Handler Control and State Register */
__IO uint32_t CFSR; /*!< Offset: 0x028 (R/W) Configurable Fault Status Register */
__IO uint32_t HFSR; /*!< Offset: 0x02C (R/W) HardFault Status Register */
__IO uint32_t DFSR; /*!< Offset: 0x030 (R/W) Debug Fault Status Register */
__IO uint32_t MMFAR; /*!< Offset: 0x034 (R/W) MemManage Fault Address Register */
__IO uint32_t BFAR; /*!< Offset: 0x038 (R/W) BusFault Address Register */
__IO uint32_t AFSR; /*!< Offset: 0x03C (R/W) Auxiliary Fault Status Register */
__I uint32_t PFR[2]; /*!< Offset: 0x040 (R/ ) Processor Feature Register */
__I uint32_t DFR; /*!< Offset: 0x048 (R/ ) Debug Feature Register */
__I uint32_t ADR; /*!< Offset: 0x04C (R/ ) Auxiliary Feature Register */
__I uint32_t MMFR[4]; /*!< Offset: 0x050 (R/ ) Memory Model Feature Register */
__I uint32_t ISAR[5]; /*!< Offset: 0x060 (R/ ) Instruction Set Attributes Register */
uint32_t RESERVED0[5];
__IO uint32_t CPACR; /*!< Offset: 0x088 (R/W) Coprocessor Access Control Register */
} SCB_Type;
typedef struct
{
__IO uint32_t ISER[8]; /*!< Offset: 0x000 (R/W) Interrupt Set Enable Register */
uint32_t RESERVED0[24];
__IO uint32_t ICER[8]; /*!< Offset: 0x080 (R/W) Interrupt Clear Enable Register */
uint32_t RSERVED1[24];
__IO uint32_t ISPR[8]; /*!< Offset: 0x100 (R/W) Interrupt Set Pending Register */
uint32_t RESERVED2[24];
__IO uint32_t ICPR[8]; /*!< Offset: 0x180 (R/W) Interrupt Clear Pending Register */
uint32_t RESERVED3[24];
__IO uint32_t IABR[8]; /*!< Offset: 0x200 (R/W) Interrupt Active bit Register */
uint32_t RESERVED4[56];
__IO uint8_t IP[240]; /*!< Offset: 0x300 (R/W) Interrupt Priority Register (8Bit wide) */
uint32_t RESERVED5[644];
__O uint32_t STIR; /*!< Offset: 0xE00 ( /W) Software Trigger Interrupt Register */
} NVIC_Type;
从SCB结构体的定义中我们可以看到SHP是System Handlers Priority Registers(系统优先处理寄存器),IP是NVIC寄存器中的Interrupt Priority Register (中断优先级寄存器),再看看中断号小于0的是那些中断
typedef enum IRQn
{
/****** Cortex-M4 Processor Exceptions Numbers ****************************************************************/
NonMaskableInt_IRQn = -14, /*!< 2 Non Maskable Interrupt */
MemoryManagement_IRQn = -12, /*!< 4 Cortex-M4 Memory Management Interrupt */
BusFault_IRQn = -11, /*!< 5 Cortex-M4 Bus Fault Interrupt */
UsageFault_IRQn = -10, /*!< 6 Cortex-M4 Usage Fault Interrupt */
SVCall_IRQn = -5, /*!< 11 Cortex-M4 SV Call Interrupt */
DebugMonitor_IRQn = -4, /*!< 12 Cortex-M4 Debug Monitor Interrupt */
PendSV_IRQn = -2, /*!< 14 Cortex-M4 Pend SV Interrupt */
SysTick_IRQn = -1, /*!< 15 Cortex-M4 System Tick Interrupt
/*省略*/
}IRQn_Type;
通过上述分析,我们可以了解到,在设置中断优先级时若中断优先级为负数,则那些中断都是系统级的中断,需要优先处理,在函数中通过中断号确定数组下标,以数组形式访问SCB->SHP寄存器,将优先级写入相关位。当中断号大于0时,设置的则是NVIC->IP寄存器,同样以数组形式访问并将优先级写入,很明显不同的数组元素位置对应不同的中断。
再回到最初的问题,中断优先级分组的作用,在设置优先级的函数中可以看到在写入优先级时将参数转换为uint_8,说明中断优先级由八位确认,而中断优先级的不同分组就是确认在在这8位中那几位确定主优先级(高位),那些位确定子优先级(低位),比如主优先级占3位,则主优先级就有8钟,子优先级有32种。
如果同时有中断发生,则先处理优先级高的中断,中断优先级的确定顺序是:主优先级>子优先级>硬件中断编号(IRQn_Type)
中断编程
- 使能外设的中断,一般由具体外设的相关寄存器控制
/*NVIC初始化结构体*/
typedef struct
{
uint8_t NVIC_IRQChannel; /*!< Specifies the IRQ channel to be enabled or disabled.
This parameter can be an enumerator of @ref IRQn_Type
enumeration (For the complete STM32 Devices IRQ Channels
list, please refer to stm32f4xx.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 MISC_NVIC_Priority_Table
A lower priority value indicates a higher priority */
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 MISC_NVIC_Priority_Table
A lower priority value indicates a higher priority */
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初始化函数*/
/**
* @brief Initializes the NVIC peripheral according to the specified
* parameters in the NVIC_InitStruct.
* @note To configure interrupts priority correctly, the NVIC_PriorityGroupConfig()
* function should be called before.
* @param NVIC_InitStruct: pointer to a NVIC_InitTypeDef structure that contains
* the configuration information for the specified NVIC peripheral.
* @retval None
*/
void NVIC_Init(NVIC_InitTypeDef* NVIC_InitStruct)
{
uint8_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 = NVIC_InitStruct->NVIC_IRQChannelPreemptionPriority << tmppre;
tmppriority |= (uint8_t)(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** 的 ** ISER ** 和 ICER操作使中断的通道使能或失能。但这里的通道的失能或失能并不是外设中断的失能或者失能。
- 初始化NVIC_InitTypeDef结构体,配置中断优先级分组、设置抢占优先级和子优先级、使能中断请求(misc.c 中提供的函数)
- 编写中断服务函数,可以在stm32f4xx_it.c中(终端服务名称是固定的,涉及中断向量表。可参考startup文件)
使能外设的中断:未理解,待补充…
外部中断 EXTI
EXIT 外部中断/事件控制器
exti 有23个中断/事件线,0~15 对应于GPIO的不同分组,16 ~ 22 对应7个特定的事件。通过配置SYSCFG->EXTIx寄存器可以配置EXTI关联信息(使用GPIO时)。
GPIO 个各个引脚通过EXTI0~EXTI15连接到16个外部中断/事件线,另外7个中断/时间线的连接如下:
● EXTI 线 16 连接到 PVD 输出
● EXTI 线 17 连接到 RTC 闹钟事件
● EXTI 线 18 连接到 USB OTG FS 唤醒事件
● EXTI 线 19 连接到以太网唤醒事件
● EXTI 线 20 连接到 USB OTG HS(在 FS 中配置)唤醒事件
● EXTI 线 21 连接到 RTC 入侵和时间戳事件
● EXTI 线 22 连接到 RTC 唤醒事件
而连接到中断/事件线之后就进入了EXTI内部,EXTI内部功能框图如下:
可以看到在EXTI内部是由EXTI相关的寄存器配置的,通个不同的配置可以选择是在被触发后生成事件还是中断(事件一般通过脉冲发生器产生一个方波)。
总结:
通过对EXTI的分析我们可以知道,要使用EXTI由23个,7个(16~22)是固定连接的,在使用这7个事件线时只需要配置EXTi内部相关的寄存器就可以了。但是如果要使用GPIO触发中断或事件就需要多一步配置,因为不同GPIO的相同引脚连接到了SYSCFG->EXTICRx(x=0-15),然后SYSCFG->EXTICRx连接到了EXTI的0 ~15 号中断/事件线。所以要使用GPIO触发中断,要先将GPIO引脚设置为IN模式,然后然后配置SYSCFG->EXTICRx,确定是那一组GPIO的哪一个引脚,然后配置EXTI内部的寄存器。
**注意1:**EXTI内部寄存器是一组,23个线占各自寄存器的一位。
**注意2:**GPIO触发中断要使用SYSCFG寄存器,所以要开启SYSCFG的时钟。
EXIT的使用
在STM32提供的固件库函数中,使用一个外设必然要先通过配置相关挂的结构体来进行初始化的,EXTI也是如此。
typedef struct
{
uint32_t EXTI_Line; /*!< Specifies the EXTI lines to be enabled or disabled.
This parameter can be any combination value of @ref EXTI_Lines */
EXTIMode_TypeDef EXTI_Mode; /*!< Specifies the mode for the EXTI lines.
This parameter can be a value of @ref EXTIMode_TypeDef */
EXTITrigger_TypeDef EXTI_Trigger; /*!< Specifies the trigger signal active edge for the EXTI lines.
This parameter can be a value of @ref EXTITrigger_TypeDef */
FunctionalState EXTI_LineCmd; /*!< Specifies the new state of the selected EXTI lines.
This parameter can be set either to ENABLE or DISABLE */
}EXTI_InitTypeDef;
由EXTI初始化结构体可以看到,想使用一个EXTI首先要选择中断/事件线(0~22,据此使能对应中断/事件线),然后选择EXTI模式(中断还是事件),接着选择触发模式(上升沿触发还是下降沿触发或是混合触发),最后选择中断/事件线的状态(使能或失能)
接下来分析一下EXTI初始化函数
void EXTI_Init(EXTI_InitTypeDef* EXTI_InitStruct)
{
uint32_t tmp = 0;
/* Check the parameters */
assert_param(IS_EXTI_MODE(EXTI_InitStruct->EXTI_Mode));
assert_param(IS_EXTI_TRIGGER(EXTI_InitStruct->EXTI_Trigger));
assert_param(IS_EXTI_LINE(EXTI_InitStruct->EXTI_Line));
assert_param(IS_FUNCTIONAL_STATE(EXTI_InitStruct->EXTI_LineCmd));
tmp = (uint32_t)EXTI_BASE;
if (EXTI_InitStruct->EXTI_LineCmd != DISABLE)
{
/* Clear EXTI line configuration */
EXTI->IMR &= ~EXTI_InitStruct->EXTI_Line;
EXTI->EMR &= ~EXTI_InitStruct->EXTI_Line;
tmp += EXTI_InitStruct->EXTI_Mode;
*(__IO uint32_t *) tmp |= EXTI_InitStruct->EXTI_Line;
/* Clear Rising Falling edge configuration */
EXTI->RTSR &= ~EXTI_InitStruct->EXTI_Line;
EXTI->FTSR &= ~EXTI_InitStruct->EXTI_Line;
/* Select the trigger for the selected external interrupts */
if (EXTI_InitStruct->EXTI_Trigger == EXTI_Trigger_Rising_Falling)
{
/* Rising Falling edge */
EXTI->RTSR |= EXTI_InitStruct->EXTI_Line;
EXTI->FTSR |= EXTI_InitStruct->EXTI_Line;
}
else
{
tmp = (uint32_t)EXTI_BASE;
tmp += EXTI_InitStruct->EXTI_Trigger;
*(__IO uint32_t *) tmp |= EXTI_InitStruct->EXTI_Line;
}
}
else
{
tmp += EXTI_InitStruct->EXTI_Mode;
/* Disable the selected external lines */
*(__IO uint2_t *) tmp &= ~EXTI_InitStruct->EXTI_Line;
}
}
/*EXTI结构体*/
typedef struct
{
__IO uint32_t IMR; /*!< EXTI Interrupt mask register, Address offset: 0x00 */
__IO uint32_t EMR; /*!< EXTI Event mask register, Address offset: 0x04 */
__IO uint32_t RTSR; /*!< EXTI Rising trigger selection register, Address offset: 0x08 */
__IO uint32_t FTSR; /*!< EXTI Falling trigger selection register, Address offset: 0x0C */
__IO uint32_t SWIER; /*!< EXTI Software interrupt event register, Address offset: 0x10 */
__IO uint32_t PR; /*!< EXTI Pending register, Address offset: 0x14 */
} EXTI_TypeDef;
在EXTI初始化函数中,设置的寄存器都是EXTI内部的寄存器,若要使用GPIO则还要配置SYSCFG->EXTICRx,SYSCF作为外设也提供了对应的固件库函数。
void SYSCFG_EXTILineConfig(uint8_t EXTI_PortSourceGPIOx, uint8_t EXTI_PinSourcex)
{
uint32_t tmp = 0x00;
/* Check the parameters */
assert_param(IS_EXTI_PORT_SOURCE(EXTI_PortSourceGPIOx));
assert_param(IS_EXTI_PIN_SOURCE(EXTI_PinSourcex));
tmp = ((uint32_t)0x0F) << (0x04 * (EXTI_PinSourcex & (uint8_t)0x03));
SYSCFG->EXTICR[EXTI_PinSourcex >> 0x02] &= ~tmp;
SYSCFG->EXTICR[EXTI_PinSourcex >> 0x02] |= (((uint32_t)EXTI_PortSourceGPIOx) << (0x04 * (EXTI_PinSourcex & (uint8_t)0x03)));
}
在这个函数中通过传入的参数确定选择那几位,先清空再赋值。
GPIO触发中断流程:
1、开启GPIO时钟,SYSCFG时钟
2、配置GPIO模式,配置SYSCFG->EXTICRx寄存器
3、配置EXTI内部寄存器
4、设置NVIC,确定中断优先级
5、写中断服务函数
**注意:**在编写中断服务函数时,5~9、10 ~ 15 外部中断使用两个中断服务函数,所以在使用这两个范围的外部中断时编写中断服务函数需要判断一下具体的中断源是哪一个再做处理,使用函数ITStatus EXTI_GetITStatus(uint32_t EXTI_Line)
ITStatus EXTI_GetITStatus(uint32_t EXTI_Line)
{
FlagStatus bitstatus = RESET;
/* Check the parameters */
assert_param(IS_GET_EXTI_LINE(EXTI_Line));
if ((EXTI->PR & EXTI_Line) != (uint32_t)RESET)
{
bitstatus = SET;
}
else
{
bitstatus = RESET;
}
return bitstatus;
}
可以看到这个函数是通过EXTI->PR(挂起寄存器)寄存寄存器判断具体的中断源的
EXTI内部寄存器分析
待补充。。。。。
GPIO触发中断例程
为什么中断后要清除中断标志位
EXTI_ClearITPendingBit(EXTI_Line0); 待补充
SysTick的使用
SysTick分析
SysTick (系统定时器)是CM4内核的一个外设,是一个24位的递减的计数器,当递减为0的时候系统产生一个中断。中断编号为-1,属于可编程的中断。通过源码对SysTick的定义可以了解该寄存器
//在中断向量表中的中断编号
SysTick_IRQn = -1, /*!< 15 Cortex-M4 System Tick Interrupt
/** \brief Structure type to access the System Timer (SysTick).
*/
typedef struct
{
__IO uint32_t CTRL; /*!< Offset: 0x000 (R/W) SysTick Control and Status Register */
__IO uint32_t LOAD; /*!< Offset: 0x004 (R/W) SysTick Reload Value Register */
__IO uint32_t VAL; /*!< Offset: 0x008 (R/W) SysTick Current Value Register */
__I uint32_t CALIB; /*!< Offset: 0x00C (R/ ) SysTick Calibration Register */
} SysTick_Type;
通过SysTick的结构体可以看到SysTick有四个寄存器。下面分析这四个寄存器。
名称 | 描述 |
---|---|
CTRL(SYSTICK控制和状态寄存器) | [0]:SysTick使能位 [1]:置1,计数至0 产生中断,置0无动作 [2]:时钟源选择 [16]:在上次读取该寄存器后,若技术至0,该位置1 |
LOAD (重载寄存器) | VAL计数至0后加载该寄存器值到VAL |
VAL(当前值寄存器) | 每1/SYSCLK计数一次(递减),计数至0产生中断 |
CALIB(校准寄存器) |
**思考:**CTRL的[16]:在上次读取该寄存器后,若技术至0,该位置1,那么在计0后,该位是否需要手动置0。
很明显不需要。
/** \brief System Tick Configuration
The function initializes the System Timer and its interrupt, and starts the System Tick Timer.
Counter is in free running mode to generate periodic interrupts.
\param [in] ticks Number of ticks between two interrupts.
\return 0 Function succeeded.
\return 1 Function failed.
\note When the variable <b>__Vendor_SysTickConfig</b> is set to 1, then the
function <b>SysTick_Config</b> is not included. In this case, the file <b><i>device</i>.h</b>
must contain a vendor-specific implementation of this function.
*/
__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 计时 ms*/
void SysTick_Delay_Ms( __IO uint32_t ms)
{
uint32_t i;
SysTick_Config(SystemCoreClock/1000);
for (i=0; i<ms; i++) {
// 当计数器的值减小到 0 的时候, CRTL 寄存器的位 16 会置 1
// 当置 1 时,读取该位会清 0
while ( !((SysTick->CTRL)&(1<<16)) );
}
// 关闭 SysTick 定时器
SysTick->CTRL &=~ SysTick_CTRL_ENABLE_Msk;
}