Forum: Mikrocontroller und Digitale Elektronik Problem mit Variablen bei C Uhr


von Anfänger (Gast)


Lesenswert?

Hallo zusammen,

ich versuche gerade eine einfache Uhr in C zu programmieren.
Das ganze läuft über den Timer eines Mikrocontrollers. Mit dem 
TimerInterruptOverflow zähle ich eine Variable hoch.
Diese wiederum zählt dann meine Schleifen hoch
if(sek==59){
sek=0;
min++;
usw.

funktioniert auch soweit und auch die Ausgabe auf das LCD.
Als Variablen benutzte ich uint8_t.
Das Problem ist das nach einer gewissen Zeit meine Zeilen auf dem 
Display mit irgendeinem Kram überschrieben werden. Es scheint mir so als 
ob die Variablen irgendwann überfüllt sind.
Das verstehe ich nicht, da ich ja immer wieder bei 59 auf 0 setze usw.

Hat jemand einen Tipp?
Ich programmiere mit AVR-Studio 4

von Max H. (hartl192)


Lesenswert?

Anfänger schrieb:
> Hat jemand einen Tipp?
Ich würde zur Sicherheit auf >= 59 abfragen.
Zeig mal ein bisschen mehr vom Code und verwende dabei die C-Code 
Formatierung
1
[c]C-Code[/c]

: Bearbeitet durch User
von Dake S. (dakesew)


Lesenswert?

Poste mal den gesamten Code (als anhang). In dem Codeausschnitt kann ich 
nichts sehen, was falsch wäre. Ich würde evtl. if(sek>=59)  benützen, um 
sicher zu gehen, aber sollte eig. kein Problem sein.

von holger (Gast)


Lesenswert?

>Das Problem ist das nach einer gewissen Zeit meine Zeilen auf dem
>Display mit irgendeinem Kram überschrieben werden. Es scheint mir so als
>ob die Variablen irgendwann überfüllt sind.

Nö, ich denke das die Ausgabe auf das Display Müll ist.

>Das verstehe ich nicht, da ich ja immer wieder bei 59 auf 0 setze usw.

Wie gibst du die Zahlen aus?

Mal ein einfaches Beispiel:

Deine Minute war 59. Danach wird 0 ausgegeben.
59 -> 09. Sieht das ungefähr so aus?

von Frank W. (Firma: DB1FW) (frankw) Benutzerseite


Lesenswert?

Nein, kein Tipp.
Aber wenn du uns den Sourcecode zeigst und ggf noch sagst welche 
Umgebung du nutzt und vielleicht sogar noch erwähnst welche Art Display, 
Library usw. dann könnte ein Tipp dabei rausspringen

von Joachim B. (jar)


Lesenswert?

holger schrieb:
> Deine Minute war 59. Danach wird 0 ausgegeben.
> 59 -> 09. Sieht das ungefähr so aus?

kann sein wenn er nur 0 statt 00 ausgibt, beliebter Fehler :-)

Wer sagt denn das die IF Abfrage zeitnah erfolgt?

Anfänger schrieb:
> if(sek==59){
> sek=0;
> min++;

was ist wenn sek schon bei 60 ist?

Max H. schrieb:
> Ich würde zur Sicherheit auf >= 59

eben usw.

von Anfänger (Gast)


Lesenswert?

also wie gesagt das flag wird in der Iterrupt Routine gezählt.
Ja das Problem das beim umspringen von 59 auf 1 -> 19 steht hab ich auch 
noch ;-)
1
static uint8_t sek=0;
2
static uint8_t min=0;
3
static uint8_t std=0;
4
5
if(flag1>=61){
6
  sekunde++;
7
  flag1=0;
8
  lcd_set_cursor(7,1);
9
  lcd_print_itoa(sekunde);
10
11
if(sekunde>=59){
12
  sekunde=0;
13
  minute++;
14
  lcd_set_cursor(4,1);
15
  lcd_print_itoa(minute);
16
if(minute>=59){
17
  minute=0;
18
  stunde++;
19
  lcd_set_cursor(1,1);
20
  lcd_print_itoa(stunde);
21
if(stunde>=23){
22
  stunde=0;
23
  }
24
  }
25
  }
26
  }

