nRF52832 개발일지 (5)

치삼이·2021년 7월 20일
1

nRF52832 개발일지

목록 보기
6/8
post-thumbnail

👨‍💻 개발일지 (5) : Nordic SDK로 DWM1001 SPI 통신하기 -2-

지난 시간에 이어 오늘은 SPI 통신을 직접 코드를 통해 직접 다뤄보도록 하겠습니다. 물론 Nordic SDK에 구현되어있는 드라이버 파일을 사용하며 nrfx_spi.c, nrfx_spi.h 뿐 아니라 drv_nrf_spi.c, drv_nrf_spi.h 을 이용해 코드를 작성 하겠습니다.

1. 프로젝트 생성 및 환경설정

🔗 개발일지 (3)의 1절 프로젝트 생성 및 환경설정 부분을 참고 해주시면 됩니다. 이때 프로젝트 이름은 DWM1001_SPI 라고 하겠습니다.

다음은 어떤 파일을 이 프로젝트에 포함시킬지를 정하도록 하겠습니다. 이번 프로젝트에 포함될 드라이버 소스파일은 nrfx_spi.c, drv_nrf_spi.c 두 가지 입니다. 각각의 PATH는 👇 아래와 같습니다.

📁 $(SDK_PATH)\nRF5_SDK_17.0.2_d674dde\tmp_integration\nrfx\legacy\drv_nrf_spi.c
📁 $(SDK_PATH)\nRF5_SDK_17.0.2_d674dde\modules\nrfx\drivers\src\nrfx_spi.c

두 개의 파일을 프로젝트에 포함시켜 주세요.

이제 sdk_config.h 파일을 복사 한 뒤, 편집하도록 하겠습니다. 지난번처럼 최 하단에 GitHub repository 링크를 걸어 놓을테니 다운로드 받으시면 됩니다.

수정이 완료되면 👆 위 와 같이 깔끔해집니다.

2. Simple SPI transmitting

이번에 생성한 프로젝트의 목적은 0x00을 전송 했을때, DW1000이 0x30, 0x01, 0xCA, 0xDE 네 바이트 시퀀스를 제대로 전송하는지 확인하는 것 입니다. UART를 이용한 디버깅은 진행하지 않고 Embedded Studio에서 제공하는 디버깅을 통해 확인해 보도록 하겠습니다.

2.1 main.c 작성

차근차근 작성해 나아가 봅시다.

#include "nrf_drv_spi.h"
#include "boards.h"

#define SPI_BUFSIZE	1024
#define SPI_INSTANCE	0

uint8_t spi_tx_buf[SPI_BUFSIZE];
uint8_t spi_rx_buf[SPI_BUFSIZE];

static volatile bool spi_xfer_done;

static const nrf_drv_spi_t spi = NRF_DRV_SPI_INSTANCE(SPI_INSTANCE);

기본적인 드라이버의 헤더파일을 인클루드 시키고 버퍼 사이즈 및 0, 1, 2 총 세 가지의 SPI 인스턴스 중 어떤 인스턴스를 사용할지 매크로로 정의했습니다.

또 전역 변수로 tx, rx 버퍼, 바이트 교환이 끝났음을 판단하는 플래그, SPI0 인스턴스를 선언했습니다.

void spi_event_handler(nrf_drv_spi_evt_t const* p_event, void * p_conetext)
{
	spi_xfer_done = true;
}

void spi_init(void)
{
	nrf_drv_spi_config_t spi_config = NRF_DRV_SPI_DEFAULT_CONFIG;
	spi_config.ss_pin	= DW_CS;
	spi_config.miso_pin	= SPI1_MISO;
	spi_config.mosi_pin	= SPI1_MOSI;
	spi_config.sck_pin	= SPI1_CLK;

	nrf_drv_spi_init(&spi, &spi_config, spi_event_handler, NULL);
}

int read_single_octct(int reg, int rx_size)
{
	reg = reg & 0x3F;

	spi_xfer_done = false;

	nrf_drv_spi_transfer(&spi, spi_tx_buf, 1, spi_rx_buf, rx_size + 1);

	while(spi_xfer_done == false)
	{
		;
	}

	return 0;
}

이제 함수 부분을 살펴보도록 하겠습니다.

  • void spi_event_handler(nrf_drv_spi_evt_t const* p_event, void * p_conetext)
  • void spi_init(void)
  • int read_single_octet(int reg, int rx_size)

총 세 가지의 함수를 선언 및 정의 했습니다. 각각의 함수는 이름에서 잘 나타나듯이 이벤트 핸들러, 초기화, 헤더 하나를 송신하는 함수로 구성되었습니다.

