I struggled for a while with this: the SDK documentation and the example application did not explain a common use case: reading and writing single-byte registers in a peripheral chip. Various posts give clues without providing complete examples, so I've made a working example, replacing all of the code in the \examples\peripheral\spi example. In my case I am using a SX1276 LoRa radio chip. I offer the code here, with plenty of comments, in case it is useful for others.
#include "nrf_drv_spi.h" #include "app_util_platform.h" #include "nrf_gpio.h" #include "nrf_delay.h" #include "boards.h" #include "app_error.h" #include <string.h> #include "nrf_log.h" #include "nrf_log_ctrl.h" #include "nrf_log_default_backends.h" #define SPI_INSTANCE 0 /**< SPI instance index. */ static const nrf_drv_spi_t spi = NRF_DRV_SPI_INSTANCE(SPI_INSTANCE); /**< SPI instance. */ static uint8_t m_tx_buf[2]; /**< TX buffer. */ static uint8_t m_rx_buf[2]; /**< RX buffer. */ static ret_code_t err_code; // Registers in an SX1276 SPI device #define REG_LR_VERSION 0x42 #define REG_LR_FRFLSB 0x08 #define REG_VERSION_VALUE 0x12 // Read from an SPI device static uint8_t spiRead(uint8_t regAddress) { m_tx_buf[0] = regAddress & 0x7f; // SX1276 interprets bit 7 as write enable m_tx_buf[1] = 0; // don't care nrf_gpio_pin_clear(SPI_SS_PIN); // Manual control of the chip select pin err_code = nrf_drv_spi_transfer(&spi, ( uint8_t* )&m_tx_buf, 2, ( uint8_t* )&m_rx_buf, 2); nrf_gpio_pin_set(SPI_SS_PIN); APP_ERROR_CHECK(err_code); return m_rx_buf[1]; // Discard m_rx_buf[0] } // Write to an SPI device static void spiWrite(uint8_t regAddress, uint8_t value) { m_tx_buf[0] = regAddress | 0x80; // SX1276 interprets bit 7 as write enable m_tx_buf[1] = value; nrf_gpio_pin_clear(SPI_SS_PIN); // Manual control of the chip select pin err_code = nrf_drv_spi_transfer(&spi, ( uint8_t* )&m_tx_buf, 2, NULL, 0); // NULL seems OK on writes nrf_gpio_pin_set(SPI_SS_PIN); APP_ERROR_CHECK(err_code); } int main(void) { APP_ERROR_CHECK(NRF_LOG_INIT(NULL)); NRF_LOG_DEFAULT_BACKENDS_INIT(); // Pins are defined in sdk_config.h nrf_drv_spi_config_t spi_config = NRF_DRV_SPI_DEFAULT_CONFIG; spi_config.ss_pin = NRF_DRV_SPI_PIN_NOT_USED; // will be controlled by our code spi_config.miso_pin = SPI_MISO_PIN; spi_config.mosi_pin = SPI_MOSI_PIN; spi_config.sck_pin = SPI_SCK_PIN; // Now enable a pin to work as the chip select nrf_gpio_cfg_output(SPI_SS_PIN); nrf_gpio_pin_set(SPI_SS_PIN); // Specify blocking operation APP_ERROR_CHECK(nrf_drv_spi_init(&spi, &spi_config, NULL, NULL)); NRF_LOG_INFO(" "); NRF_LOG_INFO("***** SPI example started. *****"); NRF_LOG_FLUSH(); while (1) { // This is a read-only register containing a chip ID NRF_LOG_INFO("Read Version Register: 0x%02x. Expected 0x%02x", spiRead(REG_LR_VERSION), REG_VERSION_VALUE); NRF_LOG_INFO(" "); // Try writing then reading each bit in turn to/from a read/write register: REG_LR_FRFLSB for (int i=0; i<8; i++) { spiWrite( REG_LR_FRFLSB, 0x01 << i ); NRF_LOG_INFO("Wrote 0x%02x, read 0x%02x", 0x01 << i, spiRead( REG_LR_FRFLSB)); } NRF_LOG_INFO(" "); NRF_LOG_FLUSH(); nrf_delay_ms(2000); // Let's not go too fast } }