Hace un tiempo estuve trabajando con un OSD para un estabilizador de vuelo (
http://www.todopic.com.ar/foros/index.php?topic=42254.0) y tuve que trabajar con algo de video.
En ese momento, la idea era más o menos esta:
Definicion del buffer de video:
#define PIX_Y 16 //Reveer PIX_Y y LAST_LINE. Ver en extint.c
#define PIX_16_X 2
#define FNT3_H 14 //Pixel heigh for Font 3
extern const short fnt3[];
uint16_t membuff1[PIX_Y][PIX_16_X];
parte de la fuente es esta:
// Character bitmaps for Berlin Sans FB Demi 12pt
const uint16_t 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, //
// @28 '"' (14 pixels wide)
0b0000011011000000, // ## ##
0b0000011011000000, // ## ##
0b0000011011000000, // ## ##
0b0000011011000000, // ## ##
0b0000000000000000, //
0b0000000000000000, //
0b0000000000000000, //
0b0000000000000000, //
0b0000000000000000, //
0b0000000000000000, //
0b0000000000000000, //
0b0000000000000000, //
0b0000000000000000, //
0b0000000000000000, //
......
Y tenía 2 funciones básicas para imprimir caracteres/texto en ek buffer:
void put_char(short X, short Y, char Car);
void put_string(short X, short Y, char *pcString);
void put_char(uint16_t X, uint16_t Y, char Car){
uint16_t i;
for(i=Y; i<(Y+FNT3_H); i++)
membuff1[i][X] = fnt3[(Car - 32) * FNT3_H + (i-Y)];
}
// ***********************
// Funcion para enviar una cadena de caracteres
void put_string(uint16_t X, uint16_t Y, char *pcString){
int i = 0;
// loop through until reach string's zero terminator
while (pcString[i] != 0) {
put_char(X, Y, pcString[i]); // print each character
X++;
i++;
}
}
como ven la funcion put_string es totalmente portable, de echo es copia de una funcion para LCD. Acá lo importante es la implementacion del put_char.
El secreto de put_char está la referenciación del código ASCII del caracter a escrivir con la posicion del primer uint16_t de dicho caracter.
Como la fuente arranca por el " " (ASCII 32) al código ascii del caracter a imprimir le restamos 32. Luego lo multiplicamos por la altura de la fuente. Esto es porque cada FNT3_H (14 en este caso) uint's16_t tenemos la primer fila del siguiente caracter. Luego hay un offset que tiene en cuenta la Fila actual y el lazo for que recorre las 14 filas que compone un caracter en la fuente que creamos.
Entonces si en el main hacemos:
put_string(0, 0, "elgarbe");
conseguiríamos que esos caracteres se almacenen en el buffer de video (membuff1). El problema de este método es que la coordenada X no es en pixeles, sino que es en caracteres y el ancho del caracter está puesto en la fuente elegida. En este caso era para imprimir en una pantalla de monitor y una fuente de 16x 14 era mas o menos linda (aunque la relacion no parece que quede linda, 16 de ancho por 14 de alto?). Para este cartel necesito una fuente de 8x12 más o menos... Como hacer las fuentes lo mostraré despues. Por ahora solo plantear la idea de llenado de buffer.
Dibujar un punto no es mucho más dificil:
//-----------------------------------------------------------------------
// 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
}
En este caso hay que tener encuenta que el buffer almacena uint16_t por lo que un punto en X debe ser referenciado a un bit dentro de los uint16's_t que forman una fila del cartel.
Y dibujar una línea?
Pues implementaciones standares:
//-----------------------------------------------------------------------
// 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; }
}
}
}
Bien, con esto queda planteada la forma de trabajar con el buffer de video. Ahora quedaría la traduccion de color (3bit por cada bit del buffer de video), la inversion de los datos de los TLC impares y el envío por el SPI.
Saludos!