Forum: Mikrocontroller und Digitale Elektronik Interrupt Frage ATMEGA8535


von Robin F. (gehacktes)


Lesenswert?

Nabend,

ich bin seit langem mal wieder am programmieren und will eine Uhr mit 
vier 7-Segmentanzeigen programmieren.
Das multiplexen funktioniert, muss aber die Zeit noch etwas anpassen da 
es leicht flackert.

Jetzt zum Problem.
Ich habe das Datenblatt vom Atmega 8535 versucht zu verstehen und bin 
eigentlich der Meinung das ich den Interrupt richtig programmiert habe?.

Da es aber nicht funktioniert und den Fehler einfach nicht finde und 
verstehe, bitte ich um eine kurze Kontrolle und hoffentlich um 
Hilfestellung was ich falsch mache.
Die Anzeigen bleiben dauerhaft auf 0 0 0 0 stehen.
1
#include <avr/io.h>
2
#ifndef   F_CPU                       //Vordefinieren für delay.h
3
#define   F_CPU 8000000UL            //Definition von F_CPU in Herz
4
#endif
5
#include <avr/interrupt.h>
6
#include <util/delay.h>
7
8
//0b PD7 PD6 PD5 PD4 PD3 PD2 PD1 PD0
9
//0b -1-  A   B   C   D   E   F   G
10
#define ZERO    0b10000001
11
#define ONE     0b11001111
12
#define TWO     0b10010010
13
#define THREE   0b10000110
14
#define FOUR    0b11001100
15
#define FIVE    0b10100100
16
#define SIX     0b10100000
17
#define SEVEN   0b10001111
18
#define EIGHT   0b10000000
19
#define NINE    0b10000100
20
21
const uint8_t zaehlen[10] = {ZERO, ONE, TWO,THREE, FOUR, FIVE, SIX, SEVEN, EIGHT, NINE};
22
//                Anzeige 4, Anzeige 3, Anzeige 2, ANzeige 1
23
//                  PC6    PC5      PC4      PC3
24
volatile uint8_t driver[4] = {0b10111111, 0b11011111, 0b11101111, 0b11110111};
25
volatile uint8_t sekunden, EinerM, ZehnerM, EinerS, ZehnerS =0;
26
27
ISR (TIMER1_COMPA_vect){
28
  sekunden++;
29
  if (sekunden == 60){
30
    EinerM++;
31
    sekunden = 0;
32
    if (EinerM == 9){
33
      ZehnerM++;
34
      EinerM = 0;
35
    }
36
    if (ZehnerM == 6){
37
      EinerS++;
38
      ZehnerM = 0;
39
    }
40
    if (ZehnerS && EinerS != 24){
41
      if (ZehnerS <= 2){
42
        if(EinerS == 9){
43
          ZehnerS++;
44
          EinerS = 0;
45
        }
46
      }
47
    }
48
    else{
49
      EinerM = 0;
50
      ZehnerM = 0;
51
      EinerS = 0;
52
      ZehnerS = 0;
53
    }
54
  }
55
}
56
57
int main(void)
58
{
59
    DDRD = 0xff;
60
    PORTD = 0x00;
61
    DDRC = (1<<PC3) | (1<<PC4) | (1<<PC5) | (1<<PC6);
62
    PORTC = 0x00;
63
  
64
  TCCR1A = (1<<WGM12);
65
  TCCR1B = (1<<CS12);
66
  
67
  OCR1A = 31249;
68
  TIMSK |= (1<<OCIE1A);
69
  
70
  sei();
71
  
72
    while (1) 
73
    {
74
    PORTD = zaehlen[EinerM];
75
    PORTC = driver[0];
76
    _delay_ms(1);
77
    PORTD = zaehlen[ZehnerM];
78
    PORTC = driver[1];
79
    _delay_ms(1);
80
    PORTD = zaehlen[EinerS];
81
    PORTC = driver[2];
82
    _delay_ms(1);
83
    PORTD = zaehlen[ZehnerS];
84
    PORTC = driver[3];
85
    _delay_ms(1);
86
    }
87
return 0;
88
}

