沉浸式学习stm32--中断编程入门

一.作者小课堂

(一)中断

1.中断的概念

计算机在执行程序过程中,当出现异常情况(断电等)或特殊请求(数据传输等)时,计算机暂停现行程序的运行,转向对这些异常情况或特殊请求进行处理,处理完毕后再返回到现行程序的中断处,继续执行原程序,这就是“中断”。

2.中断处理过程

(1)单重中断的中断处理流程

(2)多重中断的中断处理流程

中断处理过程 

 

(二)STM32F103微控制器

1.优先级概念

 STM32使用Cortex-M3的8位优先级寄存器中的4位来配置中断优先级,即STM32中的NVIC只支持16级中断优先级的管理。

NVIC中断配置的相关函数存放在标准外设库misc.c和misc.h文件中,共定义了5个相关函数及NVIC初始化结构体

void NVIC_PriorityGroupConfig(uint32_t NVIC_PriorityGroup);
void NVIC_Init(NVIC_InitTypeDef* NVIC_InitStruct);
void NVIC_SetVectorTable(uint32_t NVIC_VectTab, uint32_t Offset);
void NVIC_SystemLPConfig(uint8_t LowPowerMode, FunctionalState NewState);
void SysTick_CLKSourceConfig(uint32_t SysTick_CLKSource);

用于设置中断的优先级分组,此函数只有一个参数NVIC_PriorityGroup,其取值共有5组,每组的抢占优先级和响应优先级所占位数均不同,取值范围不同。

NVIC_PriorityGroup

抢占优先级

取值范围

响应优先级

取值范围

描述

NVIC_PriorityGroup_0

0

015

抢占优先级占0位,

响应优先级占4

NVIC_PriorityGroup_1

01

07

抢占优先级占1位,

响应优先级占3

NVIC_PriorityGroup_2

0123

0123

抢占优先级占2位,

响应优先级占2

NVIC_PriorityGroup_3

01234567

01

抢占优先级占3位,

响应优先级占1

NVIC_PriorityGroup_4

015

0

抢占优先级占4位,

响应优先级占0

2.中断向量表

当发生了异常或中断,内核要想响应这些异常或中断,就需要知道这些异常或中断的服务程序的入口地址,再由入口地址找到相应的中断服务程序,由中断入口地址组成的表称作中断向量表。

入口地址一般存放在程序存储器(ROM),默认情况下,Cortex-M3内核的中断向量表从零地址处开始,且每个向量占用4个字节。

STM32在标准外设库stm32f10x.h文件中,通过宏定义将中断通道号(中断号)与宏名联系起来,这样在使用中断时,直接引用宏名即可。

typedef enum IRQn
{
        /*Cortex-M3 Processor Exceptions Numbers */
  NonMaskableInt_IRQn= -14,   
          /*!< 2 Non Maskable Interrupt  */
  MemoryManagement_IRQn= -12, 
        /*!< 4 Cortex-M3 Memory Management Interrupt */
  BusFault_IRQn = -11,    
        /*!< 5 Cortex-M3 Bus Fault Interrupt */
……
} IRQn_Type;

3.EXTI标准外设库中断配置步骤

(1)GPIO初始化配置
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_3;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
    //初始化I/O口为浮空输入模式
(2)开启I/O端口的时钟和复用时钟

 GPIO用作EXTI外部中断功能引脚,必须开启复用功能,打开相应引脚的AFIO时钟,通过调用RCC_APB2PeriphClockCmd()函数实现:

RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOE|RCC_APB2Periph_AFIO,ENABLE);
(3)设置I/O引脚与中断线路的映射关系

 GPIO引脚作为中断功能引脚用来触发外部中断时,需将GPIO的引脚与相应的中断线关联在一起,使用库函数GPIO_EXTILineConfig来实现这种映射关系。如

GPIO_EXTILineConfig(GPIO_PortSourceGPIOE,GPIO_PinSource3);
(4) 初始化EXTI,配置EXTI相关参数并使能
EXTI_InitStructure.EXTI_Line = EXTI_Line3; //设置外部中断线3中断
EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt; 
     //设置为中断模式
EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Rising_Falling;
     //设置为下降沿触发
EXTI_InitStructure.EXTI_LineCmd = ENABLE; //使能该外部中断线
EXTI_Init(&EXTI_InitStructure);

二.实践出真知

(一)实验要求

1.实验一

用stm32F103核心板的GPIOA端一管脚接一个LED,GPIOB端口一引脚接一个开关(用杜邦线模拟代替)。采用中断模式编程,当开关接高电平时,LED亮灯;接低电平时,LED灭灯。如果完成后,尝试在main函数while循环中加入一个串口每隔1s 发送一次字符的代码片段,观察按键中断对串口发送是否会带来干扰或延迟。