이벤트 핸들러에서는 이벤트와 관련 없이 특정 이벤트가 발생하면 무조건 spi_xfer_done 변수를 false로 설정합니다.

int read_single_octet(int reg, int rx_size) 에서 reg = reg & 0x3F; 구문은 앞자리 7번째 와 6번째 비트를 0으로 만들어 주기 위하여 넣었습니다.

2.2 Compile 및 Debugging

이제 컴파일을 하고 오류가 있는지 확인을 해보도록 하겠습니다. 🛠️

역시 한번에 되는게 없네요.. 벌래 잡으러 가야겠습니다. 🐛🐛

일단 에러로그를 한번 보도록 하겠습니다.

❌ undefined symbol: nrfx_spim_init
❌ undefined symbol: nrfx_spim_xfer

두 에러는 nrfx_spim_initnrfx_spim_xfer 라는 심볼을 찾을 수 없어서 나는 에러 입니다. 당연히 nrfx_spim.c 혹은 nrfx_spim.h에 정의 되어 있습니다.

3. SDK Source file 수정

2.2 절에서 본 에러는 nrfx_spim_initnrfx_spim_xfer 라는 심볼을 찾을 수 없어서 나는 에러 이며 저희는 SPIM 인스턴스를 사용하지 않기 때문에 이번 프로젝트에서 nrfx_spim.c, nrfx_spim.h을 포함하지 않았습니다. 우리는 매크로를 적절히 활용해서 SPI 와 SPIM 인스턴스를 분리해 보도록 해보겠습니다.

결론 부터 말하자면 nrf_drv_spi.cnrf_drv_spi.h를 수정해야 합니다. 그전에 nrf_drv_uart.hnrf_drv_spi.h 를 비교해보도록 하겠습니다.

//----------- nrf_drv_spi.h ------------//

#ifndef NRF_DRV_SPI_H__
#define NRF_DRV_SPI_H__

#include <nrfx.h>
#ifdef SPIM_PRESENT
    #include <nrfx_spim.h>
#else
    // Compilers (at least the smart ones) will remove the SPIM related code
    // (blocks starting with "if (NRF_DRV_SPI_USE_SPIM)") when it is not used,
    // but to perform the compilation they need the following definitions.
    #define nrfx_spim_init(...)             0
    #define nrfx_spim_uninit(...)
    #define nrfx_spim_start_task_get(...)   0
    #define nrfx_spim_end_event_get(...)    0
    #define nrfx_spim_abort(...)
#endif

#ifdef SPI_PRESENT
    #include <nrfx_spi.h>
#else
    // Compilers (at least the smart ones) will remove the SPI related code
    // (blocks starting with "if (NRF_DRV_SPI_USE_SPI)") when it is not used,
    // but to perform the compilation they need the following definitions.
    #define nrfx_spi_init(...)              0
    #define nrfx_spi_uninit(...)
    #define nrfx_spi_start_task_get(...)    0
    #define nrfx_spi_end_event_get(...)     0
    #define nrfx_spi_abort(...)
...
//----------- nrf_drv_uart.h ------------//

#ifndef NRF_DRV_UART_H__
#define NRF_DRV_UART_H__

#include <nrfx.h>

#if defined(UARTE_PRESENT) && NRFX_CHECK(NRFX_UARTE_ENABLED)
    #define NRF_DRV_UART_WITH_UARTE
#endif
#if defined(UART_PRESENT) && NRFX_CHECK(NRFX_UART_ENABLED)
    #define NRF_DRV_UART_WITH_UART
#endif

#if defined(NRF_DRV_UART_WITH_UARTE)
    #include <nrfx_uarte.h>
    #define NRF_DRV_UART_CREATE_UARTE(id) \
        .uarte = NRFX_UARTE_INSTANCE(id),
#else
    // Compilers (at least the smart ones) will remove the UARTE related code
    // (blocks starting with "if (NRF_DRV_UART_USE_UARTE)") when it is not used,
    // but to perform the compilation they need the following definitions.
    #define nrfx_uarte_init(...)                0
    #define nrfx_uarte_uninit(...)
    #define nrfx_uarte_task_address_get(...)    0
    #define nrfx_uarte_event_address_get(...)   0
    #define nrfx_uarte_tx(...)                  0
    #define nrfx_uarte_tx_in_progress(...)      0
    #define nrfx_uarte_tx_abort(...)
    #define nrfx_uarte_rx(...)                  0
    #define nrfx_uarte_rx_ready(...)            0
    #define nrfx_uarte_rx_abort(...)
    #define nrfx_uarte_errorsrc_get(...)        0
    #define NRF_DRV_UART_CREATE_UARTE(id)