Danke und liebe Grüße
Robin

: Bearbeitet durch User
von Marc V. (Firma: Vescomp) (logarithmus)


Lesenswert?

Robin F. schrieb:
> Die Anzeigen bleiben dauerhaft auf 0 0 0 0 stehen.
1
    if (ZehnerM == 6)
2
    {
3
      ZehnerM = 0;
4
      EinerS++;
5
      ...               << Und hier kommt zuerst die Abfrage auf ZehnerS
6
        if(ZehnerS < 2)
7
        {
8
           ...              // EinerS geht bis 9
9
        }
10
        else
11
        {
12
           ...              // EinerS geht bis 3
13
        }
14
    }
15
16
17
    if (ZehnerS && EinerS != 24)  << ??? Was soll das sein ?
18
    {
19
      ...
20
    }
21
    else   <<<  Wird immer angesprungen = dauernd Null 
22
    {
23
      EinerM = 0;
24
      ZehnerM = 0;
25
      EinerS = 0;
26
      ZehnerS = 0;
27
    }

 P.S.
 Nur weil ein paar selbsternannte Experten hier im Forum die
 Klammern so schreiben, heisst es noch lange nicht, dass es so
 richtig ist.
 Aber wenn man die Klammern so schreibt, dass es übersichtlich und
 leichter zu debugen ist, wird man als blutiger Anfänger angesehen,
 oder ?

 Bei while(1) hast du es doch auch anders geschrieben, bleib dabei,
 nur bisschen einrücken.

: Bearbeitet durch User
von Noch nicht Rentner (Gast)


Lesenswert?

Die Zaehlerei macht man nicht im Interrupt. Und im Hauptprogramm wird 
nicht gewartet. Delay gibt es nicht. Das macht ein Timer.

von Robin F. (gehacktes)


Lesenswert?

Morgen,

danke für die schnellen Antworten. Ich habe vorhin "schnell" mal die 
erste Antwort ausprobiert, leider bislang noch ohne Erfolg. Ich glaube 
das der Interrupt nicht auslöst, da sonst ja wenigstens die Minuten 
hochgezählt und angezeigt werden müssten.

Ich werde das nach der Arbeit noch einmal versuchen zu realisieren.

Des weiteren werde ich mein Progrmam dann weiter anpassen und die 
Antwort zwei berücksichtigen. Hier weiß ich leider aber noch nicht wie 
ich dann den Sekundentakt ohne delay realisieren soll.
Dazu werde ich mich aber noch einmal belesen und ggf. hier nochmal 
nachfragen müssen.

Marc V. schrieb:
> P.S.
>  Nur weil ein paar selbsternannte Experten hier im Forum die
>  Klammern so schreiben, heisst es noch lange nicht, dass es so
>  richtig ist.
>  Aber wenn man die Klammern so schreibt, dass es übersichtlich und
>  leichter zu debugen ist, wird man als blutiger Anfänger angesehen,
>  oder ?

Danke für deine konstruktive Kritik.
Ich habe das damals so gelernt und von dort an immer so geschrieben, das 
es im main anders geschrieben war, war eigentlich eher ein versehen.

Ich werde aber in Zukunft versuchen deinen Ratschlag zu beherzigen und 
dies so schreiben.

In der zwischen Zeit (bis heute Nachmittag) werde ich mich noch einmal 
vermehrt über Interrupts schlau machen.
Sobald ich nachher das ganze erneut probiert habe, werde ich mich noch 
mal melden und meine Erkenntnisse mit euch teilen.

Beste Grüße

von Robin F. (gehacktes)


Lesenswert?

Hallo,

ich habe jetzt das Datenblatt und weitere Internetseiten durchwühlt und 
mir ist ein Fehler bei dem Interrupt aufgefallen.

