Electrical – SPI transaction between FPGA and Microcontroller

fpgamicrocontrollerspi

I'm trying to write a communication protocol between an FPGA and a Microcontroller through an SPI, the µC being the Master here.

  • FPGA: Lattice iCE40 Ultra Breakout Board Rev.A (iCE5LP4K)
  • µC : Nordic
    nRF52 (PCA10040)

Here's the code I'm using for the FPGA part, simulation works nicely within Active HDL with process simulating the Master behavior. Here's how the transaction is supposed to look like (as explained in the link above):

  1. There is data available in tx_load_data registry –> trdy is high
  2. Master send command "0000 0000" instructing the slave to load the subsequent MOSI data into the receive register, while simultaneously outputting the transmit register to MISO (Master receives tx_load_data)
  3. trrdy goes low when the last bit is sent
  4. Likewise, rrdy goes high to assess that new data sent by the master has been received.

This works in simulation, I've managed to get the same as Figure 4 (see link above).

Trying to reproduce the same behavior with the µC attached, data is loaded, trrdy goes high, I send a command like "0x0A", it gets transmitted and there's something going on the MISO link, but I can't read it properly.
From what I understood with SPI communications, I'm supposed to receive the response while I'm presenting data through MOSI. Is that right?

The following are samples of my C code, I'm pretty sure I'm doing something wrong but I lack experience.
When debugging, the registers of the receive buffer are often filled with "111111" (words of 6 or 8 bits, it's inconsistent).
The sendByte function is called within a button handler, and the rx buffer is printed out in the spi event handler, called at every transfer.

 /*
 * This function is supposed to ouput 00000000 01000001 to MOSI
 * Data received is buffered in m_rx_buf[20] array, declared in the header
 */
 void sendByteTest() {
    ret_code_t err_code;

    uint8_t m_tx[2];
    m_tx[0] = 0x0;
    m_tx[1] = 'A';

    m_length = sizeof(m_tx);        /**< Transfer length. */
    err_code = nrf_drv_spi_transfer(&spi, m_tx, m_length, m_rx_buf, m_length);
    if (err_code != NRF_SUCCESS)
        NRF_LOG_PRINTF(" Error during transfer.\r\n");

    memset(m_rx_buf, 0, m_length);
}

int main(void) {
    /** CLOCK, BUTTONS, GPIOTE initialization omitted **/

    nrf_drv_spi_config_t spi_config = NRF_DRV_SPI_DEFAULT_CONFIG(SPI_INSTANCE);
    spi_config.ss_pin = SPI_CS_PIN;
    spi_config.mode = NRF_DRV_SPI_MODE_2;   // ss_n is active low, set accordingly
    spi_config.frequency = NRF_DRV_SPI_FREQ_125K;
    spi_config.bit_order = NRF_SPI_BIT_ORDER_MSB_FIRST;
    APP_ERROR_CHECK(nrf_drv_spi_init(&spi, &spi_config, spi_event_handler));
    int i = 0;

    while(1) {
    nrf_gpio_pin_toggle(LED_1);
    nrf_delay_ms(200);
    }
}

Best Answer

The thing I didn't have in mind, is that SPI communications suggests that you should send as much data as you want to receive, not more, not less. So my buffer was noisy because of that. By setting m_length to 1 : err_code = nrf_drv_spi_transfer(&spi, m_tx, m_length, m_rx_buf, m_length); I am successfully transmitting one byte, while receiving exactly one byte in my receive buffer m_rx_buf

For the first transfer (command) I'm receiving dummy data that I don't process, for the actual transfer I'm buffering it and processing it.