Archivo de la etiqueta: dma

Mejora DMA en Prototipo de Driver Dynamixel con Teensy 3.2

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:

Teensy with UART DMA
Teensy con UART manejada por DMA

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.