Ich habe bei TCCR1 das falsche Register verwendet.

Das TCCR1A ist nicht für den CTC Mode ausgelegt, ich muss dort das 
Register B nehmen.

Hoffentlich ist das der Fehler gewesen!

von Marc V. (Firma: Vescomp) (logarithmus)


Lesenswert?

Robin F. schrieb:
> Ich habe bei TCCR1 das falsche Register verwendet.

 Ja, deswegen schreibt man normalerweise alle bits ab und setzt bei
 den unbenutzten bits Null ein.
1
  TCCR1B = (0<<ICNC1) | (0<<ICES1) | (0<<WGM13) | (1<<WGM12)
2
           (0<<CS12) | (0<<CS11) | (0<<CS10);

 So weiss man, dass keine bits vergessen sind.

von Thomas E. (thomase)


Lesenswert?

Noch nicht Rentner schrieb:
> Die Zaehlerei macht man nicht im Interrupt.

Unsinn. Wo denn sonst?

von Matthias S. (Firma: matzetronics) (mschoeldgen)


Lesenswert?

Thomas E. schrieb:
>> Die Zaehlerei macht man nicht im Interrupt.
>
> Unsinn. Wo denn sonst?

'Türlich in der ISR. Das geht so schnell, das die ISR in Nullkomamnix 
durchläuft.
Hier mal ein Getriebe für eine 50Hz Uhr, ist bei mir etwas einfacher, 
weil ich die Stellen nicht getrennt brechnen muss:
1
// a simple clock gearbox
2
volatile uint8_t fifties = 0;
3
volatile uint8_t seconds = 0;
4
volatile uint8_t minutes = 0;
5
volatile uint8_t hours = 0;
6
volatile uint16_t days = 0;
7
// clock gearbox is called every 1/50 of a second
8
// Note that it is really the CTC interrupt but the 8515 uses the COMP vector
9
// 6 byte clock : fifties:seconds:minutes:hours:days
10
// this clock will wrap around after 65535 days ~= 179 years 
11
12
ISR(TIMER0_COMP_vect) {
13
     fifties++;
14
     if (fifties > 49) {
15
  fifties = 0;
16
  seconds++;
17
  if (seconds > 59) {
18
    seconds = 0; 
19
    minutes++;
20
    if (minutes > 59) {
21
      minutes = 0;
22
      hours++;
23
      if (hours > 23) {
24
        hours = 0; 
25
        days++;
26
        }
27
      }
28
    }
29
  }
30
}
Nur jede Sekunde wird die gesamte Routine durchlaufen, sonst nicht.

von Dietrich L. (dietrichl)


Lesenswert?

Robin F. schrieb:
> Ich glaube
> das der Interrupt nicht auslöst, da sonst ja wenigstens die Minuten
> hochgezählt und angezeigt werden müssten.

Dann lass doch zuerst die Zählerei weg und toogle im Interrupt nur eine 
LED. So kannst Du Dich ganz auf die Funktion des Interrupts 
konzentrieren.
Solange die LED nicht blinkt brauchst Du gar nicht weitermachen.

von Robin F. (gehacktes)


Lesenswert?

Dietrich L. schrieb:
> Solange die LED nicht blinkt brauchst Du gar nicht weitermachen.

Da hast du vollkommen Recht. Wenn das mit dem TCCR Register immer noch 
nicht die Lösung bringt, werde ich das heute nochmal so versuchen.

Kann ich ich eigentlich in einem Programm auch mehrere Timer verwenden?

Also um z.b. das Zählen in Timer 0 ablaufen zu lassen und um ggf. einen 
Taster oder die Anzeigen in einem weiteren Timer 1 unterbringen?

Wenn ja, muss man da irgendwas beachten oder eifnach beide 
initialisieren?

von Marc V. (Firma: Vescomp) (logarithmus)


Lesenswert?

