STM32第五课:外部中断


需求

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.对于寄存器和库函数开发流程,比较熟悉了。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值