nRF52840 QSPI 드라이버 활용
QSPI(Quad Serial Peripheral Interface)는 SPI(Serial Peripheral Interface)의 확장된 버전으로, 네 개의 데이터 라인을 사용하여 데이터를 전송하기 때문에 빠르고 효율적인 통신이 가능합니다. nRF52840와 같은 일부 nRF52 시리즈 칩은 QSPI를 지원하여 외부 플래시 메모리와의 빠른 데이터 전송이 가능하며, 이를 통해 시스템의 성능을 높일 수 있습니다. 이 글에서는 nRF52840에서 QSPI를 설정하고 활용하는 방법을 설명합니다.
1. nRF52840 QSPI의 특징
- 빠른 통신 속도: nRF52840은 빠른 통신 속도를 이용해 외부 플래시 메모리와의 통신이 가능하며, 이를 통해 효율적인 외부 메모리 관리가 가능합니다.
- 풀 듀플렉스 통신: QSPI는 풀 듀플렉스 통신이 가능하여 데이터를 송신과 수신을 동시에 처리할 수 있습니다.
- 클럭 속도 조정 가능: QSPI는 클럭 속도를 프로그래밍하여 다양한 응용에서 속도와 에너지 효율을 조절할 수 있습니다.
- 간단한 시리얼 통신 프로토콜: QSPI는 마이크로컨트롤러와 외부 메모리 간의 데이터 교환을 용이하게 하며, 간단하고 효율적인 통신 프로토콜을 제공합니다.
2. QSPI 모듈 설정
QSPI를 사용하기 위해 SDK에서 몇 가지 설정이 필요합니다. 설정 파일인 sdk_config.h에서 다음과 같이 QSPI를 활성화합니다.
#define NRFX_QSPI_ENABLED 1
#define QSPI_ENABLED 1
또한, 드라이버 소스 파일은 다음 위치에 있습니다.
./modules/nrfx/drivers/src/nrfx_qspi.c
3. QSPI 드라이버 초기화
nRF52840 SDK에서 QSPI를 초기화하기 위해 nrf_drv_qspi_config_t 구조체를 사용합니다. 이 구조체는 QSPI의 핀 설정과 클럭 속도, 딜레이 등을 포함합니다.
io2_pin, io3_pin은 선택적이며, 사용하지 않는 경우 NRF_QSPI_PIN_NOT_CONNECTED로 설정하면 됩니다.
#define NRFX_QSPI_DEFAULT_CONFIG \
{ \
.xip_offset = NRFX_QSPI_CONFIG_XIP_OFFSET, \
.pins = { \
.sck_pin = NRFX_QSPI_PIN_SCK, \
.csn_pin = NRFX_QSPI_PIN_CSN, \
.io0_pin = NRFX_QSPI_PIN_IO0, \
.io1_pin = NRFX_QSPI_PIN_IO1, \
.io2_pin = NRFX_QSPI_PIN_IO2, \
.io3_pin = NRFX_QSPI_PIN_IO3, \
}, \
.prot_if = { \
.readoc = (nrf_qspi_readoc_t)NRFX_QSPI_CONFIG_READOC, \
.writeoc = (nrf_qspi_writeoc_t)NRFX_QSPI_CONFIG_WRITEOC, \
.addrmode = (nrf_qspi_addrmode_t)NRFX_QSPI_CONFIG_ADDRMODE, \
.dpmconfig = false, \
}, \
.phy_if = { \
.sck_delay = (uint8_t)NRFX_QSPI_CONFIG_SCK_DELAY, \
.dpmen = false, \
.spi_mode = (nrf_qspi_spi_mode_t)NRFX_QSPI_CONFIG_MODE, \
.sck_freq = (nrf_qspi_frequency_t)NRFX_QSPI_CONFIG_FREQUENCY, \
}, \
.irq_priority = (uint8_t)NRFX_QSPI_CONFIG_IRQ_PRIORITY, \
}
QSPI 드라이버 초기화 단계에서는 qspi_handler() 함수를 통해 QSPI 통신이 완료되었는지 확인할 수 있습니다.
#define NRF_DRV_QSPI_DEFAULT_CONFIG NRFX_QSPI_DEFAULT_CONFIG
volatile bool m_finished = false;
static void qspi_handler(nrf_drv_qspi_evt_t event, void * p_context)
{
UNUSED_PARAMETER(event);
UNUSED_PARAMETER(p_context);
m_finished = true;
}
void main(void)
{
uint32_t err_code;
nrf_drv_qspi_config_t config = NRF_DRV_QSPI_DEFAULT_CONFIG;
err_code = nrf_drv_qspi_init(&config, qspi_handler, NULL);
APP_ERROR_CHECK(err_code);
...
}
4. QSPI 드라이버 활용
nRF QSPI 드라이버를 사용하여 외부 플래시 메모리를 제어하는 예제를 살펴보겠습니다. 아래는 QSPI를 통해 Flash Memory에 데이터를 읽고 쓰는 코드입니다.
4.1 데이터 쓰기
nrfx_err_t nrfx_qspi_write(void const * p_tx_buffer,
size_t tx_buffer_length,
uint32_t dst_address)
{
NRFX_ASSERT(m_cb.state != NRFX_DRV_STATE_UNINITIALIZED);
NRFX_ASSERT(p_tx_buffer != NULL);
if (!nrfx_is_in_ram(p_tx_buffer) || !nrfx_is_word_aligned(p_tx_buffer))
{
return NRFX_ERROR_INVALID_ADDR;
}
nrf_qspi_write_buffer_set(NRF_QSPI, p_tx_buffer, tx_buffer_length, dst_address);
return qspi_task_perform(NRF_QSPI_TASK_WRITESTART);
}
4.2 데이터 읽기
nrfx_err_t nrfx_qspi_read(void * p_rx_buffer,
size_t rx_buffer_length,
uint32_t src_address)
{
NRFX_ASSERT(m_cb.state != NRFX_DRV_STATE_UNINITIALIZED);
NRFX_ASSERT(p_rx_buffer != NULL);
if (!nrfx_is_in_ram(p_rx_buffer) || !nrfx_is_word_aligned(p_rx_buffer))
{
return NRFX_ERROR_INVALID_ADDR;
}
nrf_qspi_read_buffer_set(NRF_QSPI, p_rx_buffer, rx_buffer_length, src_address);
return qspi_task_perform(NRF_QSPI_TASK_READSTART);
}
4.3 데이터 지우기
nrfx_err_t nrfx_qspi_erase(nrf_qspi_erase_len_t length,
uint32_t start_address)
{
NRFX_ASSERT(m_cb.state != NRFX_DRV_STATE_UNINITIALIZED);
if (!nrfx_is_word_aligned((void const *)start_address))
{
return NRFX_ERROR_INVALID_ADDR;
}
nrf_qspi_erase_ptr_set(NRF_QSPI, start_address, length);
return qspi_task_perform(NRF_QSPI_TASK_ERASESTART);
}
5. QSPI 드라이버를 이용한 메모리 제어 예제
아래는 QSPI를 사용하여 플래시 메모리에 데이터를 쓰고, 읽고, 지우는 예제 코드입니다.
#define WAIT_FOR_PERIPH() do { \
while (!m_finished) {} \
m_finished = false; \
} while (0)
void main(void)
{
ret_code_t err_code = NRF_SUCCESS;
err_code = nrf_drv_qspi_erase(NRF_QSPI_ERASE_LEN_64KB, 0);
APP_ERROR_CHECK(err_code);
WAIT_FOR_PERIPH();
NRF_LOG_INFO("Process of erasing first block start");
err_code = nrf_drv_qspi_write(m_buffer_tx, QSPI_TEST_DATA_SIZE, 0);
APP_ERROR_CHECK(err_code);
WAIT_FOR_PERIPH();
NRF_LOG_INFO("Process of writing data start");
err_code = nrf_drv_qspi_read(m_buffer_rx, QSPI_TEST_DATA_SIZE, 0);
WAIT_FOR_PERIPH();
NRF_LOG_INFO("Data read");
}
6. 주의 사항
- 명령 완료 확인 : QSPI 통신에서 중요한 점은 이전 명령이 완료되기 전에 새로운 명령을 실행하지 않는 것입니다. 예제 코드에서는 이를 방지하기 위해 WAIT_FOR_PERIPH() 매크로를 사용하여 QSPI 명령이 완료되었는지 확인합니다. 만약 명령이 완료되기 전에 새로운 명령을 실행하면 오류가 발생할 수 있습니다.
- Flash Memory 호환성 : QSPI는 병렬 데이터 전송과 적은 핀 사용으로 외부 플래시 메모리와의 통신을 지원합니다. 그러나 모든 플래시 메모리가 동일한 명령 체계를 따르지는 않기 때문에, 사용하고자 하는 플래시 메모리의 데이터 시트를 반드시 확인해야 합니다.
7. 결론
nRF52840의 QSPI 드라이버를 활용하면 빠르고 효율적인 외부 플래시 메모리와의 통신이 가능합니다. 이 글에서는 QSPI의 특징부터 드라이버 설정 및 초기화, 그리고 실제 활용 방법까지 다루었습니다. QSPI는 풀 듀플렉스 통신과 적은 핀 사용으로 외부 메모리와의 데이터 전송 효율을 극대화할 수 있는 강력한 도구입니다.
하지만, QSPI 사용 시에는 명령의 완료 여부를 철저히 확인하고, 사용하는 플래시 메모리의 명령 체계를 정확히 이해하는 것이 중요합니다. 이러한 주의사항을 염두에 두고 개발한다면, 안정적이고 성능이 우수한 메모리 인터페이스를 구현할 수 있을 것입니다.
이 가이드가 nRF52840 QSPI 드라이버를 활용하는 데 도움이 되기를 바라며, 다양한 응용 분야에서 성공적인 개발이 이루어지기를 바랍니다.
'nRF52' 카테고리의 다른 글
nRF52840에서 FatFs 사용 - 파일 시스템 (0) | 2024.09.06 |
---|---|
nRF52840 SAADC, ADC 활용 (0) | 2024.09.05 |
nRF52840 SPI 드라이버 활용하기 (0) | 2024.09.03 |
nRF52840 TWI(I2C) Slave 드라이버 활용하기 (0) | 2024.09.02 |
nRF52840 TWI(I2C) Master 드라이버 활용하기 (0) | 2024.09.01 |