STM32第四课:串口中断详解与实践

文章目录

  • 需求
  • 一、中断的作用
  • 二、中断实现流程
  • 1.中断优先级分组
  • 2.配置串口中断
  • 三、需求的实现
  • 总结

  • 需求

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

    作者:小白橘颂

    物联沃分享整理
    物联沃-IOTWORD物联网 » STM32第四课:串口中断详解与实践

    发表回复