STM32LL库系列教程(四)——外部中断

 **开发平台**  :  
 正点原子  探索者STM32F407ZET6
 cube mx:V 5.1.0
 keil uVision5

项目地址:https://github.com/liliang1918/STM32LL

注意:
本教程默认读者已经对stm32和cubemx软件有一定了解;
为节省篇幅,详细配置cubemx的步骤仅在第一篇中说明,以后仅会简略介绍配置相关内容。
STM32LL库系列教程(四)——外部中断
实验目的:学会GPIO_IDR寄存器操作;
		 学会EXIT_PR寄存器操作;
		 学会外部中断的原理(重点);
		 了解按键消抖
实验现象:按下按键KEY_UP  LED1翻转一次(红灯);
		 按下KEY2 LED2翻转一次(黄灯)

文章目录

        • 1. 配置cubemx
          • 1.1 配置LED
          • 1.2 配置按键
          • 1.3 配置中断优先级
        • 2.编写代码
          • 2.1 main.c
          • 2.2 stm32f4xx_it.c
        • 3.相关寄存器介绍
          • 3.1 GPIO 端口输入数据寄存器 (GPIOx_IDR) (x = A..I)
          • 3.2 挂起寄存器 (EXTI_PR)
        • 4. 补充知识点
          • 4.1 NVIC(嵌套向量中断控制器)
            • 4.1.1 中断分组
            • 4.1.2 抢占优先级与响应优先级
            • 4.1.3 cubemx配置的解释
            • 4.1.4 中断向量表
          • 4.2 外部中断过程
            • 4.2.1 外部中断/事件线映射
            • 4.2.2 外部中断过程
          • 4.3 按键消抖
            • 4.3.1 概念
            • 4.3.2 硬件消抖
            • 4.3.3 软件消抖

1. 配置cubemx

1.1 配置LED

板载的LED是PF9,PF10引脚,配置为Output即可
STM32LL库系列教程(四)——外部中断_第1张图片

1.2 配置按键

配置GPIO为EXIT
STM32LL库系列教程(四)——外部中断_第2张图片

注意:如下原理图
WK_UP(PA0)按下按键会接到3.3V,我们配置中断类型为上升沿中断,内部下拉,当按下按键后,GPIO电平由低变高,触发上升沿中断
KEY0(PE2)按下会接到地,我们配置中断类型为下降沿中断,内部上拉,当按下按键后,GPIO电平由高变低,触发下降沿中断

STM32LL库系列教程(四)——外部中断_第3张图片
STM32LL库系列教程(四)——外部中断_第4张图片

1.3 配置中断优先级

这里就选用系统默认的优先级了,不做修改,具体含义在后面再具体解释
STM32LL库系列教程(四)——外部中断_第5张图片

2.编写代码

2.1 main.c

因为只对中断进行响应和操作,在这里对main函数中的while(1)中是不需要编写代码的。
STM32LL库系列教程(四)——外部中断_第6张图片

2.2 stm32f4xx_it.c

stm32f4xx_it.c是存放各类中断服务函数的文件。

首先,对EXIT0的(PA0),因为正点原子的开发板并没有涉及硬件防抖,因此我们需要软件进行按键防抖,否侧,会导致按一次按键,会进入多次中断的问题。读者也可以把按键防抖的程序去掉再看一看效果如何。

EXTI0_IRQHandler(void)函数中,LL库自身做了判断、清除标志位的操作,保证了程序不会一直卡在中断服务函数中

void EXTI0_IRQHandler(void)
{
  /* USER CODE BEGIN EXTI0_IRQn 0 */
	LL_mDelay(10);
	if(LL_GPIO_IsInputPinSet(GPIOA,LL_GPIO_PIN_0)==SET)//按键消抖
			LL_GPIO_TogglePin(GPIOF,LL_GPIO_PIN_9);//翻转LED1
  /* USER CODE END EXTI0_IRQn 0 */
  if (LL_EXTI_IsActiveFlag_0_31(LL_EXTI_LINE_0) != RESET)//检验EXIT0的标志位
  {
    LL_EXTI_ClearFlag_0_31(LL_EXTI_LINE_0);//清除EXIT0的标志位
    /* USER CODE BEGIN LL_EXTI_LINE_0 */
		
    /* USER CODE END LL_EXTI_LINE_0 */
  }
  /* USER CODE BEGIN EXTI0_IRQn 1 */

  /* USER CODE END EXTI0_IRQn 1 */
}

