Después de hacer un poco de investigación. decidí configurar mi Prototipo de Driver Dynamixel con Teensy 3.2 para mi DARwIn-OP de forma de eliminar el código de retransmisión del loop:
void loop() { if (Serial1.available()) { uint8_t c = Serial1.read(); Serial1.write(c); } }
Este loop simplemente espera por un byte recibido por la UART y lo retransmite en la misma UART, la cual está configurada con un pin de dirección por hardware (RTS manejado por el transmisor).
Si pudiera usar el controlador DMA (Direct Memory Access) del Teensy para hacer la retransmisión, podría vaciar el código loop y utilizar el Teensy para otras tareas sin afectar el rendimiento de la retransmisión.
Los requerimientos para el DMA son bien simples:
- Gatillar un requerimiento DMA cuando 1 byte es recibido por la UART.
- La transferencia DMA tiene que ser de 1 byte.
- La transferencia DMA debe leer desde el registro de DATA de la UART (data recibida).
- La transferencia DMA debe escribir en el registro de DATA de la UART (para retransmisión).
- No se tiene que involucrar la CPU en el proceso, no debe necesitarse eventos de interrupción o pooling.
Mirando a las capacidades del DMA de la CPU ARM MK20DX256VLH7 del Teensy, se ve factible, quedando operativo después de algunas pruebas.
Ahora la retransmisión tiene una latencia menor y un timing más parejo:
Las señales en el gráfico son:
- RX del Teensy (viene del TX del Odroid).
- TX del Teensy.
- Pin de Dirección por Hardward.
La codificación ahora tiene un setup más grande para configurar el DMA y la UART (la API serial del Teensy ya no es útil), pero la función loop() ahora está vacía:
#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() { }
En realidad, por ahora ejecuto el típico ‘blink’ en la funcion loop() para saber que el Teensy está corriendo.