Verständnisfrage zur Programmierung ATMega8

#953863
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?
Gast #953919
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.
Persönliche Seite #953927
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
#954033
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.
#954393
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
}
#954410
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.
#954443
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!
#954450
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.
Gast #955152
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
Gast #955155
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
#955187
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...

Antwort schreiben

Bitte melde dich an, um einen Beitrag zu schreiben.

oder

Mit Google-Account einloggen

Die Registrierung ist kostenlos und dauert nur eine Minute.

Jetzt registrieren