이번 시간에는 CPU 밖의 환경과 CPU가 어떻게 통신하는지 인터페이스에 대해 배워보자.
MCU가 연산하는 결과가 의미를 갖기 위해서는 외부 peripherals (주변기기)와 연결돼서 연산 결과를 조회하거나 활용할 수 있어야 한다. 이를 가능하게 해주는 가장 간단한 방법이 GPIO다. GPIO를 통해 peripheral에게 digital 또는 analog signal을 출력하거나 입력받을 수 있다.
MCU와 peripheral은 결국 전기적인 신호를 가지고 논리적으로 HIGH
또는 LOW
로 해석하면서 통신한다. 따라서 MCU와 peripheral이 HIGH
또는 LOW
라고 인식하는 valid한 전압의 범위를 잘 알아야 한다. 일반적으로 작동전압 Vdd 기준 -0.5V까지가 HIGH
, GND 기준 +0.5V 까지가 LOW
로 인식된다.
임베디드 SW 엔지니어들이 타겟 프로세서 내부의 모든 register에 대해 전부 외우고 아는 상태에서 코딩을 하는 것은 사실상 불가능한 이야기다. 따라서 프로세서 제공 업체는 datasheet와 함께 SW 엔지니어가 프로세서에 대한 상세하고 지엽적이고 전자전기적인 내용을 모른다고 할지라도 충분히 코딩을 할 수 있도록 라이브러리와 API 그리고 SDK를 제공한다. ARM 역시 이런 편의를 CMSIS (Cortex Microcontroller Software Interface Standard)라는 이름으로 제공해서 SW 엔지니어가 다소 편리하게 C 코딩할 수 있도록 돕는다.
예를 들면 아래와 같다.
SIM_SCGC5
라는 System Clock Gating Control Register에 대한 값을 조작해야하는 상황을 가정해보자.
원래라면 datasheet를 뒤적거리며 아래와 같은 내용을 직접 찾아야 한다.
SIM_SCGC5
라는 control register가 존재하는지 여부위 내용을 찾았다면, 이제 직접 C언어로 physical address를 건들여가면서 bit 연산 해야한다.
하지만 위 과정이 너무 복잡하고 어려우므로, 제조사가 제공해주는 라이브러리를 사용하면 SIM->SCGC5.TSI = 1
또는 SIM->SCGC5 = 0x05
또는 SIM->SCGC5 |= (1UL << 5)
이런 식으로 쉽게 사용할 수 있다.
SIM
control register이 clock signal을 인가해준다.우리가 MCU를 사용하다보면, 특정 핀에 map된 기능이 많은 것을 확인할 수 있다. 예를 들어 1번 핀이 Digital GPIO로 사용되기도 하고, UART로 사용되기도 하고 외부 인터럽트를 위한 핀으로 사용되기도 한다. 이것이 가능한 이유는 아래와 같다.
x번 I/O 포트의 n번째 bit를 담당하는 PORTx_PCRn
이라는 32-bit register가 있기 때문이다.
PCR이란 Pin Control Register의 약자로, 특정 pin을 담당하는 32-bit register를 의미한다. 즉, 하나의 포트는 여러 개의 pin으로 구성되며 각 pin을 담당하는 register가 있다.
PCR에는 multiplexer (MUX)를 위한 select bits 가 포함돼있는데, 이 bit가 어떻게 set 돼있냐에 따라 해당 pin은 다양한 기능들 중 하나를 수행하게 된다.
A
번 포트에 포함된 PTA1
이라는 핀은 PORTA_PCR1
이라는 register가 담당하며, MUX
bit에 따라 GPIO
, UART0
, TPM2
중 한 가지 기능을 수행하게 된다.
이 과정을 직접 하기 위해서는 엄청 복잡한 과정을 거쳐야 하지만, 위에서 배웠듯이 CMSIS의 도움으로 간단한 함수 1, 2개 정도로 특정 pin을 원하는 기능으로 활용할 수 있다. (제공되지 않을 때는 직접 datasheet를 읽고 register를 조작해야한다.)
마지막으로, 특정 핀이 어떻게 input과 output으로 동시에 사용될 수 있는지 알아보자.
우리는 특정 핀을 상황에 따라 input으로 사용할 수도 또는 output으로 사용할 수도 있다. 다르게 해석하면 우리는 항상 특정 핀의 mode를 설정해준다. 아두이노를 다뤄봤던 사람이라면 이런 개념이 익숙할 것이다. 왜냐하면 setup()
문에서 pinMode(GPIO.pin번호, OUTPUT 또는 INPUT)
으로 pin의 mode를 설정하기 때문이다.
GPIOx_PDDR
register에서 x번 핀에 대한 mode를 설정한다. ‘OUTPUT’ 또는 ‘INPUT’GPIOx_PDIR
register는 x번 핀을 INPUT
으로 사용할 때 input data를 처리하기 위해 사용한다.GPIOx_PDOR
register는 x번 핀을 OUTPUT
으로 사용할 때 output data를 처리하기 위해 사용한다.#define LED1_SHIFT (1)
#define PORT_PCR_MUX_MASK 0x700u
#define PORT_PCR_MUX_SHIFT 8
#define PORT_PCR_MUX(x) (((uint32_t)(((uint32_t)(x)) << PORT_PCR_MUX_SHIFT)) & PORT_PCR_MUX_MASK)
PORTA->PCR[LED1_SHIFT] &= ~PORT_PCR_MUX_MASK; // A.1 핀의 PCR register의 MUX bits를 clear함
PORTA->PCR[LED1_SHIFT] |= PORT_PCR_MUX(1); // A.1 핀의 PCR register의 MUX bits를 GPIO로 set함
PTA->PDDR |= MASK(LED1_SHIFT); // PDDR register의 1번 핀을 set해서 OUTPUT으로 설정함
실제로 1번 핀에 LED가 연결돼있다고 가정하면 위와 같이 코딩할 수 있다. 주석에 설명을 함께 첨부했다.