中断编程入门

目录

一、STM32以中断的方式点亮LED灯(标准库)

1.1、中断

1.1.1、定义

1.1.2、中断优先级与中断嵌套

1.1.3、中断处理流程

1.1.4、STM32与中断

(一)STM32中断优先级:

(二)STM32外部中断EXTI

1.2、具体过程

1.2.1、keil搭建项目

1.2.2、代码实现

1.2.3、面包板点亮LED灯

二、串口中断发送信息

2.1、实验1

2.1.1、具体实现代码

2.1.2、烧录效果

2.2、实验2

2.2.1、具体代码实现

2.2.2、烧录效果

三、总结


一、STM32以中断的方式点亮LED灯(标准库)

主要任务:用stm32F103核心板的GPIOA端一管脚接一个LED,GPIOB端口一引脚接一个开关(用杜邦线模拟代替)。采用中断模式编程,当开关接高电平时,LED亮灯;接低电平时,LED灭灯。

1.1、中断

1.1.1、定义

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

中断在嵌入式系统中占有极其重要的地位,中断机制使得系统能更有效更合理的发挥效能和提高效能。

1.1.2、中断优先级与中断嵌套

中断优先级:当有多个中断源同时请求中断时,CPU会根据中断源的轻重缓急进行判断,优先响应更加紧急的中断源。

中断嵌套:当一个中断程序正在运行时,又有新的更高优先级的中断源申请中断,CPU再次暂停当前中断程序,转而去处理新的中断程序,处理完成后依次进行返回。

1.1.3、中断处理流程

中断处理流程:中断请求、中断响应、中断服务和中断返回

中断请求:中断请求是中断源向CPU发出中断请求信号,此时中断控制系统的中断请求寄存器被置位,向CPU请求中断。

中断响应:CPU的中断系统判断中断源的中断请求是否符合中断响应条件,如果符合条件,则暂时中断当前程序并控制程序跳转到中断服务程序。

中断服务:为处理中断而编写的程序称为中断服务程序,是由开发人员针对具体中断所要实现的功能进行设计和编写的,需要由开发人员来实现。

中断返回:CPU退出中断服务程序,返回到中断请求响应之前被中止的位置继续执行主程序。这部分操作同样由硬件来实现,不需要开发人员进行处理。

1.1.4、STM32与中断

STM32的内部中断处理机制:

(一)STM32中断优先级:

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);

中断分组管理函数:

void NVIC_PriorityGroupConfig(uint32_t NVIC_PriorityGroup)

用于设置中断的优先级分组,此函数只有一个参数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

中断初始化函数(设置抢占优先级和响应优先级):

void NVIC_Init(NVIC_InitTypeDef * NVIC_InitStruct)

指向NVIC_InitTypeDef结构体的指针:

typedef struct
{
  uint8_t  NVIC_IRQChannel;//配置中断源,IRQ通道
  uint8_t  NVIC_IRQChannelPreemptionPriority; //配置抢占优先级
  uint8_t  NVIC_IRQChannelSubPriority; //配置响应优先级
  FunctionalState  NVIC_IRQChannelCmd; //使能中断通道
} NVIC_InitTypeDef;

中断优先级的判断原则:

(二)STM32外部中断EXTI

EXTI(External interrupt/event controller,外部中断/事件控制器)支持19个外部中断/事件请求,每个中断/事件都有独立的触发和屏蔽设置,具有中断模式和事件模式两种设置模式。 STM32芯片之外的外设的中断(I/O端口)由EXTI和NVIC共同负责,即STM32的每一个GPIO引脚都可以配置成一个外部中断触发源。

STM32 EXTI内部功能图:

 STM32中,每一个GPIO都可以触发一个外部中断。

GPIO的中断是以组为单位的,同组的外部中断公用一条外部中断线。     例如:PA0、PB0、PC0、PD0、PE0、PF0、PG0这些为一组,如果使用PA0作为外部中断源,那么PB0、PC0、PD0、PE0、PF0、PG0就不能同时再作为外部中断使用了,在此情况下,只能使用类似于PB1、PC2这种末端序号不同的外部中断源。

1.2、具体过程

