Forum Rule: Always post complete source code & details to reproduce any issue!
Results 1 to 6 of 6

Thread: GPS and DMA

  1. #1
    Junior Member
    Join Date
    Jun 2020
    Posts
    15

    GPS and DMA

    First some preliminary background that may be useful to someone else doing something similar.
    A teensy 4.1 at 800 mhz and a TAU1202 GPS unit outputting at 115k baud to the teensy on serial8.
    I used the DMA code sample from the PJRC forum and it worked with minor tweaking. My tests indicate that the DMA buffer needs to be about 512 bytes, to prevent loss of data. I tried smaller, but have not tried larger yet. Tests using normal serial (not DMA) had problems with loss of data, regardless of the buffer size even though I jiggered the .h file to use 256, instead of the standard 64.
    Putting a memcpy routine to copy the data to a secondary buffer, in the DMA interrupt routine was not satisfactory, as expected. It is now in the loop routine.
    The TAU1202 has an active antenna hooked to it, but one that receives the L5 channel is on order but won't be here till the end of the month. Often get 20-23 sats, even with the antenna inside the house.

    Now, for the next problem. I am streaming the gps data to a bluetooth HC-06 at 115k baud. Using the standard serial library with SerialX.write(buffer, count). The overhead and delay of this write inside the loop has an unacceptable impact on the loop performance.
    I intend to increase the HC-06 baud rate to some higher level, but I would like to convert this stream to DMA and remove most of the overhead from the loop. But, am flummoxed as how to setup the DMA to write from memory to the serial port that the HC-06 is on: Serial5.

    A pointer to even a code sample of something similar would probably do.
    I have looked over the forum and even the internet in general and haven't found any simple usable example.
    Thanks in advance...

  2. #2
    Senior Member
    Join Date
    May 2015
    Location
    USA
    Posts
    1,071
    I assume the teensy is quite busy doing other things? Otherwise, 115 kbps is quite slow and a negligible load on a T4 (no need for DMA).

  3. #3
    Junior Member
    Join Date
    Jun 2020
    Posts
    15
    The load on the T4 is very high. The DMA is very necessary.
    I think I have identified the major issue holding up progress. The DMA .h file allows a call to set the destination using a register, rather than a buffer. I have not figured out what the register name is, yet. Otherwise, the code solution does not seem overly complex. Maybe...

  4. #4
    Senior Member+ defragster's Avatar
    Join Date
    Feb 2015
    Posts
    15,081
    There are add memory to Serial functions that can provide a local buffer to extend the GPS Rx and the HC-06 Tx.

    How big and how often if the GPS incoming message?

    If the serialEvent() funcs are used they will get called after every loop and on any yield() or delay() call. It is not interrupt level code - but if the code in loop ever calls those those functions will be called to allow pushing Serial# data along.

    If the Tx buffer is large enough - and not full - there should be minimal delay on the SerialX.write(buffer, count) - as it just transfers to the Serial# _isr() buffer code. If interrupts are disabled much ? - then the Serial# code may fail to keep up with Rx and Tx duties as the FIFO's on the T_4.1 are only 4 bytes.

    If any of that fails to solve the issue it might be worth the DMA effort.

    There was a busy 9DOF uNAV system years back and I put an interrupt on the GPS incoming pin to time stamp it - that would alert to the begin of next GPS message and allow assuring the prior message was cleared and started forward to Bt. Though doing that in that interrupt would be questionable - it can set a flag to know it was time to service the Serial# port.

  5. #5
    Junior Member
    Join Date
    Jun 2020
    Posts
    15
    The code that I have built to do the DMA to Serial.5 is below. It is based on the link id 22 below.
    The GPIO1_DR in the "source" line needs to be changed.
    It is not clear why LPUART5 is associated with Serial8 in the code I have built that does a DMA read from Serial8. I can find no documentation to explain the relation between LPUART5 and Serial8. Such explanation should shed light on what is needed for the DMA write to Serial5.

    The .destination(GPIO1_DR) needs to be changed, but I don't see to what or the logic that would justify the change.

    Any suggestions?

    Code:
    #include "DMAChannel.h"
    
    #define DMA_TRANSMIT  1
    
    DMAChannel dmachannel2;
    
    
    //https://  pjrc.com/threads/63353-Teensy-4-1-How-to-start-using-DMA
    // #22.
    
    #define DMABUFFER_SIZE  4096
    uint8_t dmaBuffer[DMABUFFER_SIZE];
    unsigned long prevTime;
    unsigned long currTime;
    bool error = false;
    bool dmaDone = false;
    
    void setupOutputDMA()
    {
      // prepare the output buffer
      for( int i=0; i<DMABUFFER_SIZE; ++i )
      {
        dmaBuffer[i] = ( i & 0xFF ) << 18;
      }
    
      // set GPIO1 to output
      GPIO1_GDIR |= 0x03FC0000u;// THis line and the next are not needed I think.
    
      // Need to switch the IO pins back to GPI1 from GPIO6
      IOMUXC_GPR_GPR26 &= ~(0x03FC0000u);
    
      // configure DMA channels
      dmachannel2.begin();
      dmachannel2.sourceBuffer( dmaBuffer, DMABUFFER_SIZE );  
      dmachannel2.destination( GPIO1_DR );  // This is NOT the Serial5 port, so what goes in place of GPIO1_DR ??
    
      dmachannel2.interruptAtCompletion();  
      dmachannel2.attachInterrupt( outputDMAInterrupt );
    
      // trigger our DMA channel at the request from XBAR
     // dmachannel2.triggerAtHardwareEvent( DMAMUX_SOURCE_XBAR1_0 );
    }
    
    void outputDMAInterrupt()
    {
      dmachannel2.clearInterrupt();  // tell system we processed it.
      asm("DSB");           // this is a memory barrier
    
      prevTime = currTime;
      currTime = micros();  
    
      error = false;
    
      dmaDone = true;
    }
    void kickOffDMA()
    {
      prevTime = micros();
      currTime = prevTime;
    
      dmachannel2.enable();  
    }
    
    
    void setup()
    {
      Serial5.begin(115200);  // Bluetooth HC-06 for output
    
      setupOutputDMA();
    
      kickOffDMA();
    }
    
    
    
    
    void loop() {
      delay(1000);  // Slow down the TX transfer
     kickOffDMA();    // repeat
    }
    Last edited by defragster; 08-05-2021 at 07:32 PM. Reason: Added # / CODE marker

  6. #6
    DMA works best with things that provide data in nice known block sizes.

    You have what is effectively a continuous stream, since the GPS is not going to wait after each 4KB, you must always be processing data. The ISR that is called when each block of DMA completes must setup for the next block. Using at least 2 buffers.

    With 2 buffers, then after a buffer fills you have 4K character times to deal with it before the next byte of data arrives. With a single buffer you must finish before the buffer capacity built into the serial port is filled. Which is a lot less than 4K.

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •