본문 바로가기
Robot/Device Control

[STM32F10x-StdPeriph] 7. Interrupt를 이용한 GPIO Input Control

by lifeseed 2015. 4. 17.

0. 들어가기전에...

GPIO에 S/W를 비롯한 외부 Signal을 연결하였을 때, 이에 대한 효과적인 처리를 위한 방법을 살펴보자.

 

예상 되는 구간에서 신호 상태를 확인하려면, 원하는 소스에 GPIO 데이터를 읽어서 High 혹은 Low 값을 기다렸다가 처리하면 되지만, 비 주기적으로 신호가 발생하면 이는 어떻게 처리를 할까??

 

아시는 분은 다 아시겠지만 바로 이때를 위해 Interrupt 를 사용한다.

Interrupt는 신호가 발생되면 하던 일을 정리하고 Interrupt Handler를 호출한다.

호출된 Interrupt Handler에서 할일을 예약만 해두던, 아니면 실제 동작을 처리하던 입력된 시그널에 대한 처리를 수행하면 되는 것이다.

 

그럼 GPIO를 통해 들어오는 값을 Detect해서 Interrupt를 발생시킬 수 있을까?

물론 가능하다. STM32에서는 EXTI 라는 Peripheral을 이용하여 GPIO를 통해 들어온 신호를 Interrupt 로 처리하도록 한다.

 

1. Configuration

Step1] AFIO Enable

EXTI를 이요하려면 AFIO 블럭이 Enable 되어야 한다.

즉 RCC Register에서 AFIO 부분을 Enable시켜 주어야 한다.

 

RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO);


 

Step2] AFIO_EXTICRx 설정

STM32F103RB 기준으로 EXTI는 16개의 Interrupt Source를 제공한다.

이 때 이용되는 Interrupt Handler의 개수는 총 7개이다.

EXTI0 ~ 4번 라인을 위해 각각 하나씩 Interrupt Handler 가 할당되며, EXTI5~9 를 위해 하나의 Interrupt Handler, 그리고 마지막으로 EXTI10~15를 위해 하나의 Interrupt Handler가 할당되어 있다.

(물론 Handler의 이름은 사용자가 다르게 정의해서 사용해도된다. 각 Chipset에 맞는 IRQ Number만 맞춰주면 된다.)

 

EXTI Line 

 IRQ Number

Interrupt Handler 

 EXTI0

 IRQ 6

 EXTI0_IRQHandler

 EXTI1 

 IRQ 7 

 EXTI1_IRQHandler

 EXTI2

 IRQ 8 

 EXTI2_IRQHandler

 EXTI3

 IRQ 9

 EXTI3_IRQHandler

 EXTI4

 IRQ 10 

 EXTI4_IRQHandler

 EXTI5-9

 IRQ 23

 EXTI9_5_IRQHandler

 EXTI10-15

 IRQ 40

 EXTI15_10_IRQHandler

 

각각의 EXTI Line은 GPIO의 Line과 연결되어 있다.

즉 GPIO A, B, C, D ~ G 모든 포트에 대해 각각의 0번 Pin이 EXIT Line0에 연결가능하며, 1번 핀은 EXIT 1 Line에 연결된다.

이때 GPIO B 포트의 Pin0 을 EXTI Line0에 연결 시키고 싶을 경우엔 다음과 같이 AFIO_EXTICRx Register를 설정하게 된다.

Line 0~ 3 번은 AFIO_EXTICR1, Line 4 ~ 7 번은 AFIO_EXTICR2, 8~11번은 AFIO_EXTICR3, 12~15번은 AFIO_EXTICR4 를 이용하여 설정한다.
 

 

아래는 AFIO_EXTICR1 에대한 설명이지만, AFIO_EXTICR4까지 모두 Port에 대한 설정이므로 그 내용은 동일하다.

 

Step3] EXTI Setup
아래는 EXTI Regiser Map이다.

GPIO를 통해 입력되는 신호를 Interrupt로 처리하기 위해서는 아래 붉은색 Box 처리된 Register를 이용하게된다.

 


 

