STM32开发(中断模式:外部中断、串口中断)

目录

一、处理器的工作模式

1、异常源

2、异常向量表

二、异常处理流程

1、自动保存现场(CPU自动完成)

2、手动返回现场(程序员手动完成)

3、ARM汇编代码模拟异常事件处理流程

三、分析芯片手册(EXTI、NVIC)

1、外部中断章节介绍(EXTI)

1)输入事件编号

2)EXTI的主要特点

3)EXTI框图分析

4)寄存器功能分析

5)GPIO端口选择

6)寄存器分析

(1)EXTI_FTSR1寄存器(下降沿触发寄存器)

(2)EXTI_FPR1寄存器(下降沿触发标志寄存器)

(3)EXTI_EXTICRm寄存器(GPIO端口引脚选择寄存器)

(4)EXTI_IMR1寄存器(输入事件频闭寄存器)

2、中断嵌套向量章节介绍(NVIC)

1)NVIC的主要特点

2)中断号

四、标准库实现外部中断

1、标准库初始化外部中断

2、标准库代码

五、HAL库实现外部中断

1、STM32CudeMX配置初始化

1)配置为外部中断触发

2)使能NVIC下的EXTI的中断信号

3)配置中断触发的优先级

2、STM32CudeMX初始化代码

1)异常处理流程

2)API接口(异常处理函数)

3)示例代码

六、标准库配置串口中断

1、标准库初始化串口中断

2、标准库操作串口中断

七、HAL库配置串口中断

1、串口中断执行流程

2、串口发送中断(不常用)

1)API接口函数

(1)串口发送中断执行函数

(2)串口发送中断回调函数

2)示例代码

3)代码效果

3、串口接收中断(经常用)

1)API接口函数

(1)串口接收中断启用函数

(2)串口接收中断回调函数

2)示例代码

3)代码效果

4、串口空闲中断(经常用)

1)USART_CR1寄存器

2)USART_ISR寄存器

3)USART_ICR寄存器

4)串口空闲中断的工作原理

5)API接口函数

(1)__HAL_UART_ENABLE_IT(使能中断/状态标志位)

(2)__HAL_UART_GET_FLAG(获取中断/状态标志位)

(3)__HAL_UART_CLEAR_FLAG(清楚中断/状态标志位)

6)串口句柄对象提供的成员变量

5、实现串口的实时不定长接收(示例代码)


一、处理器的工作模式

复制代码
Cortex-M核的工作模式:
    异常模式        线程模式

Cortex-A核的工作模式:
    ARM-v7架构设计的A核的工作模式:
        非特权模式:    User(用户模式)
        特权模式:    
            1.非异常模式:Sys
            2.异常模式
                IRQ模式(普通中断模式)
                FIQ模式(快速中断模式)
                SVC模式(超级管理员模式)
                ABT模式(中止访问模式)
                UDF模式(未定义异常模式)
    ARM-v8架构设计的A核的工作模式:
            在v7架构的基础上多以下两个工作模式:
                MON(电源管理模式)
                HYP(虚拟化技术模式)

1、异常源

异常源是指触发处理器执行异常事件对应的处理逻辑的源头
异常源是异常事件的集合
异常源下支持很多异常事件

|-------|---------------------|-------------------------|
| 异常模式 | 异常源 | 异常源下的异常事件 |
| IRQ模式 | 一系列触发处理器进入IRQ模式的异常源 | 普通中断,定时器中断 |
| FIQ模式 | 一系列触发处理器进入FIQ模式的异常源 | 内核中断,高优先级中断 |
| SVC模式 | 复位异常源 | 上电复位,按键复位 |
| SVC模式 | 软中断异常源 | swi执行软中断指令 |
| ABT模式 | 数据终止访问异常源 | 非法访问或使用没有权限的数据(段错误) |
| ABT模式 | 指令终止访问异常源 | 汇编指令格式错误、 汇编指令访问没有权限的空间 |
| UDF模式 | 未定义异常源 | 指令/变量未被定义过 |

复制代码
1、5种异常模式对应七种异常源
2、每种异常源下存在很多的异常事件
3、SVC模式下的复位异常源的优先级等级最高,处理器优先处理

