mikrocontroller.net

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


Autor: Sascha M. (saschx)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Habe hier mal Code Segmente die ich nicht genau verstehe!
SIGNAL (SIG_OUTPUT_COMPARE1A)
{
  cli();              // Interrupts nicht zulassen
  TCNT1H=0;            // Timerstand zurücksetzen
  TCNT1L=0;
  
  Tue etwas;
}

int main(void)
{

  

  OCR1AH = 0x1E;            
  OCR1AL = 0x83;            //  !!!! verstehe diese Zuweisungen nicht
  
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?

Autor: crazy horse (Gast)
Datum:

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

Autor: fubu1000 (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: Stefan B. (stefan) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht 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-Tutori...

Autor: fubu1000 (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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

Autor: crazy horse (Gast)
Datum:

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

Autor: Johannes M. (johnny-m)
Datum:

Bewertung
0 lesenswert
nicht 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:
OCR1A = 0x1E83;
//oder identisch:
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.

Autor: Sascha M. (saschx)
Datum:

Bewertung
0 lesenswert
nicht 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?
#include <inttypes.h>    // uint8_t usw.
#include <avr/io.h>
#include <avr/interrupt.h>  // Interrupts
#include <avr/eeprom.h>    // EEPROM Zugriffe

#define EEPROM  __attribute__ ((section (".eeprom")))        // für EEPROM-Zugriffe

#ifndef F_CPU
#warning "F_CPU war noch nicht definiert, wird nun mit 1000000 definiert"
#define F_CPU 1000000UL     
#endif
#include <util/delay.h>



//  Welcher Port an welchem Segement:
#define segm_a PD0
#define segm_b PD1
#define segm_c PD2
#define segm_d PD3
#define segm_e PD4
#define segm_f PD5
#define segm_g PD6
#define segm_p PD7

// Welcher Port an welcher Digit:
#define digit1 PB0    // Einer
#define digit2 PB1    // Zehner
#define digit3 PB2    // Hunderter

// An welchem Port sind Taster
#define btn_up    PC3
#define btn_down  PC4
#define btn_start  PC5

// Ports definieren
#define port_segment  PORTD
#define port_digit    PORTB
#define port_keys    PORTC
#define dir_segment    DDRD
#define dir_digit    DDRB
#define dir_keys    DDRC
#define pin_keys    PINC

// Definiert, welche Segmente für die einzelnen Zahlenwerte aufleuchten sollen. LOW-Aktiv
#define sign0 (1 << segm_g) | (1 << segm_p)
#define sign1 (1 << segm_a) | (1 << segm_d) | (1 << segm_e) | (1 << segm_f) | (1 << segm_g)  | (1 << segm_p)
#define sign2 (1 << segm_c) | (1 << segm_f) | (1 << segm_p) 
#define sign3 (1 << segm_e) | (1 << segm_f) | (1 << segm_p) 
#define sign4 (1 << segm_a) | (1 << segm_d) | (1 << segm_e) | (1 << segm_p)  
#define sign5 (1 << segm_b) | (1 << segm_e) | (1 << segm_p)  
#define sign6 (1 << segm_b) | (1 << segm_p) 
#define sign7 (1 << segm_d) | (1 << segm_e) | (1 << segm_f) | (1 << segm_g) | (1 << segm_p)
#define sign8 (1 << segm_p) 
#define sign9 (1 << segm_e) | (1 << segm_p) 

uint8_t num[] = { sign0, sign1, sign2, sign3, sign4, sign5, sign6, sign7, sign8, sign9 };
uint16_t startwert EEPROM;          // Speicherplatz für letzten Startwert im EEPROM
volatile uint16_t   display_value=0;    // Startwert. 
volatile uint8_t    run=0;          // =0 Counter Stop; =1 Counter läuft
volatile uint8_t    key_up=0,        // Puffer für Tastendrücke
          key_down=0,
          key_start=0;


void ausgabe (uint16_t zahl)
{
  uint16_t output = zahl;
  uint16_t warten = 50;    // Enable Zeit für einzelnes Segment. Zeit in der LED leuchten
  
  
  // Hunderter-Dezimalstelle
  if (zahl >= 100)
  {
    port_segment = num[output/100];        // Zahlenwert darstellen 
    port_digit = (1 << digit2) | (1 << digit1);  // 3. Digit Ein
    _delay_ms(warten);              // LEDs müssen gewisse Zeit leuchten
    port_digit = (1 << digit3) | (1 << digit2) | (1 << digit1);              // 3. Digit HIGH
  }
   output = output % 100;
  
  
  // Zehner-Dezimalstelle
  if (zahl >= 10)
  {
    port_segment = num[output/10];    // Zahlenwert darstellen 
    port_digit = (1 << digit3) | (1 << digit1);  // 2. Digit Ein
    _delay_ms(warten);              // LEDs müssen gewisse Zeit leuchten
    port_digit = (1 << digit3) | (1 << digit2) | (1 << digit1);                // 2. Digit HIGH
  }
   output = output % 10;
  
  // einer Dezimalstelle 
  port_segment = num[output];      // Zahlenwert darstellen 
  port_digit = (1 << digit3) | (1 << digit2);    // 1. Digit Ein
  _delay_ms(warten);                // LEDs müssen gewisse Zeit leuchten
  port_digit = (1 << digit3) | (1 << digit2) | (1 << digit1);                // 1. Digit HIGH

  sei();              // Interrupts zulassen
}


ISR (TIMER0_OVF_vect)
{
  cli();            // Interrupts nicht zulassen
  ausgabe (display_value);  // Zahl darstellen
}



ISR (TIMER2_OVF_vect)
{
  cli();    // Interrupts nicht zulassen
  
  if (!(pin_keys & (1 << btn_up)))    // Up-Taste gedrückt
    key_up++;
    
  if (!(pin_keys & (1 << btn_down)))    // Down-Taste gedrückt
    key_down++;
    
  if (!(pin_keys & (1 << btn_start)))    // Start-Taste gedrückt
    key_start++;
  
  sei();    // Interrupts zulassen
}


ISR (TIMER1_COMPA_vect)
{
  cli();              // Interrupts nicht zulassen
  TCNT1H=0;            // Timerstand zurücksetzen
  TCNT1L=0;
  
  //display_value -= run;
  display_value--;
}

int main(void)
{

  dir_digit = (1 << digit1) | (1 << digit2) | (1 << digit3);      // Ausgänge, für Digits
  dir_segment = 0xFF;                          // alles Ausgänge, für Segmente 
  dir_keys &= ~(1 << btn_up) & ~(1 << btn_down) & ~(1 << btn_start);// Eingänge für Tasten
  port_keys |= (1 << PC3) | (1 << PC4) | (1 << PC5);
  
  // Timer für Displayrefresh
  TCCR0 |= (1<<CS01) | (1<<CS00);    //8-Bit Timer, Timer clock = system clock/64
  TIFR |= (1<<TOV0);           //Clear TOV0 Timer/Counter Overflow Flag. clear pending interrupts
  TIMSK |= (1<<TOIE0);         //Enable Timer0 Overflow Interrupt

  // Timer für Tastenabfrage
  TCCR2 |= (1<<CS02);          //8-Bit Timer, Timer clock = system clock/256
  TIFR |= (1<<TOV2);           //Clear TOV2 Timer/Counter Overflow Flag. clear pending interrupts
  TIMSK |= (1<<TOIE2);         //Enable Timer2 Overflow Interrupt
  
  // Timer für Sekundentakt
  OCR1A = 977;            // 16Bit Timer Takt= 1MHz => 1.000.000/1024 = 976,56 
                    // Vergleichswert laden: 1 Sekunde
  TIMSK |= (1<<OCIE1A);         // Interrupt wenn Timer Vergleichswert erreicht

  sei();                // Interrupts zulassen

  while (1)
  {
    if (key_up >= 30)        // Up-Taste gedrückt
    {
      display_value++;      // Wert +1
      if (display_value > 999)  // Wenn Überlauf => Wert=0
        display_value = 1;
      key_up = 0;          // Tastencounter leeren
    }
    if (key_down >= 30)        // Down-Taste gedrückt
    {
      if (display_value <= 1)    // Wenn Unterlauf => Wert=999
        display_value = 1000;
      display_value--;      // Wert -1
      key_down = 0;        // Tastencounter leeren
    }

    if (key_start >= 30)      // Start-Taste gedrückt
    {
      key_start = 0;        // Tastencounter leeren
      if (display_value == 0)    // wenn angezeigter Wert = 0 und Start gedrückt => ermittler zuletzt verwendeten Startwert und setze diesen
      {
        display_value = eeprom_read_word(&startwert);
      }
      else
      {
        run ^= 1;          // Wert XOR 1;  => Start/Stop
        if (run)          // Wenn Counter gestartet wird
        {
          if (display_value != eeprom_read_word(&startwert))    // wenn Start gedrückt und aktueller Startwert != letzter Startwert
            eeprom_write_word(&startwert, display_value);    // dann speichere neuen Startwert
        
          TCNT1H=0;            // Timerstand zurücksetzen, damit ab jetzt eine Sekunde zählt
          TCNT1L=0;
          TCCR1A = (1<<CS10)|(1<<CS12);  // Timer mit Div 1024 starten
          
        }
        else            // Counter angehalten
        {
          TCCR1A = 0;        // Timer Stop
          
        }
      }
    }

    if (display_value == 0)      // Timer abgelaufen
    {
      run = 0;          // Stop
    }
  }
}


Autor: Johannes M. (johnny-m)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: Johannes M. (johnny-m)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: Sascha M. (saschx)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: Sascha M. (saschx)
Datum:

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

Autor: Johannes M. (johnny-m)
Datum:

Bewertung
0 lesenswert
nicht 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!

Autor: Johannes M. (johnny-m)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Übrigens, was soll denn das da:
#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.

Autor: Johannes M. (johnny-m)
Datum:

Bewertung
0 lesenswert
nicht 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?

Autor: Sascha M. (saschx)
Datum:

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

Autor: fubu1000 (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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

Autor: fubu1000 (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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

Autor: ... ... (docean) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Was stimmt denn nun?

Hier steht:
http://www.mikrocontroller.net/articles/AVR-GCC-Tu...

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)

Autor: Johannes M. (johnny-m)
Datum:

Bewertung
0 lesenswert
nicht 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

Die Angabe einer E-Mail-Adresse ist freiwillig. Wenn Sie automatisch per E-Mail über Antworten auf Ihren Beitrag informiert werden möchten, melden Sie sich bitte an.

Wichtige Regeln - erst lesen, dann posten!

  • Groß- und Kleinschreibung verwenden
  • Längeren Sourcecode nicht im Text einfügen, sondern als Dateianhang

Formatierung (mehr Informationen...)

  • [c]C-Code[/c]
  • [avrasm]AVR-Assembler-Code[/avrasm]
  • [code]Code in anderen Sprachen, ASCII-Zeichnungen[/code]
  • [math]Formel in LaTeX-Syntax[/math]
  • [[Titel]] - Link zu Artikel
  • Verweis auf anderen Beitrag einfügen: Rechtsklick auf Beitragstitel,
    "Adresse kopieren", und in den Text einfügen




Bild automatisch verkleinern, falls nötig
Bitte das JPG-Format nur für Fotos und Scans verwenden!
Zeichnungen und Screenshots im PNG- oder
GIF-Format hochladen. Siehe Bildformate.
Hinweis: der ursprüngliche Beitrag ist mehr als 6 Monate alt.
Bitte hier nur auf die ursprüngliche Frage antworten,
für neue Fragen einen neuen Beitrag erstellen.

Mit dem Abschicken bestätigst du, die Nutzungsbedingungen anzuerkennen.