中断原理图
很多情况可以产生中断,开发者可以在CubeMx中NVIC选单、通讯模块选单处配置。请自行探索;部分中断有关的内容在模块讲解时·将会提到。
※启用中断后,注意在System Core->NVIC->Code Generation中勾选生成IRQ Handler和 Select for init Sequence Ordering。
※生成的中断函数定义在stm32fxxx_it.h和stm32fxxx_it.c中,向函数中填要执行的代码即可。
对于外部中断的事件分配,多个引脚将被分配到中断事件(具体可以参考文档),若需要区分则须在中断程序内写判断。判断方式之后给到
配置引脚外部中断的方式:
①在引脚图上选中需要配置的引脚,选择中断功能(如图)
②在NVIC选单中使能中断
③在NVIC->Code Generation中勾选生成IRQ Handler和 Select for init Sequence
勾选Select for init Sequence是个人习惯,这样可以在main.c中生成独立初始化函数
④在GPIO->GPIO配置中断模式(如图)
可以配置上/下拉电阻(上拉、下拉、浮空)和中断触发模式。
⑤生成代码。(stm32fxxx_it.c中,配置为Interrupt Mode)
void EXTI15_10_IRQHandler(void)
{
/* USER CODE BEGIN EXTI15_10_IRQn 0 */
/* USER CODE END EXTI15_10_IRQn 0 */
/* USER CODE BEGIN EXTI15_10_IRQn 1 */
/* USER CODE END EXTI15_10_IRQn 1 */
}
外部触发类型与中断处理:
一、触发类型
上升沿触发、下降沿触发、上升/下降沿触发(电平变化触发)
二、中断处理※
分为中断模式(Interrupt Mode) 和 事件模式(Event Mode) 两种
中断模式即为普通中断,其概念不多解释。配置成中断模式时,stm32fxxx_it库中会自动生成中断句柄函数。
※ 事件模式 。(很少用)
参考本文最前面提供的中断原理图。在配置为事件模式时,中断信号将传输给图中的Pulse Generator而非中断控制器。
虽然中断和事件的硬件信号产生源相同,但是配置为事件时将不会发生挂起,也就是说事件过程不需CPU的参与,可以与主程序并行: 事件机制提供了一个完全由硬件自动完成的触发到产生结果的通道,不要软件的参与,降低了CPU的负荷,节省了中断资源,提高了响应速度(硬件总快于软件)。
※事件模式虽然有优势,但在实际工程中几乎不会有人使用。不讲了。之后的代码默认使用中断模式配置。
配置外部中断需要三步: 1.配置GPIO与中断线(Input Line)连接 2.中断线配置 3.中断向量配置
结合代码:
Pin:PC13
Mode:Interrupt; Rising edge Trigger detection
Pull: Floating
在gpio.c :
/*连接引脚与中断线(EXTICRn)*/
LL_GPIO_AF_SetEXTISource(LL_GPIO_AF_EXTI_PORTC, LL_GPIO_AF_EXTI_LINE13);
/*配置中断线*/
EXTI_InitStruct.Line_0_31 = LL_EXTI_LINE_13; //配置触发源的连接
EXTI_InitStruct.LineCommand = ENABLE;
EXTI_InitStruct.Mode = LL_EXTI_MODE_IT;
EXTI_InitStruct.Trigger = LL_EXTI_TRIGGER_RISING;
LL_EXTI_Init(&EXTI_InitStruct);
/*引脚GPIO设置*/
LL_GPIO_SetPinMode(GPIOC, LL_GPIO_PIN_13, LL_GPIO_MODE_FLOATING);
/* EXTI interrupt init*/
NVIC_SetPriority(EXTI15_10_IRQn, NVIC_EncodePriority(NVIC_GetPriorityGrouping(),1, 0));
NVIC_EnableIRQ(EXTI15_10_IRQn);
※关于LL_EXTI_LINE_x:
x代表的是中断线;中断线(LINEx) 是中断信号的输入硬件。
Line连接GPIO,例如LINE13可接PA13、PB13、PC13....。可以将引脚与中断线理解为短接。中断线同时只能连接一个引脚(LINEx只能连接PAx,PBx,PCx,PDx...其一)。 来自Input Line的信号输入触发中断程序的调用
※中断程序结束时别忘清除中断位挂起位
LL API分析:
Input Line引脚输入配置
stm32fxxx_ll_gpio.h中:
/**/
__STATIC_INLINE void LL_GPIO_AF_SetEXTISource(uint32_t Port, uint32_t Line);/*
使能中断引脚 Pnx;通过此函数将使引脚连接相应的EXTI_LINE_x。
@param Port =LL_GPIO_AF_EXTI_PORTn
@param Line =LL_GPIO_AF_EXTI_LINEx
※每次只能配置一个引脚 Pnx; Pnx 将连接EXTI_LINE_x
*/
__STATIC_INLINE uint32_t LL_GPIO_AF_GetEXTISource(uint32_t Line);/*
返回连接在EXTI_LINEx上的引脚信息(端口号)。
@param Line =LL_GPIO_AF_GPIO_LINEx
@retval:
LL_GPIO_AF_GPIO_PORTn
*/
EXTI_LINE配置结构体LL_EXTI_InitTypeDef
typedef struct
{
uint32_t Line_0_31;/*
EXTI_LINE选择; Line_0_31= LL_EXTI_LINE_x
*/
FunctionalState LineCommand; /*
使能位。LineCommand= ENABLE
*/
uint8_t Mode;/*
模式选择; Mode= LL_EXTI_MODE_IT 中断模式
LL_EXTI_MODE_EVENT 事件模式
LL_EXTI_MODE_IT_EVENT 中断&事件模式
*/
uint8_t Trigger;/*
触发模式选择; Trigger = LL_EXTI_TRIGGER_NONE 无触发
LL_EXTI_TRIGGER_RISING 上升沿触发
LL_EXTI_TRIGGER_FALLING 下降沿触发
LL_EXTI_TRIGGER_RISING_FALLING 上升/下降均触发
*/
} LL_EXTI_InitTypeDef;
初始化函数LL_EXTI_Init()
ErrorStatus LL_EXTI_Init(LL_EXTI_InitTypeDef *EXTI_InitStruct)/*
初始化并启用中断EXTI_LINEx。操作成功返回SUCCESS
*/
重设中断结构体
void LL_EXTI_StructInit(LL_EXTI_InitTypeDef *EXTI_InitStruct)
{
EXTI_InitStruct- >Line_0_31 = LL_EXTI_LINE_NONE;
EXTI_InitStruct- >LineCommand = DISABLE;
EXTI_InitStruct- >Mode = LL_EXTI_MODE_IT;
EXTI_InitStruct- >Trigger = LL_EXTI_TRIGGER_FALLING;
}
•
软件中断触发函数(模拟外部触发)
__STATIC_INLINE void LL_EXTI_GenerateSWI_0_31(uint32_t ExtiLine)
{//ExtiLine = LL_EXTI_LINE_x;触发Line_x对应的中断处理函数;效果与外部触发相同
SET_BIT(EXTI- >SWIER, ExtiLine);
}//软件触发寄存器SWIER改变可以通过清零挂起位PR清除
中断线判断函数:
__STATIC_INLINE uint32_t LL_EXTI_IsActiveFlag_0_31(uint32_t ExtiLine);/*
@param ExtiLine =LL_EXTI_LINE_x
当ExtiLine是引发中断函数的中断线时,返回值为!RESET ;反之返回值RESET (=0)*/
挂起位清除函数:
清除函数挂起位,使能下一次中断唤起
__STATIC_INLINE void LL_EXTI_ClearFlag_0_31(uint32_t ExtiLine);/*
@param ExtiLine =LL_EXTI_LINE_x
清除中断挂起位;若中断是由软件模拟外部触发实现的,清除时一并将SWIER位清除
*/
使用案例
void EXTI15_10_IRQHandler(void)
{
LL_mDelay(10);//按键消抖,防止多次触发(一置位PR即可能再次触发)
if (LL_EXTI_IsActiveFlag_0_31(LL_EXTI_LINE_13) != RESET)//当中断由EXTI_LINE_13引起
{
/* USER CODE BEGIN LL_EXTI_LINE_13 */
LL_EXTI_ClearFlag_0_31(LL_EXTI_LINE_13); //清除标志位
LL_GPIO_TogglePin(GPIOA, LL_GPIO_PIN_15); //翻转LED
/* USER CODE END LL_EXTI_LINE_13 */
}
if (LL_EXTI_IsActiveFlag_0_31(LL_EXTI_LINE_15) != RESET)//当中断由EXTI_LINE_15引起
{
LL_EXTI_ClearFlag_0_31(LL_EXTI_LINE_15); //清除标志位
}
}