MikroC 4.6, PIC16F88 y módulo LCD 2x24 HD44780

¡Hola!

Estoy intentando sacar lo que me llega por la UART por el módulo LCD usando la función Lcd_Chr_CP(). El primer problema con el que me encontré es que mikroC parece trabajar bien sólo con módulos de 40 caracteres de ancho. Digo esto porque después de escribir 24 caracteres tengo que escribir 16 más (que no se muestran) para que salte de línea. Esto lo he solventado llevando manualmente la posición del cursor.

En definitiva lo que quiero hacer es una interfaz estilo lcdserializer para lcdproc ( http://lcdproc.sourceforge.net/docs/current-user.html#hd44780-lcdserializer ), como el que hay en el siguiente link para el PIC16F84

http://www.mindspring.com/~tcoonan/lcd.html

El caso es que no funciona bien en lcdproc, sale todo descuadrado y con mucha basura; mientras que usando un programa de terminal como putty todo se muestra correctamente.

A continuación el código:

Código:
#define NROWS 2
#define NCOLS 24

sbit LCD_RS at RB0_bit;
sbit LCD_EN at RB1_bit;
sbit LCD_D4 at RA0_bit;
sbit LCD_D5 at RA1_bit;
sbit LCD_D6 at RA2_bit;
sbit LCD_D7 at RA3_bit;
sbit LCD_RS_Direction at TRISB0_bit;
sbit LCD_EN_Direction at TRISB1_bit;
sbit LCD_D4_Direction at TRISA0_bit;
sbit LCD_D5_Direction at TRISA1_bit;
sbit LCD_D6_Direction at TRISA2_bit;
sbit LCD_D7_Direction at TRISA3_bit;

void main() {
     char character;
     unsigned char col=0;
     unsigned char row=0;

     OSCCON=0x7E;
     ANSEL=0;
     TRISA=0;

     UART1_Init(9600);

     Lcd_Init();
     Lcd_Cmd(_LCD_CURSOR_OFF);
     Lcd_Cmd(_LCD_CLEAR);
     while(1) {
         if(UART1_Data_Ready()) {
             character = UART1_Read();
             if (0xFE !=character) {
                 if(0==col) {
                     if(0==row) {
                         Lcd_Cmd(_LCD_CLEAR);
                     }
                     else
                         Lcd_Cmd(_LCD_SECOND_ROW);
                 }
                     
                 Lcd_Chr_CP(character);

                 col=(col+1)%NCOLS;
                 if(0==col)
                     row=(row+1)%NROWS;
             }
             else {
                 while(!UART1_Data_Ready());
                 character = UART1_Read();
                 switch (character) {
                     case 1: Lcd_Cmd(_LCD_CLEAR); row=0; col=0; break;
                     case 2: Lcd_Cmd(_LCD_RETURN_HOME); break;
                     case 12: Lcd_Cmd(_LCD_CURSOR_OFF); break;
                     case 13: Lcd_Cmd(_LCD_BLINK_CURSOR_ON); break;
                     case 14: Lcd_Cmd(_LCD_UNDERLINE_ON); break;
                     case 16: Lcd_Cmd(_LCD_MOVE_CURSOR_LEFT);
                              col=(col-1)%NCOLS;
                              if(NCOLS-1==col)
                                  row=(row-1)%NROWS;
                              break;
                     case 20: Lcd_Cmd(_LCD_MOVE_CURSOR_RIGHT);
                              col=(col+1)%NCOLS;
                              if(0==col)
                                  row=(row+1)%NROWS;
                              break;
                     case 24: Lcd_Cmd(_LCD_SHIFT_LEFT); break;
                     case 28: Lcd_Cmd(_LCD_SHIFT_RIGHT); break;
                     case 192: Lcd_Cmd(_LCD_SECOND_ROW); row=1; col=0; break;
                 }
             }
         }
     }
}
 
He descubierto que tengo un problema. Después de encender el montaje, cuando mando un array de bytes al PIC, sólo recibe los tres primeros, independientemente de cuantos mande. Cuando vuelvo a mandar datos ya los recibe todos. Sé que es un problema de la recepción en la UART y no a la hora de mostrarlo en el LCD porque llevo la cuenta de caracteres recibidos.

Un ejemplo. Pongamos que mando "xxxx" (sin '\n' al final). El LCD muestra "xxx". A continuación mando "xxxxxx" y el LCD muestra "xxxxxxxxx". Con el código modificado que adjunto a continuación, que muestra la cuenta de caracteres recibidos, mandando lo mismo se mostraría "123" y "123456789". Con lo que claramente hay un byte sin recibir. Lo curioso es que no me pasa sólo tras inicializar la UART, sino también al cambiar de línea en el display con Lcd_Cmd(_LCD_SECOND_ROW).

Código:
#define NROWS 2
#define NCOLS 24

sbit LCD_RS at RB0_bit;
sbit LCD_EN at RB1_bit;
sbit LCD_D4 at RA0_bit;
sbit LCD_D5 at RA1_bit;
sbit LCD_D6 at RA2_bit;
sbit LCD_D7 at RA3_bit;
sbit LCD_RS_Direction at TRISB0_bit;
sbit LCD_EN_Direction at TRISB1_bit;
sbit LCD_D4_Direction at TRISA0_bit;
sbit LCD_D5_Direction at TRISA1_bit;
sbit LCD_D6_Direction at TRISA2_bit;
sbit LCD_D7_Direction at TRISA3_bit;

void main() {
     char character;
     unsigned char col=0;
     unsigned char row=0;
     unsigned char recibidos=0;

     OSCCON=0x7E;
     ANSEL=0;
     TRISA=0;

     UART1_Init(9600);

     Lcd_Init();
     Lcd_Cmd(_LCD_CURSOR_OFF);
     Lcd_Cmd(_LCD_CLEAR);
     while(1) {
         if(UART1_Data_Ready()) {
             character = UART1_Read();
             if (0xFE !=character) {
                 if('\n'!=character) {
                     recibidos++;
                     if(0==col) {
                         if(0==row) {
                             Lcd_Cmd(_LCD_CLEAR);
                         }
                         else
                             Lcd_Cmd(_LCD_SECOND_ROW);
                     }
                     
                     Lcd_Chr_CP(recibidos+48);
                 }
                 else
                     col=NCOLS-1;

                 col++;
                 if(NCOLS==col) {
                     col=0;
                     row++;
                     if(NROWS==row)
                         row=0;
                 }
             }
             else {
                 while(!UART1_Data_Ready());
                 character = UART1_Read();
                 switch (character) {
                     case 1: Lcd_Cmd(_LCD_CLEAR); row=0; col=0; break;
                     case 2: Lcd_Cmd(_LCD_RETURN_HOME); break;
                     case 12: Lcd_Cmd(_LCD_CURSOR_OFF); break;
                     case 13: Lcd_Cmd(_LCD_BLINK_CURSOR_ON); break;
                     case 14: Lcd_Cmd(_LCD_UNDERLINE_ON); break;
                     case 16: Lcd_Cmd(_LCD_MOVE_CURSOR_LEFT);
                              col--;
                              if(NCOLS<col) {
                                  col=NCOLS-1;
                                  row--;
                                  if(NROWS<row)
                                      row=0;
                              }
                              break;
                     case 20: Lcd_Cmd(_LCD_MOVE_CURSOR_RIGHT);
                              col++;
                              if(NCOLS==col) {
                                  col=0;
                                  row++;
                                  if(NROWS==row)
                                      row=0;
                              }
                              break;
                     case 24: Lcd_Cmd(_LCD_SHIFT_LEFT); break;
                     case 28: Lcd_Cmd(_LCD_SHIFT_RIGHT); break;
                     case 192: Lcd_Cmd(_LCD_SECOND_ROW); row=1; col=0; break;
                 }
             }
         }
     }
}

En un primer momento pensé que fuera que el buffer de recepción de la UART desbordara, pero no se explica que lo de recibir sólo tres bytes sea sólo en la primera transmisión y no en las demás. Como veis incluso he cambiado las operaciones módulo por sumas y comparaciones, ya que recuerdo que las operaciones de división suelen tomar muchos ciclos de reloj (doy por hecho que las de módulo también).

Por si sirve de algo, así es como mando los datos:

Código:
echo -n xxxx >/dev/ttyS1
echo -n xxxxxx >/dev/ttyS1

Y esta es la configuración de la UART de mi PC según stty:

Código:
$ stty -F /dev/ttyS1
speed 9600 baud; line = 0;
min = 1; time = 0;
-brkint -icrnl ixoff -imaxbel
-opost -onlcr
-isig -icanon -iexten -echo -echoe

Ya sé que igual usando interrupciones en vez de polling me quitaría de problemas, pero quisiera saber por qué ocurre esto.
 
Atrás
Arriba