Lab2 - GPIO
#GPIO란?
다용도 입출력(general-purpose input/output, GPIO)은 입력이나 출력을 포함한 동작이, runtime 시 사용자에 의해 제어될 수 있는 집적 회로나 전기 회로 기판의 디지털 신호 핀이다. 즉 마이크로프로세서가 주변장치와 통신하기 위한 목적으로 범용 사용되는 입출력 포트인 것이다. GPIO는 포트에 대해 입출력을 설정할 수 있으며 포트는 설정이 이루어져야 사용이 가능하다. GPIO가 기존적으로 가지는 주소는 다음과 같다.
#GPIO Register
- GPIO port mode register(GPIOx_MODER) - Address offset: 0x00
- GPIO port output type register(GPIOx_OTYPER) - Address offset: 0x04
- GPIO port output speed register(GPIOx_OSPEEDR) - Address offset: 0x08
- GPIO port pull-up / pull-down register(GPIOx_PUPDR) - Address offset: 0x0C
- GPIO port input data register(GPIOx_IDR) - Address offset: 0x10
- GPIO port output data register(GPIOx_ODR) - Address offset: 0x14
- GPIO port bit set / reset register(GPIOx_BSRR) - Address offset: 0x18
- GPIO alternate function low register(GPIOx_AFRL) - Address offset: 0x20
- GPIO alternate function high register(GPIOx_AFRH) - Address offset: 0x24
GPIO는 이렇게 offset에 따라 다양한 기능을 가진 Register들을 가지고 있다. 각 Register마다 Configuration bit에 따라 동작이 다르고 길이 역시 다르게 작용한다. 이번 챕터에서 진행할 실험에서는 위 Register들을 다양하게 사용하며 LED를 사용한 동작을 살펴볼 것이다.
#Lab2-1
해당 실습은, 지난번 챕터에서 사용했던 것과 비슷하게, LED를 사용하여 점멸 동작을 살펴볼 것이고, 주기를 변경해볼 것이다. 하지만 DigitalOut이나 PortOut이 아닌 GPIO In/Out을 사용하여 직접 레지스터의 동작을 제어해보며 확인할 것이다.
#include "mbed.h"
#define micro 1ms
#define BLINKING_RATE 20ms
void led2_init(void);
void button_init(void);
uint32_t button_input(void);
void led2_toggle(void);
bool flag = true;
DigitalOut yellowLed(D4);
위 실습은, 노란색 LED를 사용하여 점멸 상태를 확인한다. mbed 헤더 파일을 선언 후, 점멸 주기를 설정할 때 필요한 시간들을 define 해 주었다. 또한 main함수 외 사용할 함수들을 선언하여 기본 세팅을 해주었다.
int main(void){
int interval = 500;
uint32_t val;
led2_init();
button_init();
while(true){
val = button_input();
if(val == 0){
yellowLed = !yellowLed;
interval = interval << 1;
if(interval > 2000)
interval = 500;
}
led2_toggle();
flag = !flag;
ThisThread::sleep_for(interval*micro);
}
}
먼저 main함수이다. 점멸 주기를 설정하는 interval 변수를 초기 시간인 500ms로 설정하고 USER_BUTTON의 클릭 여부를 판단하기 위해 val 함수를 32bit 변수로 선언하였다. 따라서, 만약 버튼이 클릭되는 것이 인지되면, LED는 if문 안으로 들어가고 interval이 왼쪽으로 1칸 shift 되어 주기가 2배 증가한다. 해당 동작은 500ms에서 2초까지 버튼 클릭에 따라 주기가 변경되며 점멸한다.
void led2_init(void){
__HAL_RCC_GPIOA_CLK_ENABLE();
GPIOA->MODER &= ~(0b11 << 10);
GPIOA->MODER |=(0b01 << 10);
}
다음으로 led2_init() 함수를 살펴보면, 해당 함수는 LED를 설정한 D4핀을 인식하는 역할을 수행한다. __HAL_RCC_GPIOA_CLK_ENABLE()을 사용하여 Clock을 Enable 시키고, MODER Register를 설정한다.
위 표를 통해 MODER register를 설정할 수 있다. PA_5, 즉 D4핀을 enable 시키기 위해 clock 설정 후 MODER register에 PA_5에 해당하는 bit를 enable 시켜주어야 한다. 따라서, '&='(and) 연산을 이용해 ~(0b11), 즉 00을 10칸 shift 하여 MODER5[1:0]에 위치시켜 해당 위치를 초기화한다. 다음으로 0b01을 shift하여 '|='(or) 연산으로 MODER5를 01로 설정함으로써 LED 핀으로 설정한 PA_5 핀을 사용할 수 있게 설정한다.
void button_init(void){
__HAL_RCC_GPIOC_CLK_ENABLE();
GPIOC->MODER &= ~(0b11 << 26);
GPIOC->MODER |=(0b00 << 26);
}
버튼을 설정해 주는 과정도 위와 동일하다. USER_BUTTON은 PC_13으로 설정되어 있기 때문에, 동일한 동작을 26 shift 해주어 PC_13을 enable 시킨다.
uint32_t button_input(void){
bool result = GPIOC->IDR & 0b10 << 12;
if(result)
return 1;
else
return 0;
}
void led2_toggle(void){//포트 set or reset
if(flag)
GPIOA->BSRR = 0x01 << 5; // 05 = set 5
else
GPIOA->BSRR = 0x01 <<21; // 21 = reset 5
}
다음으로, 버튼이 클릭되는 동작을 인식하기 위한 함수와 LED를 점멸시켜주는 함수를 구현하였다. 버튼은, IDR register를 사용하여 설정한다. 앞서 말했듯이 버튼이 PC_13 핀이기 때문에, 버튼이 클릭되어 해당 함수가 호출되면, 12만큼 shift 해주어 click여부를 integer형태로 반환하여 main에서 사용한다. 점멸도 비슷한 원리로, BSRR register를 각 5, 21만큼 shift 하여 LED on/off를 설정한다.
해당 함수들을 최종적으로 동작시키면, 초기에 500ms로 점멸하는 LED는, USER_BUTTON 클릭 시에 점멸 주기가 2배가 되고, 해당 주기는 2초까지 반복되며 그 이후로는 다시 500ms로 돌아와 반복동작을 수행한다.
- 회로도
#Lab2-2 (interrupt)
이번 실습은, 앞서 진행했던 실험과 동일하지만, USER_BUTTON 클릭을, register가 아닌 GPIO interrupt로 인식하여 동작하게 할 것이다. GPIO interrupt를 사용하기 위해서는 EXTI Controller를 사용해야 한다. EXTI 컨트롤러의 주요 기능은 다음과 같다.
- 각 인터럽트 / 이벤트 라인의 독립 트리거 및 마스크
- 각 인터럽트 라인에 대한 전용 상태 비트
- 최대 24개의 소프트웨어 이벤트 / 인터럽트 요청 생성
- APB2 clock 주기보다 낮은 pulse폭을 가지는 외부 신호 검출
해당 EXTI 컨트롤러에, GPIO Input 포트에 연결된 신호에 의해 정보가 전송되고, 해당 컨트롤러를 거쳐 NVIC로 전달된다. 여기서 NVIC란, Nested Vectored Interrupt Controller의 약자로, 중첩된 인터럽트들을 제어해 주는 기능이다. 우선순위가 설정되어 있기 때문에, 효과적이고 순차적으로 interrupt를 처리한다.
NVIC_SetVector(EXTI15_10_IRQn,(uint32_t)button_Handler);
실습 2-2에는, 위 함수를 추가하여 사용한다. 해당 함수는, SRAM 기반 인터럽트 벡터 테이블에서 벡터를 설정해준다. USER_BUTTON, PC_13 핀이 포함된(10~15) EXTI15_10_IRQn을 인자로 설정해 주었다.
void button_init(void){
RCC->APB2ENR = 0x00004000;
SYSCFG->EXTICR[3] = (0x20);
EXTI->FTSR = (0x2000);
EXTI->IMR = (0x2000);
NVIC_EnableIRQ(EXTI15_10_IRQn);//EXTI13 이 속한 15_10 enable
NVIC_SetPriority(EXTI15_10_IRQn,0x01);
}
다음으로 button_init 함수이다. RCC->APB2ENR을 통해 clock을 enable 해주고, SYSCFG_EXTICR[3]을 사용한다. SYSCFG_EXTICR은 multiflexer로서, 입력된 신호들을 EXTI 컨트롤러로 보내주는 역할을 한다. PC_13을 입력신호로 설정해주고, EXTI register들을 사용하였다. 먼저 EXTI_FTSR(Falling trigger selection register)를 0x2000(=100000)으로 설정해주어, TR13에 1을 대입하여 Falling trigger를 enable 시켜주었다. 다음으로 EXTI_IMR(Interrupt Enable Register) 또한 0x2000을 대입하여 Interrupt를 enable 설정하였다. 마지막으로, 전달받은 NVIC를 enable 하고 우선순위를 설정하여 버튼을 지정하였다.
void button_Handler(void){
__HAL_RCC_GPIOC_CLK_ENABLE();
interval = interval << 1;
if(interval>2000) interval = 500;
EXTI->PR = 0x2000;
}
마지막으로 Handler함수이다. BUTTON 클릭 액션이 실행되면, interrupt가 발생하여 해당 함수가 호출된다. 따라서, 점멸 주기를 변경하고 Pending register가 순차적으로 실행되며 점멸 과정이 진행되는 것이다.
#마치며
이번 챕터에서는 단순히 LED를 점멸에도 다양한 메커니즘들이 존재하며, register 설정을 통한 동작 설정을 공부하였다. 다음 챕터에서는 timer와 ticker 등을 사용하여 Nucleo 보드로 시간을 측정하는 실습을 진행할 것이다.
'embedded' 카테고리의 다른 글
Lab1 - Digital in & out (1) | 2021.05.22 |
---|---|
임베디드 프로세스 응용 (2) | 2021.05.22 |