Beware that this post is related to an SDK in maintenance mode
More Info: Consider nRF Connect SDK for new designs
This post is older than 2 years and might not be relevant anymore
More Info: Consider searching for newer posts

Cannot read SPI external flash using nrf_spi_mngr transactions

Hi,

I'm trying to connect a nordic nrf52832 as an SPI master to an external Flash, and for that I'm using SDK 16.0.0. 

Since I wanted to already use the transaction mechanism as I use for TWI,  I tried to adapt the example from ..\examples/peripheral/spi_master_using_nrf_spi_mngr, which - unfortunately for me at least - does not perform a read, only writes.

However, I cannot really communicate with the device, at least I'm not even able to obtain the external_flash properties (3x bytes whoamI like data) to assure that I'm connected and if so to the correct device (I have a single slave SPI device and single master manager). It seems to me that I may have an issue with my SW code since I cannot even get the end_callback running. I.e., it seems to me that I'm really missing any configuration instead of having an HW issue (which I cannot currently test).

So, I'm sending part of the code, that I hope can already help in spotting the problem(s).

/* SDK */
#include "nrf_spi_mngr.h"
#include "nrf_gpio.h"
#include "nrf_delay.h"
/* C standard library */
#include <assert.h>
#include <stdbool.h>
#include <stdint.h>

/********************************** Private ***********************************/
/* SPI Specific definitions */
#define SPI_INSTANCE_ID               1    ///< SPI ID. Cannot be instance 0 due to TWI_mngr instance 0 presence.
#define SPI_QUEUE_SIZE                50  ///< SPI maximum pending transactions.
#define SPI_SCK_PIN                   (25)  ///< SCK (Serial Clock) signal pin.
#define SPI_MOSI_PIN                  (24)  ///< MOSI (Master Out Slave In) signal pin.
#define SPI_MISO_PIN                  (23)  ///< MISO (Master In Slave Out) signal pin.
#define SPI_SS_PIN                    (26)  ///< SS (Slave Select) signal pin. Optional
#define SPI_OVER_READ_CHARACTER   (0xFF)   ///< Over Read Character default
#define SPI_FREQUENCY                 (NRF_DRV_SPI_FREQ_8M)   ///< Frequency 8MHz
#define SPI_MODE                      (1)   ///< CPOL=0 (Leading), CPHA=0 (Active High)
#define SPI_BIT_ORDER             (NRF_SPI_BIT_ORDER_MSB_FIRST)   ///< Bit Order default
/* used together with OpCodes (Operation Commands) */
#define DUMMY_BYTE                                  (0x00)

/* SPI manager definition. */
NRF_SPI_MNGR_DEF(m_nrf_spi_mngr, SPI_QUEUE_SIZE, SPI_INSTANCE_ID);

/* SPI manager configuration utility. */
static ret_code_t spi_manager_init(void) {
    nrf_drv_spi_config_t const m_nrf_drv_spi_config = {
      .sck_pin            = SPI_SCK_PIN,
      .mosi_pin           = SPI_MOSI_PIN,
      .miso_pin           = SPI_MISO_PIN,
      .ss_pin             = SPI_SS_PIN,
      .irq_priority       = APP_IRQ_PRIORITY_LOW, // changed from APP_IRQ_PRIORITY_LOWEST without success
      .orc                = SPI_OVER_READ_CHARACTER,
      .frequency          = SPI_FREQUENCY,
      .mode               = SPI_MODE,
      .bit_order          = SPI_BIT_ORDER
    };
    /* Inits SPI Manager Driver */
    return nrf_spi_mngr_init(&m_nrf_spi_mngr, &m_nrf_drv_spi_config);
}

/* SPI manager uninit. */
static void spi_manager_uninit(void) {
  /* Make sure SPI is disabled */
  nrf_spi_mngr_uninit(&m_nrf_spi_mngr);
}

/* Flash chip state */
static struct _external_flash_x {
    uint8_t device_id[2]; /* Holds the device ID (Parts 1 and 2) */
    uint8_t manufacturer_id; /* Holds the manufacturer ID */
    uint8_t status_register[2]; /* Holds the manufacturer ID */
} external_flash_x = { .device_id = { 0 }, .manufacturer_id = 0, .status_register = { 0 } };

static bool finished_cb = false;

// Internal Buffer 256 bytes experience.
static uint8_t m_auxiliar_buffer[256];


/* Chip Select enabling (active low) */
__STATIC_INLINE void _start_chip_select(void) {
    nrf_gpio_pin_clear(SPI_SS_PIN);
    nrf_delay_us(10);
};

/* Chip Select disabling (disable high) */
__STATIC_INLINE void _end_chip_select(void) {
    nrf_gpio_pin_set(SPI_SS_PIN);
};

static void getManufactureAndDevice_begin_callback(void * p_user_data) {
    _start_chip_select();
};
/* This never get called. I.e., finished_cb never gets true.*/
static void getManufactureAndDevices_end_callback(ret_code_t result, void * p_user_data) {
    finished_cb = true; 
    external_flash_x.manufacturer_id = m_auxiliar_buffer[0];
    external_flash_x.device_id[0] = m_auxiliar_buffer[1];
    external_flash_x.device_id[1] = m_auxiliar_buffer[2];
    _end_chip_select();
};

