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

SPI handler getting triggered when spi is not running?

Hi,

I've built a project out of the USBD_ACM_CDC example in which I have added a SPI master driver. This project has a timer interrupt that will shoot off every ~500us and trigger a 520 bit SPI transfer. The received packets undergo *very* minor processing and are spit out over USB to my host computer.

So it seems like I'm getting all the packets I expect, only I'm also getting 6 extra invalid packets with every valid packet. Here's a screenshot of my terminal from when I'm just transferring a hex counter:

The counter is incrementing so those bits are all fine. As you can see there are consistently 6 extra invalid packets (the FFs). If I slow down how often I shoot off SPI transfers, these invalid packets will disappear, but that doesn't make too much sense. Right now it seems like the SPI handler is being called erroneously. Does anyone have insight into where these are coming from? I looked at the SPI wires with an oscope and couldn't see any issues.

Parents
  • If you are using Nordic library drivers the following should not apply as the issue is (should be) handled in the drivers; if using low-level driver note the following where a handler will be invoked twice instead of once. How do I know this? Because the counter is incremented twice instead of once for every SPI transaction if the workaround is not applied.

    Option 1:

    // Example using SPI0
    static volatile uint32_t mSpiInterruptCounter = 0UL;
    static NRF_SPIM_Type* pSPIM = NRF_SPIM0;
    static const IRQn_Type mSPIM_IRQn = SPIM0_SPIS0_TWIM0_TWIS0_SPI0_TWI0_IRQn;
    /**
     * @brief SPI interrupt handler.
     * @param event
     */
    void SPIM0_SPIS0_TWIM0_TWIS0_SPI0_TWI0_IRQHandler(void)
    {
      blahblah(); // Processing (if any)
      // Clearing ->EVENTS_END MUST be first or add void read else get double interrupt ..
      pSPIM->EVENTS_END = 0;
      mSpiPacketTransferComplete = true;
      mSpiInterruptCounter++;
    }

    Option 2:

    // Example using SPI0
    static volatile uint32_t mSpiInterruptCounter = 0UL;
    static NRF_SPIM_Type* pSPIM = NRF_SPIM0;
    static const IRQn_Type mSPIM_IRQn = SPIM0_SPIS0_TWIM0_TWIS0_SPI0_TWI0_IRQn;
    /**
     * @brief SPI interrupt handler.
     * @param event
     */
    void SPIM0_SPIS0_TWIM0_TWIS0_SPI0_TWI0_IRQHandler(void)
    {
      blahblah(); // Processing (if any)
      mAccPacketTransferComplete = true;
      mSpiInterruptCounter++;
      pSPIM->EVENTS_END = 0;
      // Data Synchronization Barrier: completes when all explicit memory accesses before this instruction complete
      __DSB();
    }

    Nordic library code mostly reads back the register to enforce the instruction completing, but that can be unsafe depending on compiler and optimisation levels.

  • Firstly, thank you!!

    This is very interesting. I'm currently using the Nordic provided spi library but I'll try adding the following lines to my spi handler to see if it helps:

      SPI->EVENTS_END = 0;
      mSpiPacketTransferComplete = true;
      mSpiInterruptCounter++;

    Are there any special considerations for how frequently I can use the SPI? I'm currently just using SPI0 (with easyDMA enabled), which from my understanding should be able to run fast enough, but maybe I misunderstood the sdk. 

  • They are not driver options, just commands to override the default settings of Standard Drive output port pins for '0' (low-level) and '1' (high-level):

    // Set to High-Drive on the SPI output pins (assuming nRF52832 is the Master)
     nrf_gpio_cfg(SPI0_CS_PIN,   NRF_GPIO_PIN_DIR_OUTPUT, NRF_GPIO_PIN_INPUT_DISCONNECT, NRF_GPIO_PIN_NOPULL, NRF_GPIO_PIN_H0H1, NRF_GPIO_PIN_NOSENSE);
     nrf_gpio_cfg(SPI0_MOSI_PIN, NRF_GPIO_PIN_DIR_OUTPUT, NRF_GPIO_PIN_INPUT_DISCONNECT, NRF_GPIO_PIN_NOPULL, NRF_GPIO_PIN_H0H1, NRF_GPIO_PIN_NOSENSE);
     nrf_gpio_cfg(SPI0_SCK_PIN,  NRF_GPIO_PIN_DIR_OUTPUT, NRF_GPIO_PIN_INPUT_DISCONNECT, NRF_GPIO_PIN_NOPULL, NRF_GPIO_PIN_H0H1, NRF_GPIO_PIN_NOSENSE);

    This must be done after the SPI init, just once. Edit: Yes, you can modify the driver code as you suggest, less safe as that is hidden; even less safe as the other bits in the snippet you post are now not handled correctly; CNF has many bit fields.

    Here is an example showing all the CNF fields for a typical SPI0 (sorry, source code not available):

    // P0.19  SPI0-SCK CNF: 0x00000303 [Sense: Off] [Drive: High 0&1] [Pull: None] [Input: Disconnect] [Dir: Output] (now 0) (SPI0 SCK)
    // P0.20 SPI0-MOSI CNF: 0x00000303 [Sense: Off] [Drive: High 0&1] [Pull: None] [Input: Disconnect] [Dir: Output] (now 1) (SPI0 MOSI)
    // P0.22 SPI0-nCS0 CNF: 0x00000303 [Sense: Off] [Drive: High 0&1] [Pull: None] [Input: Disconnect] [Dir: Output] (now 1) (SPI0 CSN)

  • Amazing! I'll try this out right now. Thank you.

    Edit: So I bumped up the SPI_CLK to 8MHz and confirmed that via an oscope & single SPI transactions. Unfortunately, when I try sending SPI transactions every 500 ms, I get the same issue as before. 

    That being said! I started printing what is received by the slave and am seeing no issues (through UART logging). On the master side, I'm outputting everything via USB to COM port on my PC. Is it possible that the data is getting corrupted through the USB transmission? Are there any considerations for frequently triggering USB transfers. The USB should be able to hit 1 Mb/s easily in theory, but I'm just using the example code. Are there any configs I should change?  

  • Not sure, from using the USB interface for serial transmission I saw that although USB, BLE and nRF52 peripheral DMA transfers are all packet-based, most of the Nordic library drivers appear to be character based. So that can mean that the 64-byte USB packet actually gets sent as 1-byte USB transfers when the data is dribbled through the driver 1 byte at a time. When that happens, throughput is severely limited. I don't know if that is an issue for you or not, but we had some problems with throughput albeit in the other direction. The host receiving end may also affect the transfer of course, although correctly buffering data for the drivers shouldn't actually corrupt data.

    Need a USB expert here .. meanwhile just use SPI to BLE :-)

  • I'm currently handing the driver a 64 byte array, but if might be doing some char shenanigans. That's a good point. I'll take a look at that.

    Haha! That's the end goal! If you have code for a SPI/BLE that can stream 1 Mbps that you'd be willing to share I certainly wouldn't complain!

  • So I fixed an issue with the USB but I'm not able to read out all the SPI packets on the master end still. I can confirm now that this is a SPI issue because I put a watch on my spi rx buffer in the debug and most of the packets being read out are FFFFFFF. It also seems that I am indeed missing packets as well.

    Everything SPI related works at slow speeds still so it's not like the whole thing is messed up. It's purely when I try pushing the SPI to faster speeds. I'm still using an 8MHz clock and the pins are configured in high drive mode:

        nrf_gpio_cfg(SPI_SS_PIN,   NRF_GPIO_PIN_DIR_OUTPUT, NRF_GPIO_PIN_INPUT_DISCONNECT, NRF_GPIO_PIN_NOPULL, NRF_GPIO_PIN_H0H1, NRF_GPIO_PIN_NOSENSE);
        nrf_gpio_cfg(SPI_MOSI_PIN, NRF_GPIO_PIN_DIR_OUTPUT, NRF_GPIO_PIN_INPUT_DISCONNECT, NRF_GPIO_PIN_NOPULL, NRF_GPIO_PIN_H0H1, NRF_GPIO_PIN_NOSENSE);
        nrf_gpio_cfg(SPI_SCK_PIN,  NRF_GPIO_PIN_DIR_OUTPUT, NRF_GPIO_PIN_INPUT_DISCONNECT, NRF_GPIO_PIN_NOPULL, NRF_GPIO_PIN_H0H1, NRF_GPIO_PIN_NOSENSE);
        nrf_gpio_cfg(SPI_MISO_PIN, NRF_GPIO_PIN_DIR_INPUT, NRF_GPIO_PIN_INPUT_CONNECT, NRF_GPIO_PIN_NOPULL, NRF_GPIO_PIN_H0H1, NRF_GPIO_PIN_NOSENSE);
        

    Are there any hints as to where I can look?