2.实验二

当stm32接收到1个字符“s”时,停止持续发送“hello windows!”; 当接收到1个字符“t”时,持续发送“hello windows!”(提示:采用一个全局标量做信号灯)

当stm32接收到字符“stop stm32!”时,停止持续发送“hello windows!”; 当接收到字符“go stm32!”时,持续发送“hello windows!”(提示:要将接收到的连续字符保存到一个字符数组里,进行判别匹配。写一个接收字符串的函数。

(二)实验过程

1.代码
(1)实验一

LED.c

#include "stm32f10x.h"                  // Device header
 
/**
  * 函    数:LED初始化
  * 参    数:无
  * 返 回 值:无
  */
void LED_Init(void)
{
    /*开启时钟*/
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);        //开启GPIOA的时钟
    
    /*GPIO初始化*/
    GPIO_InitTypeDef GPIO_InitStructure;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_All;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(GPIOA, &GPIO_InitStructure);        
 
    GPIO_ResetBits(GPIOA, GPIO_Pin_0);
}

exti_key.c

#include "exti_key.h"
#include "misc.h"
 
void EXTI_Key_Init(void)
{   
    GPIO_InitTypeDef GPIO_InitStructure;
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB | RCC_APB2Periph_AFIO, ENABLE);
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1; // 使用 B 口的引脚 1
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
    GPIO_Init(GPIOB, &GPIO_InitStructure);
    NVIC_InitTypeDef NVIC_InitStructure;
    NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
    NVIC_InitStructure.NVIC_IRQChannel = EXTI1_IRQn; // 使用与 GPIOB 引脚 1 相关的外部中断通道
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
    NVIC_Init(&NVIC_InitStructure); 
    EXTI_InitTypeDef EXTI_InitStructure;
    EXTI_ClearITPendingBit(EXTI_Line1);  
    GPIO_EXTILineConfig(GPIO_PortSourceGPIOB, GPIO_PinSource1); // 将 GPIOB 和引脚 1 配置为外部中断
    EXTI_InitStructure.EXTI_Line = EXTI_Line1;
    EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;
    EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Falling;
    EXTI_InitStructure.EXTI_LineCmd = ENABLE;
    EXTI_Init(&EXTI_InitStructure);
}

main.c

#include "stm32f10x.h"                  // Device header
#include "LED.h"
#include "exti_key.h"
 
int main(void)
{
    LED_Init();
    GPIO_ResetBits(GPIOA,GPIO_Pin_0);
    EXTI_Key_Init();
 
    while (1)
    {
    }
}
//void EXTI1_IRQHandler(void)
//{
//    if(EXTI_GetITStatus(EXTI_Line1) != RESET)
//    {
//        GPIO_WriteBit(GPIOA,GPIO_Pin_0,(BitAction)((1-GPIO_ReadOutputDataBit(GPIOA,GPIO_Pin_0))));
//        EXTI_ClearITPendingBit(EXTI_Line1);
//    }
//}
//两种方法
uint8_t led = 1;
 
void EXTI1_IRQHandler(void)
{
      if(EXTI_GetITStatus(EXTI_Line1) != RESET)
      {
        led = ~led; //状态翻转
        //如果等于1,则PB1复位点亮,否则置1熄灭
        if(led == 1)
            GPIO_ResetBits(GPIOA,GPIO_Pin_0);
        else
            GPIO_SetBits(GPIOA,GPIO_Pin_0);    
     }
     EXTI_ClearITPendingBit(EXTI_Line1); //清除EXTI1的中断标志位
}
(2)实验二 

#include "stm32f10x.h"
#include "misc.h"
#include <string.h>
 
volatile uint8_t send_enabled = 0;  // 全局变量,控制发送行为
 
void USART_Configuration(void) {
    USART_InitTypeDef USART_InitStructure;
    GPIO_InitTypeDef GPIO_InitStructure;
 
    // 打开 GPIO 与 USART 端口的时钟
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_USART1, ENABLE);
 
    // 配置 USART1 Tx (PA.09) 为复用推挽输出
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(GPIOA, &GPIO_InitStructure);
 
    // 配置 USART1 Rx (PA.10) 为浮空输入
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
    GPIO_Init(GPIOA, &GPIO_InitStructure);
 
    // 配置 USART 参数
    USART_InitStructure.USART_BaudRate = 9600;
    USART_InitStructure.USART_WordLength = USART_WordLength_8b;
    USART_InitStructure.USART_StopBits = USART_StopBits_1;
    USART_InitStructure.USART_Parity = USART_Parity_No;
    USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
    USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;
    USART_Init(USART1, &USART_InitStructure);
 
    // 使能 USART
    USART_Cmd(USART1, ENABLE);
 
    // 使能接收中断
    USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);
 
    // 配置 NVIC
    NVIC_InitTypeDef NVIC_InitStructure;
    NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
    NVIC_Init(&NVIC_InitStructure);
}
 