von Max H. (hartl192)


Lesenswert?

Anfänger schrieb:
> beim umspringen von 59 auf 1
Eine Uhr sollte eigentlich von 59 auf 0 und nicht auf 1 springen.

> lcd_print_itoa(sekunde);
Die Funktion gibt keinen führenden Nullen aus, stimmt's?

BTW: Die unteren if würde ich weiter einrücken, dann sieht man auf 
Anhieb, dass es verschachtelte if sind.

: Bearbeitet durch User
von Anfänger (Gast)


Lesenswert?

die Funktion habe ich aus der vorhandenen lib und sieht so aus:
1
   The function itoa() converts the integer value from \c val into an
2
   ASCII representation that will be stored under \c s.  The caller
3
   is responsible for providing sufficient storage in \c s.
4
5
   \note The minimal size of the buffer \c s depends on the choice of
6
   radix. For example, if the radix is 2 (binary), you need to supply a buffer
7
   with a minimal length of 8 * sizeof (int) + 1 characters, i.e. one
8
   character for each bit plus one for the string terminator. Using a larger
9
   radix will require a smaller minimal buffer size.
10
11
   \warning If the buffer is too small, you risk a buffer overflow.
12
13
   Conversion is done using the \c radix as base, which may be a
14
   number between 2 (binary conversion) and up to 36.  If \c radix
15
   is greater than 10, the next digit after \c '9' will be the letter
16
   \c 'a'.
17
    
18
    If radix is 10 and val is negative, a minus sign will be prepended.
19
20
   The itoa() function returns the pointer passed as \c s.
21
*/
22
extern char *itoa(int __val, char *__s, int __radix);


Die Uhr springt ja jeweils durch rücksetzen sekunde=0; auf Null oder 
muss ich das anderst realisieren?

von Max H. (hartl192)


Lesenswert?

Anfänger schrieb:
> die Funktion habe ich aus der vorhandenen lib und sieht so aus:
Sieht nicht so aus als wäre eine führende null.

> Die Uhr springt ja jeweils durch rücksetzen sekunde=0; auf Null oder
> muss ich das anderst realisieren?
Die if-Abfrage sollte auf >59 abfragen und das print sollte erst nach 
der Abfrage sein.

von Joachim B. (jar)


Lesenswert?

Max H. schrieb:
> print sollte erst nach
> der Abfrage sein.

und immer 2-stellig erfolgen ! 00 statt 0

sprintf 02d

oder per strcpy

: Bearbeitet durch User
von Frank W. (Firma: DB1FW) (frankw) Benutzerseite


Lesenswert?

> Sieht nicht so aus als wäre eine führende null.

Jetzt muß ich doch mal fragen wo du das erkennst.
Der TO benutzt lcd_print_itoa,
zeigt uns aber die Beschreibung von itoa. Was lcd_print_itoa ist, das 
wissen wir nicht.
Oder habe ich was übersehen ?

von Anfänger (Gast)


Lesenswert?

Ok ich verstehe schon was ihr meint.
Ich versuche mal eine Lösung zu finden bzw. eine andere Funktion für die 
itoa Variante.
Wie kann ich die Printfunktion beeinflussen damit immer 2-stellig 
geschrieben wird?

Das Problem, dass mir irgendwann alles überschrieben wird habe ich aber 
immer noch nicht verstanden. Irgendeine Idee woher das kommen könnte?

von Anfänger (Gast)


Lesenswert?

lcd_print ist nur die Funktion um etwas an das LCD zu senden

von Thomas E. (thomase)


Lesenswert?

Anfänger schrieb:
> also wie gesagt das flag wird in der Iterrupt Routine gezählt.

Das ewige und meist auch sinnvolle Dogma mit der möglichst kurzen ISR 
besagt nicht, dass man darin gar nichts machen darf. Zähl deine Uhr in 
der ISR hoch. Soviel Zeit muss sein. Und die ist auch vorhanden. Dann 
tritt auch das Problem, dass ein Inkrement verschluckt wird, gar nicht 
erst auf. Du zählst einfach bis 60 bzw. 24 und setzt dann auf 0.