2、异常向量表

复制代码
异常向量表:就是异常源的集合
当产生异常事件后,处理器会检测当前这个异常事件属于哪个异常源
此时处理器会根据异常源去异常向量表中对应的地址,查找对应的异常源中对应的异常事件

注意:
    1、处理器只会根据异常向量表中固定的地址查找异常源,异常源地址是不能随意变化的
    2、异常向量表共占内存空间32字节,每个异常源占用4字节存放异常源地址,保留了4字节作为预留空间

为什么需要引入异常向量表?
由于异常触发后,最终需要执行异常处理函数
而对于函数而言,当开发板/系统上电复位后,函数的地址、参数的地址、变量的地址可能都会发生改变,
对于CPU而言,他无法知道变化后的地址
此时,就需要固定一片内存空间地址,用于引导CPU找到最后的异常处理函数
所以,ARM公司开发并生产内核时,固定了一片32个字节的地址空间,
专门用于存放各个异常源的地址,他就是异常向量表

二、异常处理流程

1、自动保存现场(CPU自动完成)

复制代码
自动保存现场需要做四件事:
1、将CPSR寄存器内的值保存到SPSR_<MODE>寄存器中
2、修改CPSR寄存器中的位:
    1.修改CPSR寄存器中的I位和F位(选择是否屏蔽IRQ模式和FIQ模式)
    2.修改CPSR寄存器中的T位(选择使用ARM汇编指令集/Thumb汇编指令集)
    3.修改CPSR寄存器中的M位(选择需要进入的异常模式)
3、将函数返回地址保存到LR链接寄存器中
4、将PC计数寄存器指向异常向量表

2、手动返回现场(程序员手动完成)

复制代码
1、将SPSR_<MODE>寄存器中的值赋值给CPSR寄存器
2、将LR链接寄存器中的值赋值给PC计数寄存器

3、ARM汇编代码模拟异常事件处理流程

复制代码
.text		
.global _start		
	_start:			
		@ 构建异常向量表
		b reset_handler
		b undefined_handler
		b swi_handler
		b prefetch_handler
		b data_handler
		b .	@ 占4个字节空间操作
		b irq_handler
		b fiq_handler

reset_handler:
	@ 为SVC模式下的SP寄存器给定一个内存空间地址
	ldr sp, =0x40000820
	
	@ 从SVC模式切换到User模式下,执行用户代码
	msr cpsr, #0xD0
	
	@ 用户代码,以下相当于main函数
	mov r0, #0x1
	mov r1, #0x2
	
	@ 软中断异常事件触发
	@ swi 2这条汇编指令执行完毕后,需要看到的效果
	@ 1、CPSR寄存器中的值是否被保存到SPSR寄存器中
	@ 2、CPSR寄存器中的值是否被改变(IFTM位)
	@ 3、LR寄存器中是否保存函数的返回地址
	@ 4、PC是否指向异常向量表中软中断异常源的地址
	swi 2
	
	add r2, r1, r0
	
undefined_handler:

swi_handler:
	@ 压栈保存现场,保存局部变量和函数返回地址
	stmfd sp!, {r0-r1, lr}
	
	@ 软中断异常源下第一个异常事件的处理函数
	mov r0, #0xff
	mov r1, #0xee
	add r2, r1, r0
	
	@ 出栈恢复现场,恢复局部变量和函数返回地址
	@ 还需要恢复SPSR寄存器中的值给到CPSR寄存器
	@ ^的作用:用于将SPSR寄存器中的值赋值给CPSR寄存器
	ldmfd sp!, {r0-r1, pc}^
	
prefetch_handler:

data_handler:

irq_handler:

fiq_handler:
	
	stop:			
		b stop		
.end		

三、分析芯片手册(EXTI、NVIC)

复制代码
NVIC:嵌套向量中断控制器
EXTI:外部中断和事件控制器
    嵌套:中断的嵌套,指的是中断具备优先级等级,高优先级等级的中断信号可以打断低优先级的中断信号
    向量:所有异常中断都由异常向量表统一管理

在Cortex-M核中,中断处理一般使用 EXTI 和 NVIC 这两个寄存器实现
在Cortex-A核中,中断处理一般使用 EXTI 和 GIC(GICC+CPID) 这两个寄存器实现