Robin F. schrieb:
> Kann ich ich eigentlich in einem Programm auch mehrere Timer verwenden?

 Natürlich, nur die jeweiligen Routinen so kurz wie möglich halten.
 Und nur eine davon darf (soll) die gemeinsamen Variablen ändern.

von Christian S. (roehrenvorheizer)


Lesenswert?

Hallo,

ich habe meine Lösung nicht ausprobiert, sondern nur mal ins Datenblatt 
geschaut.

In TCCR1 gibt es kein WGM12. Ich würde Mode 0 vorschlagen, siehe 
Table48.

In TIMSK TOIE1 setzen, Timer1 Überlauf Interrupt. Hierzu den passenden 
overflow-irq heraus suchen. Table19 Timer1 OVF Vector9.

CS12 Takt/256 läßt den Timer laufen.

sei ()

Mit freundlichem Gruß

von Robin F. (gehacktes)


Angehängte Dateien:

Lesenswert?

Alles klar vielen Dank.

Ich habe das Datenblatt auch nochmal angeguckt und auf Seite 113 steht 
bei 16-Bit Timer bei Register B des TTCR1 das es ein WGM12 gibt.

Siehe Bild.

Ich werde deine Variante dennoch testen.

: Bearbeitet durch User
von Matthias S. (Firma: matzetronics) (mschoeldgen)


Lesenswert?

Robin F. schrieb:
> Kann ich ich eigentlich in einem Programm auch mehrere Timer verwenden?
>
> Also um z.b. das Zählen in Timer 0 ablaufen zu lassen und um ggf. einen
> Taster oder die Anzeigen in einem weiteren Timer 1 unterbringen?

Selbstverständlich geht das. Meine Uhr z.B. von oben läuft als Nebenjob 
in einem Frequenzgenerator, der eigentliche Frequenzgenerator läuft mit 
Timer 1 und kommt sich mit der Uhr nicht in die Quere. Ehrlich gesagt, 
benutze ich die Uhr zum Langzeitabgleich des Quarzes, um den 
Frequenzgenerator genauer zu machen.
Wenn die ISR einigermassen zügig fertig werden, gibts da keine Probleme.

: Bearbeitet durch User
von Robin F. (gehacktes)


Lesenswert?

Aber das Multiplexen lassen ich jetz wie gehabt im main oder sollte ich 
das dann auch lieber auslagern?

Aber erstmal abwarten. Ich will erstmal die Anzeige vernünftig zum 
laufen bekommen.

Wenn das funktioniert will ich Taster mit implementieren um manuell 
Einstellungen vornehmen zu können und dann kommt die nächste Große 
Aufgabe und das wird dann ein DCF-77 Modul sein.

: Bearbeitet durch User
von Thomas E. (thomase)


Lesenswert?

Robin F. schrieb:
> Aber das Multiplexen lassen ich jetz wie gehabt im main oder
> sollte ich
> das dann auch lieber auslagern?

Das kann da bleiben, wo es ist. Aber nicht mit Delays, sondern von einem 
Timer getriggert. Wobei es allerdings auch nichts ausmacht, die 
Mux-Ausgaben in einer ISR zu erledigen. Denn die gebetsmühlenartig 
vorgetragene So-kurz-wie-möglich-Regel ist zwar nicht kompletter Unsinn, 
sollte aber einem, bezogen auf das gesamte Programm, "So kurz wie nötig" 
weichen.

von Dietrich L. (dietrichl)


Lesenswert?

Robin F. schrieb:
> Aber das Multiplexen lassen ich jetz wie gehabt im main oder sollte ich
> das dann auch lieber auslagern?

Wenn Du im main() sonst nichts zu tun hast, kannst Du es da lassen.
Falls aber zu erwarten ist, dass da noch Funktionen hinzu kommen (was Du 
ja planst), ist das Auslagern nicht schlecht. Dann läuft der Multiplexer 
wenigstens immer "rund" und ohne Flackern.

von Matthias S. (Firma: matzetronics) (mschoeldgen)


Lesenswert?