Nur Ausgabe auf das Display machst du natürlich nicht in der ISR. Sowas 
ist meist tödlich.

mfg.

: Bearbeitet durch User
von Frank W. (Firma: DB1FW) (frankw) Benutzerseite


Lesenswert?

Und wie sieht die lcd_print_itoa aus ?
Wo kommt die her ?
Was macht die ?

: Bearbeitet durch User
von Troll (Gast)


Lesenswert?

Thomas Eckmann schrieb:
> Zähl deine Uhr in
> der ISR hoch. Soviel Zeit muss sein.

Na, na, na, es reicht doch die Sekunden in der ISR zu inkrementieren. 
Der Rest ist doch für den µC lllllaaaaannnggsssaaammmmm, sehr 
llllllllllaaaaaaaannnnnnngggggsssssaaaaaaaammmmmmm.

von Anfänger (Gast)


Lesenswert?

ich inkrementiere nur ein Flag in der Interrupt Funktion und rufe hier 
drin die Funktion Uhrzeit auf, in der dann alles weitere abläuft!
Sollte doch so richtig sein oder nicht?

von Thomas E. (thomase)


Lesenswert?

Troll schrieb:
> Na, na, na, es reicht doch die Sekunden in der ISR zu inkrementieren.

Ja. Aber in einer Long-Variable. Das reicht für 136 Jahre. Die Uhrzeit 
rechnet man erst zur Ausgabe aus.

mfg.

von Troll (Gast)


Lesenswert?

Anfänger schrieb:
> in der Interrupt Funktion und rufe hier
> drin die Funktion Uhrzeit auf, in der dann alles weitere abläuft

Das ist nicht so gut. In der ISR solltest du nur das Flag und die 
Sekunden inkrementieren. Alles weitere machst du in der Hauptschleife.

von Frank W. (Firma: DB1FW) (frankw) Benutzerseite


Lesenswert?

Wenn das C ist, dann ist lcd_print_itoa eine Funktion. Die macht 
anscheinend irgendwas  mit itoa und gibt was auf das LCD aus.
Ob sie das mit/ohne 0 oder Leerzeichen macht, das wissen wir nicht.

Wahrscheinlich/anscheinend nicht - aber wenn uns der TO nicht sagt
welche Libraries er verwendet, dann ist alles nur geraten und stochern 
im Nebel.

P.s. Die LCD Funktion solle nicht aus dem Interrupt sondern in der 
Hauptprogrammschleife aufgerufen werden.
Aber das ist das 2.te Problem.

: Bearbeitet durch User
von Troll (Gast)


Lesenswert?

Thomas Eckmann schrieb:
> Aber in einer Long-Variable

Für konstruktive Krititk unempfänglich? :-<<<

von Anfänger (Gast)


Lesenswert?

1
void lcd_data(unsigned char temp1)
2
{
3
   unsigned char temp2 = temp1;
4
 
5
   LCD_PORT |= (1<<LCD_RS);        // RS auf 1 setzen
6
 
7
   temp1 = temp1 >> 4;
8
   temp1 = temp1 & 0x0F;
9
   LCD_PORT &= 0xF0;
10
   LCD_PORT |= temp1;               // setzen
11
   lcd_enable();
12
 
13
   temp2 = temp2 & 0x0F;
14
   LCD_PORT &= 0xF0;
15
   LCD_PORT |= temp2;               // setzen
16
   lcd_enable();
17
   
18
   _delay_us(42);
19
}
20
21
void lcd_string(char *data)
22
{
23
    while(*data) {
24
        lcd_data(*data);
25
        data++;
26
    }
27
}
28
29
void lcd_print_itoa(unsigned int zahl) {
30
char buffer [1000];
31
32
    itoa(zahl, buffer, 10); 
33
    lcd_string(buffer);
34
  }

von Anfänger (Gast)


Lesenswert?

buffer[] habe ich eigentlich bei 20 und nicht bei 1000 war nur ein test

von Max H. (hartl192)


Lesenswert?

