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
}
}