#endif

#if defined(NRF_DRV_UART_WITH_UART)
    #include <nrfx_uart.h>
...

두 헤더파일에서 매크로를 정의할때 굉장히 차이가 나는 것 을 확인할 수 있습니다. uart에서는 peripheral의 존재 유무를 확인하고 enable이 되어있다면 NRF_DRV_UART_WITH_UARTE 혹은 NRF_DRV_UART_WITH_UART의 매크로를 사용하는 반면 spi에서는 peripheral의 존재 유무만 따지고 존재하면 무조건 사용시키는 방법으로 작성되었습니다. 🤔🤔

공식 SDK를 수정한다는 것을 별로 좋아하진 않지만 일단 수정해보도록 하겠습니다. nrf_drv_spi.h 파일 역시 존재 유뮤 및 enable 여부를 따져서 매크로를 생성하는 방법으로 수정하겠습니다.

#ifndef NRF_DRV_SPI_H__
#define NRF_DRV_SPI_H__

#include <nrfx.h>

#if defined(SPIM_PRESENT) && NRFX_CHECK(NRFX_SPIM_ENABLED)
    #define NRF_DRV_SPI_WITH_SPIM
#endif
#if defined(SPI_PRESENT) && NRFX_CHECK(NRFX_SPI_ENABLED)
    #define NRF_DRV_SPI_WITH_SPI
#endif

#if defined(NRF_DRV_SPI_WITH_SPIM)
    #include <nrfx_spim.h>
#else
    // Compilers (at least the smart ones) will remove the SPIM related code
    // (blocks starting with "if (NRF_DRV_SPI_USE_SPIM)") when it is not used,
    // but to perform the compilation they need the following definitions.
    #define nrfx_spim_init(...)             0
    #define nrfx_spim_uninit(...)
    #define nrfx_spim_start_task_get(...)   0
    #define nrfx_spim_end_event_get(...)    0
    #define nrfx_spim_abort(...)
#endif

#if defined(NRF_DRV_SPI_WITH_SPI)
    #include <nrfx_spi.h>
#else
    // Compilers (at least the smart ones) will remove the SPI related code
    // (blocks starting with "if (NRF_DRV_SPI_USE_SPI)") when it is not used,
    // but to perform the compilation they need the following definitions.
    #define nrfx_spi_init(...)              0
    #define nrfx_spi_uninit(...)
    #define nrfx_spi_start_task_get(...)    0
    #define nrfx_spi_end_event_get(...)     0
    #define nrfx_spi_abort(...)
 ...

이제 기존의 SPIM_PRESENT, SPI_PRESENT를 사용하는 부분을 NRF_DRV_SPI_WITH_SPIM, NRF_DRV_SPI_WITH_SPI 로 바꾸도록 하겠습니다. nrf_drv_spi.c, nrf_drv_spi.h 두 부분 다 바꿔야 합니다.

typedef struct
{
    uint8_t inst_idx;
    union
    {
#ifdef SPIM_PRESENT
        nrfx_spim_t spim;
#endif
#ifdef SPI_PRESENT
        nrfx_spi_t  spi;
#endif
    } u;
    bool    use_easy_dma;
} nrf_drv_spi_t;
typedef struct
{
    uint8_t inst_idx;
    union
    {
#if defined(NRF_DRV_SPI_WITH_SPIM)
        nrfx_spim_t spim;
#endif
#if defined(NRF_DRV_SPI_WITH_SPI)
        nrfx_spi_t  spi;
#endif
    } u;
    bool    use_easy_dma;
} nrf_drv_spi_t;

👆 위의 예시처럼 모든 매크로를 바꿔보도록 합시다.

모든 매크로를 바꾸면 다시 컴파일한 뒤, 디버그 모드로 들어가 우리의 RX BUFFER에 값이 제대로 들어가 있는지 확인해 보겠습니다.

DW1000 User Manual에서 말했던것 처럼 0x30, 0x01, 0xCA, 0xDE가 RX BUFFER에 잘 저장됩니다. 🙌🙌

0번째 버퍼의 값은 쓰레기 값이니 무시하셔도 됩니다. 🗑️

🔗 Cheesam31 GitHub nRF52832_Basic Repository(SPI_basic)

1개의 댓글

comment-user-thumbnail
2022년 6월 24일

안녕하세요. 저는 nrf52832 를 가지고 프로젝트를 진행 중인 대학생 입니다.
SPI 통신을 통하여 Slave 에 Write 작업을 하고 싶은데...
도와주실 수 있으신지.. 궁금합니다....

답글 달기