Anfänger schrieb:
> char buffer [1000];
Ist das nicht ein bisschen übertreiben?

Eine führende Null für eine eistellige Zahl könnte man so einbauen:
1
    itoa(zahl, buffer, 10); 
2
    if(buffer[1] == 0x00)
3
      lcd_data('0');
4
5
    lcd_string(buffer);

: Bearbeitet durch User
von holger (Gast)


Lesenswert?

>Eine führende Null für eine eistellige Zahl könnte man so einbauen:
>
>    itoa(zahl, buffer, 10);
>    if(buffer[1] == 0x00)
>      lcd_data('0');

OMG.

  if(zahl < 10) lcd_data('0');

Danach das andere Geraffel.

von FrankW (Gast)


Lesenswert?

Du benutzt :     lcd_print_itoa();
Du erklärst :    itoa();
Du Antwortest :  lcd_print ist nur die Funktion um etwas an das LCD zu 
senden

Jetzt haben wir schon 3 unterschiedliche Funktionen im Spiel.

Wenn das C ist, dann sind das 3 unterschiedliche Dinge.

Aus lcd_print wird nicht plötzlich lcd_print_itoa nur weil man _itoa 
and den Namen dranhängt.

Ich bin fast Sicher die Lösung wäre was in der Richtung
1
if(flag1>=61){
2
  sekunde++;
3
  flag1=0;
4
   
5
  lcd_set_cursor(7,1);
6
  if ( sekunde < 10 )
7
    lcd_print("0");
8
  lcd_print_itoa(sekunde);
9
10
  if(sekunde>60){
11
    sekunde=0;
12
    minute++;
13
    lcd_set_cursor(4,1);
14
    if ( minute < 10 )
15
      lcd_print("0");
16
    lcd_print_itoa(minute);
17
    if(minute>=59){
18
      minute=0;
19
      stunde++;
20
      lcd_set_cursor(1,1);
21
      if ( stunde < 10 )
22
        lcd_print("0");
23
      lcd_print_itoa(stunde);
24
      if(stunde > 23){
25
        stunde=0;
26
      }
27
    }
28
  }
29
}
Und das im Hautprogramm aufgerufen.


Aber halt nur geraten.

von DirkB (Gast)


Lesenswert?


von Peter D. (peda)


Lesenswert?

Warum immer so kompliziert?
1
void lcd_print_time(void)
2
{
3
  char buffer [12];
4
  lcd_set_cursor(1,1);
5
  sprintf( buffer, "%2u:%02u.%02u",
6
           (uint16_t)hour,
7
           (uint16_t)min,
8
           (uint16_t)sec );
9
  lcd_string(buffer);
10
}

von Anfänger (Gast)


Lesenswert?

ok, erstmal vielen Dank für die ganzen Hilfestellungen.
@Frank, deine Idee bringt mir aufjedenfall die führenden Nullen aber ich 
überschreibe mir tortzdem bei der Ausgabe auf dem LCD irgendwann meine 
weiteren Zeilen und Spalten mit irgendwelchen Zahlen.

Was mich wundert, dass das manchmal sofort, manchmal erst nach 3 Minuten 
passiert.

von Frank W. (Firma: DB1FW) (frankw) Benutzerseite


Lesenswert?

Machst du die LCD Ausgabe mittlerweile in der Hauptprogrammschleife oder 
noch vom Interrupt aus ?

von Troll (Gast)


Lesenswert?

Peter Dannegger schrieb:
> sprintf(

Hoffentlich reicht der Speicher ... ;-)

von Peter D. (peda)


Lesenswert?

Troll schrieb:
> Hoffentlich reicht der Speicher ... ;-)

Auf nem ATtiny2313 könnts knapp werden. Es sollte schon ein ATmega88 
sein.

von Anfänger (Gast)


Lesenswert?

Ich inkrementiere jetzt nur noch das Flag über den Interrup, der Rest 
läuft im Hauptprogramm. Es scheint jetzt zu funktionieren, keine 
komischen Zeichen mehr und nichts wird überschrieben. Hauptproblem war 
dann wohl meine Strukturierung.

Vielen Dank für die ganzen Hilfen!!!

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.