文章目录

  • 需求
  • 一、外部中断
  • 二、外部中断配置(以按键为例)
  • 1.开时钟
  • 2.配置IO
  • 3.配置检测模式和屏蔽位
  • 4.开NVIC,主函数分组
  • 三、中断函数
  • 四、需求实现
  • 总结

  • 需求

    1.设备上电后打开串口。
    2.按下KEY1,串口打印“按键1触发中断”。
    3.以此类推,设置4个按键。
    4.其中按键1,2采用寄存器开发,3,4采用库函数开发。


    一、外部中断

    外部中断
    由图可知,当外部产生信号时,会先进入边沿检测电路,此时我们需要配置是上升沿还是下降沿检测。
    配置完成后,该电路会检测是否收到该上升沿/下降沿信号。此时我们还需要配置中断屏蔽寄存器,将其置为1,这样当请求挂起寄存器为1时,就会给NVIC中断寄存器发送一个中断信号。

    二、外部中断配置(以按键为例)

    1.开时钟

    PA0,key1为例,寄存器编写

    代码如下:

    	RCC->APB2ENR |= 0x05;//打开GPIO和AFIO时钟
    

    首先,由手册可知:
    通过AFIO_EXTICRx配置GPIO线上的外部中断/事件,必须先使能AFIO时钟。

    所以直接找到APB2 外设复位寄存器来开时钟

    可见,GPIOA和AFIO的时钟在第2位和第0位(0101)
    所以直接将APB2ENR或上0x05即可打开GPIO和AFIO的时钟。

    2.配置IO

    代码如下:

    	GPIOA->CRL &=~(0X0F << 0);//PA0   key1
    	GPIOA->CRL |= 0X04 << 0;
        AFIO->EXTICR[0] &= ~(0x0F);//配置GPIOA映射EXTI线
    

    PA0是key1按键,直接无脑先清0,再配置成0100浮空输入即可。
    AFIO需要配置外部中断配置寄存器:


    其中EXTICR数组分别有四个,从零开始。因为四位配置一个引脚,每组配置4个引脚,总计16个引脚(中断)。
    所以配置引脚PA0,就是将EXTICR数组的第0个配置成0000即可。(也可不配置,默认就为0)

    3.配置检测模式和屏蔽位

    代码如下:

      EXTI->RTSR &= ~(0x1); //关闭上升沿检测
      EXTI->FTSR |= 0x01;  //打开下降沿检测
      EXTI->IMR |= 0x01; //打开exti的屏蔽位
    



    根据具体情况选择上升沿还是下降沿,这里选则下降沿触发。
    又因为key1是第零位引脚,所以不用移位,直接上升沿最后一位置零关闭,下降沿最后一位或1开启。

    屏蔽器:

    屏蔽位的开启直接看引脚,第几位引脚就将IMR第几位置1。

    4.开NVIC,主函数分组

    开NVIC
    代码如下:

        NVIC_SetPriority(EXTI0_IRQn,2);NVIC设置优先级00 10
    	NVIC_EnableIRQ(EXTI0_IRQn); //NVIC使能中断通道
    

    这个直接跳转选参数就行,引脚是第几位就是几号line。
    NVIC优先级的设置是根据分组来决定的,本次分组是5(两位抢占,两位次级)。
    理解;
    将设置的2转为2进制(00 10),其中前两位为抢占位,后两位为次级。

    主函数分组(该部分写到main函数中while(1)前)
    代码如下:

    NVIC_SetPriorityGrouping(5);//两位抢占两位次级
    

    三、中断函数

    代码如下:

    //exti0的中断服务函数
    void EXTI0_IRQHandler(void)
    {
    	//判断中断标志是否被置位
    	if((EXTI->PR&(0x1<<0))!=0){
    		//如果置位,就清理标志位
    		EXTI->PR |= 0x1<<0;//写1是清除
    		printf("按键1触发中断\r\n");
    	}
    }
    

    中断服务函数在启动文件中查找,不用声明。
    要先进行PR挂起寄存器判断,为1即为发生触发请求,进入该中断。
    写1清除是规定。

    四、需求实现

    结果显示:

    完整代码:
    main.c

    #include "stm32f10x.h"
    #include "key.h"
    #include "usart.h"
    #include "stdio.h"
    
    int main()
    {
    		NVIC_SetPriorityGrouping(5);//两位抢占两位次级
        Usart1_Config(); 
    	  KEY1_Exti_PA0_init();
    		KEY2_Exti_PC4_init();
    		KEY3_Exti_PC5_init();
    		KEY4_Exti_PC6_init(); 
        while(1)
        {	
    			
        }
    }
    
    

    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

    #ifndef _USART_H_
    #define _USART_H_
    #include "stm32f10x.h"
    #include "stdio.h"
    
    void Usart1_Config();
    void SendData(uint8_t data);
    int fputc(int ch, FILE *f);
    
    
    #endif
    		
    

    key.c

    #include "stm32f10x.h"
    #include "stdio.h"
    
    //PA0
    void KEY1_Exti_PA0_init()
    {
    	RCC->APB2ENR |= 0x05;//打开GPIO和AFIO时钟
    	GPIOA->CRL &=~(0X0F << 0);//PC4   key2
    	GPIOA->CRL |= 0X04 << 0;
      AFIO->EXTICR[0] &= ~(0x0F);//配置GPIO映射EXTI线
      EXTI->RTSR &= ~(0x1); //关闭上升沿检测
    	EXTI->FTSR |= 0x01;  //打开下降沿检测
      EXTI->IMR |= 0x01; //打开exti的屏蔽位
      NVIC_SetPriority(EXTI0_IRQn,2);NVIC设置优先级
    	NVIC_EnableIRQ(EXTI0_IRQn); //NVIC使能中断通道
    }
    
    //exti0的中断服务函数
    void EXTI0_IRQHandler(void)
    {
    	//判断中断标志是否被置位
    	if((EXTI->PR&(0x1<<0))!=0){
    		//如果置位,就清理标志位
    		EXTI->PR |= 0x1<<0;//写1是清除
    		printf("按键1触发中断\r\n");
    	}
    }
    
    //PC4
    void KEY2_Exti_PC4_init()
    {
    	RCC->APB2ENR |= (0x01<<4); //打开GPIOC时钟
    	RCC->APB2ENR |= 0x01; //AFIO时钟
      GPIOC->CRL &=~(0X0F<<16);//配置PC4   key2
    	GPIOC->CRL |= (0X04<<16);//浮空输入
      AFIO->EXTICR[1] |= 0x02;//配置GPIOC映射EXTI线,外部中断配置寄存器
      EXTI->RTSR |= (0x01<<4) ; //打开上升沿检测
    	EXTI->FTSR &= ~(0x1<<4);  //关闭下降沿检测
      EXTI->IMR |= (0x01<<4); //打开exti的屏蔽位
      NVIC_SetPriority(EXTI4_IRQn,2);NVIC设置优先级
    	NVIC_EnableIRQ(EXTI4_IRQn); //NVIC使能中断通道
    }
    
    void EXTI4_IRQHandler(void)
    {
    	//判断中断标志是否被置位
    	if((EXTI->PR&(0x1<<4))!=0){
    		//如果置位,就清理标志位
    		printf("按键2触发中断\r\n");
    		EXTI->PR |= (0x1<<4);//写1是清除
    	}
    }
    
    
    
    //PC5
    void KEY3_Exti_PC5_init()
    {
      RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC, ENABLE);
      GPIO_InitTypeDef GPIO_InitStructure={0};
      GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5;
    	GPIO_InitStructure.GPIO_Speed =GPIO_Speed_50MHz;
      GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
      GPIO_Init(GPIOC, &GPIO_InitStructure);
    
      RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE);
      GPIO_EXTILineConfig(GPIO_PortSourceGPIOC, GPIO_PinSource5);
    	EXTI_InitTypeDef EXTI_InitStructure={0};
      EXTI_InitStructure.EXTI_Line = EXTI_Line5;
      EXTI_InitStructure.EXTI_LineCmd = ENABLE;
      EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;
      EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Rising;
      EXTI_Init(&EXTI_InitStructure);
      NVIC_SetPriority(EXTI9_5_IRQn,2);NVIC设置优先级00 10
    	NVIC_EnableIRQ(EXTI9_5_IRQn); //NVIC使能中断通道
    }
    
    //PC6
    void KEY4_Exti_PC6_init()
    {
      RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC, ENABLE);
      GPIO_InitTypeDef GPIO_InitStructure={0};
      GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6;
    	GPIO_InitStructure.GPIO_Speed =GPIO_Speed_50MHz;
      GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
      GPIO_Init(GPIOC, &GPIO_InitStructure);
    
      RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE);
      GPIO_EXTILineConfig(GPIO_PortSourceGPIOC, GPIO_PinSource6);
    	EXTI_InitTypeDef EXTI_InitStructure={0};
      EXTI_InitStructure.EXTI_Line = EXTI_Line6;
      EXTI_InitStructure.EXTI_LineCmd = ENABLE;
      EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;
      EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Rising;
      EXTI_Init(&EXTI_InitStructure);
      NVIC_SetPriority(EXTI9_5_IRQn,2);NVIC设置优先级00 10
    	NVIC_EnableIRQ(EXTI9_5_IRQn); //NVIC使能中断通
    }
    
    void EXTI9_5_IRQHandler(void)
    {
    		//判断中断标志是否被置位
    	if(EXTI_GetITStatus(EXTI_Line5) != RESET)
      {
    		printf("按键3触发中断\r\n");
    		//如果置位,就清理标志位
        EXTI_ClearITPendingBit(EXTI_Line5);
      }
    	//判断中断标志是否被置位
    	if(EXTI_GetITStatus(EXTI_Line6) != RESET)
      {
    		printf("按键4触发中断\r\n");
    		//如果置位,就清理标志位
        EXTI_ClearITPendingBit(EXTI_Line6);
      }
    }
    

    key.h

    #ifndef _KEY_H_
    #define _KEY_H_
    
    void KEY1_Exti_PA0_init();
    void KEY2_Exti_PC4_init();
    void KEY3_Exti_PC5_init();
    void KEY4_Exti_PC6_init();
    	
    #endif
    
    

    总结

    1.学会了使用按键进入外部中断。
    2.了解了外部中断和内部中断。
    3.对于寄存器和库函数开发流程,比较熟悉了。

    作者:小白橘颂

    物联沃分享整理
    物联沃-IOTWORD物联网 » STM32第五课:外部中断

    发表回复