Forum: Mikrocontroller und Digitale Elektronik AT Mega 32: Countdown und LCD


von Michel S. (11michi11)


Lesenswert?

Hallo,

ich suche nach einer Anregung oder Lösungsansatz zu folgendem "Problem".
Ein Bestandteil meines derzeitiges "Projektes" ist es eine 
Countdown-Zeit vorzuwählen, diese auf einem LCD auszugeben und 
anschließend mit ISR runterzählen zu lassen.
Ich habe es auch soweit realisiert, dass ich mttels Poti an ADC7(PA7) 
eine Zeit zwischen 0 und 15 Minuten vorwählen kann und diese auf dem LCD 
ausgebe.
Der Start des Countdown wird dem Controller dann mittels Tasterdruck 
signalisiert, in Folge dessen Timer 2 initialisiert wird und die Zeit 
runtergezählt wird.
Die ermittelte Countdown-Zeit wird aus dem ADC Wert errechnet und 
mittels
dtostrf gewandelt und an mein LCD (4Bit Modus ohne R/W) nach der LCD 
Routine aus dem hiesigen Tutorial( lcd_string(...); ) übergeben.
Timer 2 initialisiere ich wie folgt:

void timer_an(void)
{
TCCR2|=(1<<CS22);      // Vorteiler 64
TCNT2 = 0x00;
TIMSK|=(1<<TOIE2);
}
ISR des Timer 2
ISR (TIMER2_OVF_vect)
{
ovf++;
TCNT2=0;
}
mein Systemtakt ist 7,3728MHz bei 450 OVFs sollte 1 Sekunde vergangen 
sein?!
Im Hauptprogramm übergebe ich den Inhalt von ovf an eine andere Variable 
und verringere nach einer Sekunde in einer if Schleife den Wert der 
Variablen für die Sekundenanzeige um 1 und wandel ihn erneut mit 
dtostrf, um den vermeindlich aktualisierten Wert erneut auszugeben.
Leider tut sich auf meinem Display rein gar nichts.
Habe Momentan keinen Ansatz wo der Fehler stecken könnte. Muss ich 
womöglich den Wert der im string aufs Display ausgegeben wurde, erst 
mittels atol oder ähnlichem zurückwandeln, um ihn dann verändern zu 
können oder stimmt was mit dem Timer nicht?

Vielen dank im Voraus für eure Antworten.

von Cyblord -. (cyblord)


Lesenswert?

Michel Schl. schrieb:
> Muss ich
> womöglich den Wert der im string aufs Display ausgegeben wurde, erst
> mittels atol oder ähnlichem zurückwandeln, um ihn dann verändern zu
> können oder stimmt was mit dem Timer nicht?

Nein du musst nichts zurückverwandeln. Neuen Wert in String und aufs LCD 
ausgeben reicht.

Wo wird dein Wert an das LCD ausgegeben? Ist deine Zählvariable 
volatile?

Aber ohne den ganzen Code wirds nix.

gruß cyblord

von Michel S. (11michi11)


Angehängte Dateien:

Lesenswert?

Hallo ...
meine Zählvariable in der ISR ovf ist volatile.
Hab mir "spaßenshalber" mal in einer neuen Zeile des LCDs die Variable 
ausgeben lassen, an die ich volatile unsigned int ovf im Hauptprogramm 
übergebe bzw. in die ich sie kopieren diese ist unsigened int tc.
Diese wird munter hochgezählt.
Nur scheint das Programm meine if schleifen zu ignorieren und setzt sie 
nicht zurück bzw. minimiert die LCD Anzeige nicht?!

Hab den Code mal kopiert, nur weiß ich nicht ob es verständlich ist, da 
es nur ein Ausschnitt ist.

von Michel S. (11michi11)


Lesenswert?

Ist sicher auch einiges überflüssig...sitz nun schon ein paar Stunden 
dabei und habe immer munter alles geändert so wie es mir gerade in den 
Sinn kam.

von Karl H. (kbuchegg)


Lesenswert?

Michel Schl. schrieb:
> Ist sicher auch einiges überflüssig...

mit Sicherheit.
Aus deinem Code kann ich wirklich nicht viel machen, selten etwwas 
derart kompliziertes gesehen.

Allerdings frage ich mich, wozu du für Zeiten ein dtostrf brauchst?


Was genau umfasst eigentlich dein komplettes Projekt. Eine Countdown-Uhr 
mit LCD benötigt vieleicht in Summe eine oder eineinhalb 
Bildschirmseiten und da steckt der meiste Code in der ISR, die die Uhr 
runterzählt.

