[nRF52] 4. CLI 적용

Dongsik·2022년 4월 1일
0

목적

  • 개발하면서 쉽게 테스트할 수 있도록 CLI (command line interface) 적용해보자.

Git

https://github.com/tlaehdtlr/nrf52_ble_base

  • 14 commit : [Feat] Enable debug log
  • 15 commit : [Feat] Add cli via uart
  • 16 commit : [Feat] Add commands

1. 뜬금 없는 Debug 강화

1-1. 에러 발생

작업을 하면서 programming 할 때마다 처음에 아래와 같은 에러가 발생한다.

디버그 모드에서는 나오지 않기 때문에 런타임 상황에서 디버깅 방법을 강화할 필요가 있었다.

1-2. 분석 및 적용

APP_ERROR_CHECK 함수를 따라가보면 app_error_weak 라이브러리를 이용하고 있다. weak 함수기 때문에 커스텀해도 되겠지만, 그냥 define 으로 해결되기에 간단하게 했다.

저 에러는 Segger 이슈로 보인다. (https://devzone.nordicsemi.com/f/nordic-q-a/77690/adding-nrf_nvic_state_t-nrf_nvic_state-0-causes-code-not-to-build-no-reason-for-build-failure/323711#323711 참고)

(미래의 나) : 저런 log 를 찍기 위해서는 IRQ 를 disable 시킨 상황이다. Debug 중이거나, 업데이트를 하던 상황에서나 먹힌 거였음.
따라서 WDT를 구현하면 얘는 그냥 reset 이나 걸리는 상황이 됨. 이후 좀 더 강화를 한다.

1-3. ToDo

생각보다 디버깅을 위한 많은 라이브러리가 제공되고 있었다. 아직 softdevice를 적극적으로 사용하고 있지 않아서 큰 문제는 없지만, 상황이 발생했을 때를 대비하여 관련 라이브러리를 메모만 해두자.

  • nrf_assert
  • ser_conn_error_handling
  • sdk_errors

2. CLI example 분석

nRF5_SDK_17.0.0_9d13099\examples\peripheral\cli\pca10100 를 참고하여 사용할 부분을 파악해보자.

2-1. 훑어보기

  • RAM 사용이 적지 않아 보이니 유의해야겠다.
  • CLI 입출력이 이벤트 처리 방식이 아닌 것 같다.
  • FDS (flash data storage) 모듈을 사용하고 있어 synchronous read, asynchronous write 이다.
  • demo cli cmds.c 를 확인해보면 커맨드를 추가하고, 처리 함수 지정하는 방식으로 간단하다.

2-2. 구상

  • UART 이용
    • main MCU 로써 사용될 것 같지 않다. (base code 라면서 너무 일찍 architecture 를 그리나 싶다...)
    • (1) PC 와 interface 로 UART 를 사용
    • (2) main MCU와 UART 로 연결하면 USB 로도 제어가 가능
      • main MCU 가 bridge 역할을 하며 (PC-nrf52) 통신이 가능해진다.
      • 참고로 이것을 이용해서 DFU 도 가능하다.

LOG 를 확인하기 위해 UART 로 사용 중인데, CLI 또한 UART 로 사용한다면 어떤 문제가 생길지 확인해 볼 필요가 있다.
일단 시도해보고 문제되면 CLI 와 LOG 의 backend 를 RTT, UART 따로 구현해야겠다.


3. CLI 적용

3-1. LOG, CLI 를 UART 로

(1) 충돌 방지

  • LOG module 을 사용

    • NRF_LOG_DEFAULT_BACKENDS_INIT() 과 config.h 의 NRF_LOG_BACKEND_UART_ENABLED
    •  nrf_log_backend_uart_init();
       backend_id = nrf_log_backend_add(&uart_log_backend, NRF_LOG_SEVERITY_DEBUG);
       ASSERT(backend_id >= 0);
       nrf_log_backend_enable(&uart_log_backend);
  • CLI module

    • nrf_cli_init() 에 uart를 backend 로 지정하면

    • if ((err_code == NRF_SUCCESS) && log_backend && NRF_CLI_LOG_BACKEND)
      {
          int32_t id = nrf_log_backend_add(p_cli->p_log_backend, init_lvl);
          if (id < 0)
          {
              return NRF_ERROR_NO_MEM;
          }
      
          nrf_log_backend_enable(p_cli->p_log_backend);
      }
  • 2개 함수가 같은 동작을 함으로 에러가 발생한다.

    • 따라서 같이 사용할 때는 NRF_LOG_DEFAULT_BACKENDS_INIT 을 제외하고 nrf_cli_init 만 쓰자.

(2) 적용

cli, queue module 추가해주고, idle 상태일 때 CLI 처리하도록 해보았다.

  • 기본적으로 cli command 들이 나오는 것은 nrf_cli_process() 함수를 뜯어보면 된다.
    • nrf_cli_process -> cli_state_collect
  • CLI 와 LOG 를 같이 쓰는데 문제가 없는 것으로 보이니 그냥 써야겠다.
  • 다음은 나의 커맨드를 등록하는 것이다. 이를 위해 base_cmds.c 를 만들겠다.

3-2. Command 추가

인자를 3개 받는 걸로 가뿐하게 하나 만들어보자.

  • base_cmds.h/c 생성
  • 커맨드, 함수, 메시지, 서브 커맨드
  • 동적으로 설정하는 것도 있어보이지만 굳이 안 써도 될 것 같다. (정확하게 뭔지도 잘 모르겠다...)

(1) CLI 예제처럼 커맨드 만들어보기


구조는 위와 같고... 커맨드를 입력하면~

이렇게 만들어봤는데, sub arguments 가 많아지면 코드가 너무 많아지는 것 같다.
NRF_CLI_CREATE_STATIC_SUBCMD_SET 을 계속 쓰는 것보다 argv와 strcmp 를 사용하는게 코드 가독성이 훨씬 좋을 것 같다. (call stack을 생각했을 때 불리할 지도?)
만들까 말까... 할까 말까.... 꾹 참고 base 니까 하나 만들겠다.

(2) 조금은 간단하게 만들기 (주관적)


음~ 확실히 편한 것 같다. (사용자에게 input 에 대해 자세한 로그를 띄우려면 hard coding이 필요하다. 귀찮으니 퉁쳤다!)
하지만 내 프로젝트에는 2번 커맨드를 만든 것처럼 짜야겠다. (나만 이용할 것이니까!)

(3) 추가로 tab을 누르면 모든 커맨드(밑의 그림)가 나오긴 하지만 내가 등록한 커맨드만 띄우고 싶어졌다.


help를 입력하면~

마음이 좀 편안해졌다.


4. 참고

Memory usage

Flash : (201.4 -> 219.2) +17.8KB
RAM : (29.0 -> 30.1) +1.1KB


4-1) Logger module

4-2) 키워드로 log, CLI, simultaneously 를 Devzone 에 검색하는 것도 좋겠다. (여러 글들을 보며 UART 로 같이 써도 되겠다는 판단을 내렸다.)

profile
There is a plenty of room at the bottom.

0개의 댓글