|
|
|
@ -106,7 +106,7 @@
|
|
|
|
|
|
|
|
|
|

|
|
|
|
|
|
|
|
|
|
### 外部中断
|
|
|
|
|
### 1.2.4. 外部中断
|
|
|
|
|
|
|
|
|
|
GPIO很多,中断资源有限这么办?复用,多个物理的GPIO公用一个中断向量。
|
|
|
|
|
|
|
|
|
@ -114,11 +114,170 @@ GPIO很多,中断资源有限这么办?复用,多个物理的GPIO公用一
|
|
|
|
|
|
|
|
|
|
[STM32中文参考手册](../../STM32官方手册/STM32中文参考手册.pdf)的131页的中断向量也可以看到。
|
|
|
|
|
|
|
|
|
|
# 2. HAL库的中断处理
|
|
|
|
|
# 2. HAL库的中断处理(p184)
|
|
|
|
|
|
|
|
|
|
## 2.1. HAL库的中断封装
|
|
|
|
|
## 2.1. 使用按键来转换LED的状态
|
|
|
|
|
|
|
|
|
|
## 2.2. 外部中断处理流程
|
|
|
|
|

|
|
|
|
|
|
|
|
|
|
使用Key1控制绿色LED(PA7),按一下开,再按一下关。
|
|
|
|
|
|
|
|
|
|
### 2.1.1. Stm32CubeMX配置
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
PA7 设置GPIO_Output,并设置label为LED;PB12设置GPIO_EXIT12,并设置label为BTN。
|
|
|
|
|
|
|
|
|
|

|
|
|
|
|
|
|
|
|
|
在 GPIO 设置中,配置PB12:
|
|
|
|
|
|
|
|
|
|

|
|
|
|
|
|
|
|
|
|
1. External Interrupt Mode with Falling edge trigger detection:外部中断,下降沿触发;
|
|
|
|
|
2. Pull up 可以不设置;
|
|
|
|
|
|
|
|
|
|
在NVIC中,使能 EXTI 10-15 的外部中断:
|
|
|
|
|
|
|
|
|
|