/* Obtains the integrity of the expected manufacturer and family code */
static bool _external_flash_x_get_integrity_data(void) {
  // NRF SPI buffer for the commands to get the Manufacture and Devices' Ids
  static NRF_SPI_MNGR_BUFFER_LOC_IND uint8_t integrity_op_cmds[] = {
      READ_MAN_DEVICE_ID_OP,
      DUMMY_BYTE,
      DUMMY_BYTE,
      DUMMY_BYTE
  }; // Note: external flash manufacture states only 3x bytes of DUMMY_BYTE (0x00)

  static nrf_spi_mngr_transfer_t NRF_SPI_MNGR_BUFFER_LOC_IND integrity_transfer_cmds[] = {
      NRF_SPI_MNGR_TRANSFER(&integrity_op_cmds[0], sizeof(integrity_op_cmds), m_auxiliar_buffer, 4) 
	  // Note: Already changed "4" by many other values without success. Manufactor states 4 commands (i.e., 1 + 3x dummies)
  };

  static nrf_spi_mngr_transaction_t const integrity_transaction_cmd = {
    .begin_callback      = getManufactureAndDevice_begin_callback,
    .end_callback        = getManufactureAndDevices_end_callback,
    .p_user_data         = NULL,
    .p_transfers         = integrity_transfer_cmds,
    .number_of_transfers = 1,
    .p_required_spi_cfg  = NULL
  };

  if (nrf_spi_mngr_schedule(&m_nrf_spi_mngr, &integrity_transaction_cmd) == NRF_SUCCESS) {
    return true;
  } else {
    return false;
  }
}

/********************************** Public ************************************/
/* Initializes the External_Flash_X device. */
bool external_flash_x_init(void) {
    /* Initialize SPI manager and interface */
    if ( spi_manager_init() != NRF_SUCCESS) {
        return false;
    }
    /* Configure GPIO pins */
	/* Initialize chip-select/ slave select but disable it (disable high) */
    nrf_gpio_cfg_output(SPI_SS_PIN);
    nrf_gpio_pin_set(SPI_SS_PIN);
    nrf_delay_us(20); // Documentation states 10 us, but I even already changed to 20.
    /* configure SCK, MOSI and MISO */
    nrf_gpio_cfg_output(SPI_SCK_PIN);
    nrf_gpio_cfg_output(SPI_MOSI_PIN);
    nrf_gpio_cfg_input(SPI_MISO_PIN,NRF_GPIO_PIN_PULLUP);

    /* Clear MOSI value */
    nrf_gpio_pin_clear(SPI_MOSI_PIN);
    nrf_delay_us(20);
    /* Clear SCK value; Since we're going to use SPI Mode 0, CPOL=0 */
    nrf_gpio_pin_clear(SPI_SCK_PIN);
    nrf_delay_us(20);
    /* Erase auxiliar buffer */
    memset(m_auxiliar_buffer, 0x00, sizeof(m_auxiliar_buffer));

    /* Read "Manufacturer and Device ID" */
    if (!_external_flash_x_get_integrity_data()) {
        return false;
    }

   /* Here I do the integrity check that is always failing since it seems that I cannot read from External Flash via SPI 
    ...
   */ 
}

#"#"#
/* main.c */
/* some #includes */

  /* Clock setup */
  lfclock_init();
  /* RTC2 setup including its enabling */
  rtc_init();
    /* Macro for initializing the application timer module */
  app_timer_init();
  
  /* calls external_flash_x_init() */
  external_flash_x_init();
  
  /* ... */

Thanks in advance for you support.

Luis Silva

Parents
  • Hi,

    Which flash device are you interfacing? Is the device expected to output the bytes during the transfer of the 4-byte command, or after? The RX buffer will be filled from the first TX byte, meaning that if you expect to receive data from the flash device after the 4-byte command is finished transmitting, you must set the RX length to TX-length + expected received bytes.

    Are you sure that SPI Mode 1 is correct for the device you are interfacing?

    Have you checked the pins with a logic analyzer, to see if data is sent/received as expected?

    It sounds a bit strange that you are not receiving the end-callback. Are you checking the return code when calling external_flash_x_init() in main? 

    Best regards,
    Jørgen

  • Hi,

    Great to hear that you was able to resolve the issue!

    LuisSilva said:
    Doubt: can I assume that I always have to ignore the very first N bytes of the rx buffer, being N the number of non-dummy OpCodes sent on tx?

    In many cases, this is correct. However, it all depends on the device you are communicating with. Some devices can be configured to output data without any commands, in which case you cannot ignore the first N bytes (or technically N would be zero, so you are correct). I would recommend you to always check the datasheet of the device you are communicating with, to make sure that you are following the interface specifications. For the nRF5x SPI peripheral, it will always start to fill the RX buffer when you are clocking out bytes of the TX buffer. This comes from the nature of SPI typically being implemented as shift registers, synchronously clocking in/out data on both ends.

    Best regards,
    Jørgen 

  • Hi Jørgen,

    Thanks again for the fast reply.

    I can now both read and write without issues.

    Best regards,

    Luis

Reply Children
No Data
Related