Robin F. schrieb:
> Aber das Multiplexen lassen ich jetz wie gehabt im main oder sollte ich
> das dann auch lieber auslagern?

Multiplex per Timer hat den Vorteil, das die Anzeige nicht flimmert, 
wenn main() gerade was anderes macht, sondern beinhart durchgezogen 
wird. Per Timerintervall kannst du dann bequem den Kompromiss zwischen 
Flimmern der Anzeige und Überlast des MC programmieren. Ausserdem kann 
der Multiplex Interrupt auch gleich die Tastenentprellroutine mit 
übernehmen.

Bewahre dir das also als Erweiterung für später auf und fummel dich 
erstmal durch den Uhren Interrupt durch.

von Robin F. (gehacktes)


Lesenswert?

Vielen Dank!

So mach ich das.

von Christian S. (roehrenvorheizer)


Lesenswert?

Ich hätte TCCR1A schreiben sollen.

von Marc V. (Firma: Vescomp) (logarithmus)


Lesenswert?

Thomas E. schrieb:
> Denn die gebetsmühlenartig
> vorgetragene So-kurz-wie-möglich-Regel ist zwar nicht kompletter Unsinn,
> sollte aber einem, bezogen auf das gesamte Programm, "So kurz wie nötig"
> weichen.

 Hmmm.
 Für mich ist es dasselbe.
 Wenn irgendetwas nötig ist, dann ist es unmöglich ohne diesen
 auszukommen, also ist "So kurz wie nötig" gleichzusetzen mit
 "So kurz wie möglich".

von Peter D. (peda)


Lesenswert?

Robin F. schrieb:
> Aber das Multiplexen lassen ich jetz wie gehabt im main oder sollte ich
> das dann auch lieber auslagern?

Das gehört in einen Timerinterrupt. Ansonsten wirst Du immer ein 
Flackern sehen, abhängig von allen anderen Tasks.

Benutzt Du noch andere Interrupts, ist es am besten, diese als
ISR(xxx_vect, ISR_NOBLOCK)
unterbrechbar zu machen. Damit erreichst Du das geringste Flackern.
Geht aber nicht immer, z.B. bei UART, SPI, I2C nicht.

von Marc V. (Firma: Vescomp) (logarithmus)


Lesenswert?

Peter D. schrieb:
> Das gehört in einen Timerinterrupt. Ansonsten wirst Du immer ein
> Flackern sehen, abhängig von allen anderen Tasks.

 Kommt auf die Refreshrate an.
 Bei ausreichend schnellem Refresh ist eher ein Unterschied in der
 Helligkeit zu sehen.

von Robin F. (gehacktes)


Lesenswert?

So ich habe das jetzt nochmal angepasst.
Jetzt funktioniert es. Eine Fragen habe ich jetzt noch.

Ich versteh noch nicht ganz wie ich den Vorteiler und den Vorladewert 
richtig berechne.
Ich habe versucht die Werte zu bestimmen aber mit keiner Rechnung komme 
ich auf das Ergebnis, welches in der Hardware funktioniert.
Ich habe jetzt den Vorladewert auf 15624 und den Vorteiler auf 64 
gestellt.
Mit diesen Werten komme ich auf einen Sekunden Takt aber wie berechne 
ich das richtig und nachvollziehbar?


