nRF52

nRF52840 USB CDC ACM 이용

임베디드 친구 2024. 9. 7. 16:00
반응형

nRF52840 USB CDC ACM 이용

1. USB CDC ACM 개요

USB CDC ACM(Communication Device Class - Abstract Control Model)은 USB를 통한 시리얼 통신을 지원하는 USB 디바이스 클래스입니다. 이 클래스는 주로 모뎀, 시리얼 포트 에뮬레이터와 같은 통신 장치에서 사용됩니다. nRF52840 모듈은 USB CDC ACM을 통해 컴퓨터 또는 다른 USB 호스트 장치와 시리얼 데이터를 주고받을 수 있습니다. 이는 UART(Universal Asynchronous Receiver-Transmitter) 인터페이스를 통해 시리얼 통신을 구현하는 것과 유사한 동작을 제공하며, USB를 통해 더 안정적이고 고속으로 데이터 전송이 가능합니다.

2. USB CDC ACM 모듈 설정

USB CDC ACM을 사용하려면 nRF52840 모듈의 SDK 설정과 라이브러리를 적절히 구성해야 합니다. 여기에서는 sdk_config.h 파일과 관련된 드라이버 및 라이브러리 설정 방법을 다룹니다.

2.1 SDK 구성 (sdk_config.h 설정)

먼저, USB 및 CDC ACM 모듈을 활성화하려면 sdk_config.h 파일에서 다음 매크로들을 설정해야 합니다.

#define USBD_ENABLED 1                // USB 드라이버 활성화
#define APP_USBD_ENABLED 1            // USB 디바이스 스택 활성화
#define APP_USBD_CDC_ACM_ENABLED 1    // CDC ACM 클래스 활성화

2.2 nRF 드라이버 및 라이브러리 경로

아래 경로에 위치한 드라이버와 라이브러리가 USB CDC ACM 기능을 위해 사용됩니다:

  • 드라이버:
    • ./modules/nrfx/drivers/src/nrfx_usbd.c (USB 하드웨어 제어)
  • 라이브러리:
    • ./components/libraries/usbd/app_usbd.c (USB 디바이스 스택)
    • ./components/libraries/usbd/class/cdc/acm/app_usbd_cdc_acm.c (CDC ACM 클래스)
    • ./components/libraries/usbd/app_usbd_core.c
    • ./components/libraries/usbd/app_usbd_serial_num.c
    • ./components/libraries/usbd/app_usbd_string_desc.c

3. USB CDC ACM 설정

USB CDC ACM을 설정하려면 USB Daemon을 초기화하고, CDC ACM 클래스를 USB 스택에 추가해야 합니다. 여기서는 FreeRTOS Task를 사용해 이를 구현하는 방법을 설명합니다.

3.1 USBD Task 준비

USB CDC ACM은 USB Daemon의 형태로 동작합니다. Daemon이 실행되기 전에 NRF LFCLK을 먼저 동작시켜야 하며, usbd_init() 함수를 통해 USB Daemon을 초기화하고 CDC ACM 클래스를 추가해야 합니다. 이후 USBD 이벤트 처리를 위한 while 루프가 실행됩니다.

다음은 FreeRTOS Task로 작성된 USB Daemon 초기화 예제입니다.

void usbd_cli_task(void* pvParameters)
{
    ret_code_t ret;

    // LFCLK 초기화
    ret = nrf_drv_clock_init();
    APP_ERROR_CHECK(ret);

#ifdef APP_USBD_CDC_ACM_ENABLED
    app_usbd_serial_num_generate();  // 시리얼 번호 생성
#endif

    // LFCLK 실행 확인 및 요청
    if (!nrf_drv_clock_lfclk_is_running())
    {
        nrf_drv_clock_lfclk_request(NULL);

        while (!nrf_drv_clock_lfclk_is_running());
    }

    cli_init();     // CLI 초기화
    usbd_init();    // USB 초기화
    cli_start();    // CLI 시작

    NRF_LOG_RAW_INFO("Command Line Interface started.\n");

    while (true)
    {
        UNUSED_RETURN_VALUE(NRF_LOG_PROCESS());

#if CLI_OVER_USB_CDC_ACM && APP_USBD_CONFIG_EVENT_QUEUE_ENABLE
        while (app_usbd_event_queue_process())
        {
            // 이벤트 처리 루프
        }
#endif
        cli_process();  // CLI 명령어 처리
    }
}