|
|
|
|
|
|
|
|
|
|
关闭并生成代码。
|
|
|
|
|
|
|
|
|
|
在main.c的大约55行加入代码:
|
|
|
|
|
|
|
|
|
|
```c
|
|
|
|
|
/* USER CODE BEGIN 0 */
|
|
|
|
|
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin){
|
|
|
|
|
HAL_GPIO_TogglePin(LED_GPIO_Port, LED_Pin);
|
|
|
|
|
}
|
|
|
|
|
/* USER CODE END 0 */
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
下载运行后,可以通过 Key1 控制绿色LED的亮灭。
|
|
|
|
|
|
|
|
|
|
> 为什么不用设置芯片内的上拉?
|
|
|
|
|
>
|
|
|
|
|
> 为什么使用上拉而不是下拉?
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
## 2.2. HAL库的中断封装
|
|
|
|
|
|
|
|
|
|
1. 设置中断触发条件。如外部中断的边沿触发,定时器更新中断的时间间隔。
|
|
|
|
|
2. 设置中断优先等级。系统中存在多个中断时,需要根据任务的紧迫程度,设置不同的中断优先级。
|
|
|
|
|
3. 使能外设的某个中断。微控制器片内集成的外设一般都有多个中断源,这些中断源的使能由外设的寄存器控制。例如,串口通信的发送数据寄存器空中断TXE和发送完成中断 TC 的使能,就是由串口控制寄存器 CR1中的 TXEIE 位和 TCIE 位控制(这个放到串口中讲解)。
|
|
|
|
|
4. 判断中断源。对于有多个中断源的外设,进入中断服务程序后,需要根据中断标志来判断具体发生了哪一种中断。
|
|
|
|
|
5. 清除中断标志。发生中断后,相应的中断标志位置位,以便 MCU 查询。这些中断标志要注意及时清除,避免重复进入中断。
|
|
|
|
|
6. 编写程序。用户根据实际项目的需求来编写中断发生时应该执行的操作。
|
|
|
|
|
|
|
|
|
|
在上述的六个中断编程步骤中,借助CubeMX软件可以通过图形化的设置方式完成前三个步骤,而后三个步骤可以借助 HAL,库提供的接口函数进一步简化。
|
|
|
|
|
|
|
|
|
|
CubeMX关于中断的文件有两个(P188)startup_stm32fXXXX.s和stm32f1xx_it.c 。
|
|
|
|
|
|
|
|
|
|
startup_stm32fXXXX.s 在我们的实验当中是 startup_stm32f103c8tx.s 在下图的位置:
|
|
|
|
|
|
|
|
|
|

|
|
|
|
|
|
|
|
|
|
该文件是汇编文件,建了中断向量表,并预先为每一个中断编写了中断服务程序。这些中断服务程序的内部都是死循环,不执行任何具体操作,其目的只是为了初始化中断向量表。中断服务程序的属性设置为“weak”,用户可以在用户文件中重新定义一个同名函数作为实际执行的中断服务程序。
|
|
|
|
|
|
|
|
|
|
```asm
|
|
|
|
|
.weak EXTI0_IRQHandler
|
|
|
|
|
.thumb_set EXTI0_IRQHandler,Default_Handler
|
|
|
|
|
|
|
|
|
|
.weak EXTI1_IRQHandler
|
|
|
|
|
.thumb_set EXTI1_IRQHandler,Default_Handler
|
|
|
|
|
|
|
|
|
|
.weak EXTI2_IRQHandler
|
|
|
|
|
.thumb_set EXTI2_IRQHandler,Default_Handler
|
|
|
|
|
|
|
|
|
|
.weak USART1_IRQHandler
|
|
|
|
|
.thumb_set USART1_IRQHandler,Default_Handler
|
|
|
|
|
|
|
|
|
|
.weak USART2_IRQHandler
|
|
|
|
|
.thumb_set USART2_IRQHandler,Default_Handler
|
|
|
|
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
注意,上面 weak 修饰的就是缺省的中断服务(一个函数),所有的中断服务都指向了一个缺省的实现:Default_Handler,其定义在该文件大约111行左右:
|
|
|
|
|
|
|
|
|
|
```asm
|
|
|
|
|
.section .text.Default_Handler,"ax",%progbits
|
|
|
|
|
Default_Handler:
|
|
|
|
|
Infinite_Loop:
|
|
|
|
|
b Infinite_Loop
|
|
|
|
|
.size Default_Handler, .-Default_Handler
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
这个函数的实现是一个死循环(汇编我忘了),不执行任何操作,目的是初始化中断向量(让中断向量指向一个函数)。
|
|
|
|
|
|
|
|
|
|
我们看看按钮如何工作的。
|
|
|
|
|
|
|
|
|
|
在 startup_stm32f103c8tx.s 的大约355行定义了一个函数:
|
|
|
|
|
|
|
|
|
|
```asm
|
|
|
|
|
.weak EXTI15_10_IRQHandler
|
|
|
|
|
.thumb_set EXTI15_10_IRQHandler,Default_Handler
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
这个函数是使用 weak 进行修饰的,接下来在 stm32f1xx_it.c 的 204 行:
|
|
|
|
|
|
|
|
|
|
```c
|
|
|
|
|
/**
|
|
|
|
|
* @brief This function handles EXTI line[15:10] interrupts.
|
|
|
|
|
*/
|
|
|
|
|
void EXTI15_10_IRQHandler(void)
|
|
|
|
|
{
|
|
|
|
|
/* USER CODE BEGIN EXTI15_10_IRQn 0 */
|
|
|
|
|
|
|
|
|
|
/* USER CODE END EXTI15_10_IRQn 0 */
|
|
|
|
|
HAL_GPIO_EXTI_IRQHandler(BTN_Pin);
|
|
|
|
|
/* USER CODE BEGIN EXTI15_10_IRQn 1 */
|
|
|
|
|
|
|
|
|
|
/* USER CODE END EXTI15_10_IRQn 1 */
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
注意:两个文件中都有 EXTI15_10_IRQHandler 这个函数,其实这在C语言中是不允许的(C语言是全局标识符,没有命名空间);因为第一个文件中的 EXTI15_10_IRQHandler 函数被标记成 weak,二个函数并没有标记成 weak,编译器在编译的时候会忽略第一个函数(有点像Java继承中的函数覆盖)。
|
|
|
|
|
|
|
|
|
|
继续看 HAL_GPIO_EXTI_IRQHandler 这个函数:
|
|
|
|
|
|
|
|
|
|
```c
|
|
|
|
|
/**
|
|
|
|
|
* @brief This function handles EXTI interrupt request.
|
|
|
|
|
* @param GPIO_Pin: Specifies the pins connected EXTI line
|
|
|
|
|
* @retval None
|
|
|
|
|
*/
|
|
|
|
|
void HAL_GPIO_EXTI_IRQHandler(uint16_t GPIO_Pin)
|
|
|
|
|
{
|
|
|
|
|
/* EXTI line interrupt detected */
|
|
|
|
|
if (__HAL_GPIO_EXTI_GET_IT(GPIO_Pin) != 0x00u)
|
|
|
|
|
{
|
|
|
|
|
__HAL_GPIO_EXTI_CLEAR_IT(GPIO_Pin);
|
|
|
|
|
HAL_GPIO_EXTI_Callback(GPIO_Pin);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
会发现其调用了 HAL_GPIO_EXTI_Callback 函数,就是我们在主程序中写的让LED反转的函数。在该函数的下面,有一个函数:
|
|
|
|
|
|
|
|
|
|
```c
|
|
|
|
|
__weak void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
|
|
|
|
|
{
|
|
|
|
|
/* Prevent unused argument(s) compilation warning */
|
|
|
|
|
UNUSED(GPIO_Pin);
|
|
|
|
|
/* NOTE: This function Should not be modified, when the callback is needed,
|
|
|
|
|
the HAL_GPIO_EXTI_Callback could be implemented in the user file
|
|
|
|
|
*/
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
看到了吗?这个函数是自动生成的,也是 weak 修饰的。因此我们在main.c 中写的同名函数其实是覆盖了这个函数。
|
|
|
|
|
|
|
|
|
|
> 仔细分析 HAL_GPIO_EXTI_IRQHandler 函数中还做了什么事情?
|
|
|
|
|
|
|
|
|
|
## 2.3. 外部中断处理流程
|
|
|
|
|
|
|
|
|
|
# 3. 外部中断的HAL库定义
|
|
|
|
|
|
|
|
|
|