NRF52840 SPI max transfer length issue

Hi guys.

I've a pretty simple issue: i'm trying to keep my mcu in low power while I receive 4kb of data via SPI.

After some checking, turns out that 52832 only allowed 8bit transfer sizes (due to DMA), but the register for 52840 is 16bit wide (TXD.MAXCNT).

And while there were some suggestions (years ago) that future iterations of the SDK (which has now reached version 17) will support 16bit transfer sizes for 52840, it seems that there's still this pesky limitation.

Do you guys have a solution/ workaround?

Parents
  • You mean you want to transfer all 4kb at once over SPI using the 52840?  Because it already does this in the nrfx driver (below code is SDK 17.0.2).  Just  make a buffer big enough and go to town.  This works fine for me:

    #include <stdint.h>
    #include "nrf_gpio.h"
    #include "nrfx_spim.h"
    
    #define SPIM3_SCK_PIN                     NRF_GPIO_PIN_MAP(1, 12)
    #define SPIM3_MOSI_PIN                    NRF_GPIO_PIN_MAP(1, 13)
    #define SPIM3_MISO_PIN                    NRF_GPIO_PIN_MAP(1, 11)
    #define SPIM3_CS_PIN                      NRF_GPIO_PIN_MAP(1, 14)
    
    /* SPIM3 to show the extended features */
    const nrfx_spim_t m_spim3 = NRFX_SPIM_INSTANCE(3);
    
    /**
     * @brief Function for application main entry.
     */
    int main(void)
    {
        nrfx_spim_xfer_desc_t desc;
        uint8_t tx_buff[1]; // if you needed to transmit something
        uint8_t rx_buff[4097];
        nrfx_spim_config_t spim_cfg = NRFX_SPIM_DEFAULT_CONFIG;
        ret_code_t error;
        
        /* Configure SPIM */
        spim_cfg.frequency  = NRF_SPIM_FREQ_8M;
        spim_cfg.ss_pin     = SPIM3_CS_PIN;
        spim_cfg.sck_pin    = SPIM3_SCK_PIN;
        spim_cfg.mosi_pin   = SPIM3_MOSI_PIN;
        spim_cfg.miso_pin   = SPIM3_MISO_PIN;
        spim_cfg.mode       = NRF_SPIM_MODE_0;
    
        spim_cfg.use_hw_ss  = true;
        //spim_cfg.rx_delay   = 0x07;
        //spim_cfg.ss_duration = 0x07;
        /* Pass a handler instead of NULL for interrupts instead of blocking */
        error = nrfx_spim_init(&m_spim3, &spim_cfg, NULL, NULL);
        APP_ERROR_CHECK(error);
    
        /* Build the transfer description and kick it off */
        desc.p_tx_buffer  = NULL;
        desc.tx_length    = 0;
        desc.p_rx_buffer  = rx_buff;
        desc.rx_length    = 4096;
    
        nrfx_spim_xfer(&m_spim3, &desc, 0);
        
        /* Uninit SPIM and disable to save power */
        nrfx_spim_uninit(&m_spim3);
        m_spim3.p_reg->TASKS_STOP = SPIM_TASKS_STOP_TASKS_STOP_Trigger;
        while (!m_spim3.p_reg->EVENTS_STOPPED);
        m_spim3.p_reg->ENABLE = SPIM_ENABLE_ENABLE_Disabled;
    
        /* Loop */
        for (;;)
        {
            
        }
    }

    If you are having trouble getting it setup right with the include directories and stuff in SES and with sdk_config hit me up and I'll zip up the example and send it to you.

Reply
  • You mean you want to transfer all 4kb at once over SPI using the 52840?  Because it already does this in the nrfx driver (below code is SDK 17.0.2).  Just  make a buffer big enough and go to town.  This works fine for me:

    #include <stdint.h>
    #include "nrf_gpio.h"
    #include "nrfx_spim.h"
    
    #define SPIM3_SCK_PIN                     NRF_GPIO_PIN_MAP(1, 12)
    #define SPIM3_MOSI_PIN                    NRF_GPIO_PIN_MAP(1, 13)
    #define SPIM3_MISO_PIN                    NRF_GPIO_PIN_MAP(1, 11)
    #define SPIM3_CS_PIN                      NRF_GPIO_PIN_MAP(1, 14)
    
    /* SPIM3 to show the extended features */
    const nrfx_spim_t m_spim3 = NRFX_SPIM_INSTANCE(3);
    
    /**
     * @brief Function for application main entry.
     */
    int main(void)
    {
        nrfx_spim_xfer_desc_t desc;
        uint8_t tx_buff[1]; // if you needed to transmit something
        uint8_t rx_buff[4097];
        nrfx_spim_config_t spim_cfg = NRFX_SPIM_DEFAULT_CONFIG;
        ret_code_t error;
        
        /* Configure SPIM */
        spim_cfg.frequency  = NRF_SPIM_FREQ_8M;
        spim_cfg.ss_pin     = SPIM3_CS_PIN;
        spim_cfg.sck_pin    = SPIM3_SCK_PIN;
        spim_cfg.mosi_pin   = SPIM3_MOSI_PIN;
        spim_cfg.miso_pin   = SPIM3_MISO_PIN;
        spim_cfg.mode       = NRF_SPIM_MODE_0;
    
        spim_cfg.use_hw_ss  = true;
        //spim_cfg.rx_delay   = 0x07;
        //spim_cfg.ss_duration = 0x07;
        /* Pass a handler instead of NULL for interrupts instead of blocking */
        error = nrfx_spim_init(&m_spim3, &spim_cfg, NULL, NULL);
        APP_ERROR_CHECK(error);
    
        /* Build the transfer description and kick it off */
        desc.p_tx_buffer  = NULL;
        desc.tx_length    = 0;
        desc.p_rx_buffer  = rx_buff;
        desc.rx_length    = 4096;
    
        nrfx_spim_xfer(&m_spim3, &desc, 0);
        
        /* Uninit SPIM and disable to save power */
        nrfx_spim_uninit(&m_spim3);
        m_spim3.p_reg->TASKS_STOP = SPIM_TASKS_STOP_TASKS_STOP_Trigger;
        while (!m_spim3.p_reg->EVENTS_STOPPED);
        m_spim3.p_reg->ENABLE = SPIM_ENABLE_ENABLE_Disabled;
    
        /* Loop */
        for (;;)
        {
            
        }
    }

    If you are having trouble getting it setup right with the include directories and stuff in SES and with sdk_config hit me up and I'll zip up the example and send it to you.