int aika_usbd_init(void)
{
    // FreeRTOS Task 생성
    xTaskCreate(usbd_cli_task, "USB_SERIAL", (configMINIMAL_STACK_SIZE * 4), NULL, tskIDLE_PRIORITY, NULL);

    return 0;
}

3.2 USB Daemon 초기화 (usbd_init() 함수)

usbd_init() 함수에서는 app_usbd_init() 함수를 통해 USB 디바이스를 초기화하고, app_usbd_class_append() 함수로 CDC ACM 클래스를 추가합니다. USB 포트의 전원 감지를 사용하는 경우, usbd_user_ev_handler에서 이벤트에 따라 USB를 제어해야 합니다. 전원 감지를 사용하지 않는다면 USB Daemon은 항상 활성화 및 시작 상태를 유지합니다.

참고: 파일 시스템을 사용하는 경우 전원 감지를 사용하는 것이 유리하며, usbd_user_ev_handler에서 USB 이벤트에 따라 파일 시스템 마운트 등을 제어하는 것이 바람직합니다.

아래는 usbd_init() 함수의 코드 예제입니다.

static void usbd_user_ev_handler(app_usbd_event_type_t event)
{
    switch (event)
    {
    case APP_USBD_EVT_STOPPED:
        if (nrf_drv_usbd_is_enabled())
            app_usbd_disable();
        break;

    case APP_USBD_EVT_POWER_DETECTED:
        if (!nrf_drv_usbd_is_enabled())
            app_usbd_enable();
        break;

    case APP_USBD_EVT_POWER_REMOVED:
        if (nrf_drv_usbd_is_started())
            app_usbd_stop();
        break;

    case APP_USBD_EVT_POWER_READY:
        if (!nrf_drv_usbd_is_started())
            app_usbd_start();
        break;

    default:
        break;
    }
}

static void usbd_init(void)
{
    ret_code_t ret;
    static const app_usbd_config_t usbd_config = {
        .ev_handler = app_usbd_event_execute,
        .ev_state_proc = usbd_user_ev_handler
    };

    // USB 디바이스 스택 초기화
    ret = app_usbd_init(&usbd_config);
    APP_ERROR_CHECK(ret);

    // CDC ACM 클래스 인스턴스 추가
    app_usbd_class_inst_t const * class_cdc_acm =
        app_usbd_cdc_acm_class_inst_get(&nrf_cli_cdc_acm);

    ret = app_usbd_class_append(class_cdc_acm);
    APP_ERROR_CHECK(ret);

    // 전원 감지 활성화 여부에 따른 처리
    if (I_USBD_POWER_DETECTION)
    {
        ret = app_usbd_power_events_enable();
        APP_ERROR_CHECK(ret);
    }
    else
    {
        NRF_LOG_INFO("No USB power detection enabled\nStarting USB now");

        app_usbd_enable();
        app_usbd_start();
    }

    // 호스트가 USB CDC 포트를 인식할 시간을 줌
    nrf_delay_ms(1000);
}

이제 PC와 연결해봅시다. nRF52840 DK 보드의 경우, nRF USB로 표시된 USB 포트를 사용하여 연결할 수 있습니다. 연결 후, 장치 관리자에서 USB 직렬 장치(COM)**을 확인할 수 있습니다.

4. CLI(Command Line Interface)

CLI는 Command Line Interface의 약어로, 텍스트 기반으로 컴퓨터와 상호 작용할 수 있는 사용자 인터페이스를 의미합니다. CLI는 그래픽 사용자 인터페이스(GUI)와 대조되며, 텍스트 명령어를 통해 시스템을 제어하고 프로그램을 실행하는 데 사용됩니다.

4.1 CLI 시작

USB CDC ACM을 이용하여 CLI 인스턴스를 생성하고 초기화한 후 nrf_cli_start() 함수를 통해 CLI를 시작합니다. 다음은 CLI 시작을 위한 코드 예제입니다.

