GPIO 포트에 해당하는 레지스터 값 설정하여 NUCLEO-L073RZ보드 LED 키기

#Embedded

GPIO 핀을 이용하여 NUCLEO-L073RZ보드 LED 키기


MCU는 기본적인 연산을 수행할 수 있다.

연산을 위해서는 어떠한 값을 입력으로 받아야 할 수도 있고 연산의 값을 출력으로 내보내야 될 수도 있다. 입력과 출력의 통로가 되는 간단한 장치로 GPIO(General-Purpose-Input & Output)이 있다.

NUCLEO-L073RZ에는 51개의 GPIO 포트가 존재한다. 그중에서 A포트의 5번핀(PA5) 은 보드의 내장 LED와 연결되어 있다. GPIO핀은 MCU의 레지스터와 연결되어 있어 레지스터의 값으로 GPIO를 제어할 수 있다. 보드의 LED를 제어하기 위해서 PA5와 연결된 레지스터의 주소를 알아야 된다.

메뉴얼을 보면 memory-map

주변장치(Peripherals) → IOPORT의 시작 주소가 0x5000 0000인 것을 알 수 있다.


GPIO는 입력과 출력을 담당할 수 있다. LED는 값을 읽는 장치가 아니므로 LED를 키기 위해서는 GPIO가 출력으로 설정되어야 한다. gpio-port-mode-register MODE5에 01을 넣으면 출력으로 설정된다는 것을 메뉴얼을 통해 알 수 있다. PORT-A의 경우 초기 값이 0xEBFF FCFF이다.

MODE5와 MODE4의 초기 값이 각각 11과 00이라는 뜻이고 MODE5를 01로 설장하면 레지스터의 값은 0xEBFF F4FF가 된다.

GPIO의 핀 설정으로는 MODE와 Otype과 Speed와 PULL UP/DOWN을 설정할 수 있다. MODE는 위와같이 설정하면 되고 나머지의 값들은 메뉴얼에 나와있는 초기값을 그대로 사용했다.


output-data-register

이제 PA5의 출력 값을 0으로 설정하면 LED가 꺼지고 1을 설정하면 LED가 켜진다. 이를 일정한 주기를 두고 반복하면 LED가 깜빡이게 된다.


또한 중요한 설정중 하나로 RCC가 있다.

포트 A를 활성화 시키기 위해서는 포트 A에 CLOCK을 넣어 주어야 한다.

rcc-base-address

RCC의 시작 주소가 0x4002 1000인 것을 확인할 수 있고

rcc-port-enable

Offset 0x2C에서 각 포트에 ENR(Enable Register) 을 설정하여 클럭을 활성화할 수 있다.

즉,

0x4002 102C에 1을 넣으면 A포트에 클럭을 넣어줄 수 있다.


위 내용을 코드로 옮기면 아래와 같다.

void delay() {
	volatile unsigned int a = 0;
	
	for(a;a<=100000;a++){
	}
}
 
 
int main(void)
{
	// RCC-IO A PORT ENABLE
	*(unsigned int *)0x4002102C = 0x01;
	
	// IO A PORT
	// 0x5000 0000
	
	// mode conf
	// first value EBFF FCFF
	*(unsigned int *)0x50000000 = 0xEBFFF4FF;
	
	// otype
	*(unsigned int *)0x50000004 = 0;	
	
	// speed
	*(unsigned int *)0x50000008 = 0x0C000000;
	
	// PULL UP/DOWN
	*(unsigned int *)0x5000000c = 0x24000000;	
	
	while(1)
	{
        // ODR 1
		*(unsigned int *)0x50000014 = 0x20;
		delay();
        // ODR 0
		*(unsigned int *)0x50000014 = 0x00;
		delay();
	}
}

위 코드의 방식은 여러모로 문제점이 많다.

0x50000000 = 0xEBFFF4FF 의 경우 A 포트의 5번핀을 출력으로 하기 위해서 값을 넣었지만 이 방식은 다른 핀들의 값들도 모두 바뀌기 때문에 치명적이다.

이를 해결하기 위해서는 비트 연산을 이용하여 A 포트의 5번핀의 값만 바뀌게 해주어야 한다.

void delay() {
    volatile unsigned int a = 0;
    for(a = 0; a <= 100000; a++);
}
 
int main(void)
{
    // RCC-IO A PORT ENABLE
    *(unsigned int *)0x4002102C |= (1 << 0); 
 
    // IO A PORT
	// 0x5000 0000
	
	// mode conf
	// first value EBFF FCFF
    *(unsigned int *)0x50000000 &= ~(3 << 10);
    *(unsigned int *)0x50000000 |=  (1 << 10);
 
    // otype
    *(unsigned int *)0x50000004 &= ~(1 << 5);
 
	// speed
    *(unsigned int *)0x50000008 &= ~(3 << 10);
    *(unsigned int *)0x50000008 |=  (2 << 10);
 
	// PULL UP/DOWN
    *(unsigned int *)0x5000000c &= ~(3 << 10);
 
    while(1)
    {
        *(unsigned int *)0x50000014 |=  (1 << 5);
        delay();
        *(unsigned int *)0x50000014 &= ~(1 << 5);
        delay();
    }
}

LED가 깜빡이는 것을 확인할 수 있다!

led-on-off