von Cyblord -. (cyblord)


Lesenswert?

Michel Schl. schrieb:
> if schleifen

Hopfen und Malz verloren für den der sowas schreibt.

Ausserdem ist das doch nicht dein ganzer Code, da fehlt doch einiges. 
Und poste ihn als .c damit man den auch gescheit angucken kann.

Von einem strukturierten Programmierstil hast du wohl noch nie was 
gehört? Gerade bei komplexeren Programmen musst du die Funktionalität 
sinnvoll in Funktionen auslagern, diese Funktion sinnvoll benennen und 
auch die Variablennamen "sprechend" machen. Ein Spaghetticode ist das, 
herrje...

gruß cyblord

von Krapao (Gast)


Lesenswert?

> mein Systemtakt ist 7,3728MHz bei 450 OVFs sollte 1 Sekunde
> vergangen sein?!
> ovf++

Dann sollte

1/ ovf eine globale Variable sein, die z.B. so definiert ist
1
volatile uint16_t ovf;

2/ bei der Abfrage der Variable ovf im Hauptprogramm sollten Lese- und 
Schreibzugriffe atomar gemacht werden (Artikel Interrupt). In deinem 
Code fehlt das z.B. an diesen Stellen:
1
  tc=ovf;  // nicht atomarer Zugriff auf ovf!  
2
  if(tc==450)
