nRF52840 BLE causing display glitch on GC9A01 using SPIM

We have a product based on the nRF52840 which uses the GC9A01 display driven using SPI on battery power. The device also functions as a Bluetooth connectable so it is normally advertising data at a 1 second interval. If the display is left on for several hours while BLE is enabled, the display will randomly become glitched and stays this way until reset. It appears that the BLE is somehow interfering with SPI and causing corrupt data eventually. We are fairly confident that the issue is not hardware related, as we have eliminated noise using stable power supplies and impedance matching the SPI lines.

Is it possible that the nRF is low on resources and corrupting the SPI data or display buffer when using BLE?

The pin-out for the display is as follows.

  • DC - P0.06
  • CS - P0.08
  • RESET - P0.11
  • SDA - P0.12
  • SCL - P1.09

We are using nRF Connect SDK v2.5.1.

Parents
  • Hi,

    There is no known issue like this. 

    How much resources your program is using? If there is an overflow then the compilation would tell you about that.

    If there is run time issue then you should debug and locate where the problem is occurring.

    /br

    Naeem

  • Here is the build output. There is no overflow.

    Memory region         Used Size  Region Size  %age Used
               FLASH:      953705 B         1 MB     90.95%
                 RAM:      145660 B       256 KB     55.56%
            IDT_LIST:          0 GB         2 KB      0.00%

    The issue occurs during runtime, but I do not see how to debug it because the issue occurs randomly after several hours to several days.

  • Enabling the workaround does not seem to do anything. The map files are identical before and after enabling this value. Unless I am not enabling the workaround correctly.

    Edit: Is this value enabled by default? I looked in the Kconfig and it was already enabled.

    CONFIG_NRF52_ANOMALY_198_WORKAROUND

  • The 198 handler code is here, not sure how it is ensuring buffer placement:

    v2.7.0\modules\hal\nordic\nrfx\drivers\src\nrfx_spim.c

    No 198 handler here:

    v2.7.0\zephyr\drivers\spi\spi_nrfx_spim.c

    Wonder just what this code is doing - 0x40000E00 some secret power control register?

    Fullscreen
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    // Workaround for nRF52840 anomaly 198: SPIM3 transmit data might be corrupted.
    static uint32_t m_anomaly_198_preserved_value;
    static void anomaly_198_enable(uint8_t const * p_buffer, size_t buf_len)
    {
    m_anomaly_198_preserved_value = *((volatile uint32_t *)0x40000E00);
    if (buf_len == 0)
    {
    return;
    }
    uint32_t buffer_end_addr = ((uint32_t)p_buffer) + buf_len;
    uint32_t block_addr = ((uint32_t)p_buffer) & ~0x1FFF;
    uint32_t block_flag = (1UL << ((block_addr >> 13) & 0xFFFF));
    uint32_t occupied_blocks = 0;
    if (block_addr >= 0x20010000)
    {
    occupied_blocks = (1UL << 8);
    }
    else
    {
    XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX

  • Yes it isn't clear to us what the CONFIG_NRF52_ANOMALY_198_WORKAROUND does. It appears to be enabled by default in ncs v2.5.1 so it is clear this is not the fix we need.

    Ensure the RAM areas used by the BLE RADIO and the SPIM are in separate RAM AHB bus masters

    How can we do this in nRF Connect SDK?


    We are confident that our issue and solution are related to the SPI speed/channel and the supporting clock. We are running a test with spim1 at 8MHz and so far there are no issues. Unfortunately, we need the spi to run at 32 MHz for better rendering performance and this is only possible with spim3.

    So far we have only been using the 32kHz clock. Now we are trying a test where the 32MHz clock is enabled to see if that has any effect on our issue.

    Edit: using the 32MHz clock does not seem to have an improvement for this issue.

  • Finally a simple solution after much time spent by both wyatt and I. Double buffers for SPIM3:

    Fullscreen
    1
    2
    3
    4
    5
    6
    7
    8
    uint8_t Spim3BufferL[8192] __attribute__((section(".spim3_data"), aligned(0x2000), used)) = {0};
    uint8_t Spim3BufferR[8192] __attribute__((section(".spim3_data"), aligned(0x2000), used)) = {0};
    // zephyr_final_map C:\ncs\v2.7.0\zephyr\samples\basic\button\build\button\zephyr\zephyr_final.map
    .spim3_data 0x0000000020002000 0x2000 load address 0x0000000000005dac
    .spim3_data 0x0000000020002000 0x2000 app/libapp.a(main.c.obj)
    0x0000000020002000 Spim3BufferR
    0x0000000020004000 Spim3BufferL
    XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX

    Just need to align to next (any) SRAM AHB section - RAM sections are 0x2000 apart on nRF52833/nRF52840 so could just bump by 0x2000 and stuff at least 0x1000 of data in that section to block other cpu/radio access to same SRAM block as SPIM3.

  • Some updates:

    First note that to search the map file as well as source files the .map file (in "Output Files") has to be open in the Visual Code IDE editor.

    Second, if a specific section is not required but just bss or .noinit (.noinit is probably best option) then the placeholder in main.c or other early link file is not required thus:

    Fullscreen
    1
    2
    3
    4
    5
    6
    7
    8
    volatile uint8_t Spim3BufferL[8192] __attribute__((section(".noinit"), aligned(0x2000), used));
    volatile uint8_t Spim3BufferR[8192] __attribute__((section(".noinit"), aligned(0x2000), used));
    // Map file: .\build\zephyr\zephyr.map
    noinit 0x000000002000c000 0x13700
    *(SORT_BY_ALIGNMENT(.noinit))
    .noinit 0x000000002000c000 0x3000 zephyr/libzephyr.a(blahBlah.c.obj)
    0x000000002000c000 Spim3BufferR
    0x000000002000e000 Spim3BufferL
    XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX

    The RAM is broken up into 0x2000 block segments each with its own AHB bus (on the nRF52840) so the buffers can be anywhere in RAM provided they occupy the entire 0x2000 (8192) byte block to stop the linker putting other data into the same block.

    I use two buffers, fill one while the other is sending to eliminate any access over the AHB bus the SPIM is using while the transmission is in progress. If transmission is in bursts then a single buffer is all that is required. If monitoring transmitted data by receiving in loopback mode (wise for testing) then you can confirm if any data gets corrupted by comparing Rx and Tx buffers. Rx and Tx buffers require separate AHB busses, so must reserve 8192 bytes each. Loopback by either linking pins or declare the Tx pin to be both input and output and set the Rx pin to the same pin. Even if only using (say) 256 byte SPI data the full 8192 bytes must be reserved. Keep to range RAM0 up to RAM7

    Fullscreen
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    // nRF52833/nRF52840 Bus Masters
    // =============================
    // Each bus master is connected to all the slave devices using an interconnection matrix. The bus masters are
    // assigned priorities, which are used to resolve access when two (or more) bus masters request access to
    // the same slave device. When that occurs, the following rules apply:
    // - If two (or more) bus masters request access to the same slave device, the master with the highest
    // priority is granted the access first.
    // - Bus masters with lower priority are stalled until the higher priority master has completed its transaction.
    // To avoid AHB bus contention when using multiple bus masters, follow these guidelines:
    // - Avoid situations where more than one bus master is accessing the same slave.
    // - If more than one bus master is accessing the same slave, make sure that the bus bandwidth is not exhausted.
    // Bus Slaves (RAM):
    // Name AdrSpace StartAdr EndAdr AccType
    // Memory = RAM0 Memory 0x20000000 0x20001FFF RW
    // Memory = RAM1 Memory 0x20002000 0x20003FFF RW
    // Memory = RAM2 Memory 0x20004000 0x20005FFF RW
    // Memory = RAM3 Memory 0x20006000 0x20007FFF RW
    // Memory = RAM4 Memory 0x20008000 0x20009FFF RW
    // Memory = RAM5 Memory 0x2000A000 0x2000BFFF RW
    // Memory = RAM6 Memory 0x2000C000 0x2000DFFF RW
    // Memory = RAM7 Memory 0x2000E000 0x2000FFFF RW
    XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX

Reply
  • Some updates:

    First note that to search the map file as well as source files the .map file (in "Output Files") has to be open in the Visual Code IDE editor.

    Second, if a specific section is not required but just bss or .noinit (.noinit is probably best option) then the placeholder in main.c or other early link file is not required thus:

    Fullscreen
    1
    2
    3
    4
    5
    6
    7
    8
    volatile uint8_t Spim3BufferL[8192] __attribute__((section(".noinit"), aligned(0x2000), used));
    volatile uint8_t Spim3BufferR[8192] __attribute__((section(".noinit"), aligned(0x2000), used));
    // Map file: .\build\zephyr\zephyr.map
    noinit 0x000000002000c000 0x13700
    *(SORT_BY_ALIGNMENT(.noinit))
    .noinit 0x000000002000c000 0x3000 zephyr/libzephyr.a(blahBlah.c.obj)
    0x000000002000c000 Spim3BufferR
    0x000000002000e000 Spim3BufferL
    XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX

    The RAM is broken up into 0x2000 block segments each with its own AHB bus (on the nRF52840) so the buffers can be anywhere in RAM provided they occupy the entire 0x2000 (8192) byte block to stop the linker putting other data into the same block.

    I use two buffers, fill one while the other is sending to eliminate any access over the AHB bus the SPIM is using while the transmission is in progress. If transmission is in bursts then a single buffer is all that is required. If monitoring transmitted data by receiving in loopback mode (wise for testing) then you can confirm if any data gets corrupted by comparing Rx and Tx buffers. Rx and Tx buffers require separate AHB busses, so must reserve 8192 bytes each. Loopback by either linking pins or declare the Tx pin to be both input and output and set the Rx pin to the same pin. Even if only using (say) 256 byte SPI data the full 8192 bytes must be reserved. Keep to range RAM0 up to RAM7

    Fullscreen
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    // nRF52833/nRF52840 Bus Masters
    // =============================
    // Each bus master is connected to all the slave devices using an interconnection matrix. The bus masters are
    // assigned priorities, which are used to resolve access when two (or more) bus masters request access to
    // the same slave device. When that occurs, the following rules apply:
    // - If two (or more) bus masters request access to the same slave device, the master with the highest
    // priority is granted the access first.
    // - Bus masters with lower priority are stalled until the higher priority master has completed its transaction.
    // To avoid AHB bus contention when using multiple bus masters, follow these guidelines:
    // - Avoid situations where more than one bus master is accessing the same slave.
    // - If more than one bus master is accessing the same slave, make sure that the bus bandwidth is not exhausted.
    // Bus Slaves (RAM):
    // Name AdrSpace StartAdr EndAdr AccType
    // Memory = RAM0 Memory 0x20000000 0x20001FFF RW
    // Memory = RAM1 Memory 0x20002000 0x20003FFF RW
    // Memory = RAM2 Memory 0x20004000 0x20005FFF RW
    // Memory = RAM3 Memory 0x20006000 0x20007FFF RW
    // Memory = RAM4 Memory 0x20008000 0x20009FFF RW
    // Memory = RAM5 Memory 0x2000A000 0x2000BFFF RW
    // Memory = RAM6 Memory 0x2000C000 0x2000DFFF RW
    // Memory = RAM7 Memory 0x2000E000 0x2000FFFF RW
    XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX

Children
No Data