Blog Datasheets Home About me Clients My work Services Contact

G2Labs Grzegorz Grzęda

Implementing SPI communication on the nRF52

November 23, 2023

Implementing SPI Communication on the nRF52

Welcome to another exciting blog post on programming! In this article, we’ll dive into the implementation of Serial Peripheral Interface (SPI) communication on the powerful nRF52 microcontroller. SPI is a widely-used communication protocol that enables robust data exchange between devices in a synchronous fashion.

Introduction to SPI

SPI is a full-duplex, synchronous communication protocol that involves a master device and one or more slave devices. It uses four wires:

Setting Up the Hardware

Before we explore the software implementation, let’s wire up our nRF52 board to a slave device. To keep things simple, we’ll use a single slave device: an SPI-enabled EEPROM (24LC256).

First, connect the following pins on your nRF52 board to the corresponding pins on the EEPROM:

Ensure that the connections are secure, as any loose connection can hinder successful communication.

SPI Driver Initialization

To implement SPI communication on the nRF52, we’ll be using the Nordic SDK (Software Development Kit) and its peripheral libraries. Let’s get started by initializing the SPI driver.

1
2
3
4
5
#include "nrf_drv_spi.h"

#define SPI_INSTANCE  0        // SPI instance to be used

static const nrf_drv_spi_t spi = NRF_DRV_SPI_INSTANCE(SPI_INSTANCE);     // SPI instance used in the application

In the above code snippet, we include the necessary header file and define the SPI instance to be used (we chose instance 0). We create an instance of the nrf_drv_spi_t structure called spi using the NRF_DRV_SPI_INSTANCE macro.

SPI Configuration

Next, we need to configure the SPI peripheral settings. We can define those settings in a data structure and pass it to the SPI driver using the nrf_drv_spi_init function.

1
2
3
4
5
6
7
8
9
nrf_drv_spi_config_t spi_config = NRF_DRV_SPI_DEFAULT_CONFIG;    // Default SPI configuration

spi_config.sck_pin = SPI_SCLK_PIN;          // Configure the SCLK pin
spi_config.mosi_pin = SPI_MOSI_PIN;        // Configure the MOSI pin
spi_config.miso_pin = SPI_MISO_PIN;         // Configure the MISO pin
spi_config.ss_pin = SPI_SS_PIN;                 // Configure the SS/CS pin
spi_config.frequency = NRF_DRV_SPI_FREQ_1M; // Set the clock frequency

nrf_drv_spi_init(&spi, &spi_config, NULL, NULL);     // Initialize the SPI module

In the above code snippet, we set up a nrf_drv_spi_config_t structure called spi_config and configure the necessary pins and clock frequency. Finally, we initialize the SPI module using nrf_drv_spi_init.

SPI Communication

With the SPI driver initialized and the configuration set, we can now perform SPI communication.

1
2
3
4
void spi_send_receive(uint8_t *tx_data, uint8_t *rx_data, size_t size)
{
    nrf_drv_spi_transfer(&spi, tx_data, size, rx_data, size);
}

The spi_send_receive function above uses the nrf_drv_spi_transfer function to perform a full-duplex data exchange. It takes the pointers to the transmit and receive data buffers, along with the size of the data, as parameters.

Putting It All Together

Now that we have covered all the necessary steps, let’s combine everything to read from and write to the SPI-enabled EEPROM.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
#include <stdbool.h>
#include "nrf_drv_spi.h"

#define SPI_INSTANCE  0
#define EEPROM_CMD_READ 0x03
#define EEPROM_CMD_WRITE 0x02

static const nrf_drv_spi_t spi = NRF_DRV_SPI_INSTANCE(SPI_INSTANCE);

void spi_send_receive(uint8_t *tx_data, uint8_t *rx_data, size_t size)
{
    nrf_drv_spi_transfer(&spi, tx_data, size, rx_data, size);
}

void eeprom_read(uint16_t address, uint8_t *data, size_t len)
{
    uint8_t tx_buffer[3] = {EEPROM_CMD_READ, address >> 8, address & 0xFF};
  
    spi_send_receive(tx_buffer, NULL, 3);     // Send command and address
    spi_send_receive(NULL, data, len);         // Receive data
}

void eeprom_write(uint16_t address, uint8_t *data, size_t len)
{
    uint8_t tx_buffer[3 + len];
  
    tx_buffer[0] = EEPROM_CMD_WRITE;
    tx_buffer[1] = address >> 8;
    tx_buffer[2] = address & 0xFF;
  
    memcpy(&tx_buffer[3], data, len);    // Copy data to the transmission buffer
  
    spi_send_receive(tx_buffer, NULL, 3 + len);    // Send command, address, and data
}

In the code above, we define functions eeprom_read and eeprom_write to read and write data to the EEPROM device, respectively. We use the spi_send_receive function defined earlier to send and receive data.

Conclusion

Congratulations! You have successfully implemented SPI communication on the nRF52 microcontroller. By following the steps outlined in this article, you should now have a good understanding of how to set up and use SPI on the nRF52 using the Nordic SDK.

Feel free to experiment further by adding error handling, additional features, or integrating more slave devices into your project. The possibilities are limitless!

I hope you found this blog post informative and helpful. Stay tuned for more exciting programming topics. Happy coding!


➡️ Interfacing sensors with the nRF52


⬅️ Using I2C protocol with the nRF52


Go back to Posts.