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

Issues getting QSPI working with SDK 15.2.0

Hey everyone, I'm having an issue communicating with a QSPI flash on a custom development board, using the nRF52840. The flash is a Winbond W25Q16JVSSIQ. Schematic and pins used are the same as the nRF52840 DK and as far as I can tell, the flash I'm using uses the same commands as the default defines. I've been at this for a whole day and don't have much hair left to pull out. I've checked and double checked the hardware and it looks good to me, I have connectivity from the module to the flash.

The device seems to initialize ok, there are no errors returned from nrfx_qspi_init, but I can't get nrfx_qspi_cinstr_xfer to return anything other than 0... I've tried every combo of 'length' and tx/rx string lengths I can come up with, I just can't wrap my head around how this is supposed to work. I also never get a finished flag when doing nrfx_qspi_write. If I just don't wait, nrfx_qspi_read gives me a BUSY error.

Any help anyone can offer is much appreciated. I fought my way though getting an ADC, UART, and BLE set up, but this is killing me!

Here's my defines and function:

#define QSPI_STD_CMD_MFG_DEV_ID 0x94
#define QSPI_STD_CMD_WRSR   0x01
#define QSPI_STD_CMD_RSTEN  0x66
#define QSPI_STD_CMD_RST    0x99

#define QSPI_RX_BUFFER_SIZE 10
#define SAMPLES_IN_BUFFER   10    //used to buffer adc readings, and also to size the qspi tx buffer

static volatile bool m_qspi_finished = false;
static uint8_t m_qspi_buffer_tx[SAMPLES_IN_BUFFER];
static uint8_t m_qspi_buffer_rx[QSPI_RX_BUFFER_SIZE];    //this will also be the BLE transmit package size/2

static void qspi_handler(nrfx_qspi_evt_t event, void * p_context)
{
    UNUSED_PARAMETER(event);
    UNUSED_PARAMETER(p_context);
    m_qspi_finished = true;
}

static void qspi_init(void){

    uint32_t err_code;
    static uint8_t temp_rx_buf[9];
    static uint8_t temp_tx_buf[9];

    nrfx_qspi_config_t config = NRFX_QSPI_DEFAULT_CONFIG;

    err_code = nrfx_qspi_init(&config, qspi_handler, NULL);
    APP_ERROR_CHECK(err_code);

    nrf_qspi_cinstr_conf_t cinstr_cfg = {
        .opcode    = QSPI_STD_CMD_MFG_DEV_ID,
        .length    = NRF_QSPI_CINSTR_LEN_9B,
        .io2_level = true,
        .io3_level = true,
        .wipwait   = true,
        .wren      = true
    };    

    err_code = nrfx_qspi_cinstr_xfer(&cinstr_cfg, temp_tx_buf, temp_rx_buf);
    APP_ERROR_CHECK(err_code);

    printf("qspi_init mfg id: %d device id: %d\r\n", temp_rx_buf[7], temp_rx_buf[8]);

    temp_tx_buf[0] = 69;
    
    err_code = nrfx_qspi_write(temp_tx_buf, 1, 0);
    printf("nrfx_qspi_write err_code %d\r\n", err_code);
    //APP_ERROR_CHECK(err_code);
    while (!m_qspi_finished);
    m_qspi_finished = false;

    err_code = nrfx_qspi_read(temp_rx_buf, 1, 0);
    printf("nrfx_qspi_read err_code %d\r\n", err_code);
    //APP_ERROR_CHECK(err_code);
    while (!m_qspi_finished);
    m_qspi_finished = false;

    printf("Test Data: %d \r\n", temp_rx_buf[0]);

}

Here's the sdk_config lines:

// <e> NRFX_QSPI_ENABLED - nrfx_qspi - QSPI peripheral driver
//==========================================================
#ifndef NRFX_QSPI_ENABLED
#define NRFX_QSPI_ENABLED 1
#endif
// <o> NRFX_QSPI_CONFIG_SCK_DELAY - tSHSL, tWHSL and tSHWL in number of 16 MHz periods (62.5 ns).  <0-255> 


#ifndef NRFX_QSPI_CONFIG_SCK_DELAY
#define NRFX_QSPI_CONFIG_SCK_DELAY 1
#endif

// <o> NRFX_QSPI_CONFIG_XIP_OFFSET - Address offset in the external memory for Execute in Place operation. 
#ifndef NRFX_QSPI_CONFIG_XIP_OFFSET
#define NRFX_QSPI_CONFIG_XIP_OFFSET 0
#endif

// <o> NRFX_QSPI_CONFIG_READOC  - Number of data lines and opcode used for reading.
 
// <0=> FastRead 
// <1=> Read2O 
// <2=> Read2IO 
// <3=> Read4O 
// <4=> Read4IO 

#ifndef NRFX_QSPI_CONFIG_READOC
#define NRFX_QSPI_CONFIG_READOC 4
#endif

// <o> NRFX_QSPI_CONFIG_WRITEOC  - Number of data lines and opcode used for writing.
 
// <0=> PP 
// <1=> PP2O 
// <2=> PP4O 
// <3=> PP4IO 

