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

SPI Manager to Transfer Data From Program Memory (CONST/ROM)

I hate to be a squeaky wheel on this but I've found some strange behavior when trying to use the SPI Manager to schedule a transfer where the .p_tx_data address is a constant. I've had no trouble scheduling transactions from RAM. What I want to be able to do is schedule a transfer that will send a large block of memory declared as a constant. Something like this:

#define GIANT_BUFER_SIZE 4096
const uint8_t giant_buffer[GIANT_BUFER_SIZE];

When I try to schedule a transfer like this the SPI Manager just stop dead and skips over it. The transaction still reports a success, the CS line goes low, but nothing is transmitted. The transaction/transfer that follows, if it's from RAM, works perfect. I don't see any error log messages generated. 

Here's an example test to show what happens (I've reduced the code to a more simple form):

Two small buffers are defined here. TEST_DATA is in RAM, CONST_TEST_DATA is declared as const. The transfer has a NULL address which is setup later.

uint8_t TEST_DATA[] = {0x02, 0x03};
const uint8_t CONST_TEST_DATA[] = {0x07, 0x08};
nrf_spi_mngr_transfer_t TEST_TRANSFER = NRF_SPI_MNGR_TRANSFER(NULL, 0, NULL, 0);

Setup the SPI config and the SPI Manager:

// SPI CONFIG
nrf_drv_spi_config_t const m_disp_ILI9341_spi_config =
{
	.sck_pin        = SPI_SCK_PIN,
	.mosi_pin       = SPI_MOSI_PIN,
	.miso_pin       = SPI_MISO_PIN,
	.ss_pin         = DISP_CS,
	.irq_priority   = APP_IRQ_PRIORITY_LOWEST,
	.orc            = 0xFF,
	.frequency      = NRF_DRV_SPI_FREQ_8M,
	.mode           = NRF_DRV_SPI_MODE_3,
	.bit_order      = NRF_DRV_SPI_BIT_ORDER_MSB_FIRST
};

//SPI Manager Instance
NRF_SPI_MNGR_DEF(m_nrf_spi_mngr, 15, 0);
nrf_spi_mngr_init(&m_nrf_spi_mngr, &m_master_spi_config);

Setup up the transaction:

nrf_spi_mngr_transaction_t TEST_DATA_TRANSACTION =
{
    .begin_callback      = NULL,
    .end_callback        = NULL,
    .p_user_data         = (void *)&ILI9341_DATA,
    .p_transfers         = &TEST_TRANSFER,
    .number_of_transfers = 1,
    .p_required_spi_cfg  = &m_disp_ILI9341_spi_config
};

Finally, three transactions are scheduled. First from RAM, then from a const buffer, then from RAM again:

// Schedule the TEST transactions
TEST_TRANSFER.p_tx_data = TEST_DATA;
TEST_TRANSFER.tx_length = sizeof(TEST_DATA);
ret_val = nrf_spi_mngr_schedule(&m_nrf_spi_mngr, &TEST_DATA_TRANSACTION);

TEST_TRANSFER.p_tx_data = CONST_TEST_DATA;
TEST_TRANSFER.tx_length = sizeof(CONST_TEST_DATA);
ret_val = nrf_spi_mngr_schedule(&m_nrf_spi_mngr, &TEST_DATA_TRANSACTION);

TEST_TRANSFER.p_tx_data = TEST_DATA;
TEST_TRANSFER.tx_length = sizeof(TEST_DATA);
ret_val = nrf_spi_mngr_schedule(&m_nrf_spi_mngr, &TEST_DATA_TRANSACTION);

On the scope you can see that the RAM buffer transactions work but the one from const buffer doesn't transmit anything. The chip select goes low but nothing else happens.

I made it wide so all three transactions are visible at once. In the middle (~21us) you can see the chip select go low but no other activity. It stays low until the third transaction starts. I've removed the code that checks the return values just for the example. All of the calls to nrf_spi_mngr_schedule return NRF_SUCCESS. 

All the examples I find only transmit from RAM. Can anyone comment on why the transfers from a const fail or the correct way to do it?

Thanks

.

Parents
  • DMA only works from RAM not Flash; it's an internal hardware bus restriction on the nRF52. If the const data is linked into RAM it will work, but special placement instructions are required.

  • Thanks for the reply!  Ouch. I was afraid it was something strange like that. I turned off the DMA (SPI0_USE_EASY_DMA = 0) and then it does transfer from const but at an appallingly slower rate.

    In a pinch I could buffer the const data into RAM in chunks and then queue it for SPI transmission. That could be a bit faster.

    I dug around for some documentation on how to link const data into RAM but didn't find anything. If you know of any docs or examples out there please post them.

    Thanks again.

    .

  • Here is an example of linking at a specific section which can be in RAM for SES and IAR; the section has to be defined and placed at a RAM address in the correponding linker script

    // Segger Embedded Studio format
    // Place following data in section .Buffer
      const uint8_t Stuff[] __attribute__((section(".Buffer"))) = "Hello World";
    // End - Place following data in section
    
    // IAR Format:
    // Place following data in section .Buffer
    #pragma default_variable_attributes = @ ".Buffer"
      const uint8_t Stuff[100];
    #pragma default_variable_attributes =
    // End - Place following data in RAM section

    This is how to modify SES linker script:

    In linker script:
      <MemorySegment name="RAM" start="$(RAM_PH_START)" size="$(RAM_PH_SIZE)">
        <ProgramSection alignment="0x100" load="No" name=".vectors_ram" start="$(RAM_START)" address_symbol="__app_ram_start__"/>
    blahblah
        <ProgramSection alignment="4" load="No" name=".non_init" />
        <ProgramSection alignment="4" load="Yes" name=".Buffer" />
        <ProgramSection alignment="4" size="__HEAPSIZE__" load="No" name=".heap" />
        <ProgramSection alignment="8" size="__STACKSIZE__" load="No" place_from_segment_end="Yes" name=".stack"  address_symbol="__StackLimit" end_symbol="__StackTop"/>
        <ProgramSection alignment="8" size="__STACKSIZE_PROCESS__" load="No" name=".stack_process" />
      </MemorySegment>
    
    from map file:
    .Buffer         0x0000000020003010        0x0
                    0x0000000020003010                __Buffer_start__ = .

    There are more examples with other compilers in this ram-retention-example

  • Thanks for that! There's even a GCC example there.
    This is all for bitmap image loading. If things get to be too much for the Flash->RAM buffer solution to handle I may try this. 
    Thanks again.

    .

Reply Children
No Data
Related