이번 시리즈에서는 STM32F407 기반 EVM Borad인 STM32F407G-DISC1을 IAR과 Standard Peripheral Libraries로 제어하기 위한 튜토리얼 내용이 담긴 포스팅입니다.

👨‍💻 개발일지 (3) : GPIO with External Interrupt

지난번 포스팅인 🔗 STM32F407G-DISC1 개발일지 (2) STM32F407G-DISC1 Board에 내장되어있는 LED 4개를 Button을 이용해 Polling 기법으로 토글하는 방법을 알아봤다. 이번에는 외부 인터럽트와 Button을 이용해 LED를 토글하는 법을 알아보자. 만약 Polling 과 Interrupt 기법의 차이가 궁금하다면 nRF52832의 개발일지에서 확인해 보자. 👇👇

🔗 nRF52832 개발일지 (3)

1. STM32F407VG EXIT

EXIT(External Interrupt)는 외부에서 오는 신호에의해 발생하는 Interrupt를 말한다. 이러한 외부 신호는 Trigger를 이용한다. 즉 High ➞ Low, Low ➞ High로 둘 다 신호 레벨이 변경되는 시점에서 인터럽트를 발생시킨다. 이런 EXIT를 이용하면 단순한 Button 혹은 Switch를 이용해 신호를 주는 것 뿐아니라, MCU 간의 통신 등 여러가지 방법으로 사용될 수 있다.

우선 STM32F407의 외부 인터럽트가 가능한 GPIO Port 및 Pin을 알아보자. EXIT에 관련 정보는 🔗 STM32F047 Reference Manual 의 page 371에서 찾을 수 있다.

위 내용은 메뉴얼 page 382에 Figure 42. External interrupt/event GPIO mapping (STM32F405xx/07xx and STM32F415xx/17xx) 의 일부를 가져온 것이다. STM32F407 시리즈에서 각 GPIO Port는 같은 Pin 라인을 공유한다. 즉 Port A가 Line 0을 사용하면 Port B 혹은 Port C는 외부인터럽트 0를 사용하지 못한다.

Port I를 제외하면 각 Line을 16개(0 ~ 15)를 사용하므로 총16개의 외부인터럽트를 사용할 수 있지만, 실제 인터럽트 핸들러는 총 7가지의 인터럽트 핸들러가 존재한다.

  • EXIT0_IRQHandler
  • EXIT1_IRQHandler
  • EXIT2_IRQHandler
  • EXIT3_IRQHandler
  • EXIT4_IRQHandler
  • EXIT9_5_IRQHandler
  • EXIT15_10_IRQHandler

즉 외부 인터럽트 5 ~ 9 사이는 같은 인터럽트 핸들러에 진입하며, 10 ~ 15 사이의 외부인터럽트 역시 같은 인터럽트 핸들러로 진입하게 된다.

STL을 이용한 EXIT

Standard Periph Library를 이용해 STM32F407G-DISC1을 제어하는 코드를 작성해 업로드해보자. 📝

2.1 STM32F407G-DISC1 EXIT Code ✍

이번 코드 역시 LED를 토글링하는 코드지만, 이번에는 Polling이 아닌 Interrupt기반으로 좀 더 CPU 자원 낭비를 하지 않는 효율적인 코드를 작성해 보자.

/**
  ******************************************************************************
  * @file    Project/GPIO/GPIO_EXIT/main.c
  * @author  Geunhyeok Lee
  * @version V0.0.1
  * @date    05-February-2022
  * @brief   Main program
  ******************************************************************************
  */

#include "main.h"

int main()
{
	GPIO_EXIT_Configuration();

	while(1)
	{
		;
	}
	
  return 0;
}

void GPIO_EXIT_Configuration(void)
{
	GPIO_InitTypeDef GPIO_InitStructure;
	EXTI_InitTypeDef EXTI_InitStructure;
    NVIC_InitTypeDef NVIC_InitStructure;
	
	RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOD | RCC_AHB1Periph_GPIOA, ENABLE);
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_SYSCFG, ENABLE);
	
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_12 | GPIO_Pin_13 | GPIO_Pin_14 | GPIO_Pin_15;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;
	GPIO_InitStructure.GPIO_Speed = GPIO_Medium_Speed;
	GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
	GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_DOWN;
	GPIO_Init(GPIOD, &GPIO_InitStructure);
	
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN;
	GPIO_InitStructure.GPIO_Speed = GPIO_Medium_Speed;
	GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_DOWN;
	GPIO_Init(GPIOA, &GPIO_InitStructure);
	
	SYSCFG_EXTILineConfig(EXTI_PortSourceGPIOA, EXTI_PinSource0);
	
	EXTI_InitStructure.EXTI_Line = EXTI_Line0;
	EXTI_InitStructure.EXTI_LineCmd = ENABLE;
	EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;
	EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Rising;
	EXTI_Init(&EXTI_InitStructure);
	
	NVIC_InitStructure.NVIC_IRQChannel = EXTI0_IRQn;
	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0x060;
	NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0x00;
	NVIC_Init(&NVIC_InitStructure);
	
	return;
} 
/******************************** END OF FILE *********************************/
/**
	******************************************************************************
	* @file    Project/GPIO/GPIO_EXIT/main.h
	* @author  Geunhyeok Lee
	* @version V0.0.1
	* @date    06-February-2022
	* @brief   Main program header
	******************************************************************************
	*/