Children
  • And actually, because I'm avoiding the stuff I should be doing, here is a version that uses EasyDMA in case you were interested:

    #include <stdint.h>
    #include "nrf_gpio.h"
    #include "nrfx_spim.h"
    
    #define SPIM3_SCK_PIN                     NRF_GPIO_PIN_MAP(1, 12)
    #define SPIM3_MOSI_PIN                    NRF_GPIO_PIN_MAP(1, 13)
    #define SPIM3_MISO_PIN                    NRF_GPIO_PIN_MAP(1, 11)
    #define SPIM3_CS_PIN                      NRF_GPIO_PIN_MAP(1, 14)
    
    #define LED_PIN                           NRF_GPIO_PIN_MAP(1, 15)
    
    #define BUFF_SIZE                         4096
    #define ARRAYLIST_SIZE                    4096
    
    /**
     * @brief ArrayList struct to use SPIM List mode
     */
    typedef struct
    {
      uint8_t buffer[BUFF_SIZE];
    } ArrayList_t;
    
    /* SPIM3 to show the extended features */
    const nrfx_spim_t m_spim3 = NRFX_SPIM_INSTANCE(3);
    
    /* ArrayList for EasyDMA */
    static ArrayList_t array_list_buffer[ARRAYLIST_SIZE];
    
    
    /**@brief SPIM callback.
     */
    static void spim_event_handler(nrfx_spim_evt_t const * p_event, void * p_context)
    {
        switch(p_event->type)
        {
            case NRFX_SPIM_EVENT_DONE:
                if (p_event->xfer_desc.p_rx_buffer[0] != 0)
                {
                    nrf_gpio_pin_toggle(LED_PIN);
                }
                /* Uninit SPIM and disable to save power */
                nrfx_spim_uninit(&m_spim3);
                m_spim3.p_reg->TASKS_STOP = SPIM_TASKS_STOP_TASKS_STOP_Trigger;
                while (!m_spim3.p_reg->EVENTS_STOPPED);
                m_spim3.p_reg->ENABLE = SPIM_ENABLE_ENABLE_Disabled;
                break;
            default:
                break;
        }
    }
    
    /**
     * @brief Function for application main entry.
     */
    int main(void)
    {
        nrfx_spim_xfer_desc_t desc;
        uint8_t tx_buff[1]; // if you needed to transmit something
        uint8_t rx_buff[4096];
        nrfx_spim_config_t spim_cfg = NRFX_SPIM_DEFAULT_CONFIG;
        ret_code_t error;
    
        /* A blinky or something */
        nrf_gpio_pin_clear(LED_PIN);
        nrf_gpio_cfg_output(LED_PIN);
        
        /* Configure SPIM */
        spim_cfg.frequency  = NRF_SPIM_FREQ_8M;
        spim_cfg.ss_pin     = SPIM3_CS_PIN;
        spim_cfg.sck_pin    = SPIM3_SCK_PIN;
        spim_cfg.mosi_pin   = SPIM3_MOSI_PIN;
        spim_cfg.miso_pin   = SPIM3_MISO_PIN;
        spim_cfg.mode       = NRF_SPIM_MODE_0;
    
        spim_cfg.use_hw_ss  = true;
        //spim_cfg.rx_delay   = 0x07;
        //spim_cfg.ss_duration = 0x07;
        /* Pass a handler instead of NULL for interrupts instead of blocking */
        error = nrfx_spim_init(&m_spim3, &spim_cfg, spim_event_handler, NULL);
        APP_ERROR_CHECK(error);
    
        /* Build the transfer description and kick it off */
        desc.p_tx_buffer  = NULL;
        desc.tx_length    = 0;
        desc.p_rx_buffer  = rx_buff;
        desc.rx_length    = 4096;
        /* Annoying way to get Array List mode with nrfx */
        m_spim3.p_reg->RXD.LIST   = (SPIM_RXD_LIST_LIST_ArrayList << SPIM_RXD_LIST_LIST_Pos);
    
        /* You would just connect this to a timer via PPI and count up to ARRAYLIST_SIZE */
        nrfx_spim_xfer(&m_spim3, &desc, 0);
    
        /* Loop */
        for (;;)
        {
            
        }
    }

  • Thanks a million! This seems pretty straightforward. As the current consumption is a big issue, i really hoped for a solution using DMA and keeping the mcu in a WFE loop. I'll test your solution right away.

    Thanks again!