#一、EXTI外部中断
#二、NVIC中断
##1)中断流程
##2)STM32中断
##3)中断NVIC与EXTI基本结构
##4)中断优先级分组
##5)AFIO复用IO口
##6)EXTI功能框图
#三、EXTI外部中断结构
#四、EXTI中断函数配置
#五、程序代码
一、EXTI外部中断
-
中断是指计算机运行过程中,出现某些意外情况需主机干预时,机器能自动停止正在运行的程序并转入处理新情况的程序,处理完毕后又返回原被暂停的程序继续运行。
-
EXTI可以检测指定GPIO口的电平信号,当其指定的GPIO口产生电平变化时,EXTI将立即向NVIC发出中断申请,经过NVIC裁决后即可中断CPU主程序,使CPU执行EXTI对应的中断程序。
-
支持触发方式:上升沿/下降沿/双边沿/软件触发
-
支持GPIO口:所有的GPIO口,但相同的Pin不能同时触发中断(如,PA1、PB1、PC1则不能同时触发中断,经AFIO_EXTICR1寄存器分配,任选其一产生中断)
-
通道数:16个GPIO_Pin,外加PVD输出(注1)、RTC闹钟(注2)、USB唤醒、以太网唤醒
-
在程序中应用中断可以代替While(1)以减少单片机在程序运行过程中等待的时间,进一步提高了单片机的利用率及其工作效率,今天程序应用到的是外部中断EXTI。
-
注:
1)PVD = Programmable Votage Detector 可编程电压监测器
它的作用是监视供电电压,在供电电压下降到给定的阀值以下时,产生一个中断,通知软件做紧急处理。
2)RTC是Real Time Clock的简称,定时开机闹钟,它在硬件电路上单独供电,当系统关机时,CPU和其他外部硬件设备全部掉电,但是RTC仍然继续工作。硬件如上个项目一致
二、NVIC中断
1)中断流程
- 中断:在主程序运行过程中,出现了特定的中断触发条件(中断源),使得CPU暂停当前正在运行的程序,转而去处理中断程序,处理完成后又返回原来被暂停的位置继续运行
- 中断优先级:当有多个中断源同时申请中断时,CPU会根据中断源的轻重缓急进行裁决,优先响应更加紧急的中断源
- 中断嵌套:当一个中断程序正在运行时,又有新的更高优先级的中断源申请中断,CPU再次暂停当前中断程序,转而去处理新的中断程序,处理完成后依次进行返回
2)STM32中断
向量表:
- 68个可屏蔽中断通道,包含EXTI、TIM、ADC、USART、SPI、I2C、RTC等多个外设(灰色部分为芯片内部中断)
3)中断NVIC与EXTI基本结构
- EXTI中断结构图分析:
- GPIOA、GPIOB、GPIOC等各有16个Pin口(0~15),相同Pin口由AFIO中断引脚选择寄存器选择GPIOx为通道之一,如, GPIOA_Pin1,GPIOB_Pin1,GPIOC_Pin1,AFIO中断引脚选择寄存器选择GPIOA_Pin1为中断通道,则GPIOA_Pin1为EXTI1通道,其他两个Pin口产生的中断不会得到响应。
- 通道数:AFIO中断引脚选择器选择出16个GPIO_Pin,外加PVD输出、RTC闹钟、USB唤醒、以太网唤醒,一共20个通道通往EXTI边沿检测及控制寄存器
- 由EXTI边沿检测及控制寄存器检测中断信号,产生相应的中断请求信号沿通道送往NVIC中断
- NVIC中断结构图分析:
- NVIC中断收集各个外设的中断请求,分配优先级组,将中断事件依次送入CPU处理。
4)中断优先级分组
-
使用NVIC统一管理中断,每个中断通道都拥有16个可编程的优先等级,可对优先级进行分组,进一步设置
-
NVIC的中断优先级由优先级寄存器的4位(0~15)决定,这4位可以进行切分,分为高n位的抢占优先级和低4-n位的响应优先级
-
抢占优先级高的可以中断嵌套,响应优先级高的可以优先排队,抢占优先级和响应优先级均相同的按中断号排队
5)AFIO复用IO口
- AFIO主要用于引脚复用功能的选择和定义
- 在STM32中,AFIO主要完成两个任务:复用功能引脚重映射、中断引脚选择
如图为中断引脚选择功能
6)EXTI功能框图
具体介绍:EXTI中断运行分析
四、EXTI中断函数配置
外部中断软件设计
a) 软件设计思想
i. 初始化中断
ii. EXTI(外部中断)
- 所有使用到的外设时钟使能
- 初始化GPIO口
- 外部中断线配置
a) AFIO外部中断寄存器选择哪个端口 - EXTI相关寄存器
- NVIC配置
iii. 中断服务函数
- 中断服务函数的编写
a) 判断是哪一个中断(共用中断服务函数)
b) 清对应中断挂起标志位
c) 中断服务函数
五、程序代码
1、Exti.c
#include "stm32f10x.h"
#include "exti.h"
void Exti_Init(void)
{
//1.配置GPIO
GPIO_InitTypeDef shake_init;
EXTI_InitTypeDef exti_init;
NVIC_InitTypeDef nvic_init;
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);//优先级分组配置
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA ,ENABLE);//配置GPIO时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE);//配置EXTI中断时钟,又输出又中断
GPIO_EXTILineConfig(GPIO_PortSourceGPIOA,GPIO_PinSource2);
shake_init.GPIO_Mode = GPIO_Mode_IPD;
shake_init.GPIO_Pin = GPIO_Pin_2;
shake_init.GPIO_Speed = GPIO_Speed_10MHz;
GPIO_Init(GPIOA,&shake_init);
//2.配置Exti外部中断
exti_init.EXTI_Line = EXTI_Line2;//外部中断通道
exti_init.EXTI_LineCmd = ENABLE;//使能外部中断通道
exti_init.EXTI_Mode = EXTI_Mode_Interrupt;//选择外部中断模式
exti_init.EXTI_Trigger = EXTI_Trigger_Falling;//中断触发方式
EXTI_Init(&exti_init);//中断初始化
//3.配置NVIC中断控制器
nvic_init.NVIC_IRQChannel = EXTI2_IRQn;//中断通道2
nvic_init.NVIC_IRQChannelCmd = ENABLE; //使能通道
nvic_init.NVIC_IRQChannelPreemptionPriority = 1; //抢占优先级
nvic_init.NVIC_IRQChannelSubPriority = 1;//子优先级
NVIC_Init(&nvic_init);
}
2、Exti.h
#include "stm32f10x.h"
void Exti_Init(void);
3、shake.c
#include "stm32f10x.h"
#include "shake.h"
void Shake_Init(void)
{
GPIO_InitTypeDef shake_init;
//1.使能GPIOA时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA , ENABLE);
//2.结构体配置
shake_init.GPIO_Mode = GPIO_Mode_IPD;
shake_init.GPIO_Pin = GPIO_Pin_2;
shake_init.GPIO_Speed = GPIO_Speed_10MHz;
GPIO_Init(GPIOA,&shake_init);
}
4、skake.h
#include "stm32f10x.h"
void Shake_Init(void);
5、relay.c
#include "stm32f10x.h"
#include "relay.h"
void Relay_Init(void)
{
GPIO_InitTypeDef relay_init;
//1.使能GPIOA时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
//2.GPIOA3结构体配置
relay_init.GPIO_Mode = GPIO_Mode_Out_PP;
relay_init.GPIO_Pin = GPIO_Pin_3 ;
relay_init.GPIO_Speed = GPIO_Speed_10MHz;
GPIO_Init(GPIOA, &relay_init);
}
6、relay.h
#include "stm32f10x.h"
void Relay_Init(void);
7、main.c
#include "stm32f10x.h"
#include "main.h"
#include "LED.h"
#include "usart.h"
#include "relay.h"
#include "shake.h"
#include "exti.h"
void delay(uint16_t time)//延迟函数
{
uint16_t i=0;
while(time--)
{
i=12000;
while(i--);
}
}
int main()
{
LED_Init();
usart_init();
Relay_Init();
Shake_Init();
Exti_Init();
GPIO_SetBits(GPIOA,GPIO_Pin_3);
}
void EXTI2_IRQHandler(void)
{
if(EXTI_GetITStatus(EXTI_Line2)!=RESET)//如果函数发生中断
{
GPIO_ResetBits(GPIOA,GPIO_Pin_3);//接通继电器,开灯
delay(1000);
GPIO_SetBits(GPIOA,GPIO_Pin_3);//关闭继电器,关灯
}
else
{
GPIO_SetBits(GPIOA,GPIO_Pin_3);//关闭继电器,关灯
}
EXTI_ClearITPendingBit(EXTI_Line2);//中断标志清除
}
8、USART.C
#include "stm32f10x.h"
#include "usart.h"
void usart_init(void) //串口初始化
{
GPIO_InitTypeDef GPIO_initstructure;
USART_InitTypeDef USART_initstructure;
NVIC_InitTypeDef Nvic_Init;
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); //配置中断组为中断组2 misc.h
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE); //配置中断复用时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1,ENABLE);
//配置PA9 TX
GPIO_initstructure.GPIO_Mode =GPIO_Mode_AF_PP; //推挽输出
GPIO_initstructure.GPIO_Pin =GPIO_Pin_9;
GPIO_initstructure.GPIO_Speed =GPIO_Speed_50MHz;
GPIO_Init(GPIOA,&GPIO_initstructure);
//配置PA10 RX
GPIO_initstructure.GPIO_Mode =GPIO_Mode_IN_FLOATING; //浮空输入
GPIO_initstructure.GPIO_Pin =GPIO_Pin_10;
GPIO_Init(GPIOA,&GPIO_initstructure);
//串口1打开
USART_initstructure.USART_BaudRate=115200; //波特率
USART_initstructure.USART_HardwareFlowControl=USART_HardwareFlowControl_None;
USART_initstructure.USART_Mode=USART_Mode_Rx | USART_Mode_Tx;
USART_initstructure.USART_Parity=USART_Parity_No; //校验位
USART_initstructure.USART_StopBits= USART_StopBits_1; //停止位
USART_initstructure.USART_WordLength= USART_WordLength_8b; //有效字节长度 8位
USART_Init(USART1,&USART_initstructure);
//使能串口1
USART_ITConfig(USART1,USART_IT_RXNE,ENABLE); //串口中断配置
USART_Cmd(USART1,ENABLE);
//配置中断
Nvic_Init.NVIC_IRQChannel =USART1_IRQn; //设置中断通道 stm32f10x.h
Nvic_Init.NVIC_IRQChannelCmd =ENABLE; //控制是否使能
Nvic_Init.NVIC_IRQChannelPreemptionPriority =1; //设置抢占优先级
Nvic_Init.NVIC_IRQChannelSubPriority =1; //设置子优先级
NVIC_Init(&Nvic_Init);
}
//发送单个字符 调用方法 USARTSendByte(USART1,'o');
void USARTSendByte(USART_TypeDef* USARTx, uint16_t Data){
USART_SendData(USART1,Data);
while(USART_GetFlagStatus(USART1, USART_FLAG_TXE)==RESET); //判断是否发送完成
}
//发送字符串 //原理字符发送最后一位是\0判断是否最后一位\0就能发送字符串 调用方法 USARTSendStr(USART1,"ruanzebin");
void USARTSendStr(USART_TypeDef* USARTx, char *str){
uint16_t i=0; //无符号整形十六位
do{
USARTSendByte(USARTx, *(str+i));
i++;
}while(*(str+i)!='\0');
while(USART_GetFlagStatus(USART1, USART_FLAG_TC)==RESET); //判断是否发送完成
}
//printf 函数 putchar函数(输出单个字符) 通过重定向 输出数值
int fputc(int ch ,FILE *f){
USART_SendData(USART1,(uint8_t)ch);
while(USART_GetFlagStatus(USART1, USART_FLAG_TXE)==RESET); //判断是否发送完成
return (ch);
}
//scanf() getchar函数
int fgetc(FILE *f)
{
while(USART_GetFlagStatus(USART1, USART_FLAG_RXNE)==RESET); //判断是否接收成功
return (int)USART_ReceiveData(USART1);
}