#ifndef NRFX_QSPI_CONFIG_WRITEOC
#define NRFX_QSPI_CONFIG_WRITEOC 2
#endif

// <o> NRFX_QSPI_CONFIG_ADDRMODE  - Addressing mode.
 
// <0=> 24bit 
// <1=> 32bit 

#ifndef NRFX_QSPI_CONFIG_ADDRMODE
#define NRFX_QSPI_CONFIG_ADDRMODE 0
#endif

// <o> NRFX_QSPI_CONFIG_MODE  - SPI mode.
 
// <0=> Mode 0 
// <1=> Mode 1 

#ifndef NRFX_QSPI_CONFIG_MODE
#define NRFX_QSPI_CONFIG_MODE 0
#endif

// <o> NRFX_QSPI_CONFIG_FREQUENCY  - Frequency divider.
 
// <0=> 32MHz/1 
// <1=> 32MHz/2 
// <2=> 32MHz/3 
// <3=> 32MHz/4 
// <4=> 32MHz/5 
// <5=> 32MHz/6 
// <6=> 32MHz/7 
// <7=> 32MHz/8 
// <8=> 32MHz/9 
// <9=> 32MHz/10 
// <10=> 32MHz/11 
// <11=> 32MHz/12 
// <12=> 32MHz/13 
// <13=> 32MHz/14 
// <14=> 32MHz/15 
// <15=> 32MHz/16 

#ifndef NRFX_QSPI_CONFIG_FREQUENCY
#define NRFX_QSPI_CONFIG_FREQUENCY 15
#endif

// <s> NRFX_QSPI_PIN_SCK - SCK pin value.
#ifndef NRFX_QSPI_PIN_SCK
#define NRFX_QSPI_PIN_SCK 19
#endif

// <s> NRFX_QSPI_PIN_CSN - CSN pin value.
#ifndef NRFX_QSPI_PIN_CSN
#define NRFX_QSPI_PIN_CSN 17
#endif

// <s> NRFX_QSPI_PIN_IO0 - IO0 pin value.
#ifndef NRFX_QSPI_PIN_IO0
#define NRFX_QSPI_PIN_IO0 20
#endif

// <s> NRFX_QSPI_PIN_IO1 - IO1 pin value.
#ifndef NRFX_QSPI_PIN_IO1
#define NRFX_QSPI_PIN_IO1 21
#endif

// <s> NRFX_QSPI_PIN_IO2 - IO2 pin value.
#ifndef NRFX_QSPI_PIN_IO2
#define NRFX_QSPI_PIN_IO2 22
#endif

// <s> NRFX_QSPI_PIN_IO3 - IO3 pin value.
#ifndef NRFX_QSPI_PIN_IO3
#define NRFX_QSPI_PIN_IO3 23
#endif

// <o> NRFX_QSPI_CONFIG_IRQ_PRIORITY  - Interrupt priority
 
// <0=> 0 (highest) 
// <1=> 1 
// <2=> 2 
// <3=> 3 
// <4=> 4 
// <5=> 5 
// <6=> 6 
// <7=> 7 

#ifndef NRFX_QSPI_CONFIG_IRQ_PRIORITY
#define NRFX_QSPI_CONFIG_IRQ_PRIORITY 6
#endif

// </e>

Here are the command tables from the Flash data sheet:

Parents
  • Hi,

    Have you checked the QSPI lines with a logic analyzer to see if anything is transferred between the devices?

    Best regards,
    Jørgen

  • I've got some better data, somehow... I moved the Device ID read to the main loop and repeat ever 100ms, which gives me exactly what I would expect on my analyser, but I can't get the right data out of the nrfx_qspi_cinstr_xfer function. I changed my Flash to a MX25V1635F so it should be the same as the DK, with a smaller size.

    Here's my initialization code:

    static void qspi_init(void){
    
        uint32_t err_code;
    
        nrfx_qspi_config_t config = NRFX_QSPI_DEFAULT_CONFIG;
    
        config.pins.sck_pin = BSP_QSPI_SCK_PIN;
        config.pins.csn_pin = BSP_QSPI_CSN_PIN;
        config.pins.io0_pin = BSP_QSPI_IO0_PIN;
        config.pins.io1_pin = BSP_QSPI_IO1_PIN;
        config.pins.io2_pin = BSP_QSPI_IO2_PIN;
        config.pins.io3_pin = BSP_QSPI_IO3_PIN;
    
        err_code = nrfx_qspi_init(&config, qspi_handler, NULL);
        APP_ERROR_CHECK(err_code);
    
        nrf_qspi_cinstr_conf_t cinstr_cfg = {
            .opcode    = QSPI_STD_CMD_RSTEN,
            .length    = NRF_QSPI_CINSTR_LEN_1B,
            .io2_level = true,
            .io3_level = true,
            .wipwait   = true,
            .wren      = true
        };    
    
        err_code = nrfx_qspi_cinstr_xfer(&cinstr_cfg, NULL, NULL);
        APP_ERROR_CHECK(err_code);
        
        cinstr_cfg.opcode    = QSPI_STD_CMD_RST;
        err_code = nrfx_qspi_cinstr_xfer(&cinstr_cfg, NULL, NULL);
        APP_ERROR_CHECK(err_code);
    
        uint8_t temporary = 0x40;
    
        cinstr_cfg.opcode = QSPI_STD_CMD_WRSR;
        cinstr_cfg.length = NRF_QSPI_CINSTR_LEN_2B;
        err_code = nrfx_qspi_cinstr_xfer(&cinstr_cfg, &temporary, NULL);
        APP_ERROR_CHECK(err_code);
    }

    And my main while loop:

        nrf_qspi_cinstr_conf_t cinstr_cfg = {
            .opcode    = QSPI_STD_CMD_MFG_DEV_ID,
            .length    = NRF_QSPI_CINSTR_LEN_6B,
            .io2_level = true,
            .io3_level = true,
            .wipwait   = true,
            .wren      = false
        };
    
        static uint8_t rx_buf[6];
        static uint8_t tx_buf[] = {0,0,0,0,0,0};
    
        nrfx_qspi_cinstr_xfer(&cinstr_cfg, tx_buf, rx_buf);
    
        printf("%#X %#X %#X %#X %#X %#X\r", rx_buf[0], rx_buf[1], rx_buf[2], rx_buf[3], rx_buf[4], rx_buf[5]);
        
        nrf_delay_ms(100); //pause 10ms

    My analyzer screen shot:

    However, in my printf function I'm getting 0X0 0X0 0X0 0X0 0X15 0X0. I'm not sure how the 0x15 is getting to the 4th byte, and the 0xC2 is disappearing.

  • This looks correct to me. Can you check what is in the CINSTRDAT0 and CINSTRDAT1 registers? Also, can you check the memory area around rx_buf to see if the expected data is found in memory?