1、外部中断章节介绍(EXTI)

1)输入事件编号

2)EXTI的主要特点

3)EXTI框图分析


4)寄存器功能分析

复制代码
中断挂起:是指中断信号到达,等待被处理,需要执行这个中断信号对应的逻辑代码
中断挂起标志位:是表示当前中断信号到达等待处理的标志位
中断:就是判断中断标志位是否被挂起,挂起后执行中断信号对应的异常事件处理代码

5)GPIO端口选择

6)寄存器分析

复制代码
以KEY1 - PC9为例,需要实现
KEY1按下(产生下降沿信号)时,LED灯点亮

对EXTI外设控制器中控制PC9引脚的寄存器做出处理:
    1、需要找到PC9接入到EXTI外设控制器的IO端口选择寄存器(找这个寄存器中用于代表PC9引脚的位)
    2、将PC9配置为下降沿触发方式
    3、设置不屏蔽PC9引脚产生的中断信号
    4、检测PC9引脚是否触发中断,也就是PC9引脚产生的下降沿信号是否被硬件挂起
(1)EXTI_FTSR1寄存器(下降沿触发寄存器)


(2)EXTI_FPR1寄存器(下降沿触发标志寄存器)


复制代码
当我们检测按键是否按下时,也就是检测中断是否被挂起,也就是读取这个寄存器对应位是否为1
读取到1:就代表中断被挂起,可以执行处理逻辑
读到到0:就代表中断没有被挂起,不需要执行处理逻辑

当我们读取到中断被挂起后,为了让中断不一直处于被挂起状态,需要人为手动解除中断的挂起
(3)EXTI_EXTICRm寄存器(GPIO端口引脚选择寄存器)


(4)EXTI_IMR1寄存器(输入事件频闭寄存器)

2、中断嵌套向量章节介绍(NVIC)

1)NVIC的主要特点

2)中断号

四、标准库实现外部中断

1、标准库初始化外部中断

2、标准库代码

五、HAL库实现外部中断

1、STM32CudeMX配置初始化

1)配置为外部中断触发

外部中断是GPIO输入模式中的一种,并不属于复用模式,配置为双边沿触发

2)使能NVIC下的EXTI的中断信号

3)配置中断触发的优先级

不要配置外部中断的优先级和系统自带中断的优先级同级,这会影响系统的绝对优先,这里优先级设置成 1 即可

2、STM32CudeMX初始化代码

在工程中的启动文件中(唯一的.s文件),有异常向量表,根据异常向量表可以找到,对应的异常中断函数,在发生异常事件时,CPU也是先找到启动文件中的异常向量表 ,根据异常向量表查找对应的异常事件函数 。可以在异常事件的函数中编写处理异常的代码,也可以在HAL库提供的处理异常事件的空函数中编写代码。

1)异常处理流程




2)API接口(异常处理函数)

复制代码
__weak void HAL_GPIO_EXTI_Falling_Callback(uint16_t GPIO_Pin)
功能:
    HAL库提供用于执行下降沿中断信号触发后需要执行的弱函数(支持被重写)
参数:
    GPIO_Pin:触发下下降沿中断信号的GPIO引脚编号
返回值:
    无


__weak void HAL_GPIO_EXTI_Rising_Callback(uint16_t GPIO_Pin)
功能:
    HAL库提供的用于执行上升沿中断信号触发后需要执行的弱函数(支持被重写)
参数:
    GPIO_Pin:触发下下降沿中断信号的GPIO引脚编号
返回值:
    无

3)示例代码


六、标准库配置串口中断

1、标准库初始化串口中断

2、标准库操作串口中断

七、HAL库配置串口中断


1、串口中断执行流程






2、串口发送中断(不常用)

1)API接口函数

(1)串口发送中断执行函数
复制代码
HAL_StatusTypeDef 
HAL_UART_Transmit_IT
(UART_HandleTypeDef *huart, const uint8_t *pData, uint16_t Size)

功能:
    HAL库提供的用于通过串口进行中断的方式发送数据的函数
参数:
    huart:USART1的句柄对象
    pData:需要发送的数据
    Size:需要发送的数据的大小,单位为字节