Reply
  • So I fixed an issue with the USB but I'm not able to read out all the SPI packets on the master end still. I can confirm now that this is a SPI issue because I put a watch on my spi rx buffer in the debug and most of the packets being read out are FFFFFFF. It also seems that I am indeed missing packets as well.

    Everything SPI related works at slow speeds still so it's not like the whole thing is messed up. It's purely when I try pushing the SPI to faster speeds. I'm still using an 8MHz clock and the pins are configured in high drive mode:

        nrf_gpio_cfg(SPI_SS_PIN,   NRF_GPIO_PIN_DIR_OUTPUT, NRF_GPIO_PIN_INPUT_DISCONNECT, NRF_GPIO_PIN_NOPULL, NRF_GPIO_PIN_H0H1, NRF_GPIO_PIN_NOSENSE);
        nrf_gpio_cfg(SPI_MOSI_PIN, NRF_GPIO_PIN_DIR_OUTPUT, NRF_GPIO_PIN_INPUT_DISCONNECT, NRF_GPIO_PIN_NOPULL, NRF_GPIO_PIN_H0H1, NRF_GPIO_PIN_NOSENSE);
        nrf_gpio_cfg(SPI_SCK_PIN,  NRF_GPIO_PIN_DIR_OUTPUT, NRF_GPIO_PIN_INPUT_DISCONNECT, NRF_GPIO_PIN_NOPULL, NRF_GPIO_PIN_H0H1, NRF_GPIO_PIN_NOSENSE);
        nrf_gpio_cfg(SPI_MISO_PIN, NRF_GPIO_PIN_DIR_INPUT, NRF_GPIO_PIN_INPUT_CONNECT, NRF_GPIO_PIN_NOPULL, NRF_GPIO_PIN_H0H1, NRF_GPIO_PIN_NOSENSE);
        

    Are there any hints as to where I can look?

Children
  • First step is always disconnect MISO pin from the FPGA (without touching or changing anything else); connect MISO to nRF52 to Gnd and every returned packet should be all 0x00, connect MISO to nRF52 to Vcc and every returned packet should be 0xFF. If so, then the issue is with the FPGA not the nRF52, I would propose ..

  • Sorry, I should have mentioned that! I have indeed disconnected the FPGA and am just working with two NRF52 dev kits. One is operating as a slave and is logging data over UART. The other dev kit has a SPI_master + USB driver running on it.

    I'll tie the MISO line to GND as a sanity check.

    Could it be that that leaving logging enabled on the SPI is slowing down the slave's response? The SPI should be controlled via hardware so maybe that doesn't make sense.  

    Edit: It was indeed related to logging. Setting the slave's NRF_LOG_ENABLED to 0 fixed everything!

Related