Reply Children
  • Jorgen, my home internet is down so I can’t prove a screen shot but CINSTRDAT0.BYTE3 does indeed have C2h in it, and CONSTRDAT1.BYTE4 has 15h. The rx_buf has 15h in byte 4 and does not have C2h anywhere. C2 doesn’t show up anywhere around the 15.

    I also confirmed (I think) that this happens on the DK with the qpsi example project in Segger.

    Up and running again:

  • I found the bug, it's in nrf_qspi.h of SDK 15.2.0 around line 710, function nrf_qspi_cinstrdata_get. The author has two fallthrough switch statements, and looks like they intended one to fall through to the other, which, of course, won't happen.

    __STATIC_INLINE void nrf_qspi_cinstrdata_get(NRF_QSPI_Type const * p_reg,
                                                 nrf_qspi_cinstr_len_t length,
                                                 void *                p_rx_data)
    {
        uint8_t *p_rx_data_8 = (uint8_t *) p_rx_data;
    
        uint32_t reg = p_reg->CINSTRDAT1;
        switch (length)
        {
            case NRF_QSPI_CINSTR_LEN_9B:
                p_rx_data_8[7] = (uint8_t)(reg >> QSPI_CINSTRDAT1_BYTE7_Pos);
                /* fall-through */
            case NRF_QSPI_CINSTR_LEN_8B:
                p_rx_data_8[6] = (uint8_t)(reg >> QSPI_CINSTRDAT1_BYTE6_Pos);
                /* fall-through */
            case NRF_QSPI_CINSTR_LEN_7B:
                p_rx_data_8[5] = (uint8_t)(reg >> QSPI_CINSTRDAT1_BYTE5_Pos);
                /* fall-through */
            case NRF_QSPI_CINSTR_LEN_6B:
                p_rx_data_8[4] = (uint8_t)(reg);
                /* fall-through */
            default:
                break;
        }
    
        reg = p_reg->CINSTRDAT0;
        switch (length)
        {
            case NRF_QSPI_CINSTR_LEN_5B:
                p_rx_data_8[3] = (uint8_t)(reg >> QSPI_CINSTRDAT0_BYTE3_Pos);
                /* fall-through */
            case NRF_QSPI_CINSTR_LEN_4B:
                p_rx_data_8[2] = (uint8_t)(reg >> QSPI_CINSTRDAT0_BYTE2_Pos);
                /* fall-through */
            case NRF_QSPI_CINSTR_LEN_3B:
                p_rx_data_8[1] = (uint8_t)(reg >> QSPI_CINSTRDAT0_BYTE1_Pos);
                /* fall-through */
            case NRF_QSPI_CINSTR_LEN_2B:
                p_rx_data_8[0] = (uint8_t)(reg);
                /* fall-through */
            case NRF_QSPI_CINSTR_LEN_1B:
                /* Send only opcode. Case to avoid compiler warnings. */
                break;
            default:
                break;
        }
    }

    I fixed this bug by inserting an if between the two switches:

        if (length >= NRF_QSPI_CINSTR_LEN_5B)
          length = NRF_QSPI_CINSTR_LEN_5B;

    I now get the expected values in my rx_buf.

  • Thank you for reporting back the issue! I see now that this was previously reported in this case. I was looking at the implementation of nrf_qspi.h in SDK 15.3.0 yesterday and did not see this issue, as the bug had already been fixed there.

Related