Autor Tema: Diseño de un OSD  (Leído 14956 veces)

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

Desconectado elgarbe

  • Moderador Local
  • PIC24H
  • *****
  • Mensajes: 2177
Re: Diseño de un OSD
« Respuesta #30 en: 03 de Marzo de 2014, 21:20:03 »
Este es el nuevo template del OSD:



aaa y tengo puesto la distancia tambien, pero lo implemente despues de esa foto....

Hoy no pude probar porque el viento calmo medio tarde. Pero mañana lo voy a probar si o si!!!!

Saludos!
-
Leonardo Garberoglio

Desconectado elgarbe

  • Moderador Local
  • PIC24H
  • *****
  • Mensajes: 2177
Re: Diseño de un OSD
« Respuesta #31 en: 09 de Marzo de 2014, 15:58:45 »
Bueno, solo comentar que estoy trabajando en transformar el "engendro OSD" en algo mas prolijo, que entre dentro del planeador. Como pueden ver en el diseño del medidor de corriente, tension y potencia, esa es una placa que quedó lista.
Ahora estoy terminando de diseñar la placa que aloja a la LPCXpresso y tiene:
Conector I2C para la IMU. Esto es para cuando empiece a probar el sistema de estabilizacion.
Conector I2C para el INA219
Conector para lectura PPM del Rx del radio control
Conector para 4 servos o ESC, son salidas PWM
Conector para el Video y el circuito con el LM1881 para el OSD.
Reguladores de tension.

El esquemático es este:



el PCB esta quedando así:



Espero ir terminando pronto para volver a las pruebas!

Saudos!
-
Leonardo Garberoglio

Desconectado elgarbe

  • Moderador Local
  • PIC24H
  • *****
  • Mensajes: 2177
Re: Diseño de un OSD
« Respuesta #32 en: 10 de Marzo de 2014, 21:18:24 »
Bueno, hoy me hice un ratito en el laburo y me hice el PCB.



pero como siempre, de apurado, me olvide del conector para el GPS... asi que los cables soldados por debajo:



pero bueno, con esas placas tengo para experimentar un buen rato. Tengo para trabajar con el OSD, con las mag. electricas del motor, una segunda entrada I2C para la IMU y el sistema de estabilizacion, las salidas para 4 servos, la entrada PPM del receptor... y con un peso razonable para el pobre planeador!!!

Saludos!
-
Leonardo Garberoglio

Desconectado elgarbe

  • Moderador Local
  • PIC24H
  • *****
  • Mensajes: 2177
Re: Diseño de un OSD
« Respuesta #33 en: 28 de Marzo de 2014, 23:47:46 »
Bueno, despues de 20 días de lucha he conseguido pasar el OSD a modo gráfico y manejado por DMA!!!!!




Mañana pongo el código y les comento de que se trata...

Saludos
-
Leonardo Garberoglio

Desconectado rivale

  • Colaborador
  • PIC24H
  • *****
  • Mensajes: 1707
Re: Diseño de un OSD
« Respuesta #34 en: 29 de Marzo de 2014, 00:00:03 »
Felicidades el garde, sigo tus progresos y me gusta mucho tu trabajo  ((:-)) ((:-)) (((:-))) (((:-)))
"Nada es imposible, no si puedes imaginarlo"

Desconectado elgarbe

  • Moderador Local
  • PIC24H
  • *****
  • Mensajes: 2177
Re: Diseño de un OSD
« Respuesta #35 en: 06 de Abril de 2014, 12:16:57 »
Bueno, voy a tratar de documentar el avance que he conseguido en este arduo mes.

Lo último que habia puesto es que tenía algunos problemas al sacar informacion por el SPI usnado poolling, el código que estaba usando era algo así:

Código: [Seleccionar]
    if ((line >= lTop) && (line <= lTop + lWid -1)){ //Cada caracter tiene 7 pixeles de alto
    ltemp = (line - lTop) * 27 - 64;
    LPC_SSP0->DR = ~ltrs[head[0] + ltemp]; Wait();
    LPC_SSP0->DR = ~ltrs[head[1] + ltemp]; Wait();
    LPC_SSP0->DR = ~ltrs[head[2] + ltemp]; Wait();
    LPC_SSP0->DR = ~ltrs[head[3] + ltemp]; Wait();
    LPC_SSP0->DR = ~ltrs[head[4] + ltemp]; Wait();
    LPC_SSP0->DR = ~ltrs[head[5] + ltemp]; Wait();
    LPC_SSP0->DR = ~ltrs[head[6] + ltemp]; Wait();
    }