返回值:
    函数执行成功,返回HAL_OK(0)
    函数执行失败,返回错误码
(2)串口发送中断回调函数
复制代码
__weak void HAL_UART_TxCpltCallback(UART_HandleTypeDef *huart)
功能:
    HAL库提供的用于串口发送中断触发后,执行的回调函数(弱函数,可以被重写)
    当串口发送中断函数执行完毕后,这个函数会被调用
    当使用串口发送中断函数发送完完整数据后,此时发送中断的中断信号触发,这个函数才被执行
参数:
    huart:USART1的句柄对象
返回值:
    无

------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------(不常用)
__weak void HAL_UART_TxHalfCpltCallback(UART_HandleTypeDef *huart)
这个函数是,当使用串口发送中断函数发送一半数据后,此时发送中断的中断信号触发,这个函数被执行

2)示例代码



3)代码效果

3、串口接收中断(经常用)

使用串口接收中断可以实现随时收发

1)API接口函数

(1)串口接收中断启用函数
复制代码
HAL_StatusTypeDef 
HAL_UART_Receive_IT
(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size)
功能:
    HAL库提供的用于开启串口接收中断,并使用中断的方式接收数据的函数
参数:
    huart:USART1句柄对象
    pData:需要接收的数据
    Size:需要接收的数据的大小,单位为字节
返回值:
    函数执行成功,返回HAL_OK
    函数执行失败,返回错误码
(2)串口接收中断回调函数
复制代码
__weak void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
功能:
    HAL库提供的用于串口接收中断触发后,执行的回调函数(弱函数,支持被重写)
    当串口通过中断的方式接受完完整数据后,此时接收中断信号触发,这个函数会被执行
参数:
    huart:USART1句柄对象
返回值:
    无

2)示例代码




3)代码效果

4、串口空闲中断(经常用)

复制代码
串口空闲中断:是指串口中断和状态寄存器中的一个特殊标志位,叫做串口空闲标志位
通过串口空闲中断可以实现串口的不定长接收数据
串口空闲中断可以判断一次数据是否接收完毕

1)USART_CR1寄存器


2)USART_ISR寄存器


3)USART_ICR寄存器


4)串口空闲中断的工作原理

5)API接口函数

复制代码
想要使用空闲中断标志位:
    1、使能空闲中断标志位
    2、读取空闲中断标志位是否被置1
    3、如果被置1,清除空闲中断标志位,方便下一次硬件置1
    4、需要执行的逻辑
(1)__HAL_UART_ENABLE_IT(使能中断/状态标志位)
复制代码
/** @brief  Enable the specified UART interrupt.
  * @param  __HANDLE__ specifies the UART Handle.
  * @param  __INTERRUPT__ specifies the UART interrupt source to enable.
  *          This parameter can be one of the following values:
  *            @arg @ref UART_IT_RXFF  RXFIFO Full interrupt
  *            @arg @ref UART_IT_TXFE  TXFIFO Empty interrupt
  *            @arg @ref UART_IT_RXFT  RXFIFO threshold interrupt
  *            @arg @ref UART_IT_TXFT  TXFIFO threshold interrupt
  *            @arg @ref UART_IT_CM    Character match interrupt
  *            @arg @ref UART_IT_CTS   CTS change interrupt
  *            @arg @ref UART_IT_LBD   LIN Break detection interrupt
  *            @arg @ref UART_IT_TXE   Transmit Data Register empty interrupt
  *            @arg @ref UART_IT_TXFNF TX FIFO not full interrupt
  *            @arg @ref UART_IT_TC    Transmission complete interrupt
  *            @arg @ref UART_IT_RXNE  Receive Data register not empty interrupt
  *            @arg @ref UART_IT_RXFNE RXFIFO not empty interrupt
  *            @arg @ref UART_IT_RTO   Receive Timeout interrupt
  *            @arg @ref UART_IT_IDLE  Idle line detection interrupt
  *            @arg @ref UART_IT_PE    Parity Error interrupt
  *            @arg @ref UART_IT_ERR   Error interrupt (frame error, noise error, overrun error)
  * @retval None
  */
