一、中断的概念
1.1.1中断的基本概念
中断:单片机应对突发事件的一种方式
先暂停常规程序,先去实现响应函数,再实现常规程序的过程
1.2.1为什么要用到中断
我们以之前串口发送举例
int main(void)
{
uint8_t byte;
while(1)
{
GPIO_WriteBit(GPIOC,GPIO_Pin_13,Bit_RESET);//亮灯
Delay(byte);//延迟
GPIO_WriteBit(GPIOC,GPIO_Pin_13,Bit_SET);//灭灯
Delay(byte);//延迟
if(USART_GetFlagStatus(USART1,USART_FLAG_RXNE)== SET)
{
byte =USART_ReceiveData(USART1);//根据收到的数据改变闪灯速度
}
}
}
假如单片机的波特率是115200,串口一次性发送一个起始位8个数据位一个停止位(共十个字)那么完成一次发送需要【1/(115200/10)】*1000~~0.1ms
完成一次LED亮灭共耗时200ms,一次循环发送单片机只需要0.1ms就能完成,而等待需要200ms 这样等待这200ms单片机可以做其它的事情,所以我们引入中断
2.1.1中断编程举例
int main(void)
{
uint8_t byte;
while(1)
{
GPIO_WriteBit(GPIOC,GPIO_Pin_13,Bit_RESET);//亮灯
Delay(byte);//延迟
GPIO_WriteBit(GPIOC,GPIO_Pin_13,Bit_SET);//灭灯
Delay(byte);//延迟
}
}
void USART_IRQHandler(void)
{
//接收数据
uint8_t data = USART_ReceiveData(USART1);
//对收到的数据进行处理
}
二、中断优先级
1.1.1中断优先级的概念
中断优先级:用数字表示中断的优先程度
注: TXE 这些标志位都是中断标志位
2.1.1NVIC相当于中断管理员(中断嵌套控制器Nested Vectored Interrupt Controller)
3.1.1NVIC内部
中断优先级分组可以移动有如图5种情况,
以分组0举例:此时抢占优先级没有数值为0,子优先级有四个空位,假如四位数字为1010,则抢占优先级为0,子优先级为8+2=10;
以分组1举例:假如四位数字为1010,则抢占优先级为1,子优先级为2;
4.1.1抢占优先级与中断嵌套之间的关系
数字越小抢占优先级越高
5.1.1子优先级与中断排队
三、中断实验(用中断控制LED亮灭)
1.1.1连接电路
1.2.1编写闪灯代码
1.3.1初始化板载LED
2.1.1初始化串口(先对收发引脚进行初始化再对内部进行初始化)
3.1.1让RxNE标志位触发中断
4.1.1配置NVIC模块
5.1.1代码部分(main.c)
#include "stm32f10x.h"
#include "delay.h"
#include "usart.h"
uint32_t blinkInterval =1000;
void USART1_IRQHandler(void);
void APP_OnBoardLED_Init(void);
void APP_USART1_Init(void);
int MY_SendBytes(I2C_TypeDef *I2Cx,uint8_t Addr,uint8_t *pData,uint16_t Size);
int main(void)
{
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
APP_OnBoardLED_Init();
APP_USART1_Init();
My_USART_SendString(USART1,"HELLO WORLD.\r\n");
while(1)
{
GPIO_WriteBit(GPIOC,GPIO_Pin_13,Bit_RESET);
Delay(blinkInterval);
GPIO_WriteBit(GPIOC,GPIO_Pin_13,Bit_SET);
Delay(blinkInterval);
USART1_IRQHandler();
}
}
int MY_SendBytes(I2C_TypeDef *I2Cx,uint8_t Addr,uint8_t *pData,uint16_t Size)
{
//#1等待总线空闲
while(I2C_GetFlagStatus(I2Cx,I2C_FLAG_BUSY)==SET)
//#2发送起始位
I2C_GetFlagStatus(I2Cx,ENABLE);
while(I2C_GetFlagStatus(I2Cx,I2C_FLAG_SB)==RESET)
//#3寻址阶段
I2C_ClearFlag(I2Cx,I2C_FLAG_AF);
I2C_SendData(I2Cx,Addr & 0xFE);
while(1)
{
if(I2C_GetFlagStatus(I2Cx,I2C_FLAG_ADDR)==SET)
{
break;
}
if(I2C_GetFlagStatus(I2Cx,I2C_FLAG_AF)==SET)
{
I2C_GenerateSTOP(I2Cx,ENABLE);
return -1;//寻址失败
}
}
//清除ADDR
I2C_ReadRegister(I2Cx, I2C_Register_SR1);
I2C_ReadRegister(I2Cx, I2C_Register_SR2);
//#4发送数据
for(uint16_t i=0;i<Size;i++)
{
while(1)
{
if(I2C_GetFlagStatus(I2Cx,I2C_FLAG_AF)==SET)
{
I2C_GenerateSTOP(I2Cx,ENABLE);
return -2;//寻址失败
}
if(I2C_GetFlagStatus(I2Cx,I2C_FLAG_BTF)==SET)
{
break;
}
}
}
//#5发送停止位
I2C_GenerateSTOP(I2Cx,ENABLE);
return 0;
}
void APP_OnBoardLED_Init(void)
{
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC,ENABLE);
GPIO_InitTypeDef GPIO_InitStruct;
GPIO_InitStruct.GPIO_Mode =GPIO_Mode_Out_OD;
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_13;
GPIO_InitStruct.GPIO_Speed=GPIO_Speed_2MHz;
GPIO_Init(GPIOC,&GPIO_InitStruct);
}
void APP_USART1_Init(void)
{
//#1.初始化IO引脚
GPIO_InitTypeDef GPIO_InitStruct;
//PA9 AF_PP
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_9 ;
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_2MHz;
GPIO_Init(GPIOA,&GPIO_InitStruct);
//PA10 IPU
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IPU;
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_10 ;
GPIO_Init(GPIOA,&GPIO_InitStruct);
//#2初始化USART1
//开启USART1的时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1,ENABLE);
//初始化USART1
USART_InitTypeDef USART_InitStruct;
USART_InitStruct.USART_BaudRate =115200;
USART_InitStruct.USART_HardwareFlowControl =USART_HardwareFlowControl_RTS;
USART_InitStruct.USART_Mode =USART_Mode_Rx |USART_Mode_Tx;
USART_InitStruct.USART_Parity = USART_Parity_No;
USART_InitStruct.USART_StopBits =USART_StopBits_1;
USART_InitStruct.USART_WordLength = USART_WordLength_8b;
USART_Init(USART1,&USART_InitStruct);
//闭合总开关
USART_Cmd(USART1,ENABLE);
//#3.配置中断
USART_ITConfig(USART1,USART_IT_RXNE,ENABLE);
//#4配置NVIC
NVIC_InitTypeDef NVIC_InitSTruct;
NVIC_InitSTruct.NVIC_IRQChannel =USART1_IRQn;
NVIC_InitSTruct.NVIC_IRQChannelPreemptionPriority = 0;
NVIC_InitSTruct.NVIC_IRQChannelSubPriority =0;
NVIC_InitSTruct.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitSTruct);
}
void USART1_IRQHandler(void)
{
if(USART_GetFlagStatus(USART1,USART_FLAG_RXNE)==SET)
{
uint8_t dataRcvd =USART_ReceiveData(USART1);
if(dataRcvd == '0')
{
blinkInterval = 1000;
}
else if(dataRcvd == '1')
{
blinkInterval = 200;
}
else if(dataRcvd == '2')
{
blinkInterval = 50;
}
}
}
四、EXTI工作原理
1.1.1EXTI模块简介:
功 能:可以实现输入信号的上升沿检测和下降沿的检测(检测输入信号上升沿和下降沿的电压),可以实现对每个中断/事件线进行单独配置,以及触发事件的属性。
2.1.1EXTI特性(了解)
● 每个中断/事件都有独立的触发和屏蔽
● 每个中断线都有专用的状态位
● 支持多达20个软件的中断/事件请求
● 检测脉冲宽度低于APB2时钟宽度的外部信号
通过查阅参考手册我们可以得知内部
3.1.1EXTI产生中断
要产生中断,必须先配置好并使能中断线,
根据需要的边沿检测设置2个触发寄存器,同时在中
断屏蔽寄存器的相应位写’1’允许中断请求。当外部中断线上发生了期待的边沿时,将产生一个
中断请求,对应的挂起位也随之被置’1’。在挂起寄存器的对应位写’1’,将清除该中断请求。如图
4.1.1EXTI产生事件
如果需要产生事件,必须先配置好并使能事件线。根据需要的边沿检测通过设置2个触发寄存
器,同时在事件屏蔽寄存器的相应位写’1’允许事件请求。当事件线上发生了需要的边沿时,将
产生一个事件请求脉冲,对应的挂起位不被置’1’。
通过在软件中断/事件寄存器写’1’,也可以通过软件产生中断/事件请求。
简化后长这样
5.1.1线的概念
EXTI内部共有20条线,其中连接GPIOD的线有16条,每条单独连接37个GPIO引脚
例如EXTI0连接PA0;EXTI1连接PA0;EXTI0连接PA8;各算一条,参考下图
五、EXTI按钮实验
1.1.1搭建电路
按下左边按钮LED点亮,按下右边按钮LED熄灭;
2.1.1代码示例(main.c部分)
#include "stm32f10x.h"
#include "delay.h"
#include "usart.h"
void APP_OnBoardLED_Init(void);
void EXTI9_5_IRQHandler(void);
void APP_Button_Init(void);
int main(void)
{
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
APP_OnBoardLED_Init();
APP_Button_Init();
EXTI9_5_IRQHandler();
while(1)
{
}
}
void APP_OnBoardLED_Init(void)
{
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC,ENABLE);
GPIO_InitTypeDef GPIO_InitStruct;
GPIO_InitStruct.GPIO_Mode =GPIO_Mode_Out_OD;
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_13;
GPIO_InitStruct.GPIO_Speed=GPIO_Speed_2MHz;
GPIO_Init(GPIOC,&GPIO_InitStruct);
}
void APP_Button_Init(void)
{
GPIO_InitTypeDef GPIO_InitStruct;
//PA5
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IPU;
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_5 ;
GPIO_Init(GPIOA,&GPIO_InitStruct);
//PA6
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IPU;
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_5 ;
GPIO_Init(GPIOA,&GPIO_InitStruct);
//#2.为EXTI5和EXTI6分配引脚
RCC_APB2PeriphClockCmd (RCC_APB2Periph_AFIO,ENABLE);
GPIO_EXTILineConfig(GPIO_PortSourceGPIOA,GPIO_PinSource5);
GPIO_EXTILineConfig(GPIO_PortSourceGPIOA,GPIO_PinSource6);
//#3.初始化EXTI的线
EXTI_InitTypeDef EXTI_InitStruct={0};
EXTI_InitStruct.EXTI_Line = EXTI_Line5;
EXTI_InitStruct.EXTI_Mode =EXTI_Mode_Interrupt;
EXTI_InitStruct.EXTI_Trigger = EXTI_Trigger_Rising;
EXTI_InitStruct.EXTI_LineCmd =ENABLE;
EXTI_Init(&EXTI_InitStruct);
EXTI_InitStruct.EXTI_Line = EXTI_Line6;
EXTI_InitStruct.EXTI_Mode =EXTI_Mode_Interrupt;
EXTI_InitStruct.EXTI_Trigger = EXTI_Trigger_Rising;
EXTI_InitStruct.EXTI_LineCmd =ENABLE;
EXTI_Init(&EXTI_InitStruct);
//#4.配置中断
NVIC_InitTypeDef NVIC_InitSTruct;
NVIC_InitSTruct.NVIC_IRQChannel =USART1_IRQn;
NVIC_InitSTruct.NVIC_IRQChannelPreemptionPriority = 0;
NVIC_InitSTruct.NVIC_IRQChannelSubPriority =0;
NVIC_InitSTruct.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitSTruct);
}
void EXTI9_5_IRQHandler(void)//清除标志位
{
if(EXTI_GetFlagStatus(EXTI_Line5)==SET)
{
EXTI_ClearFlag(EXTI_Line5);
GPIO_WriteBit(GPIOC,GPIO_Pin_13,Bit_RESET);
}
if(EXTI_GetFlagStatus(EXTI_Line6)==SET)
{
EXTI_ClearFlag(EXTI_Line5);
GPIO_WriteBit(GPIOC,GPIO_Pin_13,Bit_SET);
}
}
————————————————
版权声明:本文为CSDN博主「inevitable boy」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/inevitableboy/article/details/152454436
|
|