티스토리 뷰

Embedded OOP

GPIO Out

Just4Fun 2020. 4. 15. 12:33

객체 지향 언어로 임베디드 시스템 프로그램 개발이 가능한 지 시험하기 위하여 가장 간단한 프로젝트를 하나 만들어 보기로 하였다.

보드에 있는 2개의 LED(Green, Red)를 제어하는 코드를 구동 시켜 봄으로써 C++로 객체를 생성하여 원하는 동작이 되는지 확인해 보겠다.

회로도에 보면 Green LED는 Port G 13번 pin에 연결되어 있고 Red LED는 같은 포트 14번 pin에 연결되어 있는것으로 표시되어 있다.  따라서 Port G 13, 14 핀을 GPIO out mode로 설정하여 LED를 켜고 끄도록 하겠다.  LED가 GND에 연결되어 있으므로 Out level를 high 로 설정하면 LED가 켜지게 된다는 것을 알 수 있다.

class Gpio
{
protected:
    struct Gpio_reg
    {
        uint32_t moder;
        uint32_t otyper;
        uint32_t ospeedr;
        uint32_t pupdr;
        uint32_t idr;
        uint32_t odr;
        uint32_t bsrr;
        uint32_t lckr;
        uint32_t afr[2];
    };

    volatile Gpio_reg*  reg;
    uint32_t    io_pin;

public:
    Gpio(PORT port, uint32_t pin, MODE mode, OTYPE otype, OSPEED ospeed, PUPD pupd);
};

class Gpio_out : public Gpio
{
public:
    Gpio_out(PORT port, uint32_t pin, OTYPE otype=PUSH_PULL, 
             OSPEED ospeed=HIGH, PUPD pupd=PULL_DN)
        : Gpio{port, pin, MODE_OUT, otype, ospeed, pupd}
    {}

    void Set_pin(uint32_t pin) { if (pin & io_pin) reg->odr |= pin; }
    void Reset_pin(uint32_t pin) { if (pin & io_pin) reg->bsrr |= (pin << 16); }
    void Toggle_pin(uint32_t pin) { if (pin & io_pin) reg->odr ^= pin; }
    void Set(uint32_t pins);
    void Reset(uint32_t pins);
    void Toggle(uint32_t pins);
};

GPIO는 크게 out mode, in mode, af mode가 있으므로 Gpio class를 base로 만들고 Gpio_out, Gpio_in, Gpio_af를 상속받는것으로 하였다.

 

Gpio::Gpio(PORT port, uint32_t pin, MODE mode, OTYPE otype, OSPEED ospeed, PUPD pupd)
    : io_pin{pin}
{
    *(uint32_t*)0x40023830 |= 1<<port;

    reg = reinterpret_cast<Gpio_reg*>(GPIO_BASE_REG + (port * GPIO_OFFSET));

    for (int i=0; i<num_pins; i++)
    {
        uint32_t pos = 1 << i;
        uint32_t pos_2 = i * 2;

        if (pin & pos)
        {
            uint32_t val = reg->moder;
            val &= ~(3 << pos_2);
            val |= mode << pos_2;
            reg->moder = val;

            if (otype == PUSH_PULL)
                reg->otyper &= ~pos;
            else
                reg->otyper |= pos;

            val = reg->ospeedr;
            val &= ~(3 << pos_2);
            val |= ospeed << pos_2;
            reg->ospeedr = val;

            val = reg->pupdr;
            val &= ~(3 << pos_2);
            val |= pupd << pos_2;
            reg->pupdr = val;
        }
    }
}

void Gpio_out::Set(uint32_t pins)
{
    if ((io_pin & pins) == pins)
        reg->odr |= pins;
}

void Gpio_out::Reset(uint32_t pins)
{
    if ((io_pin & pins) == pins)
        reg->bsrr |= (pins << 16);
}

void Gpio_out::Toggle(uint32_t pins)
{
    if ((io_pin & pins) == pins)
        reg->odr ^= pins;
}

레지스터 설정은 base class인 Gpio에서 하고 output 설정을 위해서 따로 Gpio_out에 대한 member 함수를 추가하였다.

