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

SPIM3 MOSI, CLK, and D/CX not working as expected after enabling/disabling ArrayList and using PPI and Timer

Hi,

I am using the nRF52840 with no soft device, and I am experiencing an issue with SPIM3 when turning on/off the ArrayList and Shortcut (EVENTS_END and TASKS_START) where the MOSI, CLK, and D/CX lines are not toggling as expected after sending a very large amount of bytes while also using PPI and the Timer Peripherals (following this guide devzone.nordicsemi.com/.../71994 Is there something that I missing here?  Any help would be greatly appreciated!

Here is the sequence that I am issuing:

  1. Initialize SPIM3 module at 32Mbps frequency
    1. Set Config to Active High, Leading clock edge, MSB first
    2. Set Frequency to 32Mbps
    3. Initialize all SCK, MOSI, MISO, CSN, D/CX to High Drive
    4. Set SCK, MOSI, CSN, and D/CX pin to Connected
    5. Set MISO pin to Disconnected
    6. Set IFTIMING.CSNDUR = 3
    7. Set Shortcut to Disabled
    8. Set TX.D List to Disabled
  2. Send several 8-bit commands using SPIM3 to initialize external hardware (D/CX should be low for commands and high for data, automated with SPIM3 peripheral)
    1. Set TXD.PTR, TXD.MAXCNT = 1, RXD.PTR = NULL, RXD.MAXCNT = 0, DCXCNT = 1
    2. Enable the peripheral
    3. Set TASKS_START = 1
    4. Repeat (a) through (c) for several different commands
  3. Set up PPI and Timer2 so that they can be used for the long transmission, but do not enable yet
    1. Timer2
      1. Set mode to Counter
      2. Clear any existing Timer tasks
      3. Set prescaler to 0
      4. Set Bitmode to 16 bit
      5. Enable shortcut to auto-clear when the Compare0 count is reached
      6. Set CC[0] to 4
      7. Enable Compare0 Interrupt
      8. Set Timer2 Priority to 7 and enable Timer2IRQ through NVIC
    2. PPI CH 5
      1. NRF_PPI->CH[5].EEP = (uint32_t) &NRF_SPIM3->EVENTS_END;
      2. NRF_PPI->CH[5].TEP = (uint32_t) &NRF_TIMER2->TASKS_COUNT;
    3. PPI CH 6
      1. NRF_PPI->CH[6].EEP = (uint32_t) &NRF_TIMER2->EVENTS_COMPARE[0];
      2. NRF_PPI->CH[6].TEP = (uint32_t) &NRF_SPIM3->TASKS_STOP;
      3. NRF_PPI->FORK[6].TEP = (uint32_t) &NRF_TIMER2->TASKS_STOP;
  1. Prepare ArrayList prior to long transmission
    1. ArrayList has this structure:

 

#define BUFFER_SIZE     38400

#define ARRAYLIST_SIZE  5

typedef struct ArrayList

{

     uint8_t buffer[BUFFER_SIZE];

} ArrayList_type;

ArrayList_type Frame_ArrayList[ARRAYLIST_SIZE];

  • Use a For loop to fill every buffer entry with 0xFF for all ArrayList entries (total = 38400 * 5 = 192000 locations filled with 0xFF)
  1. Repeat step 2(a) through 2(c) to send one 8-bit command (D/CX should be low, automated with SPIM3 peripheral)
  2. Enable PPI and Timer2. Then send long transmission using EasyDMA ArrayList with PPI and Timer2 to control when to stop SPIM3
    1. Enable interrupt on Compare0 for Timer2
    2. Start the Timer
    3. Enable PPI for CH5 and CH6
    4. Enable SPIM3 shortcut for EVENTS_END/TASKS_START
    5. Enable TX.D List for ArrayList
    6. Set TXD.PTR to the first entry of the ArrayList[0]
    7. Set TXD.MAXCNT = BUFFERSIZE
    8. Set RXD.PTR = NULL and RXD.MAXCNT = 0
    9. Set DCXCNT = 0
    10. Enable the SPIM3 peripheral and set TASKS_START = 1
  3. The Timer is basically counting after one whole ArrayList is sent (38400 bytes transmitted). Since Timer2 CC[0] = 4, the Timer will send an interrupt after the 4th ArrayList was sent (total 38400 * 4 = 153600 bytes transmitted).
  4. When the Timer2 Interrupt is triggered:
    1. SPIM3 should be stopped and Timer2 should be stopped from the PPI
    2. Disable SPIM3
    3. Clear / disable the Timer2 Interrupt using:
      NRF_TIMER2->INTENCLR = ( TIMER_INTENCLR_COMPARE0_Clear << TIMER_INTENCLR_COMPARE0_Pos );
    4. Clear the Timer2 tasks
    5. Clear the pending IRQ in NVIC for Timer2
    6. Disable shortcut on SPIM3
    7. Disable TXD.List
    8. Disable PPI on CH5 and CH6 using CHENCLR
    9. Repeat step 2(a) through 2(c) to send one 8-bit command (D/CX should be low for the command)

 

**Everything behaves as expected until step 8(i) where I am experiencing several issues:

  • The SPIM3 module is sending out 18 clock cycles (sometimes more or less) instead of 8 even though I set TXD.MAXCNT = 1
  • The MOSI line is sending out all “0” (MOSI is set up to send 0xAA), and sometimes “0” plus garbage data, and sometimes “0” plus the actual data I intended to send but offset
  • The D/CX line is set to “1” even though I set up DCXCNT = 1

  

In the above image, this is an example of what I see for step 8(i).  10 clock cycles are sent, followed by 8 additional clocks with MOSI actually being correct but offset by 1 clock.  D/CX is not set low as I intended.

Some interesting discoveries while debugging:

  • If I use the debugger – I set a breakpoint at step 8(i)
    • All the registers for SPIM3, TIMER2, and PPI are set up properly and as expected
      • EVENTS_STOPPED, EVENTS_END, EVENTS_ENDTX, EVENTS_ENDRX, EVENTS_STARTED are all set to “1”
      • SPIM3 shortcut and interrupt were successfully disabled and SPIM3 is currently disabled
      • MAXCNT and TXD.AMOUNT = 0x9600 (as expected since we just finished sending the 38400 bytes and haven’t set up the next 8-bit command yet)
      • PPI is cleared / disabled
    • If I manually step through steps 2(a) through 2(c) using the debugger, then everything behaves properly and works as intended (D/CX is sent properly, the correct command is sent and no extra clocks are sent)
  • If I change BUFFER_SIZE to something smaller, I tried 1000 and 5000 and 10000 instead of 38400, similar behavior is observed, but sometimes the MOSI is eventually able to send what I commanded after 10 clocks of “0” (D/CX still not responding)
  • As a side question, when I set IFTIMING.CSNDUR = 3, I am expecting this to be 46.875ns, but instead I am measuring 80-90ns on the scope. I thought the calculation was 15.625ns * 3 = 46.875ns, but am I missing something?

 

Thanks for the help!

Parents
  • Those traces look much better :-) I will try to look at this tomorrow, but meanwhile it's best not to do this:

    // .. here I am in a SPI3 interrupt
    
    // .. send a command that will generate an SPI3 interrupt:
    	// Send No-op command to tell the display we are done writing to the RAM
    	SPIM3_Command(0xAA);	
    // .. start length interrupt low-level exit code

    Instead set a flag which in main() will send the command 0xAA, as this interrupt will ensure main() has woken up

Reply
  • Those traces look much better :-) I will try to look at this tomorrow, but meanwhile it's best not to do this:

    // .. here I am in a SPI3 interrupt
    
    // .. send a command that will generate an SPI3 interrupt:
    	// Send No-op command to tell the display we are done writing to the RAM
    	SPIM3_Command(0xAA);	
    // .. start length interrupt low-level exit code

    Instead set a flag which in main() will send the command 0xAA, as this interrupt will ensure main() has woken up

Children
Related