Forum: Mikrocontroller und Digitale Elektronik Verständnisfrage zur Programmierung ATMega8


von Sascha M. (saschx)


Lesenswert?

Habe hier mal Code Segmente die ich nicht genau verstehe!
1
SIGNAL (SIG_OUTPUT_COMPARE1A)
2
{
3
  cli();              // Interrupts nicht zulassen
4
  TCNT1H=0;            // Timerstand zurücksetzen
5
  TCNT1L=0;
6
  
7
  Tue etwas;
8
}
9
10
int main(void)
11
{
12
13
  
14
15
  OCR1AH = 0x1E;            
16
  OCR1AL = 0x83;            //  !!!! verstehe diese Zuweisungen nicht
17
  
18
TIMSK |= (1<<OCIE1A);         // Interrupt wenn Timer Vergleichswert erreicht

Und gleich im Voraus, ich habe das Tutorial gelesen.

Ich will doch in dem Fall warten bis mein Zähler eine Sekunde erreicht 
und dann soll die Interruptroutine ausgelöst werden die dann was macht, 
oder nicht!? Verstehe aber nicht wieso hier 2 Vergleichswerte gesetzt 
werden?
Und dann noch ne Verständnisfrage zu dem errechnen der Takte. Ich habe 
aus dem Datenblatt des ATMega8-16PU herausgefunden das der mit 16MHz 
arbeitet aber dann auch wieder gelesen das der interne "Quarz" mit 1MHz. 
Welcher Takt gilt denn nun bei den Berechnungen für die Timer?

von crazy horse (Gast)


Lesenswert?

die, mit der MC nun wirklich läuft.
Bei Timern zus. evtl. noch der Wert des Vorteilers.

von fubu1000 (Gast)


Lesenswert?

Hallo,
für mich sieht das so aus, als ob der Timer1 des Atmega8 im 
Compare-Modus läuft.
Der Interrupt wird alle 7811 Takte ausgeführt(siehe OCR1A = 0x1E83), 
weil kein Vorteiler gesetzt ist anscheinend ? Oder du hast den Teil 
nicht gepostet, weil so wirds wohl nix mit einer Sek.
Im Interrupt die Register TCNT1H und L auf 0x00 zu setzen ist 
überflüssig.
Nach dem "Tue etwas", sollte wieder sei() gesetzt werden.

Gruss.

von Stefan B. (stefan) Benutzerseite


Lesenswert?

Sascha M. wrote:

> Und dann noch ne Verständnisfrage zu dem errechnen der Takte. Ich habe
> aus dem Datenblatt des ATMega8-16PU herausgefunden das der mit 16MHz
> arbeitet aber dann auch wieder gelesen das der interne "Quarz" mit 1MHz.
> Welcher Takt gilt denn nun bei den Berechnungen für die Timer?

Derjenige mit dem du den Atmega8 betreibst. Wenn du mit dem internen 
Oszillator (RC-Oszillator, kein "Quarz") arbeitest, dann 1 MHz und wenn 
du in deiner Schaltung eine externe Taktquelle hast und die AVR 
Fusebits (Fuses) so umprogrammiert hast, dass diese auch benutzt wird, 
dann gilt die Taktfrequenz der externen Taktquelle.
http://www.mikrocontroller.net/articles/AVR-Tutorial:_Equipment#Erg.C3.A4nzende_Hinweise_zur_Taktversorgung_.28kann_.C3.BCbersprungen_werden.29

von fubu1000 (Gast)


Lesenswert?

Achso Sreg sollte am Anfang der Interruptroutine noch gesichert werden 
und anschliessend wieder gesetzt werden.

cli();
unsigned char temp = SREG;
......
SREG = temp;
sei();

Gruss

von crazy horse (Gast)


Lesenswert?

cli und sei kannst du getrost weglassen...
Und in einer Hochsprache brauchst du dich auch um das SREG nicht 
kümmern.

von Johannes M. (johnny-m)


Lesenswert?

fubu1000 wrote:
> cli();
> unsigned char temp = SREG;
> ......
> SREG = temp;
> sei();
Unsinn! Das cli() und sei() wird durch die Controller-Hardware 
automatisch gemacht, und SREG sichert der Compiler! Erzähle hier keinem 
Anfänger, er solle am SREG rumfummeln!

Sascha wrote:
> Verstehe aber nicht wieso hier 2 Vergleichswerte gesetzt
> werden?
Es werden nicht zwei Vergleichswerte gesetzt, sondern nur einer, der 
aber 16 Bit lang ist. Da die AVRs nur 8 Bit auf einmal verwursten 
können, sind die Werte auf jeweils zwei Register aufgeteilt und müssen 
auf Hardware-Ebene (Assembler) getrennt beschrieben werden. Ein 
C-Compiler kann Dir allerdings die Arbeit des Doppelzugriffs (bei dem es 
auch noch auf die Reihenfolge ankommt, siehe Datenblatt!) abnehmen, und 
Du kannst außerdem auch direkt einen Dezimalwert hineinschreiben, was 
die Lesbarkeit des Programms erheblich erhöhen kann:
1
OCR1A = 0x1E83;
2
//oder identisch:
3
OCR1A = 7811;

SIGNAL ist übrigens veraltet. Lies Dir bitte im AVR-GCC-Tutorial 
durch, wie es geht. Da dürften eigentlich alle Deine Fragen beantwortet 
werden.

von Sascha M. (saschx)


Lesenswert?

Ich habe jetzt mal die Hinweise befolgt und die Interruptroutinen von 
Signal in ISR geändert. Leider funktionieren jetzt meine Tastendrücke 
gar nicht mehr und die Compare Funktion vom Timer1 geht nach wie vor 
nicht. Was mache ich denn falsch?
1
#include <inttypes.h>    // uint8_t usw.
2
#include <avr/io.h>
3
#include <avr/interrupt.h>  // Interrupts
4
#include <avr/eeprom.h>    // EEPROM Zugriffe
5
6
#define EEPROM  __attribute__ ((section (".eeprom")))        // für EEPROM-Zugriffe
7
8
#ifndef F_CPU
9
#warning "F_CPU war noch nicht definiert, wird nun mit 1000000 definiert"
10
#define F_CPU 1000000UL     
11
#endif
12
#include <util/delay.h>
13
14
15
16
//  Welcher Port an welchem Segement:
17
#define segm_a PD0
18
#define segm_b PD1
19
#define segm_c PD2
20
#define segm_d PD3
21
#define segm_e PD4
22
#define segm_f PD5
23
#define segm_g PD6
24
#define segm_p PD7
25
26
// Welcher Port an welcher Digit:
27
#define digit1 PB0    // Einer
28
#define digit2 PB1    // Zehner
29
#define digit3 PB2    // Hunderter
30
31
// An welchem Port sind Taster
32
#define btn_up    PC3
33
#define btn_down  PC4
34
#define btn_start  PC5
35
36
// Ports definieren
37
#define port_segment  PORTD
38
#define port_digit    PORTB
39
#define port_keys    PORTC
40
#define dir_segment    DDRD
41
#define dir_digit    DDRB
42
#define dir_keys    DDRC
43
#define pin_keys    PINC
44
45
// Definiert, welche Segmente für die einzelnen Zahlenwerte aufleuchten sollen. LOW-Aktiv
46
#define sign0 (1 << segm_g) | (1 << segm_p)
47
#define sign1 (1 << segm_a) | (1 << segm_d) | (1 << segm_e) | (1 << segm_f) | (1 << segm_g)  | (1 << segm_p)
48
#define sign2 (1 << segm_c) | (1 << segm_f) | (1 << segm_p) 
49
#define sign3 (1 << segm_e) | (1 << segm_f) | (1 << segm_p) 
50
#define sign4 (1 << segm_a) | (1 << segm_d) | (1 << segm_e) | (1 << segm_p)  
51
#define sign5 (1 << segm_b) | (1 << segm_e) | (1 << segm_p)  
52
#define sign6 (1 << segm_b) | (1 << segm_p) 
53
#define sign7 (1 << segm_d) | (1 << segm_e) | (1 << segm_f) | (1 << segm_g) | (1 << segm_p)
54
#define sign8 (1 << segm_p) 
55
#define sign9 (1 << segm_e) | (1 << segm_p) 
56
57
uint8_t num[] = { sign0, sign1, sign2, sign3, sign4, sign5, sign6, sign7, sign8, sign9 };
58
uint16_t startwert EEPROM;          // Speicherplatz für letzten Startwert im EEPROM
59
volatile uint16_t   display_value=0;    // Startwert. 
60
volatile uint8_t    run=0;          // =0 Counter Stop; =1 Counter läuft
61
volatile uint8_t    key_up=0,        // Puffer für Tastendrücke
62
          key_down=0,
63
          key_start=0;
64
65
66
void ausgabe (uint16_t zahl)
67
{
68
  uint16_t output = zahl;
69
  uint16_t warten = 50;    // Enable Zeit für einzelnes Segment. Zeit in der LED leuchten
70
  
71
  
72
  // Hunderter-Dezimalstelle
73
  if (zahl >= 100)
74
  {
75
    port_segment = num[output/100];        // Zahlenwert darstellen 
76
    port_digit = (1 << digit2) | (1 << digit1);  // 3. Digit Ein
77
    _delay_ms(warten);              // LEDs müssen gewisse Zeit leuchten
78
    port_digit = (1 << digit3) | (1 << digit2) | (1 << digit1);              // 3. Digit HIGH
79
  }
80
   output = output % 100;
81
  
82
  
83
  // Zehner-Dezimalstelle
84
  if (zahl >= 10)
85
  {
86
    port_segment = num[output/10];    // Zahlenwert darstellen 
87
    port_digit = (1 << digit3) | (1 << digit1);  // 2. Digit Ein
88
    _delay_ms(warten);              // LEDs müssen gewisse Zeit leuchten
89
    port_digit = (1 << digit3) | (1 << digit2) | (1 << digit1);                // 2. Digit HIGH
90
  }
91
   output = output % 10;
92
  
93
  // einer Dezimalstelle 
94
  port_segment = num[output];      // Zahlenwert darstellen 
95
  port_digit = (1 << digit3) | (1 << digit2);    // 1. Digit Ein
96
  _delay_ms(warten);                // LEDs müssen gewisse Zeit leuchten
97
  port_digit = (1 << digit3) | (1 << digit2) | (1 << digit1);                // 1. Digit HIGH
98
99
  sei();              // Interrupts zulassen
100
}
101
102
103
ISR (TIMER0_OVF_vect)
104
{
105
  cli();            // Interrupts nicht zulassen
106
  ausgabe (display_value);  // Zahl darstellen
107
}
108
109
110
111
ISR (TIMER2_OVF_vect)
112
{
113
  cli();    // Interrupts nicht zulassen
114
  
115
  if (!(pin_keys & (1 << btn_up)))    // Up-Taste gedrückt
116
    key_up++;
117
    
118
  if (!(pin_keys & (1 << btn_down)))    // Down-Taste gedrückt
119
    key_down++;
120
    
121
  if (!(pin_keys & (1 << btn_start)))    // Start-Taste gedrückt
122
    key_start++;
123
  
124
  sei();    // Interrupts zulassen
125
}
126
127
128
ISR (TIMER1_COMPA_vect)
129
{
130
  cli();              // Interrupts nicht zulassen
131
  TCNT1H=0;            // Timerstand zurücksetzen
132
  TCNT1L=0;
133
  
134
  //display_value -= run;
135
  display_value--;
136
}
137
138
int main(void)
139
{
140
141
  dir_digit = (1 << digit1) | (1 << digit2) | (1 << digit3);      // Ausgänge, für Digits
142
  dir_segment = 0xFF;                          // alles Ausgänge, für Segmente 
143
  dir_keys &= ~(1 << btn_up) & ~(1 << btn_down) & ~(1 << btn_start);// Eingänge für Tasten
144
  port_keys |= (1 << PC3) | (1 << PC4) | (1 << PC5);
145
  
146
  // Timer für Displayrefresh
147
  TCCR0 |= (1<<CS01) | (1<<CS00);    //8-Bit Timer, Timer clock = system clock/64
148
  TIFR |= (1<<TOV0);           //Clear TOV0 Timer/Counter Overflow Flag. clear pending interrupts
149
  TIMSK |= (1<<TOIE0);         //Enable Timer0 Overflow Interrupt
150
151
  // Timer für Tastenabfrage
152
  TCCR2 |= (1<<CS02);          //8-Bit Timer, Timer clock = system clock/256
153
  TIFR |= (1<<TOV2);           //Clear TOV2 Timer/Counter Overflow Flag. clear pending interrupts
154
  TIMSK |= (1<<TOIE2);         //Enable Timer2 Overflow Interrupt
155
  
156
  // Timer für Sekundentakt
157
  OCR1A = 977;            // 16Bit Timer Takt= 1MHz => 1.000.000/1024 = 976,56 
158
                    // Vergleichswert laden: 1 Sekunde
159
  TIMSK |= (1<<OCIE1A);         // Interrupt wenn Timer Vergleichswert erreicht
160
161
  sei();                // Interrupts zulassen
162
163
  while (1)
164
  {
165
    if (key_up >= 30)        // Up-Taste gedrückt
166
    {
167
      display_value++;      // Wert +1
168
      if (display_value > 999)  // Wenn Überlauf => Wert=0
169
        display_value = 1;
170
      key_up = 0;          // Tastencounter leeren
171
    }
172
    if (key_down >= 30)        // Down-Taste gedrückt
173
    {
174
      if (display_value <= 1)    // Wenn Unterlauf => Wert=999
175
        display_value = 1000;
176
      display_value--;      // Wert -1
177
      key_down = 0;        // Tastencounter leeren
178
    }
179
180
    if (key_start >= 30)      // Start-Taste gedrückt
181
    {
182
      key_start = 0;        // Tastencounter leeren
183
      if (display_value == 0)    // wenn angezeigter Wert = 0 und Start gedrückt => ermittler zuletzt verwendeten Startwert und setze diesen
184
      {
185
        display_value = eeprom_read_word(&startwert);
186
      }
187
      else
188
      {
189
        run ^= 1;          // Wert XOR 1;  => Start/Stop
190
        if (run)          // Wenn Counter gestartet wird
191
        {
192
          if (display_value != eeprom_read_word(&startwert))    // wenn Start gedrückt und aktueller Startwert != letzter Startwert
193
            eeprom_write_word(&startwert, display_value);    // dann speichere neuen Startwert
194
        
195
          TCNT1H=0;            // Timerstand zurücksetzen, damit ab jetzt eine Sekunde zählt
196
          TCNT1L=0;
197
          TCCR1A = (1<<CS10)|(1<<CS12);  // Timer mit Div 1024 starten
198
          
199
        }
200
        else            // Counter angehalten
201
        {
202
          TCCR1A = 0;        // Timer Stop
203
          
204
        }
205
      }
206
    }
207
208
    if (display_value == 0)      // Timer abgelaufen
209
    {
210
      run = 0;          // Stop
211
    }
212
  }
213
}

von Johannes M. (johnny-m)


Lesenswert?

Sascha M. wrote:
> Ich habe jetzt mal die Hinweise befolgt
Nein, hast Du nicht! Du hast weder die cli() und sei() aus den Interrupt 
Handlern entfernt, noch hast Du die Zugriffe auf TCNT1H und L durch 
Zugriffe auf die entsprechenden 16-Bit-Register ersetzt.

von Johannes M. (johnny-m)


Lesenswert?

Wenn Du den Timer übrigens im CTC-Modus betreibst, kannst Du Dir das 
auf-Null-Setzen von TCNT1 im Interrupt Handler schenken und das ganze 
wird genauer (wobei das hier kaum eine Rolle spielen dürfte). Außerdem 
tritt das Compare-Ereignis immer erst einen Timertakt nach der 
Feststellung der Übereinstimmung mit dem Compare-Wert ein. Wenn also ein 
Compare-Ereignis nach 977 Timertakten gewünscht ist, dann kommt ins 
Compare-Register eine 976.

von Sascha M. (saschx)


Lesenswert?

Habe jetzt alle sei und cli entfernt außer dei sein in der main() und in 
der ausgabe(). Für den Timer fehlt mir trotzdem noch das richtige 
Verständnis.

von Sascha M. (saschx)


Lesenswert?

Ach ja, meine Tastendruckinterrupts funktionieren jetzt auch nicht mehr? 
Die gingen aber vorher mit SIGNAL schon mal!?

von Johannes M. (johnny-m)


Lesenswert?

Sascha M. wrote:
> Habe jetzt alle sei und cli entfernt außer dei sein in der main() und in
> der ausgabe().
Und warum das in der ausgabe() nicht? Noch mal: Es hat da nichts 
verloren. ausgabe() wird offensichtlich nur aus einem Interrupt Handler 
heraus aufgerufen. Und während eines Interrupt Handlers ist die 
Bearbeitung anderer Interrupts SOWIESO AUTOMATISCH GESPERRT, weshalb das 
cli() in dem betreffenden Interrupt Handler ÜBERFLÜSSIG IST und das 
sei() am Ende der ausgabe()-Funktion u.U. sogar zu UNERWÜNSCHTEN 
NEBENEFFEKTEN FÜHREN KANN!

von Johannes M. (johnny-m)


Lesenswert?

Übrigens, was soll denn das da:
1
#define EEPROM  __attribute__ ((section (".eeprom")))        // für EEPROM-Zugriffe
Warum benutzt Du nicht das bereits in der eeprom.h definierte EEMEM?

Außerdem solltest Du Dir möglichst angewöhnen, Makros in GROSSBUCHSTABEN 
zu benennen, damit sie nicht mit Variablen verwechselt werden.

von Johannes M. (johnny-m)


Lesenswert?

Sascha M. wrote:
> Ach ja, meine Tastendruckinterrupts funktionieren jetzt auch nicht mehr?
> Die gingen aber vorher mit SIGNAL schon mal!?
Sicher? Du weißt auch, dass Du bei Deinen Einstellungen die Taster 
mindestens 2 Sekunden gedrückt halten musst, bis was passiert?

von Sascha M. (saschx)


Lesenswert?

Hast recht, meine Taster hab ich jetzt wieder hin bekommen.
Das starten meines Timers funktioniert aber immer noch nicht.

von fubu1000 (Gast)


Lesenswert?

Johannes wrote:
Unsinn! Das cli() und sei() wird durch die Controller-Hardware
automatisch gemacht, und SREG sichert der Compiler! Erzähle hier keinem
Anfänger, er solle am SREG rumfummeln!


Schwachsinn die HardWare setzt keine Interrupts frei !
cli und sei müssen gesetzt werden ansonsten finden keine Interrupts 
statt, zumindest bei WinAvr und ASM.
Das mit SREG stimmt bin halt eher Assembler Programmierer.

Gruss

von fubu1000 (Gast)


Lesenswert?

Ok, Johannes.
Du meintest wahrscheinlich, das die ISR nicht von einem Interrupt 
unterbrochen wird auf nem ATmega.
Das mit Schawachsinn war nicht persönlich gemeint nicht heulen.
Bin selber zu sehr in die ARM Architektur eingestiegen mit Interrupt 
Prioritäten.

Gruss

von ... .. (docean) Benutzerseite


Lesenswert?

Was stimmt denn nun?

Hier steht:
http://www.mikrocontroller.net/articles/AVR-GCC-Tutorial#Interrupts_mit_dem_AVR_GCC_Compiler_.28WinAVR.29

Man mus SREG von Hand sichern und wieder zurückschreiben, damit man 
nicht unabsichtlich die IRQs einschaltet obwohl nicht gewollt...
(OK in einer ISR muss man das nicht tun, das ist schon klar)

von Johannes M. (johnny-m)


Lesenswert?

Jan-h. B. wrote:
> (OK in einer ISR muss man das nicht tun, das ist schon klar)
Und genau um die ISRs ging es oben, weshalb die ganze Diskussion 
überflüssig ist! Normale Programmabschnitte, die nicht unterbrochen 
werden sollen, sind eine komplett andere Kiste. In kleineren Projekten 
tritt der Fall, dass man bei einer Deaktivierung der Interrupts nicht 
weiß, ob sie nicht schon vorher deaktiviert waren, auch eher selten auf.

fubu1000 wrote:
> Du meintest wahrscheinlich, das die ISR nicht von einem Interrupt
> unterbrochen wird auf nem ATmega.
Erraten! Um nichts anderes ging es hier. Hast aber lange gebraucht, um 
dahinterzukommen...

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.