STM32第四课:串口中断详解与实践
文章目录
需求
1.设备上电后四个灯灭。
2.按下KEY1,LED1灯亮,同时串口发送“LED1灯亮”。
3.再次按下KEY1,LED1灯灭,同时串口发送“LED1灯灭”。
4.以此类推,设置KEY2,KEY3,KEY4以及对应的LED灯。
5.加入中断,添加串口控制LED灯,且和按键相互不冲突。
一、中断的作用
中断就是为了解决当主程序运行时,在不影响主程序的情况下,去运行其他外设和功能。
中断作用流程:
1、外设发出请求。
2、主程序暂停当前任务,并保护现场进行压栈处理。
3.程序跳转到外设程序,开始执行。
4.执行完毕后回到跳转前,恢复并执行之前压栈处理的程序。
二、中断实现流程
1.中断优先级分组
使用中断,首先必须要设置优先级分组(一次即可)
组号-3即为次位优先级的个数(负数为零)。
注意:内核里面有8位可以配置中断的优先级分组:(stm32只支持四位优先级分组:高四位)
抢占优先级:可以打断中断,高级优先级可以打断低级优先级。
次级优先级:不能打断中断,在抢占一样的情况下,谁次级优先级高谁先执行。
每个中断在使用前也都要设置中断优先级:抢占优先级能够打断低抢占优先级的中断。
了解了以上之后,我们就可以开始配置寄存器了
NVIC_SetPriorityGrouping(X);
直接在这个函数中填写分组的序号X即可。
注意:该函数要放在main函数中,类似于初始化。
组号为7时只能打断主程序,不能打断中断。
分组代码如下:
NVIC_SetPriorityGrouping(5);//两位抢占两位次级
2.配置串口中断
首先要打开手册找串口1的接收非空中断使能位置
由上图可知CR1控制寄存器的第五位即为接收非空中断使能位置,置1打开即可。
配置中断通道的优先级。
优先级配置函数:
NVIC_SetPriority();
注意:值为0~15但是只有高4位有用。
最后,使能中断通道打开即可使用中断函数。
使能中断通道函数:
NVIC_EnableIRQ();
注意:每个中断通道都能单独打开和关闭。
配置示例代码如下:
USART_ITConfig(USART1,USART_IT_RXNE,ENABLE);//USART1->CR1 |= 0x1<<5;//使能串口1的接收非空中断
NVIC_SetPriority(USART1_IRQn,7);//设置优先级0~15
NVIC_EnableIRQ(USART1_IRQn);//使能中断通道
中断函数:
//中断服务函数 名字是固定的
//无参无返名字固定,不需要调用
void USART1_IRQHandler(void)
三、需求的实现
完整代码:
main.c
#include "stm32f10x.h"
#include "led.h"
#include "key.h"
#include "delay.h"
#include "usart.h"
#include "stdio.h"
int a,b,c,d;//LED的开关标志位
int main()
{
NVIC_SetPriorityGrouping(5);//两位抢占两位次级
Led_Init();
key_Init();
Usart1_Config();
USART_SendData(USART1, 'B');
while(1)
{
if(GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_0)==1)//key1
{
Delay_nms(10);
if(GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_0)==1)
{
while(GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_0)==1)
{}
Led_Toggle(1);
a++;
if(a==2)
{
printf("LED1灯灭\r\n");
a=0;
}
else
{
printf("LED1灯亮\r\n");
}
}
}
if(GPIO_ReadInputDataBit(GPIOC,GPIO_Pin_4)==0)//key2
{
Delay_nms(10);
if(GPIO_ReadInputDataBit(GPIOC,GPIO_Pin_4)==0)
{
while(GPIO_ReadInputDataBit(GPIOC,GPIO_Pin_4)==0)
{}
Led_Toggle(2);
b++;
if(b==2)
{
printf("LED2灯灭\r\n");
b=0;
}
else
{
printf("LED2灯亮\r\n");
}
}
}
if(GPIO_ReadInputDataBit(GPIOC,GPIO_Pin_5)==0)//key3
{
Delay_nms(10);
if(GPIO_ReadInputDataBit(GPIOC,GPIO_Pin_5)==0)
{
while(GPIO_ReadInputDataBit(GPIOC,GPIO_Pin_5)==0)
{}
Led_Toggle(3);
c++;
if(c==2)
{
printf("LED3灯灭\r\n");
c=0;
}
else
{
printf("LED3灯亮\r\n");
}
}
}
if(GPIO_ReadInputDataBit(GPIOC,GPIO_Pin_6)==0)//key4
{
Delay_nms(10);
if(GPIO_ReadInputDataBit(GPIOC,GPIO_Pin_6)==0)
{
while(GPIO_ReadInputDataBit(GPIOC,GPIO_Pin_6)==0)
{}
Led_Toggle(4);
d++;
if(d==2)
{
printf("LED4灯灭\r\n");
d=0;
}
else
{
printf("LED4灯亮\r\n");
}
}
}
}
}
//中断服务函数 名字是固定的
//无参无返名字固定,不需要调用
void USART1_IRQHandler(void)
{
uint8_t data=0;
if((USART1->SR&0x1<<5)!=0)
{//执行该中断函数的原因有很多,所以判断一下是不是接收导致的
//接收数据
data = USART_ReceiveData(USART1);//读操作,同时也是清空中断标志位
USART_SendData(USART1, data);
Led_Toggle(data);
switch(data)
{
case 1:
{
a++;
if(a==2)
{
printf("LED1灯灭\r\n");
a=0;
}
else
{
printf("LED1灯亮\r\n");
}
break;
}
case 2:
{
b++;
if(b==2)
{
printf("LED2灯灭\r\n");
b=0;
}
else
{
printf("LED2灯亮\r\n");
}
break;
}
case 3:
{
c++;
if(c==2)
{
printf("LED3灯灭\r\n");
c=0;
}
else
{
printf("LED3灯亮\r\n");
}
break;
}
case 4:
{
d++;
if(d==2)
{
printf("LED4灯灭\r\n");
d=0;
}
else
{
printf("LED4灯亮\r\n");
}
break;
}
}
}
}
usart.c
#include "usart.h"
#include "stdio.h"
void Usart1_Config()
{
//开时钟:GPIOA,USART1
RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1|RCC_APB2Periph_GPIOA,ENABLE);
//配置对应的IO口 PA9(tx):复用推挽 PA10(RX):浮空输入
GPIO_InitTypeDef GPIO_InitStruct = {0};
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_9;
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA,&GPIO_InitStruct);
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IN_FLOATING;
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_10;
GPIO_Init(GPIOA,&GPIO_InitStruct);
//配置串口1 8数据位,0校验位,1停止位,波特率115200
USART_InitTypeDef USART_InitStruct = {0};//可以通过结构体类型跳转
USART_InitStruct.USART_BaudRate = 115200;//波特率
USART_InitStruct.USART_HardwareFlowControl = USART_HardwareFlowControl_None;//硬件控制流不开
USART_InitStruct.USART_Mode = USART_Mode_Tx|USART_Mode_Rx;//打开发送和接收
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);
//配置串口1的中断
//在串口1产生接收的时候,会产生中断,我们直接去中断函数里面处理就可以了
//选择串口1的中断原因
USART_ITConfig(USART1,USART_IT_RXNE,ENABLE);//USART1->CR1 |= 0x1<<5;//使能串口1的接收非空中断
NVIC_SetPriority(USART1_IRQn,7);//设置优先级0~15
NVIC_EnableIRQ(USART1_IRQn);//使能中断通道
}
void SendData(uint8_t data)
{
while((USART1->SR&0x01<<6)==0){}//等待上次发送完成
USART1->DR = data;//发送数据
}
int fputc(int ch, FILE *f)
{
//printf函数最终会跳转到这里来运行
while((USART1->SR&0x1<<6)==0);
//发送数据
USART1->DR = (uint8_t)ch;
return ch;
}
usart.h、led、key、delay代码见上篇文章。
总结
1.学习了中断的各种理论,脑子里有了大致概念。
2.学会了如何配置中断,能够自己配置简单的中断。
3.能够将中断结合之前学过的知识进行综合应用。
作者:小白橘颂