STM32中断学习
- 1.NVIC(Nested vectoredinterrupt controller)
- 1.1.什么是NVIC?
- 1.2 中断优先级
- 1.3 NVIC配置
- 1.4 编写中断服务函数
- 1.5 NVIC部分编写的库函数
- 1.5.1 NVIC_PriorityGroupConfig(uint32_t NVIC_PriorityGroup)
- 1.5.2 NVIC_Init(NVIC_InitTypeDef* NVIC_InitStruct)
- 1.5.3 NVIC_SetVectorTable(uint32_t NVIC_VectTab, uint32_t Offset)
- 1.5.4 NVIC_SystemLPConfig(uint8_t LowPowerMode, FunctionalState NewState)
- 1.5.5 SysTick_CLKSourceConfig(uint32_t SysTick_CLKSource)
- 2.EXTI
- 2.1什么是EXTI?
- 2.2 EXTI 配置
- 2.3 EXTI 部分编写的库函数
- 2.3.1 EXTI_DeInit(void)
- 2.3.2 EXTI_Init(EXTI_InitTypeDef* EXTI_InitStruct)
- 2.3.3 EXTI_StructInit(EXTI_InitTypeDef* EXTI_InitStruct)
- 2.3.4 EXTI_GenerateSWInterrupt(uint32_t EXTI_Line)
- 2.3.5 FlagStatus EXTI_GetFlagStatus(uint32_t EXTI_Line)
- 2.3.6 void EXTI_ClearFlag(uint32_t EXTI_Line)
- 2.3.7 ITStatus EXTI_GetITStatus(uint32_t EXTI_Line)
- 2.3.8 void EXTI_ClearITPendingBit(uint32_t EXTI_Line)
- 3.EXTI和NVIC的关系
- 4.编程实例:
1.NVIC(Nested vectoredinterrupt controller)
1.1.什么是NVIC?
NVIC的全称是Nested vectoredinterrupt controller,即嵌套向量中断控制器。用于为中断分组,从而分配抢占优先级和响应优先级;
NVIC是嵌套向量中断控制器,控制着整个芯片中断相关的功能,它跟内核紧密耦合,是内核里面的一个外设。但是各个芯片厂商在设计芯片的时候会对Cortex-M3内核里面的NVIC进行裁剪,把不需要的部分去掉,所以说STM32的NVIC是Cortex-M3的NVIC的一个子集。
可以看到系统内核里的外设,其中NVIC就在里面。
NVIC结构体定义:
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;
在配置中断的时候我们一般只用ISER、ICER 和IP这三个寄存器。
1.ISER用来使能中断,
2.ICER用来失能中断,
3.IP用来设置中断优先级。
1.2 中断优先级
1.2.1优先级定义
在NVIC有一个专门的寄存器:中断优先级寄存器NVIC_IPRx,用来配置外部中断的优先级,IPR 宽度为8bit,原则上每个外部中断可配置的优先级为0~255,数值越小,优先级越高。在Cortex-M3中可以看到对应的寄存器:
但是绝大多数CM3芯片都会精简设计,以致实际上支持的优先级数减少,在F103中,只使用了高4bit,如下所示:
用于表达优先级的这4bit,又被分组成抢占优先级和子优先级。如果有多个中断同时响应,抢占优先级高的就会抢占抢占优先级低的优先得到执行,如果抢占优先级相同,就比较子优先级。如果抢占优先级和子优先级都相同的话,就比较他们的硬件中断编号,编号越小,优先级越高。
1.2.2优先级分组
优先级的分组由内核外设SCB的应用程序中断及复位控制寄存器AIRCR的==PRIGROUP[10:8]==位决定,
F103分为了5组,具体如下:主优先级=抢占优先级
1.3 NVIC配置
在配置每个中断的时候一般有 3 个编程要点:
1、 使能外设某个中断,这个具体由每个外设的相关中断使能位控制。比如串口有发送完成中断,接收完成中断,这两个中断都由串口控制寄存器的相关中断使能位控制。
2、== 初始化 NVIC_InitTypeDef 结构体==,配置中断优先级分组,设置抢占优先级和子优先级,使能中断请求。 NVIC_InitTypeDef 结构体在固件库头文件 misc.h 中定义。
1.3.1 NVIC初始化结构体
typedef struct
{
uint8_t NVIC_IRQChannel; //中断源
uint8_t NVIC_IRQChannelPreemptionPriority; //抢占优先级
uint8_t NVIC_IRQChannelSubPriority; //子优先级
FunctionalState NVIC_IRQChannelCmd; //中断使能或者失能
} NVIC_InitTypeDef;
1.3.1.1 NVIC_IROChannel:
1.用来设置中断源,不同的中断中断源不一样.不可写错,即使写错了程序也不会报错,只会导致不响应中断。具体的成员配置可参考stm32f10x.h头文件里面的IRQn_Type结构体定义,这个结构体包含了所有的中断源。
/****** STM32 specific Interrupt Numbers *********************************************************/
WWDG_IRQn = 0, /*!< Window WatchDog Interrupt */
PVD_IRQn = 1, /*!< PVD through EXTI Line detection Interrupt */
TAMPER_IRQn = 2, /*!< Tamper Interrupt */
RTC_IRQn = 3, /*!< RTC global Interrupt */
FLASH_IRQn = 4, /*!< FLASH global Interrupt */
RCC_IRQn = 5, /*!< RCC global Interrupt */
EXTI0_IRQn = 6, /*!< EXTI Line0 Interrupt */
//........
2.比如我们要用 EXTI Line0 Interrupt,比如PA0引脚对应Line 0,则将中断源选择EXTI0_IRQn ,如下:
static void EXTI_NVIC_Config(void)
{
NVIC_InitTypeDef NVIC_InitStruct;//1.实例化
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1);
NVIC_InitStruct.NVIC_IRQChannel = EXTI0_IRQn;//2.中断源选择EXTI0_IRQn
NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 1;
NVIC_InitStruct.NVIC_IRQChannelSubPriority = 1;
NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStruct);
}
1.3.1.2 NVIC_IRQChannelPreemptionPriority:
抢占优先级,具体的值要根据优先级分组来确定,具体参考表格17-5优先级分组真值表。
1.3.1.3 NVIC_IRQChannelSubPriority:
子优先级,具体的值要根据优先级分组来确定,具体参考表格17-5优先级分组真值表。
1.3.1.4 NVIC_IRQChannelCmd:
中断使能(ENABLE)或者失能(DISABLE)。操作的是NVIC_ISER和NVIC_ICER这两个寄存器。
1.4 编写中断服务函数
在启动文件 startup_stm32f10x_hd.s中我们预先为每个中断都写了一个中断服务函数,只是这些中断函数都是为空,为的只是初始化中断向量表。实际的中断服务函数都需要我们重新编写,为了方便管理我们把中断服务函数统一写在stm32f10x it.c这个库文件中。
关于中断服务函数的函数名必须跟启动文件里面预先设置的一样,如果写错,系统就在中断向量表中找不到中断服务函数的入口,直接跳转到启动文件里面预先写好的空函数,并且在里面无限循环,实现不了中断。
1.5 NVIC部分编写的库函数
NVIC编写的库函数在misc.c中,声明在misc.h中,包含如下:
/** @defgroup MISC_Exported_Functions
* @{
*/
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);
1.5.1 NVIC_PriorityGroupConfig(uint32_t NVIC_PriorityGroup)
Configures the priority grouping: pre-emption priority and subpriority.
配置优先级分组,对应的有抢占优先级和子优先级。
原型:
/**
* @brief Configures the priority grouping: pre-emption priority and subpriority.
* @param NVIC_PriorityGroup: specifies the priority grouping bits length.
* This parameter can be one of the following values:
* @arg NVIC_PriorityGroup_0: 0 bits for pre-emption priority
* 4 bits for subpriority
* @arg NVIC_PriorityGroup_1: 1 bits for pre-emption priority
* 3 bits for subpriority
* @arg NVIC_PriorityGroup_2: 2 bits for pre-emption priority
* 2 bits for subpriority
* @arg NVIC_PriorityGroup_3: 3 bits for pre-emption priority
* 1 bits for subpriority
* @arg NVIC_PriorityGroup_4: 4 bits for pre-emption priority
* 0 bits for subpriority
* @retval None
*/
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 */
1.5.2 NVIC_Init(NVIC_InitTypeDef* NVIC_InitStruct)
@brief Initializes the NVIC peripheral according to the specified parameters in the NVIC_InitStruct.
根据指定NVIC_InitStruct中的参数,初始化NVIC外设。
1.5.3 NVIC_SetVectorTable(uint32_t NVIC_VectTab, uint32_t Offset)
@brief Sets the vector table location and Offset.
设置向量表位置和偏移量。
1.5.4 NVIC_SystemLPConfig(uint8_t LowPowerMode, FunctionalState NewState)
@brief Selects the condition for the system to enter low power mode.
选择系统进入低功耗模式的条件。
1.5.5 SysTick_CLKSourceConfig(uint32_t SysTick_CLKSource)
@brief Configures the SysTick clock source.
配置SysTick时钟源。
2.EXTI
2.1什么是EXTI?
1.上一部分我们已经详细介绍了 NVIC,对 STM32F10x 系列中断管理系统有个全局的了解,我们这一部分的内容是 NVIC 的实例应用。
EXTI(External interrupt/event controller)—外部中断/事件控制器,管理了控制器的 20个中断/事件线。每个中断/事件线都对应有一个边沿检测器,可以实现输入信号的上升沿检测和下降沿的检测。EXTI 可以实现对每个中断/事件线进行单独配置,可以单独配置为中断或者事件,以及触发事件的属性。
先看EXTI框图:
2.1.1产生中断线路流程
①输入线,EXTI 控制器有 20 个中断/事件输入线,可以通过寄存器设置为任意一个 GPIO,也可以是一些外设的事件,输入线一般是存在电平变化的信号。
②边沿检测电路,EXTI 可以对触发方式进行选择,通过上升沿触发选择寄存器(EXTI_RTSR)和下降沿触发选择寄存器(EXTI_FTSR)对应位的设置来控制信号触发。
③或门电路,一端输入信号线由边沿检测电路提供,一端由软件中断事件寄存器(EXTI_SWIER)(可以使用软件来启动中断/事件线)提供,只要有一个为有效信号 1,则输出有效信号 1。输出的信号会被保存到挂起寄存器内,电路输出为 1 就会把挂起寄存器对应位置 1。
④与门电路,一端输入信号线由③电路输出提供,一端由中断屏蔽寄存器(EXTI_IMR)提供,只有当两者都为有效信号 1,才会输出有效信号 1。
⑤将挂起寄存器内容(EXTI_PR)输入到 NVIC 内,从而实现系统中断事件的控制。
则上面整个流程是产生中断的过程①②③④⑤
4.1.2产生事件线路流程
①输入线,EXTI 控制器有 20 个中断/事件输入线,可以通过寄存器设置为任意一个 GPIO,也可以是一些外设的事件,输入线一般是存在电平变化的信号。
②边沿检测电路,EXTI 可以对触发方式进行选择,通过上升沿触发选择寄存器和下降沿触发选择寄存器对应位的设置来控制信号触发。
③或门电路,一端输入信号线由边沿检测电路提供,一端由软件中断事件寄存器(可以使用软件来启动中断/事件线)提供,只要有一个为有效信号 1,则输出有效信号 1。输出的信号会被保存到挂起寄存器内,电路输出为 1 就会把挂起寄存器对应位置 1。
⑥与门电路,一端来至标号 ③的输出信号,一端来至事件屏蔽寄存器(可以简单的控制事件屏蔽寄存器来实现是否产生事件),只有两者都为有效电平 1,才会输出有效高电平。
⑦脉冲发生器电路,⑥有效,脉冲发生器会输出一个脉冲信号。
⑧脉冲信号:脉冲信号,就是产生事件的线路最终的产物,这个脉冲信号可以给其他外设电路使用,比如定时器TIM、模拟数字转换器ADC 等等,这样的脉冲信号一般用来触发TIM或者ADC开始转换。
4.1.3.中断/事件线
1.EXTI有20个中断/事件线,每个GPIO都可以被设置为输入线,占用EXTI0-EXTI15,还有另外7根用于特定的外设事件。
2.EXTIO至 EXTI15用于GPIO,通过编程控制可以实现任意一个GPIO作为EXTI的输入源。由表18-1可知,在stm32F10x中,EXTIO可以通过AFIO的外部中断配置寄存器 (AFIO_EXTICR1)的EXTIO[3:0]位选择配置为PA0、PB0、PC0、PD0、PE0、PF0、PG0、PH0或者PI0,见参考手册。
3.在stm32F40xx中, 通过配置外部中断配置寄存器SYSCFG_EXTICR1的EXTIO[3:0]位选择配置为PA0、PB0、PC0、PD0、PE0、PF0、PG0、PH0或者PI0.
2.2 EXTI 配置
2.2.1 EXTI 初始化结构体
/**
* @brief EXTI Init Structure definition
*/
typedef struct
{
uint32_t EXTI_Line; /*!< Specifies the EXTI lines to be enabled or disabled.
This parameter can be any combination 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 EXTIMode_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;
2.2.1.1 EXTI_Line:
可选:即对应配置:
/** @defgroup EXTI_Exported_Constants
* @{
*/
/** @defgroup EXTI_Lines
* @{
*/
#define EXTI_Line0 ((uint32_t)0x00001) /*!< External interrupt line 0 */
#define EXTI_Line1 ((uint32_t)0x00002) /*!< External interrupt line 1 */
#define EXTI_Line2 ((uint32_t)0x00004) /*!< External interrupt line 2 */
#define EXTI_Line3 ((uint32_t)0x00008) /*!< External interrupt line 3 */
#define EXTI_Line4 ((uint32_t)0x00010) /*!< External interrupt line 4 */
#define EXTI_Line5 ((uint32_t)0x00020) /*!< External interrupt line 5 */
#define EXTI_Line6 ((uint32_t)0x00040) /*!< External interrupt line 6 */
#define EXTI_Line7 ((uint32_t)0x00080) /*!< External interrupt line 7 */
#define EXTI_Line8 ((uint32_t)0x00100) /*!< External interrupt line 8 */
#define EXTI_Line9 ((uint32_t)0x00200) /*!< External interrupt line 9 */
#define EXTI_Line10 ((uint32_t)0x00400) /*!< External interrupt line 10 */
#define EXTI_Line11 ((uint32_t)0x00800) /*!< External interrupt line 11 */
#define EXTI_Line12 ((uint32_t)0x01000) /*!< External interrupt line 12 */
#define EXTI_Line13 ((uint32_t)0x02000) /*!< External interrupt line 13 */
#define EXTI_Line14 ((uint32_t)0x04000) /*!< External interrupt line 14 */
#define EXTI_Line15 ((uint32_t)0x08000) /*!< External interrupt line 15 */
#define EXTI_Line16 ((uint32_t)0x10000) /*!< External interrupt line 16 Connected to the PVD Output */
#define EXTI_Line17 ((uint32_t)0x20000) /*!< External interrupt line 17 Connected to the RTC Alarm event */
#define EXTI_Line18 ((uint32_t)0x40000) /*!< External interrupt line 18 Connected to the USB Device/USB OTG FS
Wakeup from suspend event */
#define EXTI_Line19 ((uint32_t)0x80000) /*!< External interrupt line 19 Connected to the Ethernet Wakeup event */
#define IS_EXTI_LINE(LINE) ((((LINE) & (uint32_t)0xFFF00000) == 0x00) && ((LINE) != (uint16_t)0x00))
#define IS_GET_EXTI_LINE(LINE) (((LINE) == EXTI_Line0) || ((LINE) == EXTI_Line1) || \
((LINE) == EXTI_Line2) || ((LINE) == EXTI_Line3) || \
((LINE) == EXTI_Line4) || ((LINE) == EXTI_Line5) || \
((LINE) == EXTI_Line6) || ((LINE) == EXTI_Line7) || \
((LINE) == EXTI_Line8) || ((LINE) == EXTI_Line9) || \
((LINE) == EXTI_Line10) || ((LINE) == EXTI_Line11) || \
((LINE) == EXTI_Line12) || ((LINE) == EXTI_Line13) || \
((LINE) == EXTI_Line14) || ((LINE) == EXTI_Line15) || \
((LINE) == EXTI_Line16) || ((LINE) == EXTI_Line17) || \
((LINE) == EXTI_Line18) || ((LINE) == EXTI_Line19))
2.2.1.2 EXTI_Mode:
可选:即通过配置EXTI_SWIER 寄存器位实现,模式选取可为:
/**
* @brief EXTI mode enumeration
*/
typedef enum
{
EXTI_Mode_Interrupt = 0x00,
EXTI_Mode_Event = 0x04
}EXTIMode_TypeDef;
2.2.1.3 EXTI_Trigger:
即通过配置EXTI_RTSR或者EXTI_FTSR来实现上升沿下降沿配置。
/**
* @brief EXTI Trigger enumeration
*/
typedef enum
{
EXTI_Trigger_Rising = 0x08,
EXTI_Trigger_Falling = 0x0C,
EXTI_Trigger_Rising_Falling = 0x10
}EXTITrigger_TypeDef;
#define IS_EXTI_TRIGGER(TRIGGER) (((TRIGGER) == EXTI_Trigger_Rising) || \
((TRIGGER) == EXTI_Trigger_Falling) || \
((TRIGGER) == EXTI_Trigger_Rising_Falling))
2.2.1.3 EXTI_LineCmd:
可选:即通过控制EXTI_IMR来实现中断线使能
typedef enum {DISABLE = 0, ENABLE = !DISABLE} FunctionalState;
2.3 EXTI 部分编写的库函数
/** @defgroup EXTI_Exported_Functions
* @{
*/
void EXTI_DeInit(void);
void EXTI_Init(EXTI_InitTypeDef* EXTI_InitStruct);
void EXTI_StructInit(EXTI_InitTypeDef* EXTI_InitStruct);
void EXTI_GenerateSWInterrupt(uint32_t EXTI_Line);
FlagStatus EXTI_GetFlagStatus(uint32_t EXTI_Line);
void EXTI_ClearFlag(uint32_t EXTI_Line);
ITStatus EXTI_GetITStatus(uint32_t EXTI_Line);
void EXTI_ClearITPendingBit(uint32_t EXTI_Line);
2.3.1 EXTI_DeInit(void)
@brief Deinitializes the EXTI peripheral registers to their default reset values.
将EXTI外设寄存器初始化为其默认重置值。
2.3.2 EXTI_Init(EXTI_InitTypeDef* EXTI_InitStruct)
@brief Initializes the EXTI peripheral according to the specified parameters in the EXTI_InitStruct.
根据EXTI_InitStruct中指定的参数初始化EXTI外设。
2.3.3 EXTI_StructInit(EXTI_InitTypeDef* EXTI_InitStruct)
@brief Fills each EXTI_InitStruct member with its reset value.
用重置值填充每个EXTI_InitStruct成员
2.3.4 EXTI_GenerateSWInterrupt(uint32_t EXTI_Line)
@brief Generates a Software interrupt.
产生一个软件中断。
2.3.5 FlagStatus EXTI_GetFlagStatus(uint32_t EXTI_Line)
@brief Checks whether the specified EXTI line flag is set or not.
检查是否设置了指定的EXTI行标志。
2.3.6 void EXTI_ClearFlag(uint32_t EXTI_Line)
@brief Clears the EXTI’s line pending flags.
清除EXTI的行挂起标志
2.3.7 ITStatus EXTI_GetITStatus(uint32_t EXTI_Line)
@brief Checks whether the specified EXTI line is asserted or not.
检查指定的EXTI行是否被断言
@retval The new state of EXTI_Line (SET or RESET).
EXTI_Line的新状态(SET or RESET).即检测中断是否发生。发生则retval SET
1.EXTI_GetITStatus 函数用来获取 EXTI 的中断标志位状态,如果 EXTI 线有中断发生函数返回“ SET”否则返回“ RESET”。实际上, EXTI_GetITStatus 函数是通过读取EXTI_PR 寄存器值来判断 EXTI 线状态的。
2.3.8 void EXTI_ClearITPendingBit(uint32_t EXTI_Line)
@brief Clears the EXTI’s line pending bits.
清除EXTI的行挂起位
执行任务后需要调用 EXTI_ClearITPendingBit 函数清除 EXTI 线的中断标志位。
1.用于清除中断标志位,即SET或者RESET
3.EXTI和NVIC的关系
4.编程实例:
4.1 通过EXTI配置按键中断,点亮led灯
4.1.1 EXTI 初始化结构体详解
typedef struct
{
uint32_t EXTI_Line; /*!< Specifies the EXTI lines to be enabled or disabled.
This parameter can be any combination 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 EXTIMode_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_Line: EXTI 中断/事件线选择,可选 EXTI0 至 EXTI19,可参考表 18-1 选择。
-
EXTI_Mode: EXTI 模式选择,可选为产生中断(EXTI_Mode_Interrupt)或者产生事件(EXTI_Mode_Event)。
-
EXTI_Trigger: EXTI 边沿触发事件,可选上升沿触(EXTI_Trigger_Rising)、下降 沿 触 发 ( EXTI_Trigger_Falling) 或 者 上 升 沿 和 下 降 沿 都 触 发( EXTI_Trigger_Rising_Falling)。
-
EXTI_LineCmd:控制是否使能 EXTI 线,可选使能 EXTI 线(ENABLE)或禁用(DISABLE)。
4.2 硬件设计
轻触按键在按下时会使得引脚接通,通过电路设计可以使得按下时产生电平变化,见图 18-1。 即原理为:配置要产生中断的GPIO引脚,初始化EXTI(即EXTI里面对应的寄存器进行编程,选择上升沿触发或者下降沿触发等),配置NVIC(即配置中断的优先级等),编写中断服务函数。
4.3 软件编写
4.3.1 初始化GPIO
4.3.1.1 查阅初始化GPIO所需要配置的项/寄存器
1.可以看到控制GPIO分别有GPIOx_CRL(端口配置低寄存器),用于配置GPIO的输入输出模式以及速率。端口输入数据寄存器(GPIOx_IDR)等,用于读,读出的值为对应I/O口的状态。等等
2.在ST公式的固件库中,已经将GPIO的寄存器控制的功能提取出来,封装成GPIO的模式配置枚举体,GPIO速度配置等等,在初始化时候提供选择,并将要配置的部分封装成初始化结构体,在stm32f103_gpio.h中如下:
配置速率的枚举体
/**
* @brief Output Maximum frequency selection
*/
typedef enum
{
GPIO_Speed_10MHz = 1,
GPIO_Speed_2MHz,
GPIO_Speed_50MHz
}GPIOSpeed_TypeDef;
#define IS_GPIO_SPEED(SPEED) (((SPEED) == GPIO_Speed_10MHz) || ((SPEED) == GPIO_Speed_2MHz) || \
((SPEED) == GPIO_Speed_50MHz))
配置模式的枚举体
/**
* @brief Configuration Mode enumeration
*/
typedef enum
{ GPIO_Mode_AIN = 0x0,
GPIO_Mode_IN_FLOATING = 0x04,
GPIO_Mode_IPD = 0x28,
GPIO_Mode_IPU = 0x48,
GPIO_Mode_Out_OD = 0x14,
GPIO_Mode_Out_PP = 0x10,
GPIO_Mode_AF_OD = 0x1C,
GPIO_Mode_AF_PP = 0x18
}GPIOMode_TypeDef;
GPIO引脚定义
/** @defgroup GPIO_pins_define
* @{
*/
#define GPIO_Pin_0 ((uint16_t)0x0001) /*!< Pin 0 selected */
#define GPIO_Pin_1 ((uint16_t)0x0002) /*!< Pin 1 selected */
#define GPIO_Pin_2 ((uint16_t)0x0004) /*!< Pin 2 selected */
#define GPIO_Pin_3 ((uint16_t)0x0008) /*!< Pin 3 selected */
#define GPIO_Pin_4 ((uint16_t)0x0010) /*!< Pin 4 selected */
#define GPIO_Pin_5 ((uint16_t)0x0020) /*!< Pin 5 selected */
#define GPIO_Pin_6 ((uint16_t)0x0040) /*!< Pin 6 selected */
#define GPIO_Pin_7 ((uint16_t)0x0080) /*!< Pin 7 selected */
#define GPIO_Pin_8 ((uint16_t)0x0100) /*!< Pin 8 selected */
#define GPIO_Pin_9 ((uint16_t)0x0200) /*!< Pin 9 selected */
#define GPIO_Pin_10 ((uint16_t)0x0400) /*!< Pin 10 selected */
#define GPIO_Pin_11 ((uint16_t)0x0800) /*!< Pin 11 selected */
#define GPIO_Pin_12 ((uint16_t)0x1000) /*!< Pin 12 selected */
#define GPIO_Pin_13 ((uint16_t)0x2000) /*!< Pin 13 selected */
#define GPIO_Pin_14 ((uint16_t)0x4000) /*!< Pin 14 selected */
#define GPIO_Pin_15 ((uint16_t)0x8000) /*!< Pin 15 selected */
#define GPIO_Pin_All ((uint16_t)0xFFFF) /*!< All pins selected */
配置GPIO初始化结构体
/**
* @brief GPIO Init structure definition
*/
typedef struct
{
uint16_t GPIO_Pin; /*!< Specifies the GPIO pins to be configured.
This parameter can be any value of @ref GPIO_pins_define */
GPIOSpeed_TypeDef GPIO_Speed; /*!< Specifies the speed for the selected pins.
This parameter can be a value of @ref GPIOSpeed_TypeDef */
GPIOMode_TypeDef GPIO_Mode; /*!< Specifies the operating mode for the selected pins.
This parameter can be a value of @ref GPIOMode_TypeDef */
}GPIO_InitTypeDef;
4.3.1.2 封装自己想要的功能
由于利用外部中断控制LED灯的亮灭,可以将控制LED的部分封装起来,以供使用。
4.3.1.2.1 bsp_led.h
1.由硬件连接知道控制PB0或者PB1或者PB5
2.GPIOB挂载在APB2上,
3.RCC寄存器开启APB2对应的外设时钟
RCC外设APB2下寄存器对应的封装
/** @defgroup APB2_peripheral
* @{
*/
#define RCC_APB2Periph_AFIO ((uint32_t)0x00000001)
#define RCC_APB2Periph_GPIOA ((uint32_t)0x00000004)
#define RCC_APB2Periph_GPIOB ((uint32_t)0x00000008)
#define RCC_APB2Periph_GPIOC ((uint32_t)0x00000010)
#define RCC_APB2Periph_GPIOD ((uint32_t)0x00000020)
#define RCC_APB2Periph_GPIOE ((uint32_t)0x00000040)
#define RCC_APB2Periph_GPIOF ((uint32_t)0x00000080)
#define RCC_APB2Periph_GPIOG ((uint32_t)0x00000100)
#define RCC_APB2Periph_ADC1 ((uint32_t)0x00000200)
#define RCC_APB2Periph_ADC2 ((uint32_t)0x00000400)
#define RCC_APB2Periph_TIM1 ((uint32_t)0x00000800)
#define RCC_APB2Periph_SPI1 ((uint32_t)0x00001000)
#define RCC_APB2Periph_TIM8 ((uint32_t)0x00002000)
#define RCC_APB2Periph_USART1 ((uint32_t)0x00004000)
#define RCC_APB2Periph_ADC3 ((uint32_t)0x00008000)
#define RCC_APB2Periph_TIM15 ((uint32_t)0x00010000)
#define RCC_APB2Periph_TIM16 ((uint32_t)0x00020000)
#define RCC_APB2Periph_TIM17 ((uint32_t)0x00040000)
#define RCC_APB2Periph_TIM9 ((uint32_t)0x00080000)
#define RCC_APB2Periph_TIM10 ((uint32_t)0x00100000)
#define RCC_APB2Periph_TIM11 ((uint32_t)0x00200000)
#define IS_RCC_APB2_PERIPH(PERIPH) ((((PERIPH) & 0xFFC00002) == 0x00) && ((PERIPH) != 0x00))
bsp_led.h 编写:
1.第一步,
#ifndef __BSP_LED_H
#define __BSP_LED_H
/**
内容块
**/
#endif /* __BSP_LED_H */
第二步:
由于控制GPIOB且为GPIOB0,由因为GPIOB挂载APB2,要打开APB2对应下的时钟,即进入RCC外设。又因为要使用那些封装好的外设,因此要包含stm32f10x.h
#ifndef __BSP_LED_H
#define __BSP_LED_H
#include "stm32f10x.h" //include
#define LED_G_GPIO_PIN GPIO_Pin_0 //LED 对应的引脚0
#define LED_G_GPIO_PORT GPIOB //LED 对应GPIOB
#define LED_G_GPIO_CLK RCC_APB2Periph_GPIOB //GPIOB的时钟开始,RCC外设
#endif /* __BSP_LED_H */
第三步,在bsp_led.c文件中会写LED的配置函数,在头文件中进行声明:
#ifndef __BSP_LED_H
#define __BSP_LED_H
#include "stm32f10x.h"
#define LED_G_GPIO_PIN GPIO_Pin_0
#define LED_G_GPIO_PORT GPIOB
#define LED_G_GPIO_CLK RCC_APB2Periph_GPIOB
void LED_GPIO_Config(void); //配置函数的声明
#endif /* __BSP_LED_H */
4.3.1.2.2 bsp_led.c
bsp_led.c里面要实现LED灯的配置即可,写一个配置函数,然后配置函数调用bsp_led.h里面定义的参数。
即实现LED灯的GPIO的初始化,利用固件库里GPIO_InitTypeDef
#include "bsp_led.h"
void LED_GPIO_Config(void) //定义配置函数
{
GPIO_InitTypeDef GPIO_InitStruct; //实例化GPIO初始化结构体
RCC_APB2PeriphClockCmd(LED_G_GPIO_CLK, ENABLE); //调用RCC库函数,开启时钟
GPIO_InitStruct.GPIO_Pin = LED_G_GPIO_PIN; //对GPIO进行初始化,赋值
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(LED_G_GPIO_PORT, &GPIO_InitStruct); //调用GPIO库中的函数,GPIO_Init进行初始化
}
4.3.1.2.3 bsp_exti.h
由于此处利用按键外部中断实现LED灯的亮灭,要用到EXTI外部中断控制线
同理可以看到按键的硬件结构为:
即对应的部分为PA0或者PC13。
第一步:
#ifndef __BSP_EXTI_H
#define __BSP_EXTI_H
#include "stm32f10x.h"
/**
内容块
**/
#endif /* __BSP_EXTI_H */
第二步:
比如要控制PA0引脚。则同理:
#ifndef __BSP_EXTI_H
#define __BSP_EXTI_H
#include "stm32f10x.h"
#define KEY1_INT_GPIO_PIN GPIO_Pin_0 //KEY1 对应的引脚0
#define KEY1_INT_GPIO_PORT GPIOA //KEY1 对应的引脚GPIOA
#define KEY1_INT_GPIO_CLK RCC_APB2Periph_GPIOA //时钟
#endif /* __BSP_EXTI_H */
第三步:要在bsp_exti.c中编写KEY1配置函数,则在对应头文件中进行声明。
#ifndef __BSP_EXTI_H
#define __BSP_EXTI_H
#include "stm32f10x.h"
#define KEY1_INT_GPIO_PIN GPIO_Pin_0
#define KEY1_INT_GPIO_PORT GPIOA
#define KEY1_INT_GPIO_CLK RCC_APB2Periph_GPIOA
void EXIT_Key_Config(void); //配置函数声明
#endif /* __BSP_EXTI_H */
4.3.1.2.3 bsp_exti.c
bsp_exti.c里面要实现KEY配置函数编写,先配置NVIC(结合NVIC的初始化的需要的内容来),然后GPIO初始化,EXTI初始化。
#include "bsp_exti.h"
static void EXTI_NVIC_Config(void) //静态使用,仅仅本文档作用域
{
NVIC_InitTypeDef NVIC_InitStruct;
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1);//配置优先级分组为1
NVIC_InitStruct.NVIC_IRQChannel = EXTI0_IRQn;//配置中断源为EXTI0_IRQn,由于走的是PA0,
NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 1;//在优先级分组为1下,设置抢占优先级为1
NVIC_InitStruct.NVIC_IRQChannelSubPriority = 1;//在优先级分组为1下,设置子优先级为1
NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE;//使能
NVIC_Init(&NVIC_InitStruct); //初始化
}
void EXIT_Key_Config(void) //EXTI按键配置
{
GPIO_InitTypeDef GPIO_InitStruct;
EXTI_InitTypeDef EXTI_InitStruct;
// 调用NVIC,配置中断优先级
EXTI_NVIC_Config();
// GPIO配置
RCC_APB2PeriphClockCmd(KEY1_INT_GPIO_CLK, ENABLE);
GPIO_InitStruct.GPIO_Pin = KEY1_INT_GPIO_PIN;
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IN_FLOATING;
GPIO_Init(KEY1_INT_GPIO_PORT, &GPIO_InitStruct);
// EXTI配置
RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE);//由于要选择选择EXTIx外部中断的输入源,因此要使能RCC_APB2Periph_AFIO时钟
GPIO_EXTILineConfig(GPIO_PortSourceGPIOA, GPIO_PinSource0);//选择EXTILine的输入源
EXTI_InitStruct.EXTI_Line = EXTI_Line0;
EXTI_InitStruct.EXTI_Mode = EXTI_Mode_Interrupt;
EXTI_InitStruct.EXTI_Trigger = EXTI_Trigger_Rising;
EXTI_InitStruct.EXTI_LineCmd = ENABLE;
EXTI_Init(&EXTI_InitStruct); //初始化EXTI
}
4.3.1.2.4 stm32f10x_it.c
1.编写中断服务函数,注意中断服务函数名是写死的,参考写法可以查找启动文件,下面外部中断均为对应的中断名。
2.中断函数的编写一般都放在stm32f10x_it.c函数中,固件库编写了一些空的中断函数放在这里,同时在stm32f10x_it.h进行声明,如下:
stm32f10x_it.h进行声明:
3.前面配置好了中断NVIC,EXTI和GPIO,即通过按键触发上升沿便会进入中断,中断里再进行控制。
编写中断服务函数,当进入中断则反转LED对应的引脚PB0,LED前面配置的PB0.
stm32f10x_it.c编写如下:
void EXTI0_IRQHandler (void)
{
if(EXTI_GetITStatus(EXTI_Line0) != RESET)
{
LED_G_GPIO_PORT->ODR ^= GPIO_Pin_0;//GPIOB—>ODR值
}
EXTI_ClearITPendingBit(EXTI_Line0);//清除中断标志位值
}
stm32f10x_it.h进行声明,如下:
void EXTI0_IRQHandler (void);
4.4实验结果
1.一开始按键未按下,灯为灭状态。
2.按下KEY1,则触发中断,进入中断服务函数。
3.执行中断服务函数,实现LED灯的反转,导致亮。