Hier noch etwas Code zum lesen:
1
#include <avr/io.h>
2
#ifndef   F_CPU                       //Vordefinieren für delay.h
3
#define   F_CPU 16000000UL            //Definition von F_CPU in Herz
4
#endif
5
#include <avr/interrupt.h>
6
#include <util/delay.h>
7
8
//0b PD7 PD6 PD5 PD4 PD3 PD2 PD1 PD0
9
//0b -1-  A   B   C   D   E   F   G
10
#define ZERO    0b10000001
11
#define ONE     0b11001111
12
#define TWO     0b10010010
13
#define THREE   0b10000110
14
#define FOUR    0b11001100
15
#define FIVE    0b10100100
16
#define SIX     0b10100000
17
#define SEVEN   0b10001111
18
#define EIGHT   0b10000000
19
#define NINE    0b10000100
20
21
const uint8_t zaehlen[10] = {ZERO, ONE, TWO,THREE, FOUR, FIVE, SIX, SEVEN, EIGHT, NINE};
22
//                Anzeige 4, Anzeige 3, Anzeige 2, ANzeige 1
23
//                  PC6    PC5      PC4      PC3
24
volatile uint8_t driver[4] = {0b10111111, 0b11011111, 0b11101111, 0b11110111};
25
volatile uint8_t sekunden;
26
volatile uint8_t ZehnerS = 1;
27
volatile uint8_t EinerS = 6;
28
volatile uint8_t ZehnerM = 1;
29
volatile uint8_t EinerM = 5;
30
31
ISR (TIMER1_COMPA_vect){
32
  sekunden++;
33
  if (sekunden == 60){
34
    EinerM++;
35
    sekunden = 0;
36
    if (EinerM == 10){
37
      ZehnerM++;
38
      EinerM = 0;
39
    }
40
    if (ZehnerM == 6){
41
      EinerS++;
42
      ZehnerM = 0;
43
      
44
      if (EinerS == 10){
45
        ZehnerS++;
46
        EinerS = 0;
47
      }
48
      if (ZehnerS <2){
49
        if (EinerS == 10){
50
          ZehnerS++;
51
          EinerS = 0;
52
        }
53
      }
54
      else
55
      {
56
        if (EinerS == 4){
57
          ZehnerS = 0;
58
          EinerS = 0;
59
        }
60
      }
61
    }
62
  }
63
}
64
65
int main(void)
66
{
67
    DDRD = 0xff;
68
    PORTD = 0x00;
69
    DDRC = (1<<PC3) | (1<<PC4) | (1<<PC5) | (1<<PC6);
70
    PORTC = 0x00;
71
  //Erklärung Timer: https://www.mikrocontroller.net/articles/AVR-Tutorial:_Timer
72
  //https://sites.google.com/site/qeewiki/books/avr-guide/timer-on-the-atmega8
73
  // CTC Mode | Prescaler 64
74
  TCCR1B |= (1<<WGM12) | (1<<CS11) | (1<<CS10);
75
  //Vorladewert
76
  OCR1A = 15624;
77
  //
78
  TIMSK |= (1<<OCIE1A);
79
  
80
  sei();
81
  
82
    while (1) 
83
    {
84
    PORTD = zaehlen[EinerM];
85
    PORTC = driver[0];
86
    _delay_us(100);
87
    PORTD = zaehlen[ZehnerM];
88
    PORTC = driver[1];
89
    _delay_us(100);
90
    PORTD = zaehlen[EinerS];
91
    PORTC = driver[2];
92
    _delay_us(100);
93
    PORTD = zaehlen[ZehnerS];
94
    PORTC = driver[3];
95
    _delay_us(100);
96
    }
97
return 0;
98
}

von Matthias S. (Firma: matzetronics) (mschoeldgen)


Lesenswert?

Robin F. schrieb:
> Ich habe jetzt den Vorladewert auf 15624 und den Vorteiler auf 64
> gestellt.

Nach meiner Rechnung (F_CPU/64/15625) kämest du damit auf genau 16 Hz 
Ticker, was mich zu der Annahme verleitet, das dein Mega mit dem 
internen Oszillator läuft und damit nicht mit dem externen 16MHz Quarz, 
sondern mit den 1MHz des Auslieferzustandes.
Um das zu ändern, setzt du die Fuses auf den externen High Frequency 
Crystal Modus (CKOPT = 0), den du aber tunlichst erst dann aktivierst, 
wenn da wirklich ein externer 16MHz Quarz mit den beiden kleinen 
Kondensatoren dran hängt.

: Bearbeitet durch User
von Robin F. (gehacktes)


Lesenswert?

