Hola:
¿Qué es DMA?
Un cordial saludo.
Ya respondieron antes, pero para dar un ejemplo práctico y no quedarse en la definición...
Supongamos que tenemos un micro con el que medimos 8 variables a través del conversor analógico digital y las enviamos a la PC a través de un puerto UART.
En micros sin DMA habría que escribir un programa para que mueva byte a byte los resultados del CAD al UART.
Pero en un micro de más recursos con DMA (no hace falta que sea de 32 bits, uso los msp430f5510 que son de 16 bits y traen 3 canales DMA) lo que se puede hacer es establecer un canal DMA entre los resultados del conversor CAD, y la transmisión por UART; y todo puede funcionar sin que el programa ejecute ningún otro código aparte de la configuración inicial.
En el msp430f5510 uno podría hacer:
- Configurar el conversor AD para que muestre las entradas analógicas A0 a A6 en secuencia cada 20 milisegundos (el trigger de inicio de conversión lo daría un timer cuya salida se conecta al CAD).
- Configurar el puerto UART con el baud rate deseado.
- Configurar el canal DMA0 (hay DMA1 y DMA2 también) para que la fuente de datos (source address) sea el registro donde se guarda el resultado del CAD, el destino de los datos sea el registro donde se escribe para transmitir el dato por UART, y que las direcciones de origen y destino no se incrementen. El trigger de la transferencia se configura para que sea la señal de fin de conversión del CAD.
Una vez configurado eso se arranca el timer y ualá!!!!, tenemos un flujo de datos sin que sea necesario ejecutar nada de código en el micro, uso mínimo de ram y flash, sin interrupciones, nos queda un gran ancho de banda para ejecutar el resto de la aplicación (atender entrada de usuario por botones y actualizar un LCD, ejecutar filtros, etc).
Estamos transfiriendo 8 variables de 10 bits (pongamos 16 porque el tamaño de registro del CAD se lee como 16 bits) cada 10 ms prácticamente gratis:
8*16/10e-3 = 12.8 Kbps (ya no nos alcanzaría los típicos 9600 bps del uart, lo tendríamos que poner a 19200 mínimo).
Respecto de las incrementar/decrementar las direcciones destino y fuente, está para transferir datos desde/hacia un buffer hacia/desde un puerto serie u otro buffer.
El trigger puede ser en general señales de interrupción de cualquier tipo (de un puerto serie uart/spi/i2c, de una señal externa, timers, etc).
////////////////////////////////////////////////////////////////////////////////////////////////////////
Por ejemplo, otro caso de uso: tenemos lo mismo que antes pero funciona a baterías (por lo tanto es importante tener al micro durmiendo lo más posible) y guardamos los datos en una memoria flash externa.
Las memorias flash manejan bloques de 256/512/1024 bytes, supongamos que es de 512 (como las tarjetas SD), y también se pueden poner en bajo consumo (a través de comandos o si no tienen esa funcionalidad cortando la alimentación con un mosfet P) (sino en reposo pueden consumir cientos de uA, lo que para bajo consumo es mucho).
En este caso conviene juntar uno o más bloques para enviar a la memoria, y energizar/despertarla solo cuando hayamos juntado varios segundos/minutos de datos. Claro que para esto tenemos que tener micros con varios KB de ram, el msp430f5510 tiene 4/6 KB así que podría usarse.
- La configuración del CAD sería igual
- Configurar el SPI para manejar la memoria
- Canal DMA0: origen = CAD; destino = buffer en memoria RAM; trigger = fin de conversión CAD; cantidad de datos = 4096 bytes (8 bloques); incrementar dirección origen = no; incrementar dirección destino = sí
- Canal DMA1: origen = buffer en RAM; destino = puerto SPI para mandar los datos a la memoria; trigger = manual; cantidad de bytes = 4096; incrementar dirección origen = sí; incrementar dirección destino = no
Acá sí precisaríamos un mínimo de código en interrupciones y bucle principal. Cuando se active la interrupción de DMA0 (al tener ya 4096 bytes en buffer) va a haber que despertar la memoria (por comandos) o energizarla si no tiene funcionalidad de bajo consumo <10 uA (y en tal caso habrá también que inicializarla).
Luego enviarle los comandos para escritura de múltiples bloques, y cuando ya esté lista para aceptar los datos iniciar la transferencia de los 4096 bytes a través de DMA0 (acá ya podríamos apagar la CPU de vuelta para ahorrar energía).
Finalmente cuando nos despierte la interrupción del DMA1, enviamos los comandos para que empiece a grabar, esperamos durmiendo el fin de grabación; y luego apagamos/mandamos a dormir la memoria.
En resumen los estados serían (CAD y DMA trabaja siempre, no lo incluyo):
- Llenando buffer RAM: memoria apagada + CPU durmiendo
- Buffer RAM lleno: todo trabajando, encender memoria y enviarle comandos.
- Inicio vaciado RAM: empezamos transferencia buffer a memoria por DMA, CPU apagado, memoria encendida
- Grabación memoria: enviamos comando grabación, la memoria queda encendida un tiempo hasta completar tiempo de grabado, la CPU está la mayor parte del tiempo apagada
- Repetir...
Hay detalles que no contemplo porque se hizo muy largo y se trata de dar una idea más que una implementación.
En este último para guardar 4KB tendríamos despierto al micro la fracción de tiempo necesario para iniciar la transferencia de datos a la memoria, y para terminarla. Para dar un número, algo entre 10 y 50 milisegundos. Pongamos 25 ms.
Para juntar 4096 bytes (32Kbits) a 12.8Kbps tardamos 2.56 segundos. Ok... es poco tiempo, una aplicación portátil maneja tasas de muestreo más bajas normalmente, pero no importa.
Entonces con el DMA tenemos el micro con un ciclo de trabajo de 25e-3 s / 2.56 s < 1%.
Sin usar DMA el ciclo de trabajo podría ser algo del orden de 50%.
El consumo del micro en reposo (no en el modo de potencia más bajo porque precisamos tener los sistemas de reloj trabajando para usar el DMA) sería =
sistemas reloj siempre activo + el CAD trabajando continuamente con la referencia interna + el SPI cuando tenemos los datos listos para transferir + etc... seamos generosos y digamos 400 uA.
En modo activo corriendo a 8MHz consume aprox 3 mA
Consumo con DMA: 1% (activo + reposo) + 99% * reposo = 0.01 * 3 mA + 0.99 * 0.4 mA = 0.42 mA = 426 uA
Sin DMA: 50% (activo + reposo) + 50% reposo = 0.5 * 3 mA + 0.5 * 0.4 mA = 1.7 mA -> 4 veces más
Si es una pila botón de 220mAh con DMA: 21 días; sin DMA: 5 días
////////////////////////////////////////////////////////////////////////////////////////////////////////
Se me fué la mano escribiendo
, pero quería dejar claro que en algunas aplicaciones donde el micro maneja un buen volumen de datos el DMA hace la diferencia.
Edito: aparentemente exageré mucho con el consumo en reposo, que estaría más cerca de los 100uA, así que la diferencia sería más tirando a 80 días vs 6 días