Reloj digital de atmega8 con temporizador1 y pantalla LCD

1

Escribí un código para hacer un reloj digital usando timer1 y lo probé con Proteus (un simulador de Arduino). Ese programa se descargó en un Atmega8 y se conectó a una pantalla LCD. Los segundos se cuentan perfectamente en 1 segundo de duración. Pero cuando segundo, los minutos y las horas se incrementan a más de 60, veo problemas. Para limitar esos valores, he escrito estas instrucciones:

if (second==60)
{
  second=0;
}

Pero cuando los segundos llegan a 59, la pantalla LCD muestra el valor del segundo siguiente al azar. Por ejemplo: 59,09,19,29,39,49,59 ..... 99,10,11,12,13,14.

Mi código:

unsigned int second=0;

unsigned int minute=0;

void init_timer1(void);

int main(void)
{
    init_timer1();
    sei();
    Init_LCD();

    while(1)
    {
        goto_position_X_Y(1,0);    //first line first position
        sprintf(lcd,"second=%d",second);
        Send_A_String(lcd);

        goto_position_X_Y(2,0);   // second line first position
        sprintf(lcd,"minute=%d",minute);
        Send_A_String(lcd);
    }
}

void init_timer1(void)
{
    TCCR1B |=(1<< CS12);      // prescaler set 256;new freq=31250;
    TCCR1B |=(1<< WGM12);
    TCNT1 =0;
    OCR1A= 3107; // 16 bit max count value 65535,
    TIMSK |=(1<< OCIE1A) ;
}

ISR(TIMER1_COMPA_vect)
{
    second++;

    if(second==60)
    {
        second=0;
    }

    minute=minute+(second/60);

    if(minute==60)
    {
        minute=0;
    }
}

¿me puede sugerir que se puede producir un problema en el programa de lcd? Código:

Send_A_String(lcd); function of this instruction.

void  Send_A_String(unsigned char*String_data)
{
  while (*String _data>0) {
    send_A_data(*String_data++) ;
  }
}
    
pregunta Muzahid Karim

4 respuestas

2
  

la pantalla LCD muestra el valor del segundo siguiente al azar

En realidad, no se muestra "al azar", como explicaste más adelante. Es importante darse cuenta de que hay un patrón para resolver este problema. Siempre busque patrones en sus datos, le están diciendo algo .

Según su descripción, esto no tiene nada que ver con los valores que se mantienen en la variable real second , pero es, como sugirió más adelante, simplemente un problema de visualización. Aunque ha mencionado los caracteres mostrados en algunos lugares, citaré este comentario reciente:

  

Me he dado cuenta de que después de 59, la pantalla muestra 09,19,29,39, ... 89,99,10,11 ... el primer dígito es exacto, pero el segundo dígito aparece 9 en lugar de eliminar.

¡Bien hecho! Encontró un patrón en los datos mostrados "defectuosos" y ese patrón contiene la clave para resolver este problema.

Este es un error de visualización clásico, cuando:

  • muestra algunos caracteres en una posición fija, p. ej. en una pantalla LCD de caracteres;
  • pero no siempre envía el mismo número de caracteres a la pantalla.

El resultado es que, dependiendo del comportamiento exacto del código que se utiliza para formatear los datos que se enviarán a la pantalla, todavía pueden quedar algunos caracteres de salida anterior en la pantalla. mostrar . En resumen, un carácter no puede reemplazar a dos en la pantalla.

Al comienzo de cada minuto, su datos de salida para la variable second es: 59, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 etc.

Por lo tanto, durante los primeros 10 segundos de cada minuto, solo está enviando un carácter único a esa ubicación fija en su pantalla LCD (lo describe en el código como "primera línea, primera posición") . Ese único carácter se escribe en la ubicación donde se mostró la unidad anterior de "decenas", es decir, donde solía estar el 5 de 59 .

Eso deja el dígito 9 de 59 aún visible, hasta que se sobrescribe con los primeros datos de salida ( 10 ) que tienen 2 dígitos!

Vea esta tabla que muestra los caracteres que envía a la pantalla LCD, lo que desea (pretende) mostrar, y lo que realmente muestra (que coincide con su lista):

Data sent to display    Intended display    Actual display    Correct display
59                      59                  59                Yes
0                       00                  09                No
1                       01                  19                No
2                       02                  29                No
3                       03                  39                No
...                     ...                 ...               
8                       08                  89                No
9                       09                  99                No
10                      10                  10                Yes
11                      11                  11                Yes
...                     ...                 ...               

Veo dos lugares donde podrías arreglar esto: cualquiera de los dos:

  • En su función Send_A_String() , compruebe si la cadena de entrada es solo un carácter y agregue un 0 inicial cuando eso ocurra. Sin embargo, no lo haría, ya que cambia esa rutina de una función general de "mostrar lo que recibo" a una función con un comportamiento específico para este programa, lo que reduce su reutilización en el futuro.

O

  • Si la función de biblioteca sprintf() incluye la funcionalidad necesaria para "cadenas de formato" (algunas versiones "mínimas" sprintf() no incluyen esto), utilice:

    sprintf(lcd,"second=%02d",second);

    en lugar de su original:

    sprintf(lcd,"second=%d",second);

    Eso hará que el relleno sea con el carácter 0 en lugar de espacios ( sprintf "flags") y hará que la salida tenga un mínimo de 2 caracteres ( sprintf "width") - por lo tanto, 02 en la cadena de formato sprintf() .

respondido por el SamGibson
2

Restablece el contador second antes de hacer la división con 60 para contar los minutos. Entonces siempre agregas 0.

if(second==60)
{
    second=0;
}

minute=minute+(second/60);  // <---- This line always adds 0 because you reset the seconds before.

Reemplace el incremento de minutos antes de la instrucción if como esta:

minute=minute+(second/60);  // <---- Increment minutes if necessary, then reset the seconds

if(second==60)
{
    second=0;
}

También declare sus contadores como volátiles para asegurarse de que no se optimizarán.

volatile unsigned int second=0;
volatile unsigned int minute=0;
    
respondido por el Bence Kaulics
1

En general, querrás evitar hacer cualquier cosa "compleja" en un ISR. El problema es que la complejidad se puede ocultar de manera bastante sutil.

En este caso, está haciendo algunos cálculos matemáticos en su ISR, específicamente, calculando second/60 , lo que casi con seguridad implica una llamada de subrutina a una biblioteca de tiempo de ejecución. Esta función de biblioteca puede o no ser "segura para interrupciones" (el término técnico es "reentrante"). Si no es así, esto explicaría sus resultados "aleatorios".

Tendría mucho más sentido codificar su ISR de esta manera:

ISR(TIMER1_COMPA_vect)
{
    second++;
    if (second==60) {
        second=0;
        minute++;
        if (minute==60) {
            minute=0;
        }
    }
}

Esto evita toda aritmética que no sea el incremento y la comparación, y minimiza el número de instrucciones ejecutadas en el caso más común (segundo! = 60).

    
respondido por el Dave Tweed
1

Cambiaría tu código de "reinicio de segundos" a

if(second >= 60)
{
    second=0;
    minute++;
}

y elimine el "minuto = minuto + (segundo / 60);" línea: desea aumentar los minutos cada vez que reinicia el segundo a cero.

    
respondido por el Peter Bennett

Lea otras preguntas en las etiquetas