Okey also muss ich gucken das ich mit dem 1Mhz rechne und gucke ob dann 
eine annehmliche Zeit bei raus kommt?.

Nein ich werde keinen externen anhängen

von Matthias S. (Firma: matzetronics) (mschoeldgen)


Lesenswert?

Robin F. schrieb:
> Okey also muss ich gucken das ich mit dem 1Mhz rechne und gucke ob dann
> eine annehmliche Zeit bei raus kommt?

Das hast du ja schon. Bei 1000000 Hertz geteilt durch 64 geteilt durch 
15625 kommt ja genau 1 Hz raus. Du solltest nur dein
1
#define F_CPU 16000000UL
auf 1000000UL umstellen, damit auch etwaige Aufrufe von _delay_ms() das 
richtige Ergebnis bringen.
Für eine gut gehende Uhr ist übrigens der interne RC Oszillator viel zu 
ungenau. Wenn es dir darauf aber nicht ankommt, geht der RC aber.

: Bearbeitet durch User
von Robin F. (gehacktes)


Lesenswert?

Hab ich jetzt angepasst danke.
Dann funktioniert das jetzt alles soweit.

Jetzt baue ich die nächsten Funktionen ein:
- Sekundentakt blinken
- mit dem Taster manuell einstellen können
- DCF-77 (Zukunftsmusik)

Ich danke euch!!!

Wenn ich das Programm fertig habe, kann es gern noch einmal hier rein 
laden für weitere Verwendungen.

Beste Grüße

von Robin F. (gehacktes)


Lesenswert?

Hallo zusammen,

ich möchte mal ein kleines Update zu meinem Projekt geben.
Ein paar Probleme gab es zu lösen da es nicht richtig funktioniert hatte 
die Zeit manuell runter zu zählen.
Aus diesem Grund habe ich Taster 1 für Stunden hochzählen und Taster 2 
für Minuten hochzählen verwendet.
Des weiteren habe ich das Zählen der Zeit noch einmal anderst gelöst.

