外部中断实验
正点原子开发板实验学习复盘_CumeMX生成代码问题总结——外部中断实验
前言:本篇幅只作简易的温习与记录,方便日后回顾。故大体以模块和流程为主,具体的实现细节还是要看官方的开发指南和参考手册,以便更详细的了解相关内容。若读者同样有CumeMX复刻课堂中相应功能的习惯,那我遇到的问题和解决方案,您可以参考参考。
一、基本概念
1.1.什么是中断
打断CPU执行正常的程序,转而处理紧急程序,然后返回原暂停的程序继续运行,就叫中断。
1.2中断的意义
高效处理紧急程序,不会一直占用CPU资源。
1.3.1NVIC的基本概念
嵌套向量中断控制器(Nested vectored interrupt controller),属于内核(M3/4/7),中断向量表定义了一块固定的内存,以4字节对齐,存放各个中断服务函数程序的首地址。当发生中断时,当发生中断,CPU会自动执行对应的中断服务函数(中断向量表定义在启动文件)。
1.2.2NVIC的作用
高效地处理来自外设(如GPIO、USART、TIM等)或软件的中断请求,并决定它们的执行优先级。
1.3.3VNIC的工作原理

1.4.1EXTI的基本概念
外部(扩展)中断事件控制器(External(Extended) interrupt/event Controller),包含20个产生事件/中断请求的边沿检测器,即总共:20条EXTI线(F1)。而中断和事件的本质区别是,中断要进入NVIC,有相应的中断服务函数,需要CPU处理。而事件不进入NVIC,仅用于内部硬件自动控制,如:TIM、DMA、ADC。
1.4.2EXTI的主要特性
每条EXTI线都可以单独配置:选择类型(中断或者事件)、触发方式(上升沿,下降沿或者双边沿触发)、支持软件触发、开启/屏蔽、有挂起状态位
1.4.3EXTI的工作原理

从EXTI 功能框图可以看到有两条主线,一条是由输入线到NVIC中断控制器,一条是由 输入线到脉冲发生器。这就恰恰是EXTI 的两大部分功能,产生中断与产生事件,两者从硬件 上就存在不同。
1.5AFIO简介
Alternate Function IO,即复用功能IO,一般情况下,外设引脚重映射(如USART、SPI)、EXTI使用非默认GPIO、禁用JTAJ/SWD以释放应交、事件输出、复用功能冲突等,都需要AFIO。(在使用时,一定要切记打开AFIO的时钟)。
1.5.2EXTI和IO口的关系
需要注意的是,一个中断号(EXTIx)只能对应一个系列的引脚编号(Px0),一旦配置重复,外部中断将无法使用。

二、使用流程
2.1如何使用中断
由图可知,外设中断是无需设置EXTI和IO映射关系的,这是因为,EXTI是专门用于检测GPIO引脚电平变化的独立模块,需要明确指定哪个GPIO引脚连接到哪条EXTI线(如PA0→EXTI0,PB0→EXTI0等)。由于一个EXTI线(如EXTI0)可以映射到多个GPIO(PA0、PB0、PC0等),因此需要通过AFIO的GPIO_EXTILineConfig()函数明确选择信号来源。

2.2EXTI的配置步骤
总结下来就是分为了四步,2-5在配置GPIO模式的时候就已经一步到位配齐了,第六步主要是设置优先级、使能中断。设置优先级分组只用设置一次,例程中已将在stm321xx_hal.c中设置。所以无需再重复设置。

2.3EXTI的HAL库设置步骤(外部中断)

2.4HAL库中断回调处理机制介绍

中断服务函数:可以在startup_stm32f103xe.s中找到相应的函数,例如EXTI4_IRQHandler。
HAL库中断处理公用函数:HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_4);
HAL库数据处理回调函数:void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin){};
三、战舰板CubeMX编程实战
这个配置我改了一下,用的是PE3(KEY1)控制LED1的翻转,视频讲解用的是KEY0,原理是一样的。

3.1RCC配置
都是选择的外部晶振

3.2SYS配置
预留一个调试接口,方便下载和调试。

3.3 GPIO配置



3.4 Clock配置

3.5 Projeck配置
Project Name和Project Location不要用中文,会报错。

3.6 Code Generator 配置

3.7 生成代码
四、实验中遇到的问题
1.实验中AFIO为什么没有配置?
流程途中写到,要配置AFIO,但是课程实验以及CubeMX都没有提到相关配置和步骤,我百思不得其解,后来翻阅资料发现,
HAL_GPIO_Init()已经内部处理了EXTI与GPIO的绑定,无需额外操作。所以这个步骤可以忽略。
2.生成的代码为什么卡在中断中无法执行?
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{
delay_ms(100); //CubeMX生成的代码延时是HAL_Delay(100);
if(GPIO_Pin == GPIO_PIN_3&& HAL_GPIO_ReadPin(GPIOE,GPIO_PIN_3) == 0)
{
HAL_GPIO_TogglePin(GPIOB,GPIO_PIN_5);
}
}
-
阻塞式实现:
HAL_Delay()内部通过循环查询HAL_GetTick()的计数值,直到达到目标时间。它依赖SysTick中断来更新计数值。 HAL库实现延时功能非常简单,首先定义了一个32位全局变量uwTick,在Systick中断 服务函数SysTick_Handler中通过调用HAL_IncTick实现uwTick值不断增加,也就是每隔1ms 增加uwTickFreq,而uwTickFreq默认是1。而HAL_Delay函数在进入函数之后先记录当前 uwTick的值,然后不断在循环中读取uwTick当前值,进行减运算,得出的就是延时的毫秒数, 整个逻辑非常简单也非常清晰。 但是,HAL库的延时函数在中断服务函数中使用HAL_Delay会引起混乱(虽然一般禁止在 STM32F103 开发指南 正点原子战舰STM32F103开发板教程 中断中使用延时函数),因为它是通过中断方式实现,而Systick 的中断优先级是最低的,所以 在中断中运行HAL_Delay会导致延时出现严重误差。所以教程中推荐大家使用正点原子提供的 延时函数库。
-
中断优先级问题:
如果在更高优先级的中断中调用
HAL_Delay(),SysTick中断(默认优先级较低)无法触发,导致HAL_GetTick()的计数值永远不更新,函数无法退出。 例如:在优先级为1的中断中调用
HAL_Delay(),而SysTick的优先级为2,此时SysTick被阻塞。 -
解决方案:
使用正点原子给的延时函数实现延时,但是一定要在main函数中初始化延时函数,否者无法实现功能。
解决方案,在回调函数中,除了要判断是哪一个中断外,还要再读取一下中断引脚的电平状态,否则会有逻辑错误,实验达不到预想的效果(灯珠会随着按键的抖动跳变)。
if(GPIO_Pin == GPIO_PIN_3&& HAL_GPIO_ReadPin(GPIOE,GPIO_PIN_3) == 0)
{
HAL_GPIO_TogglePin(GPIOB,GPIO_PIN_5);
}
更多推荐



所有评论(0)