GPS and DMA

Status
Not open for further replies.

Mike Maurice

Active member
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...
 
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).
 
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...
 
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.
 
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://  [URL="forum.pjrc.com/threads/63353-Teensy-4-1-How-to-start-using-DMA"]pjrc.com/threads/63353-Teensy-4-1-How-to-start-using-DMA[/URL]
// #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 a moderator:
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.
 
An update to this thread. The active antenna for L5 arrived a couple of days ago. Notice that it only took 3 months. And the first one sent out is still sitting in Memphis at FedEx, after a week delay. Wonder not, why, as it will boggle your mind.

The L5 antenna (TAOGLAS) improves fix error by at least 5 times from about 10 meters to about 2 meters. This is with the antenna inside a house. There is a large range of L5 antennas and this is one of the latest and the cost is about $75 USD.
I am going back to testing the DMA code solution using the latest Teensyduino and Arduino IDE.

UhClem makes a good point about DMA, but since there is a lot of redundant data from the GPS I am not sure that it is all that critical that there be no loss of data. The GPS data stream assumes that not all data will come thru perfectly in any event.
Which means that a good result may be one that gives useful fixes, but not every 1/10 second.

I noticed some reference in another thread about a bug in the DMA library, maybe the issue I was having was related and maybe fixed since then.
I will post another update soon, with any incites that I get with further testing.
Hope it helps someone else.
 
Status
Not open for further replies.
Back
Top