DMA upgrade on Dynamixel Teensy 3.2 Driver

After doing some more research, I decided to jump into configuring my DARwIn-OP’s Dynamixel Teensy 3.2 Driver Prototype to get rid of the retransmitting loop code:

void loop() 
{
  if (Serial1.available())
  {
    uint8_t c = Serial1.read();
    Serial1.write(c);
  }
}

This loop just wait for a byte received by the UART and retransmit it with the same UART, which is configured with a hardware direction pin (RTS driven by the transmitter).

If I could use Teensy’s DMA (Direct Memory Access) controller to do the UART retransmission, I could empty the loop so the Teensy can be used in any other tasks without affecting the retransmission performance.

The DMA requirement is very simple:

  • To trigger a DMA request when a byte is received by the UART.
  • The DMA transfer has to be 1 byte.
  • The DMA transfer has to read from the UART DATA register (received data).
  • The DMA transfer has to write into the UART DATA register (for retransmission).
  • Do not involve the CPU in the process, no interrupt events.

Looking at the DMA capabilities of the Teensy’s ARM MK20DX256VLH7 CPU, it looked feasible, and after a few tries I got it working.

Now the retransmission has a smaller latency and a much smoother timing:

Teensy with UART DMA
Teensy with UART managed by DMA

The signals in the chart are:

  • RX into the Teensy (from Odroid’s TX).
  • TX from the Teensy.
  • Hardward Direction Pin.

The coding has a larger setup to configure the DMA and the UART (Teensy’s interrupt driven serial API is no longer useful), but the loop() function is now empty:

#define UART_TXRTSE (2)
#define UART_TXRTSPOL (4)

#define BAUD_RATE (1000000)

void setup() 
{
  int divisor = BAUD2DIV(BAUD_RATE);  

  // DMA:
  
  // p 415 source address = uart data register
  DMA_TCD1_SADDR = &UART0_D;

  // p 415 source address offset
  DMA_TCD1_SOFF = 0;

  // p 416 transfer attributes: 8 bits
  DMA_TCD1_ATTR = 0;

  // p 417 minor byte count = 1 byte
  DMA_TCD1_NBYTES_MLNO = 1;

  // p 420 last source address adjustment = 0
  DMA_TCD1_SLAST = 0;

  // p 420 destination address = uart data register
  DMA_TCD1_DADDR = &UART0_D;

  // p 421 destination address offset
  DMA_TCD1_DOFF = 0;

  // p 423 channel link disabled
  DMA_TCD1_CITER_ELINKNO = 1;

  // p 423 last destination address adjustment = 0
  DMA_TCD1_DLASTSGA = 0;

  // p 427 channel link disabled
  DMA_TCD1_BITER_ELINKNO = 1;
  
  // p 424 control and status = 8 cycle stall, active
  DMA_TCD1_CSR = DMA_TCD_CSR_BWC(3) | DMA_TCD_CSR_ACTIVE;

  // p 402 enable DMA REQ channel 1.
  DMA_SERQ = DMA_SERQ_SERQ(1);

  // clock setup
  // p 252-259 system clock gating
  SIM_SCGC6 |= SIM_SCGC6_DMAMUX;
  SIM_SCGC7 |= SIM_SCGC7_DMA;
  SIM_SCGC4 |= SIM_SCGC4_UART0;
  
  // wait for clocks to become stable.
  delay(500);

  // p366 dma mux channel configuration  
  DMAMUX0_CHCFG1 = DMAMUX_ENABLE | DMAMUX_SOURCE_UART0_RX;

  // UART:
  
  // p 1222 UART0 Control Register 5 request DMA on receiver full
  UART0_C5 = UART_C5_RDMAS;

  // RX TX pins
  CORE_PIN0_CONFIG = PORT_PCR_PE | PORT_PCR_PS |
                     PORT_PCR_PFE | PORT_PCR_MUX(3);

  CORE_PIN1_CONFIG = PORT_PCR_DSE | PORT_PCR_SRE |
                     PORT_PCR_MUX(3);

  // p 1208 uart0 baud rate  
  UART0_BDH = (divisor >> 13) & 0x1F;
  UART0_BDL = (divisor >> 5) & 0xFF;
  UART0_C4 = divisor & 0x1F;

  UART0_C1 = UART_C1_ILT;
  UART0_TWFIFO = 2; // tx watermark
  UART0_RWFIFO = 1; // rx watermark
  UART0_PFIFO = UART_PFIFO_TXFE | UART_PFIFO_RXFE;

  UART0_C2 = UART_C2_TE | UART_C2_RE | UART_C2_RIE;

  // enable PIN 6 as hardware transmitter RTS with active HIGH.
  CORE_PIN6_CONFIG = PORT_PCR_MUX(3);
  UART0_MODEM = UART_TXRTSE | UART_TXRTSPOL; 
}

void loop() 
{
}

 Actually, now I am running the typical ‘blink’ in the loop() function just so I know the Teensy is running.

2 thoughts on “DMA upgrade on Dynamixel Teensy 3.2 Driver

    1. Thanks for the update. I haven’t check the latest SDK yet.

      The main issue with the Dynamixel Controller from Robotis is that it uses FTDI which results in low performance. The alternative USB2AX Controller has better performance (and it is smaller). Both these controllers are USB 1.1, so the lag the USB introduces is in milliseconds.

      Anyway I am interested in a direct Serial interface which is faster. It can be used with the SDKs directly, it only needs to adjust the name of the Linux device in the source code. By just doing so, the lag is reduced to about 1/3 of a millisecond, which I’ll be working to further reduce it.

      Right now on my Odroid-XU4 I am using plain Ubuntu 16.04 and Debian 7, but I will move to a Real Time Kernel to develop a new framework for the DARwIn-OP with better precision in timing events.
      A couple of years ago I did try to patch Odroid’s Kernel with the RT patches but it wasn’t straight forward I didn’t have much experience in that.
      Now there are some advancements such as the following:
      http://forum.odroid.com/viewtopic.php?f=95&t=17954
      I’ll be trying it once I finish with the mechanical and electronics stuff later this year, I hope.

Leave a Reply

Your email address will not be published. Required fields are marked *

*