[STM32F1] STM32F103C8T6--中断理解及代码示例

[复制链接]
236|0
Haizangwang 发表于 2025-10-9 18:33 | 显示全部楼层 |阅读模式
一、中断的概念
1.1.1中断的基本概念
中断:单片机应对突发事件的一种方式

5780468e737c67364a.png

先暂停常规程序,先去实现响应函数,再实现常规程序的过程

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);//根据收到的数据改变闪灯速度
                }
        }
}


303568e737beeeed6.png


假如单片机的波特率是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 这些标志位都是中断标志位

6238568e737b1d44b8.png

2.1.1NVIC相当于中断管理员(中断嵌套控制器Nested Vectored Interrupt Controller)

2913768e737a4be1ec.png

3.1.1NVIC内部

2109468e7379ef1100.png

中断优先级分组可以移动有如图5种情况,

以分组0举例:此时抢占优先级没有数值为0,子优先级有四个空位,假如四位数字为1010,则抢占优先级为0,子优先级为8+2=10;

以分组1举例:假如四位数字为1010,则抢占优先级为1,子优先级为2;

4.1.1抢占优先级与中断嵌套之间的关系

4713768e737990544b.png

数字越小抢占优先级越高

5.1.1子优先级与中断排队

3684568e737935b73b.png

三、中断实验(用中断控制LED亮灭)
1.1.1连接电路

9100568e7378d852f0.png

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模块简介:

1105168e7377b01eea.png

功 能:可以实现输入信号的上升沿检测和下降沿的检测(检测输入信号上升沿和下降沿的电压),可以实现对每个中断/事件线进行单独配置,以及触发事件的属性。

2.1.1EXTI特性(了解)
● 每个中断/事件都有独立的触发和屏蔽
● 每个中断线都有专用的状态位
● 支持多达20个软件的中断/事件请求
● 检测脉冲宽度低于APB2时钟宽度的外部信号
通过查阅参考手册我们可以得知内部

380468e73774cc6e7.png

3.1.1EXTI产生中断
要产生中断,必须先配置好并使能中断线,
根据需要的边沿检测设置2个触发寄存器,同时在中
断屏蔽寄存器的相应位写’1’允许中断请求。当外部中断线上发生了期待的边沿时,将产生一个
中断请求,对应的挂起位也随之被置’1’。在挂起寄存器的对应位写’1’,将清除该中断请求。如图

8564668e7376ec23c2.png

4.1.1EXTI产生事件
如果需要产生事件,必须先配置好并使能事件线。根据需要的边沿检测通过设置2个触发寄存
器,同时在事件屏蔽寄存器的相应位写’1’允许事件请求。当事件线上发生了需要的边沿时,将
产生一个事件请求脉冲,对应的挂起位不被置’1’。
通过在软件中断/事件寄存器写’1’,也可以通过软件产生中断/事件请求。

2828968e73766539be.png

简化后长这样

5758668e73760f24aa.png

5.1.1线的概念
EXTI内部共有20条线,其中连接GPIOD的线有16条,每条单独连接37个GPIO引脚
例如EXTI0连接PA0;EXTI1连接PA0;EXTI0连接PA8;各算一条,参考下图

3756868e7375a6b900.png

5368768e73751bdcf6.png

五、EXTI按钮实验
1.1.1搭建电路

2974868e7374bc6e56.png

按下左边按钮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

您需要登录后才可以回帖 登录 | 注册

本版积分规则

85

主题

270

帖子

0

粉丝
快速回复 在线客服 返回列表 返回顶部