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:

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.