- EXTI_IMR  : Interrupt Mask Register : Interrupt를 사용하고자 하는 입력 Pin에 해당하는 Bit를 1로 설정한다.

- EXTI_RTSR : Rising Trigger(신호값이 0 -> 1 로 변할 때) 시 Interrupt를 발생시키고자 할때 입력 Pin 에  해당하는 Bit를 1로 설정한다.

- EXTI_FTSR : Falling Trigger(신호값이 1 -> 0 로 변할 때) 시 Interrupt를 발생시키고자 할때 입력 Pin 에  해당하는 Bit를 1로 설정한다.

cf) EXTI_RTSR 및 EXTI_FTSR 모두 1로 설정하면 Rising Falling 에 대하여 모두 Interrupt를 발생시킨다.

- EXTI_PR : Interrupt가 발생될 때 입력 Pin에 해당하는 비트가 1로 설정되며, 동일한 Bit에 1을 써주면 Interrupt가 Clear 된다.

 

Step 4] NVIC 설정

Cortex M은 외부 Interrupt 는 NVIC를 통해 Control 한다.

따라서 이에 대한 설정은 당연히 추가 되어야 한다.

NVIC에 대한 세부적인 내용은 다른 게시물에 정리해 두었다.  http://lifeseed.tistory.com/66 

 

2. Example

GPIO Port B의 0번 Pin으로 부터 Rising / Falling Signal에 대하여 Interrupt를 발생시키는 예제코드를 살펴보자.

1) Configuration

 

void Configure_PB0(void) {
 /* Set variables used */
 GPIO_InitTypeDef GPIO_InitStruct;
 EXTI_InitTypeDef EXTI_InitStruct;
 NVIC_InitTypeDef NVIC_InitStruct;

 

 /* Enable clock for GPIOB & AFIO*/
 RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO|RCC_APB2Periph_GPIOB, ENABLE);

 

 

/* Configuration PB0 as Input Mode */

 GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IPD;
 GPIO_InitStruct.GPIO_Pin = GPIO_Pin_0;
 GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
 GPIO_Init(GPIOB, &GPIO_InitStruct);

 

/* PB0 is connected to EXTI_Line0 :: Configuration AFIO_EXTICRx */

 GPIO_EXTILineConfig(0x1, 0);  // Port Source GPIOB : 0x1  , Pin Source : 0

 

/* EXTI Line0 Configuration */
 EXTI_InitStruct.EXTI_Line = EXTI_Line0;
 EXTI_InitStruct.EXTI_LineCmd = ENABLE;   /* Enable interrupt */
 EXTI_InitStruct.EXTI_Mode = EXTI_Mode_Interrupt;    /* Interrupt mode */
 EXTI_InitStruct.EXTI_Trigger = EXTI_Trigger_Rising_Falling;   /* Triggers on rising and falling edge */
 EXTI_Init(&EXTI_InitStruct);

 

 /* Enable NVIC for EXTI0 Interrupt */
 NVIC_InitStruct.NVIC_IRQChannel = EXTI0_IRQn;
 NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 0x00;
 NVIC_InitStruct.NVIC_IRQChannelSubPriority = 0x00;
 NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE;
 NVIC_Init(&NVIC_InitStruct);
}

 

2) Interrupt Handler

 

void EXTI0_IRQHandler(void) {
  if (EXTI_GetITStatus(EXTI_Line0) != RESET) {
    /* Todo for PB0 Interrupt */

    process_pb0_signal();

    EXTI_ClearITPendingBit(EXTI_Line0);  /* Clear interrupt flag */
 }
}

 

3) Main Code

 

void main(void)

{

 Configure_PB0();

 while (1)
 {

 }

}

 

 

이렇게 코딩하고 코드를 실행하면, PB0로 신호가 (0->1, 1->0 로) 변경될 때 마다

사용자가 정의한 process_pb0_signal(); 라는 함수가 호출된다.