// CLI 인스턴스 정의
NRF_CLI_CDC_ACM_DEF(m_cli_cdc_acm_transport);
NRF_CLI_DEF(m_cli_cdc_acm,
"usb_cli:~$ ",
&m_cli_cdc_acm_transport.transport,
'\r',
CLI_EXAMPLE_LOG_QUEUE_SIZE);

...

ret_code_t ret;

// CLI 초기화
ret = nrf_cli_init(&m_cli_cdc_acm, NULL, true, true, NRF_LOG_SEVERITY_INFO);
APP_ERROR_CHECK(ret);

...

// CLI 시작
ret = nrf_cli_start(&m_cli_cdc_acm);
APP_ERROR_CHECK(ret);
...

4.2 CLI 명령어 등록

CLI에서 명령어를 입력하면 원하는 기능을 수행할 수 있도록 명령어 실행 함수를 등록할 수 있습니다. 다음은 명령어 등록에 사용되는 주요 매크로들입니다:

  • NRF_CLI_CMD_REGISTER: 명령어 및 명령어 실행 함수, 서브 명령어를 등록합니다.
  • NRF_CLI_CREATE_STATIC_SUBCMD_SET: 서브 명령어 및 서브 명령어 실행 함수를 등록합니다.
  • NRF_CLI_CMD: 명령어 설정을 수행합니다.
    아래는 CLI 명령어 등록 예제입니다.
    NRF_CLI_CMD_REGISTER(my_command, NULL, "Description", my_command_handler);
    NRF_CLI_CREATE_STATIC_SUBCMD_SET(m_subcmds)
    {
      NRF_CLI_CMD(sub1, NULL, "Subcommand 1", sub1_handler),
      NRF_CLI_CMD(sub2, NULL, "Subcommand 2", sub2_handler),
      NRF_CLI_SUBCMD_SET_END
    };
    여기까지 설정이 완료되었다면, putty 또는 MobaXterm과 같은 터미널 프로그램을 사용해 CLI의 동작을 확인할 수 있습니다. CDC ACM을 이용한 UART 터미널을 사용하여 CLI의 동작을 확인해보세요.

5. 결론

이 글에서는 nRF52840 모듈을 사용하여 USB CDC ACM을 설정하고, 이를 통해 컴퓨터와 시리얼 통신을 구현하는 방법을 다루었습니다. USB CDC ACM은 UART와 유사한 시리얼 통신 인터페이스를 제공하면서도, 더 높은 데이터 전송 속도와 안정성을 제공하기 때문에 다양한 임베디드 시스템에서 활용할 수 있는 유용한 기술입니다.

특히, 필자는 이 USB CDC ACM 기반의 CLI(Command Line Interface)를 활용하여 제품의 양산 검증 기능을 구현했습니다. CDC ACM 인터페이스를 통해 디바이스와 직접 통신하며, 테스트와 검증을 수행할 수 있었습니다. CLI를 이용한 이러한 접근 방식은, 양산 과정에서 개별 디바이스의 성능 및 기능을 검증하는 데 있어 매우 효과적이었습니다.

뿐만 아니라, CDC ACM CLI를 통해 디바이스 검사 코드도 작성하여, 제품의 안정성과 품질을 보장하는 데 중요한 역할을 했습니다. 이러한 디바이스 검사 코드는 실제 양산 환경에서 자동화된 테스트를 가능하게 하며, 일관된 검증 프로세스를 통해 불량품의 유입을 방지하는 데 큰 도움을 주었습니다.

이 가이드에서는 sdk_config.h 파일 설정부터 FreeRTOS Task를 이용한 USB Daemon 초기화, CDC ACM 클래스를 USB 스택에 추가하는 방법을 다루었습니다. 또한, USB CLI를 구현하여 USB 통신을 통해 시스템을 제어하고 명령어를 실행하는 방법을 설명했습니다.

실제 프로젝트에서 이러한 USB CDC ACM 기반의 CLI 시스템을 구현하면, 디버깅, 시스템 관리, 그리고 대규모 양산 검증까지 다양한 응용 사례에서 큰 이점을 얻을 수 있습니다. 이 글이 nRF52840 모듈을 사용한 USB CDC ACM 프로젝트에 실질적인 도움이 되기를 바라며, 앞으로도 다양한 기능과 사례를 응용해 나가시길 바랍니다.

반응형