同理对 EXTI2_IRQHandler(void)
STM32LL库系列教程(四)——外部中断_第7张图片
自此,程序编写完成。程序可以下到开发板,观察现象了。

3.相关寄存器介绍

3.1 GPIO 端口输入数据寄存器 (GPIOx_IDR) (x = A…I)

在第二节中,我们介绍了GPIOx_ODR,这个是操作输出端口的,而GPIOx_IDR就是读取输入端口电平状态的
STM32LL库系列教程(四)——外部中断_第8张图片
我们通过调用LL_GPIO_IsInputPinSet这个函数来读取GPIO的输入,在按键消抖程序中,通过10ms后的按键状态,即可判断是真实按下,还是抖动。

__STATIC_INLINE uint32_t LL_GPIO_IsInputPinSet(GPIO_TypeDef *GPIOx, uint32_t PinMask)
{
  return (READ_BIT(GPIOx->IDR, PinMask) == (PinMask));
}
3.2 挂起寄存器 (EXTI_PR)

在进入中断服务函数 EXTI2_IRQHandler(void)后,LL库会自行提供函数LL_EXTI_IsActiveFlag_0_31(LL_EXTI_LINE_0)判断是否是由外部中断线0引发的中断并清除标志位,对这两个函数,他们都是操作挂起寄存器 (EXTI_PR) 来实现对应的功能的。
STM32LL库系列教程(四)——外部中断_第9张图片
首先是LL_EXTI_IsActiveFlag_0_31(LL_EXTI_LINE_0)

__STATIC_INLINE uint32_t LL_EXTI_IsActiveFlag_0_31(uint32_t ExtiLine)
{
  return (READ_BIT(EXTI->PR, ExtiLine) == (ExtiLine));
}

若PR0 =1,则中断线0,发生了相应的边沿事件,进一步确认中断的发生。

对于LL_EXTI_ClearFlag_0_31(LL_EXTI_LINE_0);

__STATIC_INLINE void LL_EXTI_ClearFlag_0_31(uint32_t ExtiLine)
{
  WRITE_REG(EXTI->PR, ExtiLine);
}

在确认中断线发生中断后,再对相应位写1,即可清除。

因为 NVIC 的这些寄 存器都是写 1 有效的,写 0 是无效的。
写1清除不太符合正常逻辑,记住即可

4. 补充知识点

4.1 NVIC(嵌套向量中断控制器)

本部分内容参考了正点原子F4开发指南的 5.2.6 节 中断管理函数

4.1.1 中断分组

STM32F4 将中断分为 5 个组,组 0~4。该分组 的设置是由 SCB->AIRCR 寄存器的 bit10~8 来定义的。具体的分配关系如表 5.2.6.1 所示:
STM32LL库系列教程(四)——外部中断_第10张图片

4.1.2 抢占优先级与响应优先级

抢占优先级的 级别高于响应优先级。而数值越小所代表的优先级就越高
这里需要注意两点:
第一,如果两个中断的抢占优先级和响应优先级都是一样的话,则看 哪个中断先发生就先执行;
第二,高优先级的抢占优先级是可以打断正在进行的低抢占优先级 中断的。而抢占优先级相同的中断,高优先级的响应优先级不可以打断低响应优先级的中断。

4.1.3 cubemx配置的解释

STM32LL库系列教程(四)——外部中断_第11张图片
对于文章一开始对NVIC的配置,在这里就可以解释一下了。CUBEMX默认选择4位抢占优先级,0位响应优先级,因此,抢占优先级的范围是0-15,数字越小,优先级越高,响应优先级可以配置为0-1;默认所有中断的抢占优先级都是0(最高),都不能互相打断。

