nRF52840 SPI 드라이버 활용하기
SPI(Serial Peripheral Interface)는 마이크로컨트롤러와 다양한 주변 장치 간의 고속 데이터 통신을 위한 시리얼 통신 프로토콜 중 하나입니다. 주로 짧은 거리에서 고속 데이터 전송이 필요한 응용 프로그램에 사용되며, 마스터-슬레이브 구조를 기반으로 여러 슬레이브 장치가 하나의 마스터에 의해 제어될 수 있습니다.
1. SPI의 특징
1.1. 전이중(Full-Duplex) 통신
SPI는 전이중 통신을 지원하며, 마스터와 슬레이브가 동시에 데이터를 주고받을 수 있습니다.
1.2. 동기식(Synchronous) 통신
마스터는 클럭을 제공하여 통신을 동기화합니다. 이 클럭은 SCLK(Serial Clock) 라인을 통해 전달됩니다.
1.3. 데이터 라인:
- MOSI(Master Out Slave In): 마스터에서 슬레이브로 데이터를 전송하는 라인.
- MISO(Master In Slave Out): 슬레이브에서 마스터로 데이터를 전송하는 라인.1.4. 슬레이브 선택 라인SS/CS(Slave Select/Chip Select) 라인을 통해 특정 슬레이브 장치를 선택합니다.1.5. 다수의 슬레이브 지원하나의 마스터가 여러 개의 슬레이브 장치를 제어할 수 있습니다.
2. SPI 모듈 설정
SPI 모듈을 설정하기 위해 SDK 설정 파일인 sdk_config.h를 수정해야 합니다. 아래와 같은 설정을 활성화합니다.
#define NRFX_SPIM_ENABLED 1 #define NRFX_SPIM2_ENABLED 1 #define NRFX_SPI_ENABLED 1 #define NRFX_SPI2_ENABLED 1 #define SPI_ENABLED 1 #define SPI2_ENABLED 1
또한, nRF 드라이버 파일 경로는 다음과 같습니다.
./integration/nrfx/legacy/nrf_drv_spi.c ./modules/nrfx/drivers/src/nrfx_spim.c
3. SPI 드라이버 초기화이 섹션에서는 MCP2515 SPI CAN 모듈 드라이버를 사용하여 SPI 드라이버 초기화 방법을 설명합니다.
3.1. 드라이버 구성
먼저, nrf_drv_spi_config_t 구조체를 사용하여 SPI 속도와 모드를 설정합니다. 또한 각 통신 라인(MOSI, MISO, SCLK, SS) 핀을 설정합니다. SPI 이벤트 핸들러는 통신 요청이 완료되었는지 확인하는 용도로 사용됩니다.
중요: SPI 통신을 시작하기 전, 슬레이브 장치를 선택하기 위해 SS 핀을 Low 상태로 설정합니다. 통신이 완료되면 SS 핀을 다시 High 상태로 변경해야 합니다.
#define M_PRPBE_SPI_CAN_INSTANCE 2 /**< SPI 인스턴스 인덱스. */
static const nrf_drv_spi_t can_spi = NRF_DRV_SPI_INSTANCE(M_PRPBE_SPI_CAN_INSTANCE); /**< SPI 인스턴스. */
#define MCP2515_PIN_INT NRF_GPIO_PIN_MAP(1, 2)
#define M_PROBE_SPI_SS_PIN NRF_GPIO_PIN_MAP(0, 11)
#define M_PROBE_SPI_MISO_PIN NRF_GPIO_PIN_MAP(0, 12)
#define M_PROBE_SPI_MOSI_PIN NRF_GPIO_PIN_MAP(0, 13)
#define M_PROBE_SPI_SCK_PIN NRF_GPIO_PIN_MAP(0, 14)
void spi_can_event_handler(nrf_drv_spi_evt_t const * p_event, void * p_context)
{
if (p_event->type == NRF_DRV_SPI_EVENT_DONE)
{
spi_xfer_done = true;
}
}
void mcp_spi_init(void)
{
uint32_t err_code;
mcp_can_setcs(M_PROBE_SPI_SS_PIN);
nrf_drv_gpiote_in_config_t mcp2515_int_config = GPIOTE_CONFIG_IN_SENSE_TOGGLE(true);
mcp2515_int_config.pull = NRF_GPIO_PIN_PULLUP;
err_code = nrf_drv_gpiote_in_init(MCP2515_PIN_INT, &mcp2515_int_config, mcp2515_int_pin_handler);
APP_ERROR_CHECK(err_code);
nrf_drv_gpiote_in_event_enable(MCP2515_PIN_INT, true);
nrf_drv_spi_config_t spi_config = NRF_DRV_SPI_DEFAULT_CONFIG;
spi_config.frequency = NRF_DRV_SPI_FREQ_125K;
spi_config.mode = NRF_DRV_SPI_MODE_0; // SPI 모드 설정
spi_config.ss_pin = M_PROBE_SPI_SS_PIN; // SS 핀 설정
spi_config.miso_pin = M_PROBE_SPI_MISO_PIN; // MISO 핀 설정
spi_config.mosi_pin = M_PROBE_SPI_MOSI_PIN; // MOSI 핀 설정
spi_config.sck_pin = M_PROBE_SPI_SCK_PIN; // SCLK 핀 설정
spi_config.bit_order = NRF_DRV_SPI_BIT_ORDER_MSB_FIRST; // 비트 순서 설정
err_code = nrf_drv_spi_init(&can_spi, &spi_config, spi_can_event_handler, NULL);
APP_ERROR_CHECK(err_code);
#if MCP2515_DEBUG_MODE
NRF_LOG_INFO("mcp_spi_init complete");
#endif
}
3.2 SPI 통신 시작
SPI 통신을 시작하기 위해 SS 핀을 Low 상태로 변경한 후, TX와 RX 버퍼를 준비합니다. nrf_drv_spi_transfer() 함수를 호출하여 데이터를 송수신합니다. 통신이 완료되면 SS 핀을 다시 High 상태로 설정합니다.
void mcp2515_select(void)
{
nrf_gpio_pin_clear(m_mcp_can.m_cs);
}
void mcp2515_unselect(void)
{
nrf_gpio_pin_set(m_mcp_can.m_cs);
}
uint8_t mcp2515_readRegister(const uint8_t address)
{
mcp2515_select();
uint8_t m_tx_buf[] = {MCP_READ, address, 0x00};
uint8_t m_tx_length = sizeof(m_tx_buf);
uint8_t m_rx_buf[3];
uint8_t m_rx_length = sizeof(m_rx_buf);
memset(m_rx_buf, 0, m_rx_length);
spi_xfer_done = false;
APP_ERROR_CHECK(nrf_drv_spi_transfer(&can_spi, m_tx_buf, m_tx_length, m_rx_buf, m_rx_length));
while (!spi_xfer_done)
{
if (nrf_sdh_is_enabled())
{
sd_app_evt_wait();
}
else
{
__WFE();
}
}
mcp2515_unselect();
return m_rx_buf[2];
}
3.3 SPI를 활용한 CAN 통신 초기화
CAN 통신을 초기화하는 과정은 다음과 같습니다.
void can_init(void)
{
NRF_LOG_INFO("CAN Initialization...");
mcp_spi_init();
NRF_LOG_INFO("MCP2515 SPI Initialized");
START_INIT:
if (mcp_can_begin(CAN_500KBPS, MCP_8MHz) == CAN_OK)
{
NRF_LOG_INFO("CAN BUS Initialization ok");
}
else
{
NRF_LOG_INFO("CAN BUS Initialization failed");
NRF_LOG_INFO("Init CAN BUS again");
nrf_delay_ms(1000);
goto START_INIT;
}
NRF_LOG_INFO("CAN Initialization COMPLETED.");
}
void can_thread(void* pvParameters)
{
can_init();
for (;;)
{
uint32_t can_id;
uint8_t buf[16];
uint8_t len;
if (CAN_MSGAVAIL == mcp_can_check_receive())
{
mcp_can_read_msg(&can_id, &len, buf);
NRF_LOG_INFO("%x %d", can_id, len);
continue;
}
vTaskDelay(pdMS_TO_TICKS(1));
}
}
int can_main(void * pvParameters)
{
if (pdPASS != xTaskCreate(can_thread, "can", 256, pvParameters, 2, NULL))
{
APP_ERROR_HANDLER(NRF_ERROR_NO_MEM);
}
}
4. 결론
SPI는 고속 데이터 전송이 가능하고 간단한 구조를 가진 시리얼 통신 프로토콜입니다. 이를 통해 센서, 메모리, 통신 장치 등 다양한 주변 장치와 쉽게 통신할 수 있습니다. 다만, 케이블 길이 제한과 복잡한 구성으로 인해 짧은 거리의 고속 통신에 적합합니다.
이 글에서는 nRF52840에서 SPI 드라이버를 설정하고, MCP2515 CAN 모듈과의 통신을 구현하는 방법을 설명했습니다. 이를 통해 SPI를 활용한 다양한 응용 프로그램을 개발하는 데 도움이 될 것입니다.
'nRF52' 카테고리의 다른 글
nRF52840 SAADC, ADC 활용 (0) | 2024.09.05 |
---|---|
nRF52840 QSPI 드라이버 활용 (0) | 2024.09.04 |
nRF52840 TWI(I2C) Slave 드라이버 활용하기 (0) | 2024.09.02 |
nRF52840 TWI(I2C) Master 드라이버 활용하기 (0) | 2024.09.01 |
nRF52840 TWI I2C 설정 가이드 (0) | 2024.08.31 |