mikrocontroller.net

Forum: Mikrocontroller und Digitale Elektronik Uhr für AVR mit GCC


Autor: Felix G. (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hallo!
Kann mir jemand helfen...
ich komm bei der Programmierung einer einfachen Uhr für einen AVR (mit 
GCC)einfach nicht weiter. Das LCD (47780 - 2*16) zeigt Stunden und 
Minuten nur einstellig und zählt die Sekunden in Zehnerschritten bis 
1000 hoch. Keine Ahnung warum. Außerdem läuft das ganze zu schnell.
Danke im Vorraus,
Felix


Hier der Code:

#include <avr/io.h>
#include  <inttypes.h>
#include <stdlib.h>
#include <stdint.h>
#ifndef F_CPU
#define F_CPU 16000000UL
#endif
#include <avr/interrupt.h>
#include <util/delay.h>
#include <lcd.c>
volatile uint16_t milli ;
uint8_t sekunden ;
uint8_t minuten ;
uint8_t stunden ;



int main (void)
{

lcd_init (LCD_DISP_ON);

sei();

  TCCR1A = (1<<WGM01); // CTC 
  TCCR1B |= (1<<CS10); // Prescaler 1
  OCR1A = (16000 - 1 );   //alle 1 millisek. -- (15999+1)/16000000Hz = 1ms
  
  // Compare Interrupt erlauben
  TIMSK |= (1<<OCIE1A);


 while (1)

 {

 if (milli == 1000)                    //Sekunden inkrementieren
{milli = 0;
 sekunden ++;


_delay_ms(10);

char sekundenascii [2];                //int to ascii
itoa (sekunden, sekundenascii, 10);
char minutenascii [2];
itoa (minuten, minutenascii, 10);
char stundenascii [2];
itoa (stunden, stundenascii, 10);
    
  lcd_clrscr();                       //Ausgabe jede sekunde
  
   lcd_puts (stundenascii);
   
  lcd_data (':');

   lcd_puts (minutenascii);
   
   lcd_data (':');
   
   lcd_puts (sekundenascii);
 
 }


if (sekunden == 60)                  //restliche Zähler inkrementieren
{
 minuten ++;
sekunden = 0;
}



if (minuten == 60 )
{
 stunden ++;
minuten = 0;
}



if (stunden == 24)
{
stunden = 0;
}


}
return 0;                


}
 
 

ISR(TIMER1_COMPA_vect)
{

milli++;                 //milli hochzählen

}


Autor: Falk Brunner (falk)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
@  Felix G. (Gast)

>Minuten nur einstellig und zählt die Sekunden in Zehnerschritten bis
>1000 hoch. Keine Ahnung warum. Außerdem läuft das ganze zu schnell.

>Danke im Vorraus,

>  TCCR1A = (1<<WGM01); // CTC

Falsch! Falsches Bit, falsche Register.

Eher so.

  TCCR1B |= (1<<WGM12) | (1<<CS10); // Prescaler 1, CTC Mode

> if (milli == 1000)                    //Sekunden inkrementieren
>{milli = 0;
> sekunden ++;

OK

>_delay_ms(10);

Was soll der Unsinn?

MfG
Falk

Autor: Detlev T. (detlevt)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hallo Felix,

ich vermute einen Buffer-Overflow bei den Sekunden, weil deine 
char-array nur 2 Bytes lang ist. Da ist dann kein Platz mehr für den 
String-Terminator.

Wenn die Zahl unter 10 ist, gibt itoa halt nur ein einstelliges Ergebnis 
im String. Eine Alternative wäre sprintf.

Gruß, DetlevT

Autor: Hc Zimmerer (mizch)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
> char minutenascii [2];
> itoa (minuten, minutenascii, 10);
Dein String ist gar nicht so lang, dass er eine zweistellige Zahl 
enthalten kann, denn die abschließende '\0' gehört zum String.  Du 
kriegst also Überläufe.  Zudem sorgst Du nicht für die Darstellung der 
führenden Null, der obige String wird in den ersten 10 Minuten 
tatsächlich nur einstellig ausgegeben.  Das gilt für alle Deine itoa()s.

Die Sekunden zählen vermutlich nur scheinbar in 1000er Schritten hoch, 
Was Du als Nullen hinten siehst, dürfte nicht jedesmal geschrieben 
werden, sondern Folge früherer Ausgaben sein, z.B. zweistellige 
Ausgaben, die durch Überlauf der Strings sogar möglicherweise mit noch 
viel mehr Stellen ausgegeben wurden.

Weiter habe ich nicht geschaut.

Autor: Falk Brunner (falk)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
@  Felix G. (Gast)

>Minuten nur einstellig und zählt die Sekunden in Zehnerschritten bis
>1000 hoch. Keine Ahnung warum. Außerdem läuft das ganze zu schnell.

>Danke im Vorraus,

>  TCCR1A = (1<<WGM01); // CTC

Falsch! Falsches Bit, falsche Register.

Eher so.

  TCCR1B |= (1<<WGM12) | (1<<CS10); // Prescaler 1, CTC Mode

> if (milli == 1000)                    //Sekunden inkrementieren
>{milli = 0;
> sekunden ++;

OK

>_delay_ms(10);

Was soll der Unsinn?



>char sekundenascii [2];                //int to ascii
>itoa (sekunden, sekundenascii, 10);

Wird bissel knapp mit den Sekunden. Denn die brauchen DREI Bytes, zwei 
für die Zahl, eins für das Abschlusszeichen NULL.
Ausserdem definiert man Variablen nicht mitten im Quelltext. Das gehört 
an den Anfang.

Ausserdem hast du ggf. noch ein Problem, nämlich der nichtatomare 
Vergleich und Zugriff auf milli, siehe Interrupt. Das macht man 
anders, mit einem Flag. Siehe unten.
#include <avr/io.h>
#include  <inttypes.h>
#include <stdlib.h>
#include <stdint.h>
#ifndef F_CPU
#define F_CPU 16000000UL
#endif
#include <avr/interrupt.h>
#include <util/delay.h>
#include <lcd.c>
uint16_t milli;
volatile uint8_t flag;
uint8_t sekunden ;
uint8_t minuten ;
uint8_t stunden ;

int main (void) {

    char tmp_str[3];

    lcd_init (LCD_DISP_ON);
    TCCR1B = (1<<WGM12) | (1<<CS10); // Prescaler 1, CTC
    OCR1A = (16000 - 1 );   //alle 1 millisek
    
    // Compare Interrupt erlauben
    TIMSK |= (1<<OCIE1A);

    sei();
    
    while (1) {

        if (flag == 1) {                    //Sekunden inkrementieren
            flag = 0;
            sekunden ++;
            if (sekunden == 60) {           //restliche Zähler inkrementieren
                minuten ++;
                sekunden = 0;
                if (minuten == 60 ) {
                    stunden ++;
                    minuten = 0;
                    if (stunden == 24) {
                        stunden = 0;
                    }
                }
            }
    
            lcd_clrscr();                       //Ausgabe jede sekunde
            itoa (stunden, tmp_str, 10);
            lcd_puts (tmp_str);  
            lcd_data (':');

            itoa (minuten, tmp_str, 10);
            lcd_puts (tmp_str);
            lcd_data (':');
            itoa (sekunden, tmp_str, 10);
            lcd_puts (tmp_str); 
        }
    }
}

ISR(TIMER1_COMPA_vect)
{
  milli++;
  if (milli==1000) {
    milli=0;
    flag=1;
  }
}

MfG
Falk

Autor: Felix G. (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Danke!!! Wieder was gelernt.
Hätt ich nur noch zwei kleine Fragen:
1. Was hat das mit den zwei Registern A & B beim Timer 1 auf sich? (Is 
das ein 32 bit Timer oder sowas?
2. Wäre eine voranstehende Null bei Stunden/Minuten mit wenig 
Speicheraufwand möglich (sprintf is ja eher speicherintensiv)

Autor: Felix G. (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
evt. mit
if stunden < 10
gotoxy lcd_puts('0')

nur ein Ansatz

Autor: Felix G. (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
ok 2.Frage selbst gelöst

if (minuten < 10)
{lcd_putc ('0');}

Ausgabe minuten

Autor: Felix G. (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
OK....da stimmt was nicht. Nach 7min59 springt er wieder auf 0min0
mhhh

Antwort schreiben

Die Angabe einer E-Mail-Adresse ist freiwillig. Wenn Sie automatisch per E-Mail über Antworten auf Ihren Beitrag informiert werden möchten, melden Sie sich bitte an.

Wichtige Regeln - erst lesen, dann posten!

  • Groß- und Kleinschreibung verwenden
  • Längeren Sourcecode nicht im Text einfügen, sondern als Dateianhang

Formatierung (mehr Informationen...)

  • [c]C-Code[/c]
  • [avrasm]AVR-Assembler-Code[/avrasm]
  • [code]Code in anderen Sprachen, ASCII-Zeichnungen[/code]
  • [math]Formel in LaTeX-Syntax[/math]
  • [[Titel]] - Link zu Artikel
  • Verweis auf anderen Beitrag einfügen: Rechtsklick auf Beitragstitel,
    "Adresse kopieren", und in den Text einfügen




Bild automatisch verkleinern, falls nötig
Bitte das JPG-Format nur für Fotos und Scans verwenden!
Zeichnungen und Screenshots im PNG- oder
GIF-Format hochladen. Siehe Bildformate.
Hinweis: der ursprüngliche Beitrag ist mehr als 6 Monate alt.
Bitte hier nur auf die ursprüngliche Frage antworten,
für neue Fragen einen neuen Beitrag erstellen.

Mit dem Abschicken bestätigst du, die Nutzungsbedingungen anzuerkennen.