/* Define to prevent recursive inclusion -------------------------------------*/
#ifndef __MAIN_H
#define __MAIN_H

/* Includes ------------------------------------------------------------------*/
#include "stm32f4xx.h"

/* Exported types ------------------------------------------------------------*/
/* Exported constants --------------------------------------------------------*/
/* Exported macro ------------------------------------------------------------*/
/* Exported functions ------------------------------------------------------- */
void GPIO_EXIT_Configuration(void);

#endif /* __MAIN_H */

/******************************** END OF FILE *********************************/
/**
	******************************************************************************
	* @file    Project/GPIO/GPIO_EXIT/stm32f4xx_it.c
	* @author  Geunhyeok Lee
	* @version V0.0.1
	* @date    06-February-2022
	* @brief   Main Interrupt Service Routines.
	******************************************************************************
	*/

/* Includes ------------------------------------------------------------------*/
#include "stm32f4xx_it.h"
#include "main.h"

/** @addtogroup Template_Project
  * @{
  */

/* Private typedef -----------------------------------------------------------*/
/* Private define ------------------------------------------------------------*/
/* Private macro -------------------------------------------------------------*/
/* Private variables ---------------------------------------------------------*/
/* Private function prototypes -----------------------------------------------*/
/* Private functions ---------------------------------------------------------*/

/******************************************************************************/
/*            Cortex-M4 Processor Exceptions Handlers                         */
/******************************************************************************/

/**
  * @brief  This function handles NMI exception.
  * @param  None
  * @retval None
  */
void NMI_Handler(void)
{
}

/**
  * @brief  This function handles Hard Fault exception.
  * @param  None
  * @retval None
  */
void HardFault_Handler(void)
{
  /* Go to infinite loop when Hard Fault exception occurs */
  while (1)
  {
  }
}

/**
  * @brief  This function handles Memory Manage exception.
  * @param  None
  * @retval None
  */
void MemManage_Handler(void)
{
  /* Go to infinite loop when Memory Manage exception occurs */
  while (1)
  {
  }
}

/**
  * @brief  This function handles Bus Fault exception.
  * @param  None
  * @retval None
  */
void BusFault_Handler(void)
{
  /* Go to infinite loop when Bus Fault exception occurs */
  while (1)
  {
  }
}

/**
  * @brief  This function handles Usage Fault exception.
  * @param  None
  * @retval None
  */
void UsageFault_Handler(void)
{
  /* Go to infinite loop when Usage Fault exception occurs */
  while (1)
  {
  }
}

/**
  * @brief  This function handles SVCall exception.
  * @param  None
  * @retval None
  */
void SVC_Handler(void)
{
}

/**
  * @brief  This function handles Debug Monitor exception.
  * @param  None
  * @retval None
  */
void DebugMon_Handler(void)
{
}

/**
  * @brief  This function handles PendSVC exception.
  * @param  None
  * @retval None
  */
void PendSV_Handler(void)
{
}

/**
  * @brief  This function handles SysTick Handler.
  * @param  None
  * @retval None
  */
void SysTick_Handler(void)
{
  	
}

/******************************************************************************/
/*                 STM32F4xx Peripherals Interrupt Handlers                   */
/*  Add here the Interrupt Handler for the used peripheral(s) (PPP), for the  */
/*  available peripheral interrupt handler's name please refer to the startup */
/*  file (startup_stm32f4xx.s).                                               */
/******************************************************************************/

/**
  * @brief  This function handles PPP interrupt request.
  * @param  None
  * @retval None
  */
/*void PPP_IRQHandler(void)
{
}*/

/**
  * @brief  This function handles External0 interrupt request.
  * @param  None
  * @retval None
  */ 
void EXTI0_IRQHandler(void)
{
	if(EXTI_GetITStatus(EXTI_Line0) != RESET)
	{
		EXTI_ClearITPendingBit(EXTI_Line0);
		GPIO_ToggleBits(GPIOD, GPIO_Pin_12);
	}
}

/**
  * @}
  */ 


/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/
/**
	******************************************************************************
	* @file    Project/GPIO/GPIO_EXIT/stm32f4xx_it.h
	* @author  Geunhyeok Lee
	* @version V0.0.1
	* @date    06-February-2022
	* @brief   This file contains the headers of the interrupt handlers.
	******************************************************************************
	*/
 