3
  {
4
    ovf=0; // nicht atomarer Zugriff auf ovf!

von Karl H. (kbuchegg)


Lesenswert?

Krapao schrieb:
>> mein Systemtakt ist 7,3728MHz bei 450 OVFs sollte 1 Sekunde
>> vergangen sein?!
>> ovf++
>
> Dann sollte
>
> 1/ ovf eine globale Variable sein, die z.B. so definiert ist
>
1
> volatile uint16_t ovf;
2
>
>
> 2/ bei der Abfrage der Variable ovf im Hauptprogramm sollten Lese- und
> Schreibzugriffe atomar gemacht werden (Artikel Interrupt). In deinem
> Code fehlt das z.B. an diesen Stellen:
>
1
>   tc=ovf;  // nicht atomarer Zugriff auf ovf!
2
>   if(tc==450)
3
>   {
4
>     ovf=0; // nicht atomarer Zugriff auf ovf!
5
>

grundsätzlich richtig.
Es schadet aber auch nichts, wenn er die komplette Uhrenlogik in die ISR 
verlagert. Auf die Art zählt die Countdownuhr auch dann noch im 
Hintergrund richtig weiter, wenn auf dem LCD gerade irgendwelche 
Benutzerinteraktionen laufen.


So wie das aussieht, will er ja einfach nur ein paar Pins einschalten, 
den Countdown runterzählen und nach der Vorgabezeit sollen sich die Pins 
wieder ausschalten. Das Runterzählen und Ausschalten kann ruhig alles 
geschlossen in der ISR passieren (nicht jedoch den LCD update!), die 
paar Takte die dafür notwendig sind, hat man mit Leichtigkeit.

von Krapao (Gast)


Lesenswert?

> Leider tut sich auf meinem Display rein gar nichts.

Den sei() nach dem Aufruf von timer_an() hast du schon im Code, oder?

Aber selbst ohne den solltest du auf dem Display was sehen und wenn es 
nur Nullen bei der Ausgabe von tc sind oder der Vorwahlwert für s und m.

Ich erkenne im Code das Starten des Countdowns nicht und denke der 
Countdown wird bei jedem Durchlauf der while Schleife bearbeitet.

dtostrf() ist Overkill, wie andere auch schon schrieben.

1
  while ( mode == 3 ) {
2
    PORTB &=~ (1<<PB4); // Startbit Sprachausgabe zurücksetzen
3
4
    // einmalige Textausgabe als Modus Funktionsbeschreibung
5
    if ( riegel == 255 ) {  
6
      PORTB &= ~(1<<PB3); // Heizung aus
7
      PORTD |= (1<<PD7);  // Hintergrundbeleuchtung LCD an
8
      lcd_clear();
9
      lcd_setcursor(1,1);
10
      lcd_string("Ziehzeit Countdown");
11
      lcd_setcursor(0,2);
12
      lcd_string("Vorwahl durch Drehen");
13
      lcd_setcursor(2,4);
14
      lcd_string("Start via Taster");
15
      riegel = 254;
16
    }
17
18
    // Startwert des Countdowns festlegen via Poti
19
    if ( riegel == 254 ) {
20
      sollwert = adc_soll(); // Stellung des Potis erfassen
21
      // ADC Wert/(ADCmax.{1023}/Zeitmax.{900s=15min)
22
      uhr = sollwert/umrechnung_zeit; 
23
      minuten = uhr / 60;  
24
      min = (int) minuten;
25
      sekunden = ((uhr / 60) - min) * 60;
26
      sek = (int) sekunden;
27
      m = min;
28
      s = sek;
29
      zeit_anzeigen(min, sek);
30
      // wie ändert sich der Wert für 
31
      // riegel von 254 auf einen anderen Wert?
32
    }  
33
34
    // Countdown starten via Taster?
35
    if ( riegel == 253 ) {
36
      timer_an();
37
      lcd_clear();
38
      lcd_setcursor(2,1);
39
      lcd_string("Countdown l");
40
      lcd_data(0xE1);
41
      lcd_string("uft");
42
      riegel = 252;
43
    }
44
45
// ANFANG COUNTDOWN CODE
46
// Der Countdown läuft von ANFANG bis ENDE bei jedem Wert für riegel!
47
// IMHO sollte er jedoch nur nach Tasterbestätigung laufen,
48
// d.h. eventuell nur wenn riegel == 252 ist!
49
50
    // Countdown Variablen m und s runterzählen
51
    ATOMIC_BLOCK(FORCEON) {
52
      tc = ovf;            // für Debugzwecke s.u.
53
      if ( ovf == 450 ) {  // 1s vergangen?
54
        ovf = 0;           // Timer2 counter reset
55
        if ( s != 0 ) {
56
          s -= 1;
57
        } else {
58
          s = 59;
59
          m -= 1;
60
        }
61
      }
62
    }
63
64
    // Countdown Variablen m und s ausgeben
65
    // [                ]
66
    // [                ]
67
    // [        mm:ss   ]
68
    // [                ]
69
    zeit_anzeigen(m, s);
70
71
    // Debugausgabe tc?
72
    // [                ]
73
    // [                ]
74
    // [        mm:ss   ]
75
    // [tttttt          ]
76
    PORTD ^= (1<<PD6); // 1s Puls-LED?
77
    { 
78
      char s[7];
79
      lcd_setcursor(0, 4);
80
      itoa(tc, s, 10); // #include <stdlib.h> erforderlich
81
                       // ggf. führende Nullen implementieren
82
      lcd_string(s);
83
    }
84
85
    // Countdown Alarm auslösen?
86
    if ( (min == 0) && (sek == 0) ) { // => falsche Variablen?
87
      mode = 4;
88
    }
89
    cnt = 0; 
90
// ENDE COUNTDOWN CODE
91
  } //Ende Mode 3

Mit einer Hilfsfunktion
1
void zeit_anzeigen(uint8_t minuten, uint8_t sekunden)
2
{
3
  char s[6];
4
  s[0] = ((minuten % 100) / 10) + '0';
5
  s[1] = (minuten % 10) + '0';
6
  s[2] = ':';
7
  s[3] = ((sekunden % 100) / 10) + '0';
8
  s[4] = (sekunden % 10) + '0';
9
  s[5] = 0;
10
  lcd_setcursor(8, 3);
11
  lcd_string(s);  
12
  // hier ggf. Blinken des : implementieren
13
}

von Michel S. (11michi11)


Lesenswert?

Hallo,
vielen Dank für euren zahlreichen und hilfreichen Antworten.
Hatte es derweil bereits selber zum Laufen gebracht, nach eurer Anregung 
hab ich allerdings es doch vorgezogen die Minimierung der Zeitvariablen 
innerhalb der OVF ISR vorzunehmen.
Zu meiner "Verteidigung" bzw. in Bezug zu eurer sicher angebrachten 
Kritik bezüglich meines "Programierstils", sofern man es so nennen darf 
... möchte ich sagen, dass ich bis Dezember letzten Jahres weder von 
Mikrocontrollern, noch von irgendeiner Programmiersprache Ahnung hatte.
Habe dann kurz mit Assembler begonnen und bin seit ca.Februar bei C 
gelandet.
Daher bin ich noch ein vollkommener Neuling auf diesem Gebiet und lerne 
noch bzw. probiere viel aus und mache es erstmal so wie ich es mir in 
meiner einfachen Logik denke ... ;)

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.