[nRF52840 스터디] blinky SDK를 이용하여 GPIO 제어해보기

Jace_Ryu·2022년 2월 24일
0

nRF52840 Study

목록 보기
1/1

서론

개발 블로그를 만들겠다고 마음을 먹고, velog를 개설한 뒤에 첫글이 임베디드가 될 줄은 몰랐다.
왜냐면 나는 임베디드의 E자도 모르는 사람이기 때문이다... 어쩌다보니 개발이 필요한 상황이 되어서, 얼렁뚱땅 개발에 투입이 되었고 부족한 지식을 채우기 위해 스터디한 내용을 기록해보고자 한다.

다행히 복잡한 기능들을 구현해야되는 것은 아니여서 기초적인 예제들을 수정하면 되는 수준인 것 같긴한데, 워낙 임베디드 쪽 지식이 전무하다보니 좀 어렵긴하다.

내가 스터디를 진행하는 환경은 아래와 같다.

  • Arduino Nano 33 Ble(nRF52840)
  • J-Link debugger
  • Segger Embedded Studio

Segger에서 제공하는 nRF5의 SDK를 활용하였고, 가장 먼저 제일 쉬워보이는 'blinky' 예제를 한 줄씩 살펴보면서 GPIO 제어를 위한 기초를 다져(?)보고자 한다.

기본적으로는 보드에 붙어있는 LED를 제어하는 코드이지만, 살짝만 수정하여 GPIO pin을 제어할 수 있을 것이라는 생각이 들었다.

본론

#include <stdbool.h>
#include <stdint.h>
#include "nrf_delay.h"
#include "boards.h"

int main(void)
{
    /* Configure board. */
    bsp_board_init(BSP_INIT_LEDS);

    /* Toggle LEDs. */
    while (true)
    {
        for (int i = 0; i < LEDS_NUMBER; i++)
        {
            bsp_board_led_invert(i);
            nrf_delay_ms(500);
        }
    }
}

위 코드가 blinky 예제의 main.c이다.
다행히(?) 그리 길지는 않다.

bsp_board_init(BSP_INIT_LEDS);부터 살펴보자.

bsp_board_init을 선언한 boards.c를 보면, 넣어주는 argument를 기반으로 LED를 초기화할 것인지, button을 초기화 할 것인지 정한다.

void bsp_board_init(uint32_t init_flags)
{
    #if defined(BOARDS_WITH_USB_DFU_TRIGGER) && defined(BOARD_PCA10059)
    (void) nrf_dfu_trigger_usb_init();
    #endif

    #if LEDS_NUMBER > 0
    if (init_flags & BSP_INIT_LEDS)
    {
        bsp_board_leds_init();
    }
    #endif //LEDS_NUMBER > 0

    #if BUTTONS_NUMBER > 0
    if (init_flags & BSP_INIT_BUTTONS)
    {
        bsp_board_buttons_init();
    }
    #endif //BUTTONS_NUMBER > 0
}

지금 보고 있는 코드에서는 BSP_INIT_LEDS를 전달하였으므로, bsp_board_leds_init()로 LED를 초기화 한다.

사전에 LED의 개수와 핀 번호를 매핑해두었는데, 각 LED를 nrf_gpio_cfg_output()을 이용하여 초기화 한다. 이는 설정한 pin을 output으로 설정해주는 함수이다. LED의 핀 번호가 13이면 nrf_gpio_cfg_output(13)으로 설정할 수 있다.

그 이후에는 bsp_board_leds_off()을 부르는데, 이는 결국 nrf_gpio_pin_write(m_board_led_list[led_idx], LEDS_ACTIVE_STATE ? 0 : 1)을 실행하는 것이다.

m_board_led_list[led_idx]는 단순히 LED의 핀번호(e.g. 13)인데, 뒤에 조건이 무엇인지 알아보자.

공식 문서는 아래 사진과 같다.

value에 0이 들어가면 pin을 clear하는 것이고, 1 이상의 값이 들어가면 pin을 set한다는 건데...임베디드 알못인 나는 무슨 뜻인지를 모른다....

다만, 직접 해보니까 set일 경우는 gpio핀에 전압이 나오고, clear일 경우엔 나오지 않는다. 정확한 내용은 모르지만 일단 set일때 led를 켜고, clear일 때 led를 끌 수 있다는 사실을 알게되었다.

LEDS_ACTIVE_STATE ? 0 : 1는 삼항 연산자니까, LED_ACTIVE_STATE가 True면 0, False면 1을 return하게 된다. 그러니까 LED_ACTIVE_STATE가 0일 때면 1을 반환하고, 1일때면 0을 반환해주는 것이다.
다시 한 번 말해서(..) LED가 켜져있으면 꺼주고, 꺼져있으면 켜주는 역할을 하는 듯 하다.

이제 while문 안으로 들어가보자.
for문은 단순히 사전에 설정해준 LED 개수에 맞추어 같은 작업을 해주기 위해 돌아가는 것이다.

bsp_board_led_invert(i);nrf_gpio_pin_toggle()을 부르는 함수이고, argument로는 pin number가 들어간다. nrf_gpio_pin_toggle()은 아래와 같다.

__STATIC_INLINE void nrf_gpio_pin_toggle(uint32_t pin_number)
{
    NRF_GPIO_Type * reg        = nrf_gpio_pin_port_decode(&pin_number);
    uint32_t        pins_state = reg->OUT;

    reg->OUTSET = (~pins_state & (1UL << pin_number));
    reg->OUTCLR = (pins_state & (1UL << pin_number));
}

토글 기능도 단순히, LED가 켜져있으면 clear해서 꺼주고, 꺼져있으면 set해서 켜주는 것이다.

토글 기능 말고, 내 맘대로 켜고 끄는 명령을 주려면 어떻게 해야할까 보다가 토글을 설명하고 있는 공식문서 근처에 nrf_gpio_pin_set을 설명하고 있는 함수가 있었고 찾아보니 nrf_gpio_pin_clear도 있었다.

이미 예상했겠지만, argument로 pin 번호만 넣어주면 아주 잘 작동한다. (e.g. nrf_gpio_pin_set(21);)

(의미 없는 작동 사진 / 빨간 LED만 켰음)

P.S.

위에서도 말했듯이, 나는 Arduino Nano 33 Ble를 사용했는데 놀랍게도 nRF5 SDK에 define되어 있는 ARDUINO_13_PIN 등은 실제 아두이노 핀 번호랑 무슨 상관이 있는지 모르겠다.(물론 아두이노가 모델이 많으니까 그렇겠지만...) 코드가 잘못된 줄 알고, 이것 때문에 한참 고생했었는데 그냥 schematics를 다운 받아서 보면 해결되는 문제였다.

P뒤에 x.y의 형태로 숫자가 표시되어 있는데, x가 0인 경우는 y만 argument로 넣어주면 된다. (e.g. D7핀에 꽂았으면 코드에서는 23)

한 Q&A 페이지를 보니 규칙은 아래와 같다고 한다.

P0.0 = 0, P0.1 = 1 .... P0.31 = 31 P1.0 = 32, P1.1 = 33.

pca10056.h파일을 봤을 때는 NRF_GPIO_PIN_MAP(1, 11) 이런식으로 작성하여 어떻게 활용할 수도 있을 것 같은데, 귀찮아서 아직 못 알아봤다..

이렇게 첫 번째 스터디는 끝!

profile
자동차와 여행을 좋아하는 개발자 지망생.

0개의 댓글