#define __HAL_UART_ENABLE_IT(__HANDLE__, __INTERRUPT__)   
                                    (\
                                    ((((uint8_t)(__INTERRUPT__)) >> 5U) == 1U)?\
                                    ((__HANDLE__)->Instance->CR1 |= (1U <<\
                                    ((__INTERRUPT__) & UART_IT_MASK))): \
                                    ((((uint8_t)(__INTERRUPT__)) >> 5U) == 2U)?\
                                    ((__HANDLE__)->Instance->CR2 |= (1U <<\
                                    ((__INTERRUPT__) & UART_IT_MASK))): \
                                    ((__HANDLE__)->Instance->CR3 |= (1U <<\
                                    ((__INTERRUPT__) & UART_IT_MASK))))
                                                               
功能:
    HAL库提供的用于使能中断/状态标志位的函数
参数:
    __HANDLE__:USART1的句柄对象
    __INTERRUPT__:需要使能中断/状态标志位
    
相当于 USART1->CR1 |= (0x1 << 4);
(2)__HAL_UART_GET_FLAG(获取中断/状态标志位)
复制代码
/** @brief  Check whether the specified UART flag is set or not.
  * @param  __HANDLE__ specifies the UART Handle.
  * @param  __FLAG__ specifies the flag to check.
  *        This parameter can be one of the following values:
  *            @arg @ref UART_FLAG_TXFT  TXFIFO threshold flag
  *            @arg @ref UART_FLAG_RXFT  RXFIFO threshold flag
  *            @arg @ref UART_FLAG_RXFF  RXFIFO Full flag
  *            @arg @ref UART_FLAG_TXFE  TXFIFO Empty flag
  *            @arg @ref UART_FLAG_REACK Receive enable acknowledge flag
  *            @arg @ref UART_FLAG_TEACK Transmit enable acknowledge flag
  *            @arg @ref UART_FLAG_RWU   Receiver wake up flag (if the UART in mute mode)
  *            @arg @ref UART_FLAG_SBKF  Send Break flag
  *            @arg @ref UART_FLAG_CMF   Character match flag
  *            @arg @ref UART_FLAG_BUSY  Busy flag
  *            @arg @ref UART_FLAG_ABRF  Auto Baud rate detection flag
  *            @arg @ref UART_FLAG_ABRE  Auto Baud rate detection error flag
  *            @arg @ref UART_FLAG_CTS   CTS Change flag
  *            @arg @ref UART_FLAG_LBDF  LIN Break detection flag
  *            @arg @ref UART_FLAG_TXE   Transmit data register empty flag
  *            @arg @ref UART_FLAG_TXFNF UART TXFIFO not full flag
  *            @arg @ref UART_FLAG_TC    Transmission Complete flag
  *            @arg @ref UART_FLAG_RXNE  Receive data register not empty flag
  *            @arg @ref UART_FLAG_RXFNE UART RXFIFO not empty flag
  *            @arg @ref UART_FLAG_RTOF  Receiver Timeout flag
  *            @arg @ref UART_FLAG_IDLE  Idle Line detection flag
  *            @arg @ref UART_FLAG_ORE   Overrun Error flag
  *            @arg @ref UART_FLAG_NE    Noise Error flag
  *            @arg @ref UART_FLAG_FE    Framing Error flag
  *            @arg @ref UART_FLAG_PE    Parity Error flag
  * @retval The new state of __FLAG__ (TRUE or FALSE).
  */
#define __HAL_UART_GET_FLAG(__HANDLE__, __FLAG__) 
                            (((__HANDLE__)->Instance->ISR & (__FLAG__)) == (__FLAG__))

功能:
    HAL库提供的用于获取中断/状态标志位对应值的函数
参数:
    __HANDLE__:USART1的句柄对象
    __FLAG__:需要获取的中断/状态标志位
    
