6. 임베디드 실습 : 버튼 동작(Polling, Interrupt)
버튼을 동작시켜 보자. 두 가지 방식이 있다. 첫 번째 방식은 폴링(Polling)이고, 나머지 하나는 인터럽트(Interrupt)이다. 폴링 방식은 장치, 프로그램 등에 대해 충돌 회피, 동기화 처리 등을 목적으로 다른 장치 또는 프로그램의 상태를 주기적으로 검사하여 일정 조건이 만족하는 지를 판단하는 것이다. 실습할 것을 예로 들면, 버튼이 눌렸는 지를 판단하기 위해, main loop에 상에 주기적으로 버튼이 눌렸는 지 판단하는 함수를 추가하는 것이다. 인터럽트 방식은 폴링 방식과는 다르다. 인터럽트 방식은 프로그램이 실행되고 있는 중에, 하드웨어적인 오류, 사용자 정의한 상황 등 예외상황이 발생하여 처리가 필요할 때, MCU가 이를 먼저 치리하도록 하는 것이다. 실습의 예로 들면 버튼이 눌릴 경우, 인터럽트가 발생하도록 설정하여, 눌릴 때마다 정의한 동작을 수행할 수 있도록 하는 것이다. 인터럽트의 경우 MCU가 무작정 신호를 기다릴 필요가 없다는 점에서 폴링 방식에 비해 부하가 줄어든다는 장점이 있지만. 인터럽트가 너무 많이 발생할 경우, 본 프로그램의 수행에 방해가 될 수 있다. 자주 인터럽트가 발생될 거라 예상되는 경우 인터럽트 방식을 사용하기 보다는 폴링 방식을 사용하는 것이 나을 수 있다.
이번 실습에서는 보드 상에 부착된 스위치(PORTC, 13 pin)가 눌렸을 경우 크고 키는 동작을 수행할 수 있도록 해보자. 레퍼런스 메뉴얼은 항상 참조하기를 바란다.(모든 내용을 글로 다루기에는 분량이 너무 많다.)
우선 폴링 방식을 다루어 볼 것이다. 폴링 방식은 전의 실습에서 GPIO를 다루었던 방식과 매우 유사하다.
- 클락 소스 배분
- 핀 설정
- 눌렸는 지 안 눌렀는 지를 판단
다음은 실습에서 사용할 레지스터이다.
다음은 코드이다.
/*resistor.h*/
#define PERIPHERAL_BASE ((uint32_t)0x40000000)
#define APB2PERIPH_BASE (PERIPHERAL_BASE + 0x10000)
#define AHBPERIPH_BASE (PERIPHERAL_BASE + 0x20000)
#define GPIOA_BASE (APB2PERIPH_BASE + 0x0800)
#define RCC_BASE (AHBPERIPH_BASE + 0x1000)
typedef struct
{
uint32_t CRL;
uint32_t CRH;
uint32_t IDR;
uint32_t ODR;
uint32_t BSRR;
uint32_t BRR;
uint32_t LCKR;
} GPIO_type;
typedef struct
{
uint32_t CR;
uint32_t CFGR;
uint32_t CIR;
uint32_t APB2RSTR;
uint32_t APB1RSTR;
uint32_t AHBENR;
uint32_t APB2ENR;
uint32_t APB1ENR;
uint32_t BDCR;
uint32_t CSR;
} RCC_type;
#define GPIOA ((volatile GPIO_type *) GPIOA_BASE)
#define GPIOB ((volatile GPIO_type *)(GPIOA_BASE + 0x400))
#define GPIOC ((volatile GPIO_type *)(GPIOA_BASE + 0x800))
#define GPIOD ((volatile GPIO_type *)(GPIOA_BASE + 0xC00))
#define GPIOE ((volatile GPIO_type *)(GPIOA_BASE + 0x1000))
#define GPIOF ((volatile GPIO_type *)(GPIOA_BASE + 0x1400))
#define GPIOG ((volatile GPIO_type *)(GPIOA_BASE + 0x1800))
#define RCC ((volatile RCC_type *) RCC_BASE)
/*gpio.c*/
void gpioInit()
{
uint32_t reg32;
RCC->APB2ENR |= (1 << 2); //port A
RCC->APB2ENR |= (1 << 4); //port C
//port a, 5 : output mode(50MHz, push-pull)
reg32 = GPIOA->CRL;
reg32 &= ~((uint32_t) 0b1111 << 20);
GPIOA->CRL = reg32 | (0b11 << 20) | (0b00 << 22);
//port c, 13 : input mode(pull-up/pull-down)
reg32 = GPIOC->CRH;
reg32 &= ~((uint32_t) 0b1111 << 20);
GPIOC->CRH = reg32 | (0b00 << 20) | (0b10 << 22);
}
bool gpioInput(uint8_t pin)
{
return (GPIOC->IDR & (1 << pin));
}
/*main.c*/
#include "clock.h"
#include "led.h"
#include "gpio.h"
int main()
{
rcc_init();
systick_init();
gpioInit();
while(1)
{
if(gpioInput(13))
gpioToggle(5);
msDelay(10);
}
return 0;
}
자 이제는 인터럽트 방식으로 버튼을 동작시켜 보자. 방법은 다음과 같다.
- afio, port c에 클럭 소스를 배분한다.
- port c, 13 핀 설정을 리셋한다.
- portc, 13 핀의 입력이 인터럽트를 발생시킬 수 있도록 설정한다.
- 13 번 라인에 대해서 인터럽트, 이벤트 마스크를 해제한다.
- Rising Trigger를 활성화하고, Falling Trigger를 비활성화 한다.
- NVIC를 활성화 한다.
다음은 사용한 레지스터이다. (포트 활성화, GPIO 레지스터는 위 쪽에 있다!)
다음은 코드이다.
/*resistor.h*/
#define PERIPHERAL_BASE ((uint32_t)0x40000000)
#define APB2PERIPH_BASE (PERIPHERAL_BASE + 0x10000)
#define AHBPERIPH_BASE (PERIPHERAL_BASE + 0x20000)
#define GPIOA_BASE (APB2PERIPH_BASE + 0x0800)
#define AFIO_BASE (APB2PERIPH_BASE + 0x0000)
#define EXTI_BASE (APB2PERIPH_BASE + 0x0400)
#define RCC_BASE (AHBPERIPH_BASE + 0x1000)
typedef struct
{
uint32_t CRL;
uint32_t CRH;
uint32_t IDR;
uint32_t ODR;
uint32_t BSRR;
uint32_t BRR;
uint32_t LCKR;
} GPIO_type;
typedef struct
{
uint32_t CR;
uint32_t CFGR;
uint32_t CIR;
uint32_t APB2RSTR;
uint32_t APB1RSTR;
uint32_t AHBENR;
uint32_t APB2ENR;
uint32_t APB1ENR;
uint32_t BDCR;
uint32_t CSR;
} RCC_type;
typedef struct
{
uint32_t IMR;
uint32_t EMR;
uint32_t RTSR;
uint32_t FTSR;
uint32_t SWIER;
uint32_t PR;
} EXTI_type;
typedef struct
{
uint32_t EVCR;
uint32_t MAPR;
uint32_t EXTICR1;
uint32_t EXTICR2;
uint32_t EXTICR3;
uint32_t EXTICR4;
uint32_t MAPR2;
} AFIO_type;
#define GPIOA ((volatile GPIO_type *) GPIOA_BASE)
#define GPIOB ((volatile GPIO_type *)(GPIOA_BASE + 0x400))
#define GPIOC ((volatile GPIO_type *)(GPIOA_BASE + 0x800))
#define GPIOD ((volatile GPIO_type *)(GPIOA_BASE + 0xC00))
#define GPIOE ((volatile GPIO_type *)(GPIOA_BASE + 0x1000))
#define GPIOF ((volatile GPIO_type *)(GPIOA_BASE + 0x1400))
#define GPIOG ((volatile GPIO_type *)(GPIOA_BASE + 0x1800))
#define AFIO ((volatile AFIO_type *) AFIO_BASE)
#define RCC ((volatile RCC_type *) RCC_BASE)
#define EXTI ((volatile EXTI_type *) EXTI_BASE)
/*interrupt.c*/
void nvic_irq_enable(uint8_t pos)
{
int i = pos/32;
if (i == 0)
NVIC->ISER0 |= (1 << (pos%32));
else if (i == 1)
NVIC->ISER1 |= (1 << (pos%32));
else if (i == 2)
NVIC->ISER2 |= (1 << (pos%32));
}
void nvic_irq_disable(uint8_t pos)
{
int i = pos / 32;
if (i == 0)
NVIC->ICER0 |= (1 << (pos % 32));
else if (i == 1)
NVIC->ICER1 |= (1 << (pos % 32));
else if (i == 2)
NVIC->ICER2 |= (1 << (pos % 32));
}
void nvic_irq_priority(uint8_t pos, uint8_t priority)
{
volatile uint8_t* ipri = ((volatile uint8_t*)(NVIC_IPR_BASE + pos));
*ipri = priority;
}
void bttnInterrupt(uint8_t pin)
{
uint32_t reg32;
//afio clock enable
RCC->APB2ENR |= (1 << 0); //AFIO enable
RCC->APB2ENR |= (1 << 4); // port c
reg32 = GPIOC->CRH;
reg32 &= ~((uint32_t) 0b1111 << 20);
GPIOC->CRH = reg32;
//exti interrupt enable
AFIO->EXTICR4 &= ~(0b1111 << 4);
AFIO->EXTICR4 |= (0b0010 << 4);
EXTI->IMR |= (1 << pin);
EXTI->EMR |= (1 << pin);
EXTI->RTSR |= (1 << pin);
EXTI->FTSR &= ~((uint32_t) 1 << pin);
nvic_irq_priority(40, 1);
nvic_irq_enable(40);
}
/*startup.c*/
bool pressed;
void isr_exti15_10()
{
EXTI->PR |= 1 << 13;
pressed = !pressed;
}
/*main.c*/
#include "clock.h"
#include "led.h"
#include "gpio.h"
#include "interrupt.h"
int main()
{
rcc_init();
systick_init();
gpioInit();
bttnInterrupt(13);
extern bool pressed;
while(1)
{
if (pressed)
{
gpioToggle(5);
}
msDelay(10);
}
return 0;
}
이 것으로 글을 마친다. 다음 글에서는 일반적인 timer에 대해서 다루어보고자 한다. 자세한 코드는 깃허브를 참조해줄 것!
https://github.com/youngSeok0413/embedded-system-architecture.git
GitHub - youngSeok0413/embedded-system-architecture: repository for studying embedded system architecture
repository for studying embedded system architecture - GitHub - youngSeok0413/embedded-system-architecture: repository for studying embedded system architecture
github.com