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


von Felix G. (Gast)


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:
1
#include <avr/io.h>
2
#include  <inttypes.h>
3
#include <stdlib.h>
4
#include <stdint.h>
5
#ifndef F_CPU
6
#define F_CPU 16000000UL
7
#endif
8
#include <avr/interrupt.h>
9
#include <util/delay.h>
10
#include <lcd.c>
11
volatile uint16_t milli ;
12
uint8_t sekunden ;
13
uint8_t minuten ;
14
uint8_t stunden ;
15
16
17
18
int main (void)
19
{
20
21
lcd_init (LCD_DISP_ON);
22
23
sei();
24
25
  TCCR1A = (1<<WGM01); // CTC 
26
  TCCR1B |= (1<<CS10); // Prescaler 1
27
  OCR1A = (16000 - 1 );   //alle 1 millisek. -- (15999+1)/16000000Hz = 1ms
28
  
29
  // Compare Interrupt erlauben
30
  TIMSK |= (1<<OCIE1A);
31
32
33
 while (1)
34
35
 {
36
37
 if (milli == 1000)                    //Sekunden inkrementieren
38
{milli = 0;
39
 sekunden ++;
40
41
42
_delay_ms(10);
43
44
char sekundenascii [2];                //int to ascii
45
itoa (sekunden, sekundenascii, 10);
46
char minutenascii [2];
47
itoa (minuten, minutenascii, 10);
48
char stundenascii [2];
49
itoa (stunden, stundenascii, 10);
50
    
51
  lcd_clrscr();                       //Ausgabe jede sekunde
52
  
53
   lcd_puts (stundenascii);
54
   
55
  lcd_data (':');
56
57
   lcd_puts (minutenascii);
58
   
59
   lcd_data (':');
60
   
61
   lcd_puts (sekundenascii);
62
 
63
 }
64
65
66
if (sekunden == 60)                  //restliche Zähler inkrementieren
67
{
68
 minuten ++;
69
sekunden = 0;
70
}
71
72
73
74
if (minuten == 60 )
75
{
76
 stunden ++;
77
minuten = 0;
78
}
79
80
81
82
if (stunden == 24)
83
{
84
stunden = 0;
85
}
86
87
88
}
89
return 0;                
90
91
92
}
93
 
94
 
95
96
ISR(TIMER1_COMPA_vect)
97
{
98
99
milli++;                 //milli hochzählen
100
101
}

von Falk B. (falk)


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

von Detlev T. (detlevt)


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

von Hc Z. (mizch)


Lesenswert?

1
> char minutenascii [2];
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.

von Falk B. (falk)


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.
1
#include <avr/io.h>
2
#include  <inttypes.h>
3
#include <stdlib.h>
4
#include <stdint.h>
5
#ifndef F_CPU
6
#define F_CPU 16000000UL
7
#endif
8
#include <avr/interrupt.h>
9
#include <util/delay.h>
10
#include <lcd.c>
11
uint16_t milli;
12
volatile uint8_t flag;
13
uint8_t sekunden ;
14
uint8_t minuten ;
15
uint8_t stunden ;
16
17
int main (void) {
18
19
    char tmp_str[3];
20
21
    lcd_init (LCD_DISP_ON);
22
    TCCR1B = (1<<WGM12) | (1<<CS10); // Prescaler 1, CTC
23
    OCR1A = (16000 - 1 );   //alle 1 millisek
24
    
25
    // Compare Interrupt erlauben
26
    TIMSK |= (1<<OCIE1A);
27
28
    sei();
29
    
30
    while (1) {
31
32
        if (flag == 1) {                    //Sekunden inkrementieren
33
            flag = 0;
34
            sekunden ++;
35
            if (sekunden == 60) {           //restliche Zähler inkrementieren
36
                minuten ++;
37
                sekunden = 0;
38
                if (minuten == 60 ) {
39
                    stunden ++;
40
                    minuten = 0;
41
                    if (stunden == 24) {
42
                        stunden = 0;
43
                    }
44
                }
45
            }
46
    
47
            lcd_clrscr();                       //Ausgabe jede sekunde
48
            itoa (stunden, tmp_str, 10);
49
            lcd_puts (tmp_str);  
50
            lcd_data (':');
51
52
            itoa (minuten, tmp_str, 10);
53
            lcd_puts (tmp_str);
54
            lcd_data (':');
55
            itoa (sekunden, tmp_str, 10);
56
            lcd_puts (tmp_str); 
57
        }
58
    }
59
}
60
61
ISR(TIMER1_COMPA_vect)
62
{
63
  milli++;
64
  if (milli==1000) {
65
    milli=0;
66
    flag=1;
67
  }
68
}

MfG
Falk

von Felix G. (Gast)


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)

von Felix G. (Gast)


Lesenswert?

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

nur ein Ansatz

von Felix G. (Gast)


Lesenswert?

ok 2.Frage selbst gelöst

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

Ausgabe minuten

von Felix G. (Gast)


Lesenswert?

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

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.