Autor Tema: Alternativa más rapida para generar gráficos  (Leído 3426 veces)

0 Usuarios y 1 Visitante están viendo este tema.

Desconectado elgarbe

  • Moderador Local
  • PIC24H
  • *****
  • Mensajes: 2178
Alternativa más rapida para generar gráficos
« en: 20 de Marzo de 2014, 20:07:24 »
Bueno, a ver si ordeno un poco las ideas y me ayudan con este tema.
Estoy con el diseño del OSD (http://www.todopic.com.ar/foros/index.php?topic=42254.0)

Explico mas o menos como es.
Hay una camarita que tiene salida de video compuesto. Sobre esa señal se coloca un integrad, el LM1881, el cual me entrega un pulso en el comienzo del barrido horizontal y un pulso en el comienzo de un cuadro nuevo.
La distancia entre pulsos de sincronismo horizontal es de 64us, de los cuales 52useg son útiles, estan dentro de la pantalla.
La señal de video compuesto, blanco y negro tiene 2 niveles de tension. Digamos para simplificar, 0V para el negro y 1V para el blanco.
Para "agregar" informacion en la imagen de la camarita se usa una salida del uC con una R en serie de unos 100ohm. Entonces si ponemos la salida en Alta Impedancia (configurad como entrada) el uC no agrega nada a la señal de la cámara. Si la configuramos como salida y la ponemos en 0 escriviremos en negro y si la configuramos como salida y ponemos un 1 pintaremos en blanco.
Para sacar informacion del micro se pueden usar distintas técnicas. Una es usar 1 pin como IO digital. Otra es usar el SPI, unicamente la señal MOSI para sacar datos. De esta forma, escriviendo el Data Register con una WORD, podemos sacar 32 bits (pixeles) de una, sin mas que cargar 1 dato en un registro. La contra es que de esta forma es más dificil el tema de poner en alta impedancia la salida. Si dentro de los 32 bits que sacamos hay una parte que tiene texto y otra no, estamos en problemas. Esto se soluciona a medias colocando un diodo a la salida MOSI. De esta froma si el SPI saca un 1 pintamos en blanco, si saca un 0 debido al diodo será como estar en alta impedancia y lo que se verá es la señal de la cámara. Con esto no se puede usar el Negro. La solucion, se usa una salida digital con otra R, con la cual se hace el Negro.
Lo que yo tengo funcionando hasta ahora es la escritura, usando el SPI, solo en blanco. Para escrivir lo que se hace es descomoponer el caracter en líneas de, por ejemplo, 8 bits y se va imprimiendo como lo hace una impresora de las viejas, las matriz de puntos.

Bien, el tema es que en este momento tengo que tomar una decicon para avanzar en el diseño del sistema. Yo quiero buscar una salida usando DMA, para eliminar tiempo de CPU. El tema es que el DMA es util para sacar una buena cantidad de datos, por ejemplo, una línea completa de la pantalla. Si solo voy a mandar 3 o 4 caracteres no es tan util, ya que serían 3 o 4 bytes. Entonces se presenta la posibilidad de transformar la aplicacion en forma gráfica y dividir la pantalla en pixeles y enviar por el SPI con DMA las líneas completas de datos. De esta forma si el dato es 1 se pintara el pixel de blanco. si es 0 se verá la imagen de la cámara. Debería tener en memoria un array de X x Y pixeles y con el DMA sacaría constantemente ese array. Incluso leí el uso de doble buffer, mientras la aplicacion va llenando un buffer, el DMA uso el otro para enviar a la pantalla, cuando se completa un buffer, se hace el intercambio al inicio de la imagen en pantalla, de este modo se elimina el flickering.
Bien el tema es que aun no resuelvo el negro. Si quiero pintar de negro necesito otra salida. Podría ser otro SPI en paralelo, el LPC1769 tiene 2. Con el segundo SPI tendría que comandar algo (transistor, buffer, lo que sea) de modo tal que si el SPI pone un 1 a la salida tendria que tener un 0 para que pinte negro y si pongo un 0 tendría que tener alta impedancia... se me ocurre un buffer tri-state con la entada conectado a 0. Y con el SPI manejar el enabled...
Entonces, supongamos que queremos una resolucion de 320x240. Voy a tener 40x240 bytes = 9,37K por cada buffer. O sea 18,75K para los dos buffer. El segundo SPI tendría tambien 2 buffer y sería algo así como la "mascara" para ver si se pinta en negro o se deja pasar la imagen de la camara. Necesitaría 37.5K. En esto estoy bien.
Veamos velocidad. si la línea horizontal ocupa 52useg y quiero 320 pixeles serían 162nSeg por cada pixel lo que me da una frecuencia del SPI de mas o menos 6MHz. Tambien estoy bien con esa parte.

Entonces las dudas son:

Realmente me conviene el DMA y el uso del SPI para sacar la informacion?
Lo que no va a andar es el enmascaramiento, ya que no se pueden ejecutar 2 DMA a la vez.... soné...
Si paso a usar DMA con GPIO como calculo la velocidad que puedo obtener?
Podría usar 2 bits consecutivos para el manejo de 2 salidas, la que pinta de blanco y la que elije entre negro o alta impedancia. De esa forma tendría solo 2 buffer de 18.75K...
Entonces la duda es quien me da el ritmo de salida cuando el DMA se configura entre la memoria el GPIO (el GPIO es conciderado memoria en el LPC)???

Usando DMA le gano al mejor algoritmo en assembler para sacar los datos? yo estuve investigando otros proyectos y el que hace un OSD gráfico, usa un dsPic y programa en assembler la salida de datos. Ahora lo que yo pienso es que por más asembler que use, es la CPU la  que saca los datos, y por más que sea va a estar ocupada siempre. Quizá por eso en ese proyecto quieren usar otro uC para leer el GPS y otros sensores y dejan el dsPic solo para la parte gráfica... Ahora, si yo uso el DMA, cuando comienza la línea, tengo 8useg para configurar el canal (puedo configurar los 8 canales de una) y luego la CPU queda libre, mientras el DMA envía los datos, los 320 bits... voy bien???

bueno, espero algún alma caritativa que se anime a ayudarme a pensarlo antes de embarcarme en esta tarea....

Saludos y gracias!
-
Leonardo Garberoglio

Desconectado BrunoF

  • Administrador
  • DsPIC30
  • *******
  • Mensajes: 3865
Re: Alternativa más rapida para generar gráficos
« Respuesta #1 en: 22 de Marzo de 2014, 15:22:38 »
Hola Leo,

hmmm está complicada la cosa. La velocidad del GPDMA no veo que esté especificada, pero asumo que es la máxima del BUS (asociada a su clock), siempre y cuando haya disponibilidad del AHB.

Puedo agregar componentes externos? Qué pasaría si configurás la DMA para transferir tipo Memory to peripheral (SPI) y en lugar de usar 6Mhz de clock, usás 12Mhz y enviás serialmente 1bit de color y 1bit de máscara a un 74HC595, y la señal de clock la sometés a un contador de 4 bits (con 1 bit alcanzaría) para generar la señal de STROBE del 74HC595?. Obviamente los datos válidos los sacarías directamente de las salidas S0 y S1 del 74HC595.

Sé que suena medio loco, pero por ahí lo podrías lograr así.

Actualización: si te sobra un Timer podrías usar uno de sus pines CAP y usarlo como contador de clock del SPI para generar un toggle de un MAT del Timer y lograr la señal de STROBE para el registro de desplazamiento sin requerir entonces el contador de bits externo. Sólo haría falta el registro de desplazamiento.

Saludos!
« Última modificación: 22 de Marzo de 2014, 15:50:29 por BrunoF »
"All of the books in the world contain no more information than is broadcast as video in a single large American city in a single year. Not all bits have equal value."  -- Carl Sagan

Sólo responderé a mensajes personales, por asuntos personales. El resto de las consultas DEBEN ser escritas en el foro público. Gracias.

Desconectado elgarbe

  • Moderador Local
  • PIC24H
  • *****
  • Mensajes: 2178
Re: Alternativa más rapida para generar gráficos
« Respuesta #2 en: 22 de Marzo de 2014, 22:56:09 »
Bruno, me gusta la idea. Es más dificil de codificar los gráficos en memoria, pero una buena alternativa.
Por lo que he leido, parece que se pueden ejecutar 2 transferencias por DMA hacia dos SPI a la vez, o casi a la vez. Tengo que probarlo... hay un pibe que trabajó mucho con esto en dsPic y si bien consiguio los graficos, no se si termino el proyecto.

Ahora, parece que tengo muchas dudas de DMA en el LPC. Ya que estoy haciendo pruebas básicas y los resultados no son los esperados.
a ver, tengo este código simple:

Código: [Seleccionar]
char data[64]={1,2,3,4,5,6,7,8,9,0,1,2,3,4,5,6,7,8,9,0,1,2,3,4,5,6,7,8,9,0,1,2,1,2,3,4,5,6,7,8,9,0,1,2,3,4,5,6,7,8,9,0,1,2,3,4,5,6,7,8,9,0,1,2};

#include <cr_section_macros.h>
#include <NXP/crp.h>

void SSP0Init();

/*******************************************************************************
**   Main Function  main()
*******************************************************************************/
int main (void){

SSP0Init(); // Inicializo el bus SPI

LPC_SC->PCONP |= 1 << 29; // Power up DMA

LPC_GPDMACH0->DMACCConfig = 0; // stop ch0 dma
// LPC_SC->DMAREQSEL |= 1 << 2; // Timer1 Match Compare 0 as DMA request

    LPC_GPDMACH0->DMACCSrcAddr = (uint32_t) &data[0]; // data[] is the array where I have stored alternating 1's and 0's.
LPC_GPDMACH0->DMACCDestAddr = (uint32_t) &(LPC_SSP0->DR); // SSP0
LPC_GPDMACH0->DMACCLLI = 0;
LPC_GPDMACH0->DMACCControl = 32 // Transferencia de 32 bytes, 1 burst
| (1 << 12) // Burst de 1 bytes
| (1 << 15) // Burst de 1 bytes
| ( 1 << 26 ); // Source increment.
LPC_GPDMACH0->DMACCConfig = ( 0 << 6 ) | ( 1 << 11); // Set SPI as destination request peripheral and the type pf transfer as Memory to Peripheral.

LPC_GPDMA->DMACIntErrClr |= 0xff; //Clear all DMA interrupts
LPC_GPDMA->DMACIntTCClear |= 0xff;

LPC_GPDMA->DMACConfig |= 1 << 0; // enable DMA
LPC_GPDMACH0->DMACCConfig |= 1; //enable ch0

while ( 1 ){
}
}

Como vez configuro el DMA M2P, como destino el SPI. No configuro el DMAREQSEL.
Cuando hago debug de esto funciona bien. Veo los 32bytes saliendo por el SPI. Basicamente es lo único que consigo hacer funcionar.

Si quiero que el DMA se active despues de un tiempo, cuando se active el Match Register 0 del Timer 1, no logro que funcione bien. O por lo menos no como yo pienso que debería funcionar.

Veamos este ejemplo:
Código: [Seleccionar]
/*******************************************************************************
**   Main Function  main()
*******************************************************************************/
int main (void){

SSP0Init(); // Inicializo el bus SPI

// setup timer1
LPC_SC->PCONP |= 1 << 2; // Power up
LPC_SC->PCLKSEL0 |= 0x01 << 4; // CCLK -> 10nseg ticks
LPC_TIM1->MR0 = 2000; // Match a los 10 segundos
LPC_TIM1->MCR = 1 << 1; // reset on Match Compare 0

LPC_TIM1->IR |= 0xff; // Clear all timer interrupts if there are any
LPC_TIM1->TCR = 2; // Pongo a 0 el Contador del Timer y queda desabilitado

LPC_SC->PCONP |= 1 << 29; // Power up DMA

LPC_GPDMACH0->DMACCConfig = 0; // stop ch0 dma

LPC_SC->DMAREQSEL |= 1 << 2; // Timer1 Match Compare 0 as DMA request

LPC_GPDMA->DMACIntErrClr |= 0xff;
LPC_GPDMA->DMACIntTCClear |= 0xff;

    LPC_GPDMACH0->DMACCSrcAddr = (uint32_t) &data[0]; // hora[] is the array where I have stored alternating 1's and 0's.
LPC_GPDMACH0->DMACCDestAddr = (uint32_t) &(LPC_SSP0->DR); // SSP0
LPC_GPDMACH0->DMACCLLI = 0;
LPC_GPDMACH0->DMACCControl = 16 // Transferencia de 16 bytes
| (1 << 12) // Burst de 8 bytes
| (1 << 15) // Burst de 8 bytes
| (1 << 26 ); // Source increment.
LPC_GPDMACH0->DMACCConfig = ( 10 << 6 ) | ( 1 << 11); // Set MAT1.0 as destination request peripheral and the type pf transfer as Memory to Peripheral.

LPC_GPDMA->DMACConfig |= 1 << 0; // enable DMA
LPC_GPDMACH0->DMACCConfig |= 1; //enable ch0
LPC_TIM1->TCR = 0x01; // start timer.

while ( 1 ){
}
}

Acá uso el Timer1, Match Register 0 como disparador del DMA, configurando el DMAREQSEL.
La duda biene con la configuracion del "destination request peripheral" del canal 0. No termino de ver la diferencia entre DMAREQSEL y del "destination request peripheral". Si a los dos les pongo el mismo valor, como en este caso, lo que consigo es, a los 20useg se dispara el DMA y me envía solo 4 bytes. Luego, a los 20useg del primer disparo tengo otro, de 4 bytes. Así 4 veces hasta que se transmiten los 16bytes. La duda es porque me saca 4 bytes y no los 16 de una. Tambien tengo dudas de si en el "destination request peripheral" va MAT1.0 o va el SPI.

Si pruebo poniendo el SPI en ese registro:

Código: [Seleccionar]
LPC_GPDMACH0->DMACCConfig = ( 0 << 6 ) | ( 1 << 11); // Set SPI as destination request peripheral and the type pf transfer as Memory to Peripheral.

Lo que obtengo es 16 bytes saliendo uno detras del otro, pero el DMA se dispara ni bien lo habilito, no espera al Match Register 0

Con eso concluyo que algo estoy entendiendo mal. Y por mas que le de vueltas al manual no termino de entender la diferencia entre el "det request periph" y el DMAREQSEL, tampoco entiendo porque es parte del registro LPC_SC y no de LPC_GPDMA...

Este es el proyecto que te decía que lo tiene medio resuelto:
http://www.rcgroups.com/forums/showthread.php?t=1290963
http://forum.allaboutcircuits.com/showthread.php?t=41740

en el foro de nxp no me estan dando mucha bola....

saludos y gracias por el interes!
-
Leonardo Garberoglio

Desconectado elgarbe

  • Moderador Local
  • PIC24H
  • *****
  • Mensajes: 2178
-
Leonardo Garberoglio

Desconectado BrunoF

  • Administrador
  • DsPIC30
  • *******
  • Mensajes: 3865
Re: Alternativa más rapida para generar gráficos
« Respuesta #4 en: 25 de Marzo de 2014, 13:25:35 »
Hmmm.. presté mi LPCXpresso 1768 hace unos días, me parece que esto requeriría de hacer algunas pruebas ya que hay que toquetear muchos registros!

Si me la devuelven pruebo el código y te comento, igualmente, por lo que leí, deberías poder disparar una transferencia por DMA mem-to-periph desde un match de un Timer.
"All of the books in the world contain no more information than is broadcast as video in a single large American city in a single year. Not all bits have equal value."  -- Carl Sagan

Sólo responderé a mensajes personales, por asuntos personales. El resto de las consultas DEBEN ser escritas en el foro público. Gracias.

Desconectado elgarbe

  • Moderador Local
  • PIC24H
  • *****
  • Mensajes: 2178
Re: Alternativa más rapida para generar gráficos
« Respuesta #5 en: 25 de Marzo de 2014, 15:16:11 »
Bruno, com andas, es como vos decis, es a toquetear ya que no se puede debuguear nada con el DMA... o anda o no anda...

Hasta ahora lo que consegui es configurar el SPI a 16bits (el máximo), el CPHA=1 con eso consigo que los datos sean continuos, sino veía un ciclo de clk donde no habia datos. Luego el DMA lo tengo sin DMAREQSEL y tengo el destination peripheral configurado como SPI Tx. Despues tengo el SourceWidth y dest with en 16bits. Finalmente el Transfer size aprendi que no es la cantidad de bytes sino que es la cantidad de "paquetes" de tamaño SourceWidth. Todo eso ni bien habilito el DMA salen todos los datos de una, sin espacios y sin problemas.

Ahora lo que me faltaría es ver si puedo salir con 2 SPI a la vez con canales diferentes de DMA o si uso la alternativa que me cometaste. Y finalmente si puedo disparar el DMA con un match timer. El tema es que no se mezclaróan las señales? por un lado tenemos el DMAREQSEL y por el otro el Destination request peripheral del canal en uso... no la caso bien esos dos registros. Uno es a nivel canal de DMA, pero DMAREQSEL esta en la estructura LPC_SC por lo que no sé bien como funciona.
Lo que vi que hacen es para transferencias memoria a gpio (el cual es memoria) usan el timer match ya que la transferencia memoria memoria es a velocidad de clock, entonces si uno quiere darle ritmo usan el match register en combinacion con el destination request peripheral para que traten al GPIO como un periférico. Yo no se si el DMAREQSEL en modo timer no es solamente para memoria a memoria....
Finalmente, si no puedo dispara el dma con el timer match, lo que haría es en una interrupcion en el timer match y en el isr configuro el canal del dma y lo habilito para que se dispare instantaneamente.

Saludos y gracias por el tiempo!!!!
-
Leonardo Garberoglio

Desconectado elgarbe

  • Moderador Local
  • PIC24H
  • *****
  • Mensajes: 2178
Re: Alternativa más rapida para generar gráficos
« Respuesta #6 en: 25 de Marzo de 2014, 20:34:14 »
Bueno, avancé un pasito más. Tengo los dos SPI enviando con DMA, uno con el canal 0 y el otro con el canal 1.

Este es el código:

Código: [Seleccionar]
short data[40]={170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,
170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170,170};

#include <cr_section_macros.h>
#include <NXP/crp.h>

void SSP0Init();
void SSP1Init();

/*******************************************************************************
**   Main Function  main()
*******************************************************************************/
int main (void){

SSP0Init(); // Inicializo el bus SPI
SSP1Init(); // Inicializo el bus SPI

LPC_SC->PCONP |= 1 << 29; // Power up DMA

LPC_GPDMACH0->DMACCConfig = 0; // stop ch0 dma
LPC_GPDMACH1->DMACCConfig = 0; // stop ch1 dma

    LPC_GPDMACH0->DMACCSrcAddr = (uint32_t) &data[0]; // data[] is the array where I have stored alternating 1's and 0's.
LPC_GPDMACH0->DMACCDestAddr = (uint32_t) &(LPC_SSP0->DR); // SSP0
LPC_GPDMACH0->DMACCLLI = 0;
LPC_GPDMACH0->DMACCControl = 16 // Transferencia de 16 half word
| (1 << 12) // 0 burst
| (1 << 15) // 0 burst
| (1 << 18) // Half word width
| (1 << 21) // Half word width
| (1 << 26 ); // Source increment.
LPC_GPDMACH0->DMACCConfig = ( 0 << 6 ) | ( 1 << 11); // Set SPI as destination request peripheral and the type pf transfer as Memory to Peripheral.

    LPC_GPDMACH1->DMACCSrcAddr = (uint32_t) &data[0]; // data[] is the array where I have stored alternating 1's and 0's.
LPC_GPDMACH1->DMACCDestAddr = (uint32_t) &(LPC_SSP1->DR); // SSP0
LPC_GPDMACH1->DMACCLLI = 0;
LPC_GPDMACH1->DMACCControl = 16 // Transferencia de 16 half word
| (1 << 12) // 0 burst
| (1 << 15) // 0 burst
| (1 << 18) // Half word width
| (1 << 21) // Half word width
| (1 << 26 ); // Source increment.
LPC_GPDMACH1->DMACCConfig = ( 2 << 6 ) | ( 1 << 11); // Set SPI as destination request peripheral and the type pf transfer as Memory to Peripheral.

LPC_GPDMA->DMACIntErrClr |= 0xff; //Clear all DMA interrupts
LPC_GPDMA->DMACIntTCClear |= 0xff;

LPC_GPDMA->DMACConfig |= 1 << 0; // enable DMA
LPC_GPDMACH0->DMACCConfig |= 1; //enable ch0
LPC_GPDMACH1->DMACCConfig |= 1; //enable ch1

while ( 1 ){
}
}

pero el tema es que las instrucciones de habilitacion son 2 instrucciones por lo que hay un desfazaje, de unos 750nseg a 100MHz...



explico denuevo como es la tecnica. La idea es usar un SPI que se conecte directo a un buffer tri-state. Si el SPI pone 0 es como pintar el pixel de negro. Si pongo un 1 es como pintar el pixel de blanco. Para este SPI tendría un buffer en memoria en el cual dibujaría lo que quiero mostrar. Este sería el buffe de color. El otro SPI iría a la señal de ENABLE del buffer. Entonces si pongo un 1 habilito la salida y pasa el dato de color. Si pongo un cero se tapa al dato del SPI0 y pasa la imagen como si nada. Tambien tendría un buffer en memoria, el cual seria el buffer de Máscara.

Entonces tengo que resolver el tema del desfasaje. Podría retrasar una de las señales con un pequeño filtro? la otra opcion es hacer uno de los buffer mas grande y descartar los primeros bits. por otra parte, creo que tendría que pasar a ASM la parte de habilitacion de cada canal DMA para reducir el desfazaje al mínimo...

Saludos!
-
Leonardo Garberoglio