void USART1_IRQHandler(void) {
    if(USART_GetITStatus(USART1, USART_IT_RXNE) != RESET) {
        char data = USART_ReceiveData(USART1);
        if(data == 's') {  // 接收到 's' 停止发送
            send_enabled = 0;
        } else if (data == 't') {  // 接收到 't' 开始发送
            send_enabled = 1;
        }
        USART_ClearITPendingBit(USART1, USART_IT_RXNE);
    }
}
 
void Delay(__IO uint32_t nCount) {
    for(; nCount != 0; nCount--);
}
 
int main(void) {
    SystemInit();
    USART_Configuration();
 
    char *str = "hello windows!\r\n";
    while(1) {
        if(send_enabled) {
            for(uint32_t i = 0; i < strlen(str); i++) {
                while(USART_GetFlagStatus(USART1, USART_FLAG_TXE) == RESET);
                USART_SendData(USART1, str[i]);
            }
        }
        Delay(5000000);
    }
}

 ②

NVIC.c

#include "stm32f10x.h"                  // Device header
 
 
void NVIC_Configuration(void) {
    NVIC_InitTypeDef NVIC_InitStructure;
 
    NVIC_PriorityGroupConfig(NVIC_PriorityGroup_0);
    NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;
    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
    NVIC_Init(&NVIC_InitStructure);
}
 


Serial.c

#include "stm32f10x.h"                  // Device header
#include <stdio.h>
#include <stdarg.h>
 
/**
  * 函    数:串口初始化
  * 参    数:无
  * 返 回 值:无
  */
void Serial_Init(void)
{
    /*开启时钟*/
    USART_InitTypeDef USART_InitStructure;
    GPIO_InitTypeDef GPIO_InitStructure;
 
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_USART1, ENABLE);
 
    // USART Tx (PA.09) 配置为复用推挽输出
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(GPIOA, &GPIO_InitStructure);
 
    // USART Rx (PA.10) 配置为浮空输入
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
    GPIO_Init(GPIOA, &GPIO_InitStructure);
 
    USART_InitStructure.USART_BaudRate = 9600;
    USART_InitStructure.USART_WordLength = USART_WordLength_8b;
    USART_InitStructure.USART_StopBits = USART_StopBits_1;
    USART_InitStructure.USART_Parity = USART_Parity_No;
    USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
    USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;
    USART_Init(USART1, &USART_InitStructure);
 
    USART_ITConfig(USART1, USART_IT_RXNE, ENABLE); // 开启接收中断
    USART_Cmd(USART1, ENABLE);
}

main.c

#include "stm32f10x.h"
#include "misc.h"
#include <string.h>
#include "Delay.h"
#include "Serial.h"
#include "NVIC.h"
 
 
#define BUFFER_SIZE 100
volatile char buffer[BUFFER_SIZE];
volatile int buffer_index = 0;
volatile int send_enabled = 0;
 
 
void USART1_IRQHandler(void) {
    if (USART_GetITStatus(USART1, USART_IT_RXNE) != RESET) {
        char data = (char)USART_ReceiveData(USART1);
        if (buffer_index < BUFFER_SIZE - 1) {
            buffer[buffer_index++] = data;
            buffer[buffer_index] = '\0';  // 保持字符串结尾
 
            char* temp_buffer = (char*)buffer; // 创建一个非 volatile 指针
 
            if (strstr(temp_buffer, "stop stm32!") != NULL) {
                send_enabled = 0;
                buffer_index = 0;  // 清空缓冲区
            } else if (strstr(temp_buffer, "go stm32!") != NULL) {
                send_enabled = 1;
                buffer_index = 0;  // 清空缓冲区
            }
        }
    }
}
 
int main(void) {
    SystemInit();
    Serial_Init();
    NVIC_Configuration();
 
    char *str = "hello windows!\r\n";
    while (1) {
        if (send_enabled) {
            for (uint32_t i = 0; i < strlen(str); i++) {
                while (USART_GetFlagStatus(USART1, USART_FLAG_TXE) == RESET);
                USART_SendData(USART1, str[i]);
            }
        }
        Delay_ms(500);
    }
}

2.实验结果
(1)实验一

 

  • 19
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值