1.2.1、keil搭建项目

1.2.2、代码实现

(一)配置RCC,把涉及到的外设的时钟全部打开

RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);  //打开GPIO时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE);   //打开AFIO时钟
//EXTI 和 NVIC已经默认开启时钟,不需要再开启

(二)配置GPIO,选择端口为输入模式(GPIOB14号引脚)

	GPIO_InitTypeDef GPIO_InitStruct;//定义配置初始化结构体
	GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IN_FLOATING;     //对于外部中断来说,要选择浮	空,上拉或者下拉输入其中一个模式,如果不清楚看ST参考手册GPIO外设配置表(113页EXTI输入线)
	GPIO_InitStruct.GPIO_Pin = GPIO_Pin_14;//我们
	GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz; 
	GPIO_Init(GPIOB, &GPIO_InitStruct);

(三)、配置AFIO,选择我们用的GPIO连接到后面EXTI

GPIO_EXTILineConfig(GPIO_PortSourceGPIOB,GPIO_PinSource14);//用AFIO配置需要的中断引脚选择

(四)、配置EXTI,选择触发方式(例如上升沿,下降沿或双边沿),触发响应方式(中断或者事件触发)

	EXTI_InitTypeDef EXTI_InitStruct;
	EXTI_InitStruct.EXTI_Line = EXTI_Line14;//选择PB14对应的14号线路;
	EXTI_InitStruct.EXTI_LineCmd = ENABLE;//开启中断
	EXTI_InitStruct.EXTI_Mode = EXTI_Mode_Interrupt;//选择是中断还是事件触发
	EXTI_InitStruct.EXTI_Trigger = EXTI_Trigger_Rising_Falling;//配置为下降沿触发
	EXTI_Init(&EXTI_InitStruct);

(五)、配置NVIC,给外设配置合适的优先级,最后通过NVIC中断信号进入CPU

	NVIC_InitTypeDef NVIC_InitStruct;
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);//先配置一下指定中断分组(这里选择抢占优先级和响应优先级)
	NVIC_InitStruct.NVIC_IRQChannel = EXTI15_10_IRQn;//这里参数需要到整个文件搜索,不同类型的芯片终端通道列表不一样,选择MD的芯片找到10-15EXTI的通道
	NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE;
	//指定优先级,因为只有一个中断源,优先级随意设置
	NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 1;  //指定抢占优先级
	NVIC_InitStruct.NVIC_IRQChannelSubPriority = 1; //指定响应优先级
	NVIC_Init(&NVIC_InitStruct);

 (六)、LED.c

#include "stm32f10x.h"                  // Device header