Hier der Code:
1
#include <avr/io.h>
2
#ifndef F_CPU
3
#define F_CPU 1000000UL //1MHz Takt
4
#endif
5
#include <avr/interrupt.h>
6
#include <util/delay.h>
7
8
/*
9
  Port B = Ziffern der Anzeigen
10
  Port C = Anzeigen  
11
  Port D = Taster, Sekunden LED, DCF-77, Temperaturfühler
12
  
13
  DP = PD1
14
  A = PB6
15
  B = PB5
16
  C = PB4
17
  D = PB3
18
  E = PB2
19
  F = PB1
20
  G = PB0
21
  Seg1 = PC2
22
  Seg2 = PC3
23
  Seg3 = PC4
24
  Seg4 = PC5
25
  
26
  Taster PD2 Minuten & PD3 Stunden hochzählen
27
*/
28
29
//0b PB7 PB6 PB5 PB4 PB3 PB2 PB1 PB0
30
//0b -1-  A   B   C   D   E   F   G
31
#define ZERO    0b10000001
32
#define ONE     0b11001111
33
#define TWO     0b10010010
34
#define THREE   0b10000110
35
#define FOUR    0b11001100
36
#define FIVE    0b10100100
37
#define SIX     0b10100000
38
#define SEVEN   0b10001111
39
#define EIGHT   0b10000000
40
#define NINE    0b10000100
41
42
volatile uint8_t zaehlen[10] = {ZERO, ONE, TWO,THREE, FOUR, FIVE, SIX, SEVEN, EIGHT, NINE};
43
44
volatile uint8_t driver[4] = {0b11011111, 0b11101111, 0b11110111, 0b11111011};
45
volatile uint8_t sekunden, Stunden = 12, Minuten = 0;
46
uint8_t ZehnerS;
47
uint8_t EinerS;
48
uint8_t ZehnerM;
49
uint8_t EinerM;
50
51
ISR (TIMER1_COMPA_vect)
52
{
53
  sekunden++;
54
  PORTD ^= (1<<PD0); //Toggle LED sekunden Takt
55
  if (sekunden == 60)
56
  {
57
    sekunden = 0;
58
    Minuten++;
59
    if (Minuten > 59)
60
    {
61
      Stunden++;
62
      Minuten = 0;
63
    }
64
    if (Stunden > 23)
65
    {
66
      Stunden = 0;
67
    }
68
  }
69
}
70
71
ISR(INT0_vect)
72
{
73
  _delay_ms(5);
74
  sekunden = 0;
75
  Minuten++;
76
  if (Minuten == 60)
77
  {  
78
    Stunden++;
79
    Minuten = 0;
80
  }
81
  if (Stunden == 24)
82
    Stunden = 0;
83
}
84
85
ISR (INT1_vect)
86
{
87
  _delay_ms(5);
88
  sekunden = 0;
89
  Stunden++;
90
  if (Stunden == 24)
91
    Stunden = 0;
92
}
93
94
void Berechnen(void)
95
{
96
  EinerM = Minuten % 10;
97
  ZehnerM = Minuten / 10;
98
  EinerS = Stunden % 10;
99
  ZehnerS = Stunden / 10;
100
}
101
102
void Zeigen(void)
103
{
104
  PORTB = zaehlen[EinerM];
105
  PORTC = driver[0];
106
  _delay_ms(2);
107
  PORTB = zaehlen[ZehnerM];
108
  PORTC = driver[1];
109
  _delay_ms(2);
110
  PORTB = zaehlen[EinerS];
111
  PORTC = driver[2];
112
  _delay_ms(2);
113
  PORTB = zaehlen[ZehnerS];
114
  PORTC = driver[3];
115
  _delay_ms(2);
116
}
117
118
int main(void)
119
{
120
    DDRB = 0xff;  // Ziffern auf Anzeige
121
  PORTB = 0x00;
122
  DDRC = (1<<PC2) | (1<<PC3) | (1<<PC4) | (1<<PC5);  //Azeigen
123
  PORTC = 0x00;
124
  DDRD = (1<<PD0);
125
  DDRD &= ~((1<<DDB2) | (1<<DDB3));  //Taster
126
  PORTD |= (1<<PORTD2) | (1<<PORTD3);
127
  
128
    //Erklärung Timer: https://www.mikrocontroller.net/articles/AVR-Tutorial:_Timer
129
    //https://sites.google.com/site/qeewiki/books/avr-guide/timer-on-the-atmega8
130
    //1MHz/64 = 15625
131
    //1Mhz/64/15625 = 1s
132
    // CTC Mode | Prescaler 64
133
    TCCR1B |= (1<<WGM12) | (1<<CS11) | (1<<CS10);
134
    OCR1A = 15624;  //Vorladewert
135
    TIMSK |= (1<<OCIE1A);
136
  //Interrupt für Taster Stunde und Minuten hochzählen
137
  MCUCR |= (1<<ISC00) | (1<<ISC01) | (1<<ISC10) | (1<<ISC11);
138
  GICR |= (1<<INT0) | (1<<INT1);
139
    sei();
140
    
141
    while (1)
142
    {
143
    Berechnen();
144
      Zeigen();
145
    }
146
return 0;
147
}

Meint Ihr, sind die Taster mit den externen Interrupts vernünftig gelöst 
oder sollte ich das liebe nicht mit Interrupts machen?

Gruß
Robin

von Oliver S. (oliverso)


Lesenswert?

Nein und Ja.

Und Code bitte als Anhang, nicht im Beitrag.

Oliver

von Robin F. (gehacktes)


Angehängte Dateien:

Lesenswert?

So hier noch einmal ein Update des Programms.
Jetzt sind die Taster nicht mehr mit den Interrupts realisiert.

Die nächsten Schritte sind dann DCF-77 und Temperaturfühler DS1820 zu 
implementieren.

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.