STM32中断编程入门指南
目录
1.中断原理
STM32GPIO外部中断简图:
配置GPIO口,利用AFIO实现GPIO到EXTI映射。EXTI可以监测指定GPIO口的电平信号,当其指定的GPIO口产生电平变化时,EXTI将立即向NVIC发出中断申请,经过NVIC裁决后即可中断CPU主程序,使CPU执行EXTI对应的中断程序。
使用STM32 USART外部中断的步骤:
1,打开时钟---GPIOA、USART1,(AFIO)
2.GPIO初始化
3.串口初始化
4.配置中断源,并且给这个中断源配置相应的抢占优先级和执行优先级
5.使能串口
中断过程:
AFIO:在STM32中,AFIO主要完成两个任务:复用功能引脚重映射(当需要使用PA9和PA10口时,就可以把USART1的管脚进行重映射,PB6,PB7(查表))、中断引脚选择
EXTI:
NVIC基本结构
NVIC优先级分组:
NVIC的中断优先级由优先级寄存器的4位(0~15)决定,这4位可以进行切分,分为高n位的抢占优先级和低4-n位的响应优先级
注意:1.具有高抢占式优先级的中断可以在具有低抢占式优先级的中断服务程序执行过程中被响应,即中断嵌套
2.在抢占式优先级相同的情况下,如果有低子优先级中断正在执行,高子优先级的中断要等待已被响应的低子优先级中断执行结束后才能得到响应,即子优先级不支持中断嵌套(排队)
2.开关控制LED亮灭(中断方式)
内容:用stm32F103核心板的GPIOA端一管脚接一个LED,GPIOB端口一引脚接一个开关(用杜邦线模拟代替)。采用中断模式编程,当开关接高电平时,LED亮灯;接低电平时,LED灭灯。
接线图:
代码部分:
/*main函数*/
#include "stm32f10x.h"
#include "exti_key.h"
uint8_t led =1;
void led_off()
{
GPIO_WriteBit(GPIOB,GPIO_Pin_5,0);
}
void led_on()
{
GPIO_WriteBit(GPIOB,GPIO_Pin_5,1);
}
void GPIOB_Init()
{
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Mode=GPIO_Mode_Out_PP;
GPIO_InitStructure.GPIO_Speed=GPIO_Speed_2MHz;
GPIO_InitStructure.GPIO_Pin=GPIO_Pin_5;
GPIO_Init(GPIOB,&GPIO_InitStructure);
}
void EXTI3_IRQHandler(void)
{
if(EXTI_GetITStatus(EXTI_Line3) == SET)
{
EXTI_ClearITPendingBit(EXTI_Line3);
led = ~led;
if(led == 1)
led_on();
else
led_off();
}
}
int main(void)
{
GPIOB_Init();//GPIOB——pin5初始化
GPIO_WriteBit(GPIOB,GPIO_Pin_5,1);//默认为点亮状态
EXTI_Key_Init();
while (1)
{
}
}
/*exti_key.c文件*/
#include "exti_key.h"
#include "misc.h"
void EXTI_Key_Init(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA|RCC_APB2Periph_AFIO,ENABLE);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_3;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;//外界没有输入时输入电平却容易受到外界电磁以及各种干扰的影响
GPIO_Init(GPIOA,&GPIO_InitStructure);
NVIC_InitTypeDef NVIC_InitStructure;
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
NVIC_InitStructure.NVIC_IRQChannel = EXTI3_IRQn;
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_Line3);
GPIO_EXTILineConfig(GPIO_PortSourceGPIOA,GPIO_PinSource3);
EXTI_InitStructure.EXTI_Line = EXTI_Line3;
EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;
EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Falling;
EXTI_InitStructure.EXTI_LineCmd = ENABLE;
EXTI_Init(&EXTI_InitStructure);
}
效果展示:
3.用串口中断的方式完成上篇博客的实验
3.1hello windows!
内容:接收到字符“s”时,停止持续发送“hello windows!”; 当接收到字符“t”时,持续发送“hello windows!”(提示:采用一个全局标量做信号灯)
USART中断的类型有很多,本实验用到的是:
接收中断(RXNEIE – RX Not Empty Interrupt Enable): 当接收缓冲区中接收到新数据,即接收到的数据移入USART_DR(数据寄存器)且USART_CR1中的RXNE位被置位时,会产生中断。这表示可以读取接收到的数据了。
接收中断,也就是每次接收完一个字符后就会触发一次中断
#include "stm32f10x.h"
#include "Delay.h"
int send=0;//设置标志位
void hellowindows()
{
if(send==1)
{
int i;
char a [14]={'h','e','l','l','o','w','i','n','d','o','w','s','!',' '};
for (i = 0; i < 14; i++)
{
USART_SendData(USART1,a[i]);
while (USART_GetFlagStatus(USART1, USART_FLAG_TXE) == RESET);
}
Delay_s(1);
}
}
void USART1_IRQHandler(void)
{
if(USART_GetITStatus(USART1, USART_IT_RXNE) != RESET)
{
uint8_t receivedChar = USART_ReceiveData(USART1);
if (receivedChar == 's')
{
send = 0;
}
else if (receivedChar == 't')
{
send = 1;
}
}
}
int main(void)
{
USART_InitTypeDef USART_InitStructure;
// 1,打开时钟---GPIOA(串口1),AFIO
RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1|RCC_APB2Periph_GPIOA,ENABLE);
// 2,GPIO初始化
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Pin =GPIO_Pin_9;
GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode =GPIO_Mode_AF_PP;
GPIO_Init(GPIOA,&GPIO_InitStructure );
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;
GPIO_InitStructure.GPIO_Mode=GPIO_Mode_IN_FLOATING;
GPIO_Init(GPIOA,&GPIO_InitStructure);
// 3,串口初始化
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_Mode=USART_Mode_Tx|USART_Mode_Rx;
USART_InitStructure.USART_HardwareFlowControl=USART_HardwareFlowControl_None;
USART_Init(USART1, &USART_InitStructure);
USART_ClearFlag(USART1, USART_FLAG_TC);
// 配置中断源
USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);//当USART串口接收到数据的时候,就触发USART中断
// 4.给这个中断源配置相应的抢占优先级和执行优先级
NVIC_InitTypeDef NVIC_InitStructure;
NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=3; //设置抢占(主)优先级
NVIC_InitStructure.NVIC_IRQChannelSubPriority =3; // 设置子优先级
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
// 5,使能串口
USART_Cmd(USART1, ENABLE);
while(1)
{
hellowindows();
}
}
效果如下:
3.2hello windows(plus)
内容:当stm32接收到字符“stop stm32!”时,停止持续发送“hello windows!”; 当接收到字符“go stm32!”时,持续发送“hello windows!
思路:配置USART接收中断,也就是每次接收完一个字符后就会触发一次中断,在主程序的while循环中执行hellowindows()函数,以及用strcmp函数,将缓存区的数组(receive_data[11])与“go stm32!”和“stop stm32!”做比较,如果receive_data[11]的内容跟“go stm32!”相同,那么send就被赋值为1,count被赋值为0,数组清空(以便下次接收数据),helloWindows()函数开始输出“hellowindows!”
#include "stm32f10x.h"
#include "Delay.h"
int send=0;//全局变量,记录状态
int count=0;//全局变量,表示缓冲区的数组的下标
char receive_data[11];//接收缓存区
void receive_data_init()//接收缓存区初始化
{
count=0;
for(int i=0;i<11;i++)
{
receive_data[i]=0;
}
}
void hellowindows()
{
if(send==1)//当send为1,输出hellowindows!
{
int i;
char a [14]={'h','e','l','l','o','w','i','n','d','o','w','s','!',' '};
for (i = 0; i < 14; i++)
{
USART_SendData(USART1,a[i]);
while (USART_GetFlagStatus(USART1, USART_FLAG_TXE) == RESET);
}
Delay_s(1);//延时1s
}
}
void USART1_IRQHandler(void)//中断函数,接收数据存入接收缓冲区
{
if(USART_GetITStatus(USART1, USART_IT_RXNE) != RESET)//判断中断是接收数据中断
{
receive_data[count]= USART_ReceiveData(USART1); //接收字符
count++;
}
}
int main(void)
{
USART_InitTypeDef USART_InitStructure;
// 1,打开时钟---GPIOA(串口1),AFIO
RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1|RCC_APB2Periph_GPIOA,ENABLE);
// 2,GPIO初始化
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Pin =GPIO_Pin_9;
GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode =GPIO_Mode_AF_PP;
GPIO_Init(GPIOA,&GPIO_InitStructure );
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;
GPIO_InitStructure.GPIO_Mode=GPIO_Mode_IN_FLOATING;
GPIO_Init(GPIOA,&GPIO_InitStructure);
// 3,串口初始化
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_Mode=USART_Mode_Tx|USART_Mode_Rx;
USART_InitStructure.USART_HardwareFlowControl=USART_HardwareFlowControl_None;
USART_Init(USART1, &USART_InitStructure);
USART_ClearFlag(USART1, USART_FLAG_TC);
// 配置中断源
USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);//当USART串口接收到数据的时候,就触发USART中断
// 4.给这个中断源配置相应的抢占优先级和执行优先级
NVIC_InitTypeDef NVIC_InitStructure;
NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=3; //设置抢占(主)优先级
NVIC_InitStructure.NVIC_IRQChannelSubPriority =3; // 设置子优先级
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
// 5,使能串口
USART_Cmd(USART1, ENABLE);
//-----------------------------------------------(同上)---------------------------------------------------
receive_data_init();//初始化接收缓存区
while(1)
{
hellowindows();
if ( strcmp(receive_data,"stop stm32!")==0)
{
send = 0;//结束发送
receive_data_init();
}
else if (strcmp(receive_data,"go stm32!")==0)
{
send = 1;//发送数据
receive_data_init();//重新初始化接收缓存区,以便下次接收数据
}
}
}
效果展示:
总结:
收获构成了STM32中断编程的基础,为进一步深入学习高级中断应用、优化系统性能及解决实际问题打下了坚实的基础。
参考链接:
stm32f103——中断——UART中断服务函数_stm32中断服务函数-CSDN博客
【STM32学习笔记】(12)——NVIC(嵌套向量中断控制器)详解-CSDN博客
作者:落笔太慌张~