/* Define to prevent recursive inclusion -------------------------------------*/
#ifndef __STM32F4xx_IT_H
#define __STM32F4xx_IT_H

#ifdef __cplusplus
 extern "C" {
#endif 

/* Includes ------------------------------------------------------------------*/
#include "stm32f4xx.h"

/* Exported types ------------------------------------------------------------*/
/* Exported constants --------------------------------------------------------*/
/* Exported macro ------------------------------------------------------------*/
/* Exported functions ------------------------------------------------------- */

void NMI_Handler(void);
void HardFault_Handler(void);
void MemManage_Handler(void);
void BusFault_Handler(void);
void UsageFault_Handler(void);
void SVC_Handler(void);
void DebugMon_Handler(void);
void PendSV_Handler(void);
void SysTick_Handler(void);

void EXTI0_IRQHandler(void);

#ifdef __cplusplus
}
#endif

#endif /* __STM32F4xx_IT_H */

/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/

지난 포스팅에 GPIO를 제하기위한 GPIO_Configuration 함수에 몇가지 내용을 추가하고 이름만 바꿔 GPIO_EXIT_Configuration 함수를 만들고 그 외 변경사항은 이제부터 인터럽트를 추가하기 때문에 STL 예제 템플릿에 맞춰 stm32f4xx_it.cstm32f4xx_it.h 파일을 만들고 필요한 인터럽트 핸들러를 추가했다.

2.2 STM32F407G-DISC1 EXIT Code 살펴보기 👨‍🏫

인터럽트 기능이 추가된 만큼 지난번과 비교해 달라진 부분을 살펴보자. 가장 먼저 눈에 띄는 것은 새로운 구조체이다.

EXTI_InitTypeDef EXTI_InitStructure;
NVIC_InitTypeDef NVIC_InitStructure;

EXTI_InitTypeDef 구조체는 외부 인터럽트의 초기화를 위한 구조체이며, NVIC_InitTypeDef 구조체는 인터럽트가 발생되면 ARM 코어에서 이 인터럽트의 우선순위 혹은 인터럽트 핸들러주소 같은 정보를 초기화 해주는 구조체이다.

RCC_APB2PeriphClockCmd(RCC_APB2Periph_SYSCFG, ENABLE);

SYSCFG_EXTILineConfig(EXTI_PortSourceGPIOA, EXTI_PinSource0);

EXTI_InitStructure.EXTI_Line = EXTI_Line0;
EXTI_InitStructure.EXTI_LineCmd = ENABLE;
EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;
EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Rising;
EXTI_Init(&EXTI_InitStructure);

NVIC_InitStructure.NVIC_IRQChannel = EXTI0_IRQn;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0x060;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0x00;
NVIC_Init(&NVIC_InitStructure);

이제 나머지 부분을 살펴보자. 외부 인터럽트를 이용하기 위해서는 System configuration controller(SYSCFG) 라는 컨트롤러의 레지스터를 이용해야한다. 당연히 이 컨트롤러를 활성화하기 위해 연결된 버스에 클록을 인가시켜야 하며 연결된 버스는 APB2이다.

SYSCFG_EXTILineConfig 함수는 SYSCFG 레지스터인 SYSCFG_EXTICR4x(x: 1 ~ 4)에 적절한 값을 할당하는 함수이다. 이 함수를 이용하면 GPIO Port는 어떤 외부 인터럽트 Pin을 사용하는지를 명시해 준다.

나머지는 구조체 멤버변수를 초기화하는 내용이므로 간단하게만 살펴보자.

  • EXTI_Line: EXTI_Line0 ~ 23까지 사용하며, 16~23 까지는 특별한 이벤트를위해 사용되며 나머지는 일반적인 외부 인터럽트로 사용할 수 있다.

  • EXTI_LineCmd: 외부 인터럽트를 사용/비사용을 정한다.

  • EXTI_Mode: 외부 인터럽트 혹은 외부 이벤트를 정한다.

    • EXTI_Mode_Interrupt
    • EXTI_Mode_Event
  • EXTI_Trigger: 외부 인터럽트의 트리거를 정한다.

    • EXTI_Trigger_Rising
    • EXTI_Trigger_Falling
    • EXTI_Trigger_Rising_Falling
  • NVIC_IRQChannel : 인터럽트 핸들러를 설정한다(정해져 있음).

  • NVIC_IRQChannelCmd: 외부 인터럽트 핸들러의 사용/비사용을 정한다.

  • NVIC_IRQChannelPreemptionPriority: 인터럽트의 주 우선순위

  • NVIC_IRQChannelSubPriority: 인터럽트의 부 우선순위

마지막으로 인터럽트 핸들러에 Break point를 설정하고 버튼을 누르면 핸들러로 잘 진입하는 모습을 확인할 수 있다.

🔗 Cheesam31 GitHub STM32F407VG_Basic Repository(GPIO_EXIT)

0개의 댓글