Gpio class를 위와 같이 만든 다음 실제로 객체를 생성 시켜 실제 보드에 있는 LED가 켜지는 지 확인해 보도록 하겠다.

 

int main()
{
    Gpio_out    led{Gpio::PORTG, PIN13|PIN14};

    led.Set(PIN13|PIN14);

    while (1);

    return 0;
}

위의 코드를 빌드한 후 보드에 내려서 두개의 LED 모두 켜지는 지 확인한다.

 

#define LED_GRN     PIN13
#define LED_RED     PIN14

void delay(int dly)
{
    for (int i=0; i<dly; i++)
        for (int j=0; j<1000; j++)
            asm("nop");
}

int main()
{
    Gpio_out    led{Gpio::PORTG, LED_GRN|LED_RED};

    led.Set(LED_GRN);

    while (1)
    {
        led.Toggle(LED_GRN|LED_RED);
        delay(1000);
    }

    return 0;
}

위의 코드를 적용하면 두개의 LED가 번갈아가며 깜빡이는 것을 볼 수 있다.

 

static void loop(Gpio_out io)
{
    io.Toggle(LED_GRN|LED_RED);
    delay(1000);
}

int main()
{
    Gpio_out    led{Gpio::PORTG, LED_GRN|LED_RED};

    led.Set(LED_GRN);

    while (1)
        loop(led);

    return 0;
}

main에서 생성한 led 객체를 loop함수의 인자로 넣어주는 경우에 대해서 LED가 정상적으로 깜빡이는 것을 확인한다.

위의 같은 방식으로 인자를 넘겨주면 객체에 대한 복사 과정이 일어나게 된다.  이런 경우에도 정상 동작하는지 확인해 본다.

 

static void loop(Gpio_out& io)
{
    io.Toggle(LED_GRN|LED_RED);
    delay(1000);
}

int main()
{
    Gpio_out    led{Gpio::PORTG, LED_GRN|LED_RED};

    led.Set(LED_GRN);

    while (1)
        loop(led);

    return 0;
}

위의 코드는 바로 이전의 코드와 차이가 없는 것처럼 보이지만 loop 함수의 인자를 객체에 대한 reference로 받는 경우이다.  이렇게 하면 객체 복사가 일어나지 않으므로 성능이 향상될것으로 기대한다.

 

Gpio_out    led{Gpio::PORTG, LED_GRN|LED_RED};

static void loop()
{
    led.Toggle(LED_GRN|LED_RED);
    delay(1000);
}

int main()
{
    led.Set(LED_GRN);

    while (1)
        loop();

    return 0;
}

위의 코드는 led 객체를 전역변수로 생성하는 경우이다.  이것은 led 객체의 생성이 main 함수가 불리는 시점보다 앞서 수행되어야 되는 전제 조건이 필요하다.  이를 위해서 Reset_handler 함수에 다음과 같은 코드가 필요하다.

    count = __init_array_end - __init_array_start;
    for (i = 0; i < count; i++)
        __init_array_start[i] ();

 

마지막으로

Gpio_out*    led;

static void setup()
{
    led = new Gpio_out{Gpio::PORTG, LED_GRN|LED_RED};
    led->Set(LED_GRN);
}

static void loop()
{
    led->Toggle(LED_GRN|LED_RED);
    delay(1000);
}

int main()
{
    setup();

    while (1)
        loop();

    return 0;
}

new 키워드를 이용하여 객체를 동적 할당하여 생성하는 코드를 만들어 보았다.

 

지금까지의 코드에서 모두 LED가 제대로 동작하였다면 기본적인 개발 환경과 C++ 코드가 정상 동작한다고 볼수 있겠다.

 

embed-oop.zip
0.02MB

'Embedded OOP' 카테고리의 다른 글

I2C  (0) 2020.05.01
UART  (0) 2020.04.18
System Clock 설정  (0) 2020.04.15
GPIO In  (0) 2020.04.15
객체지향 임베디드  (2) 2020.04.12
댓글
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
TAG
more
«   2024/05   »
1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30 31
글 보관함