This post is older than 2 years and might not be relevant anymore
More Info: Consider searching for newer posts

SPI on NCS

I have been trying to figure out how to use SPI on nRF Connect SDK. I cannot find a simple enough example to get started other than the convoluted SPI flash example or the BME280 and similar ones.

Should I use some specific nRF SPI library? I would prefer to make this library universal.

I have been using nRF52840-DK for development and I am trying to port miguelbalboa / rfid to Zephyr, this is my code:

mfrc522.c

#include "mfrc522.h"

#include <logging/log.h>
LOG_MODULE_REGISTER(mfrc522, LOG_LEVEL_DBG);

#include <devicetree.h>
#include <drivers/gpio.h>
#include <drivers/spi.h>

#define SPI_DEV DT_LABEL(DT_NODELABEL(spi0))

#define MFRC522_IRQ_NODE DT_ALIAS(led2)
#define MFRC522_IRQ DT_GPIO_LABEL(MFRC522_IRQ_NODE, gpios)
#define MFRC522_IRQ_PIN DT_GPIO_PIN(MFRC522_IRQ_NODE, gpios)
#define MFRC522_IRQ_FLAGS DT_GPIO_FLAGS(MFRC522_IRQ_NODE, gpios)

#define MFRC522_CS_NODE DT_ALIAS(led3)
#define MFRC522_CS DT_GPIO_LABEL(MFRC522_CS_NODE, gpios)
#define MFRC522_CS_PIN DT_GPIO_PIN(MFRC522_CS_NODE, gpios)
#define MFRC522_CS_FLAGS DT_GPIO_FLAGS(MFRC522_CS_NODE, gpios)

const struct device *spi_dev;
const struct device *reset_dev;
const struct device *irq_dev;
const struct device *cs_dev;

const struct spi_cs_control mfrc522_cs = {
    .gpio_dev = cs_dev,
    .gpio_pin = MFRC522_CS_PIN,
    .delay = 0,
    .gpio_dt_flags = MFRC522_CS_FLAGS,
};

const struct spi_config spi_conf = {
	.frequency = 20000000,
	.operation = SPI_OP_MODE_MASTER | SPI_WORD_SET(8) | SPI_TRANSFER_MSB | SPI_LINES_SINGLE,
	.slave = 0,
	.cs = &mfrc522_cs,
};

void init_peripherals(){
    cs_dev = device_get_binding(MFRC522_CS);
	if (cs_dev == NULL)
	{
		return;
	}

	if (gpio_pin_configure(cs_dev, MFRC522_CS_PIN, GPIO_OUTPUT_HIGH | MFRC522_CS_FLAGS) < 0)
	{
		return;
	}

    spi_dev = device_get_binding(SPI_DEV);
	if (!spi_dev) {
		return;
	}

    k_msleep(1);
}

uint8_t MFRC522_ReadRegister(uint8_t reg) {
    struct spi_buf_set tx_bufs;
    struct spi_buf_set rx_bufs;
	struct spi_buf txb;
	struct spi_buf rxb;
    uint16_t command = 0x80 | reg;

    txb.buf = &command;
	txb.len = 1;
    tx_bufs.buffers = (const struct spi_buf *)&txb;
	tx_bufs.count = 1;

    rxb.buf = NULL;
	rxb.len = 1;
    rx_bufs.buffers = (const struct spi_buf *)&rxb;
	rx_bufs.count = 1;

    spi_transceive(spi_dev, &spi_conf, &tx_bufs, &rx_bufs);
	return *(uint8_t *)rx_bufs.buffers->buf;
}

void MFRC522_WriteRegister(uint8_t reg, uint8_t value){
    struct spi_buf_set tx_bufs;
	struct spi_buf txb;
    
    uint8_t command;
    command = reg;

    txb.buf = &command;
	txb.len = 1;

	tx_bufs.buffers = (const struct spi_buf *)&txb;
	tx_bufs.count = 1;

    LOG_DBG("Sending SPI command");
	spi_write(spi_dev, &spi_conf, &tx_bufs);
    LOG_DBG("SPI written");
}