4.1.4 中断向量表

为了严谨,在这里补充一个不常遇到的情况——抢占优先级和响应优先级相同,在这时中断顺序由中断向量表确定。
而中断向量表可以由stm32f4xx中文参考手册中查得:
STM32LL库系列教程(四)——外部中断_第12张图片

篇幅有限,后续就不添加了,详细见参考手册 P234

4.2 外部中断过程
4.2.1 外部中断/事件线映射

以本程序为例,正是PA0映射在EXIT0上,所以触发的是EXIT0中断
STM32LL库系列教程(四)——外部中断_第13张图片

4.2.2 外部中断过程

整体的过程就是:首先main函数主程序中一直在while循环里面执行。当按键(PA0引脚)按下时,边沿检测电路检测到下降沿,触发中断,设置中断标识位。NVIC中断控制器判断EXTI0中断优先是否为最高,若为最高优先级则执行EXIT0中断。
STM32LL库系列教程(四)——外部中断_第14张图片
在执行中断服务函数之前,Contex-M4内核先将现在使用到的寄存器和主程序中断点的地址压入堆栈(保护现场)。然后程序在中断向量表中找到EXTI0中断对应的地址(0x0000 0058)。这个地址存储的为EXTI0中断服务函数的入口地址。然后程序转跳到中断服务函数执行。

如下,即为上文提到的中断向量表,EXTI0中断对应的地址为0x0000 0058

STM32LL库系列教程(四)——外部中断_第15张图片
在stm32f407的启动文件startup_stm32f407xx.s中,我们就可以找到对应的中断向量表
STM32LL库系列教程(四)——外部中断_第16张图片
在上面这张表中我们可以看到地址0x0000 0000保存的为栈顶的地址。0x0000 0004地址保存复位中断服务函数的地址。第22个中断为EXTI0中断,对应的地址为 22x4,即0x0000 0058。

正是这个设定的存在,才让EXIT0的中断服务函数,直接对应上了EXTI0_IRQHandler 这个函数

执行完中断服务函数后。内核从堆栈去除压入的寄存器数据恢复现场,取出主程序中断点的地址,转到到主程序中断点的地址继续运行主程序。

到这里就完成一次中断服务。简单的来说,触发中断时,硬件先标识中断标识位,然后NVIC中断控制器判断中断优先级是否可以执行此中断。执行中断时,先保护现在程序的状态,将数据保存进堆栈中,执行完中断服务函数后,再在堆栈中取数据回复现场,跳回主程序继续运行如果在中断服务函数中有更高优先级的中断触发,则也会将现在的数据保存去执行更高优先级的中断服务函数,即中断嵌套。高优先级的中断执行完后,恢复现场,继续执行低优先级的服务函数。

4.3 按键消抖
4.3.1 概念

按键波形的理想与实际
STM32LL库系列教程(四)——外部中断_第17张图片

按键消抖通常的按键所用开关为机械弹性开关,当机械触点断开、闭合时,由于机械触点的弹性作用,一个按键开关在闭合时不会马上稳定地接通,在断开时也不会一下子断开。因而在闭合及断开的瞬间均伴随有一连串的抖动,为了不产生这种现象而作的措施就是按键消抖。
—— 百度百科

而在抖动状态时,如果不采取一定措施,就会被单片机读到,错误的以为是按键被反复按下抬起,从而引起错误

4.3.2 硬件消抖

并联10NF电容,改善波形
STM32LL库系列教程(四)——外部中断_第18张图片

4.3.3 软件消抖

软件消抖的原理通常是,当检测到按键按下后,隔10ms再去读按键是否还是按下的状态,如果是,就可以确认按键确实被按下了。
缺点:占用单片机资源,如果是在中断服务函数中,10ms是一个很长的时间了。

LL_mDelay(10);
	if(LL_GPIO_IsInputPinSet(GPIOA,LL_GPIO_PIN_0)==SET)//按键消抖
			LL_GPIO_TogglePin(GPIOF,LL_GPIO_PIN_9);//

你可能感兴趣的:(STM32LL库)