STM32开发项目:如何配置GPIO的外部中断

如何配置GPIO的外部中断

以STM32F103为例,记录配置GPIO外部中断的一般方法与流程。

配置RCC时钟

RCC时钟配置是STM32MCU顺利运行的必备步骤,笔者使用的参考代码如下:

void RCC_Config()
{
	RCC_DeInit();
	//使能HSE,并等待 HSE 稳定
	RCC_HSEConfig(RCC_HSE_ON);
	ErrorStatus HSEStartUpStatus;
	HSEStartUpStatus = RCC_WaitForHSEStartUp();
	//HSE启动成功
	if (HSEStartUpStatus == SUCCESS)
	{
		//使能FLASH预存取缓冲区
		FLASH_PrefetchBufferCmd(FLASH_PrefetchBuffer_Enable);
		//SYSCLK周期与闪存访问时间的比例设置
		FLASH_SetLatency(FLASH_Latency_2);
		//Configures the AHB clock (HCLK): AHB clock = SYSCLK
		RCC_HCLKConfig(RCC_SYSCLK_Div1);
		//Configures the High Speed APB clock (PCLK2): APB2 clock = HCLK
		RCC_PCLK2Config(RCC_HCLK_Div1);
		//Configures the High Speed APB clock (PCLK2): APB1 clock = HCLK/2
		RCC_PCLK1Config(RCC_HCLK_Div2);
		//Configures the PLL clock source and multiplication factor: 
		//HSE oscillator clock selected as PLL clock entry
		//The PLL multiplication factor is 9
		RCC_PLLConfig(RCC_PLLSource_HSE_Div1, RCC_PLLMul_9);
		//Enables the PLL.
		RCC_PLLCmd(ENABLE);
		//等待PLL时钟稳定
		while (RCC_GetFlagStatus(RCC_FLAG_PLLRDY) == RESET)
			;
		//Configures the system clock (SYSCLK): PLL selected as system clock
		RCC_SYSCLKConfig(RCC_SYSCLKSource_PLLCLK);
		//读取时钟切换状态位,确保PLLCLK被选为系统时钟
		while (RCC_GetSYSCLKSource() != 0x08)
			;
	}
	//HSE启动失败
	else
	{
		while (1)
			;
	}
}

GPIO配置

以PB10, PB11, PB12, PB13作为外部中断输入口为例:

void GPIO_Config()
{
	GPIO_InitTypeDef GPIO_InitStructure;
	
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE);
	
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10 | GPIO_Pin_11 | GPIO_Pin_12 | GPIO_Pin_13;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_10MHz;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
	
	GPIO_Init(GPIOB, &GPIO_InitStructure);
	//选择EXTI的信号源
	GPIO_EXTILineConfig(GPIO_PortSourceGPIOB, GPIO_PinSource10);
	GPIO_EXTILineConfig(GPIO_PortSourceGPIOB, GPIO_PinSource11);
	GPIO_EXTILineConfig(GPIO_PortSourceGPIOB, GPIO_PinSource12);
	GPIO_EXTILineConfig(GPIO_PortSourceGPIOB, GPIO_PinSource13);

}

EXTI配置

EXTI (External interrupt/event controller): 外部中断/事件控制器,管理了控制器的 20个中断/事件线。每个中断/事件线都对应有一个边沿检测器,可以实现输入信号的上升沿检测和下降沿的检测。EXTI 可以实现对每个中断/事件线进行单独配置,可以单独配置为中断或者事件,以及触发事件的属性。

void EXTI_Config()
{
	EXTI_InitTypeDef EXTI_InitStructure;

	EXTI_InitStructure.EXTI_Line=EXTI_Line10 | EXTI_Line11 | EXTI_Line12 | EXTI_Line13;
	EXTI_InitStructure.EXTI_Mode=EXTI_Mode_Interrupt;
	EXTI_InitStructure.EXTI_Trigger=EXTI_Trigger_Falling;
	EXTI_InitStructure.EXTI_LineCmd=ENABLE;
	
	EXTI_Init(&EXTI_InitStructure);
}

NVIC配置

NVIC 是嵌套向量中断控制器,控制着整个芯片中断相关的功能,它跟内核紧密耦合,是内核里面的一个外设。但是各个芯片厂商在设计芯片的时候会对 Cortex-M3内核里面的 NVIC进行裁剪,把不需要的部分去掉,所以说 STM32 的 NVIC 是 Cortex-M3 的 NVIC 的一个子集。

在 NVIC 有一个专门的寄存器:中断优先级寄存器 NVIC_IPRx,用来配置外部中断的优先级,IPR 宽度为 8bit,原则上每个外部中断可配置的优先级为 0~255,数值越小,优先级越高。

但是绝大多数CM3芯片都会精简设计,以致实际上支持的优先级数减少,在F103中,只使用了高 4bit用于表达优先级的这 4bit,又被分组成抢占优先级和子优先级。

如果有多个中断同时响应,抢占优先级高的就会 抢占 抢占优先级低的优先得到执行,如果抢占优先级相同,就比较子优先级。如果抢占优先级和子优先级都相同的话,就比较他们的硬件中断编号,编号越小,优先级越高。

void NVIC_Config()
{
	NVIC_InitTypeDef NVIC_InitStructure;
	//NVIC_PriorityGroup_2: 2 bits for pre-emption priority & 2 bits for subpriority
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);

  	NVIC_InitStructure.NVIC_IRQChannel = EXTI15_10_IRQn;
  	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0x00; 
  	NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0x01;
  	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
  	
  	NVIC_Init(&NVIC_InitStructure); 
}

中断服务函数 IRQ_Handler

在启动文件 startup_stm32f10x_xd.s 中,我们已经初始化了中断向量表。实际的中断服务函数都需要重新编写,为了方便管理,可以把中断服务函数统一写在 stm32f10x_it.c 这个源文件中。关于中断服务函数的函数名必须跟启动文件里面预先设置的一样,否则系统就在中断向量表中找不到中断服务函数的入口,实现不了中断。

void EXTI15_10_IRQHandler(void)
{
	if(EXTI_GetITStatus(EXTI_Line10)!=RESET)
	{
		DoSomething_Line10();
		EXTI_ClearITPendingBit(EXTI_Line10);
	}
	else if(EXTI_GetITStatus(EXTI_Line11)!=RESET)
	{
		DoSomething_Line11();
		EXTI_ClearITPendingBit(EXTI_Line11);
	}
	else if(EXTI_GetITStatus(EXTI_Line12)!=RESET)
	{
		DoSomething_Line12();
		EXTI_ClearITPendingBit(EXTI_Line12);
	}
	else if(EXTI_GetITStatus(EXTI_Line13)!=RESET)
	{
		DoSomething_Line13();
		EXTI_ClearITPendingBit(EXTI_Line13);
	}
	else if(EXTI_GetITStatus(EXTI_Line14)!=RESET)
	{
		DoSomething_Line14();
		EXTI_ClearITPendingBit(EXTI_Line14);
	}
	else if(EXTI_GetITStatus(EXTI_Line15)!=RESET)
	{
		DoSomething_Line15();
		EXTI_ClearITPendingBit(EXTI_Line15);
	}
	else
	{

	}
}

猜你喜欢

转载自blog.csdn.net/u013441358/article/details/105931131