line es la línea actual en la que el haz de electrones esta dibujando. head[] es el array con el texto a enviar. Este código estaba en la interrupcion de Match 0 del Timer 2. Entonces a los 8useg de haver llegado el pulso de sincronismo horizontal se dispara este evento y si estoy en la linea correcta de la pantalla saco el dato por el SPI. La funcion Wait lo que hacía era verificar el FIFO del SPI. Si estaba lleno esperaba, sino volvia. Si bien eso andaba bastante bien, imprimir un texto era muy dificil y habia que escribir mucho código dificil de mantener, como el que puse arriba.
Por otra parte estube estudiando el tema del DMA. Como los datos estan en memoria y el SPI acepta DMA pensé que era buena opcion enviar todos los datos por DMA. Cuando empecé a esudiar esa posibilidad me di cuenta de algo interesantísmo. Es más facil scara con DMA una línea horizontal COMPLETA que sacar solo las zonas donde hay texto. Entonces si saco una línea completa y configuro el DMA en cada línea, puedo sacar la pantalla completa. Esto abría las puertas a algo que pensé iba a ser muy dificl, el método GRAFICO. Si es más fácil sacar todos los pixeles, entonces simplemente tengo que crear un buffer de video de 2 dimensiones, con la cantidad de filas como pixeles verticales (lineas visibles en pantalla) y la cantidad de Half Word (trabajo el SPI en modo 16 bits) que entran en la pantalla. Luego, el DMA se encargaría de sacar por sí solo cada línea de datos.
Costó bastante la configuracion y debug del DMA pero finalmente esta funcionando.

Veamos un poco el código:

Primero el extint.c

Código: [Seleccionar]
/*****************************************************************************
** Function name: EINT1_Handler
**
** Descriptions: external INT handler
**
** Interrupcion en el Pulso de Sincronismo Horizontal
**
*****************************************************************************/
void EINT1_IRQHandler (void) {
LPC_SC->EXTINT |= 2; // clear interrupt
line++; // Add 1 to current Line on screen
//First FIRST_LINE li
if (line>=FIRST_LINE && line<=(PIX_Y+FIRST_LINE)){ // If I'm on visible Line on screen
LPC_TIM2->TCR = 3; // Reset counter
LPC_TIM2->TCR = 1; // Enable counter
}else{
LPC_TIM2->TCR = 2; // Reset and disable counter
}

}

Cuando llega un pulso de sincronismo horizontal, veridico que esté dentro de los pulsos "visibles" en pantalla, ya que hay varios pulsos despues del pulso vertical que no son lineas visibles. si estamos en linea visible reseteo el TC del timer2 y lo habilito.
El timer 2 esta configurado para que interrumpa a los 8useg (desde el flanco de bajada del pulso de sinc hori hasta el comienzo de la info de video hay ese tiempo).
Veamos entonces que hace la interrupcion del Timer2:

Código: [Seleccionar]
/****************************************************************************/
/*                           Interrupt Functions                            */
/****************************************************************************/
void TIMER2_IRQHandler(void) {

if(LPC_TIM2->IR & 1){ // Verificamos que la Int es por Match Register 0
LPC_GPDMACH0->DMACCSrcAddr = (uint32_t) &membuff1[line-FIRST_LINE]; // membuff1[] is the array where I have stored
LPC_GPDMACH0->DMACCControl = PIX_X // Transferencia de PIX_X 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_GPDMA->DMACIntErrClr |= 0xff; // Clear all DMA interrupts
LPC_GPDMA->DMACIntTCClear |= 0xff;
LPC_GPDMACH0->DMACCConfig |= 1; // enable ch0
LPC_TIM2->TCR = 2; // Reset and disable counter
    }
    LPC_TIM2->IR |= 0xFF; // Clear all timer interrupts if there are any
}

Aqui simplemente configuramos el canal 0 del DMA, como dato de origen le paso la direccion de la linea actual del buffer de video. Aqui ni bien se habilita el canal, el DMA se encarga de sacar los datos. Lo bueno de esto es que se hace de forma autmática y de fondo, la CPU queda libre!!!!! de otro modo, si quería pintar la pantalla de blanco, por ejemplo, la CPU necesitaba 52uS por línea trabajando x 260 lineas 13.mseg de los 20mseg que tarda el refresco de la imagen... ahora solo le toma un par de instrucciones por línea para configurar el DMA... eso es terrible!!!!. Ni hablar de la simplicidad del código. Ya que al contar con una rutina que sola e encarga de sacar por pantalla el contenido del buffer de video, es como tener un GLCD, solo hay que dedicarse a escribir funciones para llenar el buffer de video!!!