void MFRC522_Reset(){
    MFRC522_WriteRegister(CommandReg, SoftReset);	// Issue the SoftReset command.

    LOG_DBG("Reset command sent");

	uint8_t count = 0;
	do {
		// Wait for the PowerDown bit in CommandReg to be cleared (max 3x50ms)
		k_msleep(50);
	} while ((MFRC522_ReadRegister(CommandReg) & (1 << 4)) && (++count) < 3);
}

void MFRC522_Antenna_On(){
    uint8_t value = MFRC522_ReadRegister(TxControlReg);
	if ((value & 0x03) != 0x03) {
		MFRC522_WriteRegister(TxControlReg, value | 0x03);
	}
}

void MRFRC522_init()
{
    LOG_DBG("Initializing");
    init_peripherals();
    LOG_DBG("Initialized");

    MFRC522_Reset();
    LOG_DBG("Reset done");

    MFRC522_WriteRegister(TxModeReg, 0x00);
    MFRC522_WriteRegister(RxModeReg, 0x00);
    MFRC522_WriteRegister(ModWidthReg, 0x26);

    MFRC522_WriteRegister(TModeReg, 0x80);
    MFRC522_WriteRegister(TPrescalerReg, 0xA9);
    MFRC522_WriteRegister(TReloadRegH, 0x03);
    MFRC522_WriteRegister(TReloadRegL, 0xE8);

    MFRC522_WriteRegister(TxASKReg, 0x40);
    MFRC522_WriteRegister(ModeReg, 0x3D);
    MFRC522_Antenna_On();
}

mfrc522.h

//Page 0 ==> Command and Status
#define Page0_Reserved_1 	0x00
#define CommandReg			0x01
#define ComIEnReg			0x02
#define DivIEnReg			0x03
#define ComIrqReg			0x04
#define DivIrqReg			0x05
#define ErrorReg			0x06
#define Status1Reg			0x07
#define Status2Reg			0x08
#define FIFODataReg			0x09
#define FIFOLevelReg		0x0A
#define WaterLevelReg		0x0B
#define ControlReg			0x0C
#define BitFramingReg		0x0D
#define CollReg				0x0E
#define Page0_Reserved_2	0x0F

//Page 1 ==> Command
#define Page1_Reserved_1	0x10
#define ModeReg				0x11
#define TxModeReg			0x12
#define RxModeReg			0x13
#define TxControlReg		0x14
#define TxASKReg			0x15
#define TxSelReg			0x16
#define RxSelReg			0x17
#define RxThresholdReg		0x18
#define	DemodReg			0x19
#define Page1_Reserved_2	0x1A
#define Page1_Reserved_3	0x1B
#define MfTxReg				0x1C
#define MfRxReg				0x1D
#define Page1_Reserved_4	0x1E
#define SerialSpeedReg		0x1F

//Page 2 ==> CFG
#define Page2_Reserved_1	0x20
#define CRCResultReg_1		0x21
#define CRCResultReg_2		0x22
#define Page2_Reserved_2	0x23
#define ModWidthReg			0x24
#define Page2_Reserved_3	0x25
#define RFCfgReg			0x26
#define GsNReg				0x27
#define CWGsPReg			0x28
#define ModGsPReg			0x29
#define TModeReg			0x2A
#define TPrescalerReg		0x2B
#define TReloadRegH 		0x2C
#define TReloadRegL 		0x2D
#define TCounterValReg_1	0x2E
#define TCounterValReg_2 	0x2F

