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.能够将中断结合之前学过的知识进行综合应用。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值