就相当于 USART1->ISR & (0x1 << 4)
(3)__HAL_UART_CLEAR_FLAG(清楚中断/状态标志位)
复制代码
/** @brief  Clear the specified UART pending flag.
  * @param  __HANDLE__ specifies the UART Handle.
  * @param  __FLAG__ specifies the flag to check.
  *          This parameter can be any combination of the following values:
  *            @arg @ref UART_CLEAR_PEF      Parity Error Clear Flag
  *            @arg @ref UART_CLEAR_FEF      Framing Error Clear Flag
  *            @arg @ref UART_CLEAR_NEF      Noise detected Clear Flag
  *            @arg @ref UART_CLEAR_OREF     Overrun Error Clear Flag
  *            @arg @ref UART_CLEAR_IDLEF    IDLE line detected Clear Flag
  *            @arg @ref UART_CLEAR_TXFECF   TXFIFO empty clear Flag
  *            @arg @ref UART_CLEAR_TCF      Transmission Complete Clear Flag
  *            @arg @ref UART_CLEAR_RTOF     Receiver Timeout clear flag
  *            @arg @ref UART_CLEAR_LBDF     LIN Break Detection Clear Flag
  *            @arg @ref UART_CLEAR_CTSF     CTS Interrupt Clear Flag
  *            @arg @ref UART_CLEAR_CMF      Character Match Clear Flag
  * @retval None
  */
#define __HAL_UART_CLEAR_FLAG(__HANDLE__, __FLAG__) 
                                                ((__HANDLE__)->Instance->ICR = (__FLAG__))
功能:
    HAL库提供的用于清除中断/状态标志位的函数
参数:
    __HANDLE__:USART1的句柄对象
    __FLAG__:需要清除的中断/状态标志位

就相当于 USART1->ICR |= (0x1 << 4);

6)串口句柄对象提供的成员变量

复制代码
__IO uint16_t            RxXferCount;    
RxXferCount用于接收数据时的计数,没接收1个字节,会减1
buf初始为1024个字节,RxXferCount = 1024
每当接收到1个字节后,RxXferCount会减1

举例:
    接收了500个字节的数据
    RxXferCount = 1024 - 500 = 524个字节(当前buf还可以接收数据的字节数)
    实际接收的字节数 = 1024 - RxXferCount
    
    
uint8_t                  *pRxBuffPtr;
pRxBuffPtr用于接收数据时的指针偏移
当接受完完整数据后,需要将pRxBuffPtr的指向重新冲向buf首地址的为止

5、实现串口的实时不定长接收(示例代码)

复制代码
使用 串口接收中断 + 串口空闲中断 = 实现串口的实时不定长接收数据
串口接收中断 用于实现实时接收数据
串口空闲中断 用于实现不定长接收数据

实现串口的实时不定长接收数据:
1、开启串口接收中断和串口空闲中断
2、在串口的异常处理函数中执行
    1.判断空闲中断是否产生(若产生则代表数据接收完毕)
    2.清楚空闲中断标志位
    3.选哟的逻辑处理代码
    4.重新开启串口接收中断,准备下一次数据的接收




相关推荐
文火冰糖的硅基工坊2 小时前
[硬件电路-320]:模拟电路与数字电路,两者均使用晶体管(如BJT、MOSFET),但模拟电路利用其线性区,数字电路利用其开关特性。
单片机·嵌入式硬件·数学建模·fpga开发·系统架构·信号处理
辰哥单片机设计3 小时前
D01粉尘传感器详解(STM32)
单片机·嵌入式硬件
兆龙电子单片机设计3 小时前
【STM32项目开源】STM32单片机厨房安全监测系统
stm32·单片机·物联网·开源·自动化
谁刺我心4 小时前
STM32环境配置keil5【保姆级】
stm32·keil5
一枝小雨13 小时前
【DMA】深入解析DMA控制器架构与运作原理
stm32·单片机·嵌入式硬件·系统架构·dma·嵌入式·arm
与你诗画17 小时前
为什么单片机的外接晶振要并连两个电容?
c语言·驱动开发·单片机·嵌入式硬件·硬件架构·硬件工程
翰霖努力成为专家17 小时前
STM32,新手学习
stm32·嵌入式硬件·学习
蓁蓁啊17 小时前
VMware 性能优化完整指南
开发语言·单片机·嵌入式硬件·物联网·性能优化·鸿蒙系统
一枝小雨17 小时前
【DMA】DMA入门:理解DMA与CPU的并行
单片机·系统架构·dma·嵌入式·arm