Ese es todo el código necesario para sacar la info y mostrarla en pantalla.

Veamos ahora las funciones para poner texto y gráficos en el buffer de video.
La fuente que estoy usando tiene este formato:

Código: [Seleccionar]
//
//  Font data for Berlin Sans FB Demi 12pt
//

// Character bitmaps for Berlin Sans FB Demi 12pt
const short fnt3[] =
{
// @0 ' ' (14 pixels wide)
0b0000000000000000, //
0b0000000000000000, //
0b0000000000000000, //
0b0000000000000000, //
0b0000000000000000, //
0b0000000000000000, //
0b0000000000000000, //
0b0000000000000000, //
0b0000000000000000, //
0b0000000000000000, //
0b0000000000000000, //
0b0000000000000000, //
0b0000000000000000, //
0b0000000000000000, //

// @0 '!' (14 pixels wide)
0b0000000110000000, //        ##
0b0000000110000000, //        ##
0b0000000110000000, //        ##
0b0000000110000000, //        ##
0b0000000110000000, //        ##
0b0000000110000000, //        ##
0b0000000110000000, //        ##
0b0000000110000000, //        ##
0b0000000000000000, //
0b0000000110000000, //        ##
0b0000000110000000, //        ##
0b0000000000000000, //
0b0000000000000000, //
0b0000000000000000, //

Entonces el código para colocar un caracter en una posicion del buffer de video es:

Código: [Seleccionar]
void OSD_put_char(short X, short Y, char Car){
short i;

for(i=Y; i<(Y+FNT3_H); i++)
membuff1[i][X] = fnt3[(Car - 32) * FNT3_H + (i-Y)];
}

Simplemente se va recorriendo cada fila del caracter y copiando en el buffer, en la poscion deseada. Aquí la coordenada X esta dada en Caracteres, no en pixeles. El 32 que se le resta a Car es debido a que la fuente arranca en el Chr 32 (espacio en blanco).

Para imprimir un texto tengo esta funcion:

Código: [Seleccionar]
// ***********************
// Funcion para enviar una cadena de caracteres
void OSD_put_string(short X, short Y, char *pcString){
int i = 0;
while (pcString[i] != 0) { // loop through until reach string's zero terminator
OSD_put_char(X, Y, pcString[i]); // print each character
X++;
i++;
}
}

Luego empiezan las funciones gráficas. Las funciones las tome de un post de Ariel del foro ucontrol: http://www.ucontrol.com.ar/forosmf/tutoriales-guias-y-cursos-en-ucontrol/ccs-libreria-de-graficos-para-glcd-k0108/msg24846/#msg24846

Para dibujar un punto tengo que hacer un par de pasos totalmente distintos a los del GLCD:

Código: [Seleccionar]
//-----------------------------------------------------------------------
// Dibuja un pixel
//-----------------------------------------------------------------------
void OSD_punto(unsigned short x, unsigned short y, char color){
short CarX;
char PosCar;

CarX = x/16; //Calculo en que Short cae el pixel
PosCar = 15-( 16 * ((float)x/16 - CarX)); //Calculo la posicion dentro del Short del pixel
x = (unsigned short)(1<<(PosCar)); //Armo el Short con ese pixel encendido
membuff1[y][CarX] |= x; //Pongo el Short completo en el buffer
}

Debido a que mi buffer es del tipo short y tengo todo armado para transferir de a 16 bits, es que tengo que convertir de pixel a posicion dentro de los 32 shorts que entran en el ancho de la pantalla.

Luefo para imprimir una línea uso exactemente la misma funcion de Ariel, creo que no toque nada, solo la definicion de variables... de hecho aún no estudie como funciona  :shock::

Código: [Seleccionar]
//-----------------------------------------------------------------------
// Dibuja una linea desde (x1,y1) a (x2,y2) de color (0 o 1)
//-----------------------------------------------------------------------
void OSD_linea(unsigned short x1, unsigned short y1, unsigned short x2, unsigned short y2, char color)
{
   //Declaro variables-------------------
   signed short  x, y, incremento_x, incremento_y, distancia_x, distancia_y;
   signed short P;
   short i;

   //Calculo las diferencias entre las coordenadas de origen y destino
   distancia_x = fabs((signed short)(x2 - x1));
   distancia_y = fabs((signed short)(y2 - y1));

   //Inicializo x e y con las coordenadas de origen
   x = x1;
   y = y1;

   //Calculo el sentido de los incrementos (positivos o negativos)
   //en funcion de la posicion del origen y el destino
   if(x1 > x2) incremento_x = -1; else incremento_x = 1;
   if(y1 > y2) incremento_y = -1; else incremento_y = 1;

   //Si la distancia horizontal es mayor a la vertical...
   if(distancia_x >= distancia_y)
   { P = 2 * distancia_y - distancia_x;
      for(i=0; i<=distancia_x; ++i)
      {
      OSD_punto(x, y, color);

         if(P < 0)
         { P += 2 * distancia_y;
            x += incremento_x; }
         else
         { P += 2*distancia_y - 2*distancia_x;
            x += incremento_x;
            y += incremento_y;}
      }
   }

   //Si la distancia vertical es mayor a la horizontal...
   else
   { P = 2 * distancia_x - distancia_y;
      for(i=0; i<=distancia_y; ++i)
      { OSD_punto(x, y, color);
         if(P < 0)
         {  P += 2 * distancia_x;
            y += incremento_y; }
         else
         {  P += 2 * distancia_x - 2 * distancia_y;
            x += incremento_x;
            y += incremento_y; }
      }
   }
}
//-----------------------------------------------------------------------

Bueno, para usar esas funciones hago lo siguiente en el main:

Código: [Seleccionar]
  for(i=0;i<PIX_Y;i++)
for(j=0;j<PIX_X;j++)
membuff1[i][j] = 0;

OSD_put_string(11, 110, "elgarbe");
OSD_linea(0, 0, 467, 239, 1);
OSD_linea(467, 0, 0, 239, 1);

SSP0Init(); // Inicializo el bus SPI
DMAInit();
tim2_init(0); // Inicializamos el Tmr2 y le establecemos prioridad 0. La mas alta
EINTInit(); // Inicializo las interrupciones externas - LM1881

while ( 1 ){
}
}