uint16_t LED_Count;//计数器(中断触发次数)
void LED_Init(void)
{
	GPIO_InitTypeDef GPIO_InitStruct;//定义配置初始化结构体
	EXTI_InitTypeDef EXTI_InitStruct;
	NVIC_InitTypeDef NVIC_InitStruct;
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);  //打开GPIO时钟
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE);   //打开AFIO时钟
	//EXTI 和 NVIC已经默认开启时钟,不需要再开启
	GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IN_FLOATING;     //对于外部中断来说,要选择浮空,上拉或者下拉输入其中一个模式,如果不清楚看ST参考手册GPIO外设配置表(113页EXTI输入线)
	GPIO_InitStruct.GPIO_Pin = GPIO_Pin_14;
	GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz; 
	GPIO_Init(GPIOB, &GPIO_InitStruct);
	//第三步,给AFIO配置,库函数文件在GPIO一个文件里
	GPIO_EXTILineConfig(GPIO_PortSourceGPIOB,GPIO_PinSource14);//用AFIO配置需要的中断引脚选择
	
	//第四步配置EXTI,选择触发的方式
	EXTI_InitStruct.EXTI_Line = EXTI_Line14;//选择PB14对应的14号线路;
	EXTI_InitStruct.EXTI_LineCmd = ENABLE;//开启中断
	EXTI_InitStruct.EXTI_Mode = EXTI_Mode_Interrupt;//选择是中断还是事件触发
	EXTI_InitStruct.EXTI_Trigger = EXTI_Trigger_Rising_Falling;//配置为下降沿触发
	EXTI_Init(&EXTI_InitStruct);
	//第五步,配置NVIC(NVIC属于内核,库函数需要在杂项即misc.h里寻找)
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);//先配置一下指定中断分组(这里选择抢占优先级和响应优先级)
	NVIC_InitStruct.NVIC_IRQChannel = EXTI15_10_IRQn;//这里参数需要到整个文件搜索,不同类型的芯片终端通道列表不一样,选择MD的芯片找到10-15EXTI的通道
	NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE;
	//指定优先级,因为只有一个中断源,优先级随意设置
	NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 1;  //指定抢占优先级
	NVIC_InitStruct.NVIC_IRQChannelSubPriority = 1; //指定响应优先级
	NVIC_Init(&NVIC_InitStruct);
	
}
//第六步写中断函数(在启动文件startup里面找,找对应之前开启的10-15通道)
void EXTI15_10_IRQHandler(void)//必须无参无返回值
{
	//因为有10-15EXTI都能进来所以一般首先要进行判断EXTI14的中断标志位是不是为1
	if(EXTI_GetITStatus(EXTI_Line14)== SET)
	{
			
			GPIO_InitTypeDef GPIO_InitStructure;	
			RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
			GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP ;
			GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5;
			GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
			GPIO_Init(GPIOA,&GPIO_InitStructure);
	if(GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_14)==Bit_RESET)//判断现在是低电平还是高电平,对应开关等
		{
				
			GPIO_SetBits(GPIOA,GPIO_Pin_5);
			//GPIO_ResetBits(GPIOA,GPIO_Pin_5);
		}
	    else if(GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_14)==Bit_SET)
		{
			//GPIO_SetBits(GPIOA,GPIO_Pin_5);
			GPIO_ResetBits(GPIOA,GPIO_Pin_5);
		}
				
		//每次执行中断后都应该清除中断标志位,不然会一直申请中断,程序就卡死在中断里面
	EXTI_ClearITPendingBit(EXTI_Line14);
	}
	
}

(七)LED.h

#ifndef _LED_H
#define _LED_H
void LED_Init(void);
#endif

(八)main.c

#include "stm32f10x.h"                  // Device header
#include "LED.h"
int main(void)
{
	LED_Init();
	while(1)
	{
		
	}
} 
1.2.3、面包板点亮LED灯

二、串口中断发送信息

2.1、实验1

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

2.1.1、具体实现代码

main.c

#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, &amp;GPIO_InitStructure);

    // 配置 USART1 Rx (PA.10) 为浮空输入
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
    GPIO_Init(GPIOA, &amp;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, &amp;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(&amp;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);
    }
}

2.1.2、烧录效果

2.2、实验2

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

2.2.1、具体代码实现
#include "stm32f10x.h"
#include "misc.h"
#include <string.h>

#define BUFFER_SIZE 100
volatile char buffer[BUFFER_SIZE];
volatile int buffer_index = 0;
volatile int send_enabled = 0;

void Delay(__IO uint32_t nCount) {
    for (; nCount != 0; nCount--);
}

void USART_Configuration(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, &amp;GPIO_InitStructure);

    // USART Rx (PA.10) 配置为浮空输入
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
    GPIO_Init(GPIOA, &amp;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, &amp;USART_InitStructure);

    USART_ITConfig(USART1, USART_IT_RXNE, ENABLE); // 开启接收中断
    USART_Cmd(USART1, ENABLE);
}

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(&amp;NVIC_InitStructure);
}

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();
    USART_Configuration();
    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(5000000);
    }
}
2.2.2、烧录效果

三、总结

此次我们学习了用串口中断的方式点亮LED灯,同时用串口中断的方式重做了前一篇文章信息发送的实验。对我个人来说整个过程还是比较复杂难做的,本次学习也进一步强化了我的动手能力。文章多有不足,欢迎大家批评指正!

参考博客:STM32以中断的方式点亮LED小灯(标准库)_stm32按键中断控制led灯-优快云博客

                   STM32的USART窗口通讯程序——串口通讯_uart#窗口-优快云博客

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值