//Page 3 ==> TestRegister
#define Page3_Reserved_1 	0x30
#define TestSel1Reg			0x31
#define TestSel2Reg			0x32
#define TestPinEnReg		0x33
#define TestPinValueReg		0x34
#define TestBusReg			0x35
#define AutoTestReg			0x36
#define VersionReg			0x37
#define AnalogTestReg		0x38
#define TestDAC1Reg			0x39
#define TestDAC2Reg			0x3A
#define TestADCReg			0x3B
#define Page3_Reserved_2 	0x3C
#define Page3_Reserved_3	0x3D
#define Page3_Reserved_4	0x3E
#define Page3_Reserved_5	0x3F

// Commands
#define SoftReset       0x0F

void MRFRC522_init();

main.c

#include <logging/log.h>
LOG_MODULE_REGISTER(door_opener, LOG_LEVEL_DBG);

#include "io.h"
#include "coap.h"
#include "mfrc522.h"

#include <errno.h>
#include <sys/printk.h>
#include <sys/byteorder.h>
#include <zephyr.h>

void main(void)
{
	int r = 0;
	LOG_DBG("Initializing IO");
	initialize_io();
	LOG_DBG("IO initialized");
	
	//r = start_coap_server();
	if (r < 0){
		LOG_DBG("Failed to start CoAP Server");
		return;
	} else {
		LOG_DBG("Started CoAP Server successfully");
	}
	MRFRC522_init();	

	while (1)
	{
		//r = process_client_request();
		check_button();
	}
}

prj.conf

CONFIG_GPIO=y
CONFIG_SPI=y
CONFIG_MAIN_STACK_SIZE=4096
CONFIG_NEWLIB_LIBC=y

# Logging
CONFIG_LOG=y

error:

mfrc522.c:28:17: error: initializer element is not constant
   28 |     .gpio_dev = cs_dev,

When I move the spi_conf and spi_cs_control initializers to WriteRegister and ReadRegister I get the following on the serial port:

[00:00:00.256,652] <dbg> mfrc522.MRFRC522_init: Initializing
[00:00:00.256,683] <dbg> mfrc522.MRFRC522_init: Initialized
[00:00:00.256,683] <dbg> mfrc522.MFRC522_WriteRegister: Sending SPI command
[00:00:00.256,683] <err> os: ***** MPU FAULT *****
[00:00:00.256,683] <err> os:   Instruction Access Violation
[00:00:00.256,713] <err> os: r0/a1:  0x00000000  r1/a2:  0x200024ec  r2/a3:  0x200024d0
[00:00:00.256,713] <err> os: r3/a4:  0x00000000 r12/ip:  0x20000114 r14/lr:  0x0000068f
[00:00:00.256,713] <err> os:  xpsr:  0x81000000
[00:00:00.256,713] <err> os: Faulting instruction address (r15/pc): 0xabf7fbb4
[00:00:00.256,713] <err> os: >>> ZEPHYR FATAL ERROR 0: CPU exception on CPU 0
[00:00:00.256,744] <err> os: Current thread: 0x20000328 (unknown)
[00:00:01.732,177] <err> fatal_error: Resetting system

  • After changing the way pointers were being dealt with the code worked:

    mfrc522.c

    void MFRC522_WriteRegister(uint8_t reg, uint8_t value){
        const struct spi_cs_control mfrc522_cs = {
        .gpio_dev = cs_dev,
        .gpio_pin = MFRC522_CS_PIN,
        .delay = 0,
        .gpio_dt_flags = MFRC522_CS_FLAGS,
        };
    
        const struct spi_config spi_conf = {
    	.frequency = 200000,
    	.operation = SPI_WORD_SET(8) | SPI_TRANSFER_MSB,
    	.slave = 0,
    	.cs = &mfrc522_cs,
        };
    
        struct spi_buf_set tx_bufs;
    	struct spi_buf txb[2];
    
        txb[0].buf = ®
    	txb[0].len = 1;
        txb[1].buf = &value;
    	txb[1].len = 1;
    
    	tx_bufs.buffers = (const struct spi_buf *)&txb;
    	tx_bufs.count = 2;
    
    	spi_write(spi_dev, &spi_conf, &tx_bufs);
    }

Related