El resultado?
Este:



Bueno, ahora sí estoy en condiciones de pasar a las cosas interesantes del proyecto... el horizonte artificial y los datos de los sensores...

Saludos!
-
Leonardo Garberoglio

Desconectado AngelGris

  • Colaborador
  • PIC24H
  • *****
  • Mensajes: 2479
Re: Diseño de un OSD
« Respuesta #36 en: 06 de Abril de 2014, 15:04:46 »
  Está bárbaro todo ésto, pero aún así dices que ahora puedes pasar a la parte interesante  :shock: :shock: :shock:

¿horizonte artificial? Aaahhhhhh!!!!!!!!! suena espectacular!!!!!!!  :-/ :-/ :-/
De vez en cuando la vida
nos besa en la boca
y a colores se despliega
como un atlas

Desconectado elgarbe

  • Moderador Local
  • PIC24H
  • *****
  • Mensajes: 2177
Re: Diseño de un OSD
« Respuesta #37 en: 06 de Abril de 2014, 15:52:34 »
  Está bárbaro todo ésto, pero aún así dices que ahora puedes pasar a la parte interesante  :shock: :shock: :shock:

¿horizonte artificial? Aaahhhhhh!!!!!!!!! suena espectacular!!!!!!!  :-/ :-/ :-/

jaja, es cierto, esta parte ya es un proyecto bastante grande en sí... pero el proyecto completo es un poco más grande.
La idea es juntar todo lo que fui poniendo en este subforo de UAV y fabricar un piloto automático como este (el que aparece en la pantalla dentro de la pantalla grande):


aquí mas informacion:


Saludos!
-
Leonardo Garberoglio

Desconectado jorgeneo560

  • PIC10
  • *
  • Mensajes: 11
Re: Diseño de un OSD
« Respuesta #38 en: 29 de Mayo de 2014, 02:37:43 »
yo te puedo ayudar en el horizonte artificial, trabaje con el mpu6050 y se como se maneja, tengo el trabajo echo en mikroc y mikrobasic, yo tambien quiero armar un osd, estuve armando un dakar osd pero podemos hacer algo mucho mejor si nos ayudamos entre todos

Desconectado elgarbe

  • Moderador Local
  • PIC24H
  • *****
  • Mensajes: 2177
Re: Diseño de un OSD
« Respuesta #39 en: 29 de Mayo de 2014, 08:18:14 »
Me parece bárbaro! yo estuve medio parado con esto porque se me quemó la LPCXpresso. Pero si definimos un micro y comenzamos a trabajar me engancho denuevo.

El primer tema a definir es que tipo de informacion se quiere mostrar. Si es en modo texto con gráficos bien básicos se puede usar un pic 18F. Si queremos hacer modo gráfico hay que pasar a otro micro para trabajar cómodo. Por eso yo tenía el LPC1769, un cortex M3 a 120MHz...

En fin, comentame como te gustaría empezar y lo vamos viendo!

Saludos!
-
Leonardo Garberoglio