www.mikrocontroller.net

Forum: Mikrocontroller und Digitale Elektronik Anfängerfrage zum Interrupt


Autor: Thomas D (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hallo,

bin µC Neuling. Ich möchte, dass beim Überlauf des Timer 0 etwas 
bestimmtes passiert. Hier der riesengroße bisherige Code:

main ()            // Hauptprogramm, startet bei Power ON und Reset
{
  TCCR1A=(1<<CS00)|(1<<CS00);    // Timer 0: Clock/1024
  TIMSK=(1<<TOIE0)      // Timer 0: Interrupt bei Überlauf
  while(1)
  {

  }
}

Im GCC Tutorial steht zu TOIE0: "Das Global Enable Interrupt Flag muss 
selbstverständlich auch gesetzt sein. "


Ist der gesetzt? Welches Register ist das?



Und die mainfrage: Wie schreibe ich jetzt eine Funktion was beim 
Überlauf passieren soll?

Vielen Dank.

Autor: Frank Link (franklink)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hallo Thomas,

schau mal hier rein, da wird Dir geholfen.

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

Gruß
Frank

Autor: Robert K. (molch) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hi,

da das nicht direkt in deinem Post steht treffe ich folgende Annahmen:
- du verwendest den WinAVR Comiler
- du nutzt einen Atmel AVR µC

Das globale Interrupt Flag I befindet sich im Statusregister (SREG).
Das kannst du setzen mit sei() (alle Int. erlauben) und löschen mit 
cli() (alle Int. sperren).

Die Interruptfunktion dann wie folgt:
ISR(TIMER0_OVF_vect){
  dein Code;
}

Zu deinem Code:
main ()            // Hauptprogramm, startet bei Power ON und Reset
{
  TCCR1A=(1<<CS00)|(1<<CS00);    // Timer 0: Clock/1024
  TIMSK=(1<<TOIE0)      // Timer 0: Interrupt bei Überlauf
  while(1)
  {

  }
}

Du beschreibst wenn ich das richtig sehe das Controlregister A für Timer 
1 willst aber den Timer 0 verwenden. Solltest also TCCR0 verwenden.
Und wenn du den Timer 1 verwenden willst stehen die Clock Select Bits 
(CS02 ..CS00) nicht im TCCR1A sondern im TCCR1B (zumindest beim 
ATmega8).
Dazu kommt das du zweimal das selbe CS bit setzt, ansich nicht schlimm 
nur dein Vorteiler ist dann nicht so eingestellt wie du möglicherweise 
vorhattest.

Gruß Robert


@Frank: Ich glaube da war er schon, findet nur vor lauter Informationen 
nicht das richtige :)

Autor: ... (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
> Ich glaube da war er schon ...

Ich glaube nicht, die Infos stehen da ab Kapitel 18 und speziell ab 18.5

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

Autor: Thomas D (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hallo,

mann da hab ich ja einiges übersehen.


Vielen Dank an euch!

Autor: Thomas D (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Ich steuere mit dem ATMega8 eine 4-fach 7 Segmentanzeige an die 
multigeplext wird.

Die globale Variable "number" wird auf der Anzeige ausgegeben, das 
funktioniert soweit.


Jetzt möchte ich noch den 16 Bit Timer dazunehmen. Er soll number jede 
Sekunde um 1 erhöhen.


Komischerweise kennt der Compiler OCR1H und OCR1L nicht. Fehlermeldung:

main.c:69: error: 'OCR1H' undeclared (first use in this function)
main.c:69: error: (Each undeclared identifier is reported only once
main.c:69: error: for each function it appears in.)
main.c:70: error: 'OCR1L' undeclared (first use in this function)



Woran liegt das?


Hier der Code:


#include <avr/io.h>
#include <avr/interrupt.h>

//----------------------------------------------------------------------

void showDigit(unsigned int digit);

//----------------------------------------------------------------------

unsigned int number=1234;  // number wird ständig angezeit
unsigned int digitNo[4];  // Die einzelnen Ziffern von number

//----------------------------------------------------------------------

ISR(TIMER0_OVF_vect){  // Multiplexing: Ansteuern der nächsten 7 Segment 
Anzeite
  if((PINB&0b00000001)==0b00000001)
  {
    PORTD=0b01111111;    // Ausschalten der 7 Segment Anzeige
    PORTB=0b00000010;    // Aktivieren der nächsten Ziffer
    showDigit(digitNo[1]);  // Anschalten der 7 Segment Anzeige
  }
  else if((PINB&0b00000010)==0b00000010)
  {
    PORTD=0b01111111;
    PORTB=0b00000100;
    showDigit(digitNo[2]);
  }
  else if((PINB&0b00000100)==0b00000100)
  {
    PORTD=0b01111111;
    PORTB=0b00001000;
    showDigit(digitNo[3]);
  }
  else
  {
    PORTD=0b01111111;
    PORTB=0b00000001;
    showDigit(digitNo[0]);
  }
}

ISR(TIMER1_OVF_vect){  // 1 Sekunde ist vergangen
  number++;
  /* digits berechnen */
  digitNo[0]=number%10;
  digitNo[1]=(number/10)%10;
  digitNo[2]=(number/100)%10;
  digitNo[3]=(number/1000);
}


int main (void)              // Hauptprogramm, startet bei Power ON und 
Reset
{
  DDRB=0b00001111;          // Ziffern 0 bis 3
  DDRD=0b01111111;          // 7 Segmente
  /* digits berechnen */
  digitNo[0]=number%10;
  digitNo[1]=(number/10)%10;
  digitNo[2]=(number/100)%10;
  digitNo[3]=(number/1000);
  sei();                // Interrupts aktivieren
  TCCR0=(1<<CS01);          // Timer 0: Clock/8
  TIMSK=(1<<TOIE0);          // Timer 0: Interrupt bei Überlauf

  TCCR1B=(1<<CS11)|(1<<CS10)|(1<<CTC1);    // Timer 1: Clock/64, Löschen 
von TCNT1L/H bei erreichen von OCR1L/H
  OCR1H=15625/256;  // <= FEHLER!!!!!!!!!!!
  OCR1L=15625%256;        // <= FEHLER!!!!!!!!!!!

  while(1)
  {

  }
  return 0;
}
//----------------------------------------------------------------------

void showDigit(unsigned int digit)
{
  switch(digit) {
    case 0:
    {
      PORTD=0b01000000;
      break;
    }
    case 1:
    {
      PORTD=0b01111001;
      break;
    }
    case 2:
    {
      PORTD=0b00100100;
      break;
    }
    case 3:
    {
      PORTD=0b00110000;
      break;
    }
    case 4:
    {
      PORTD=0b00011001;
      break;
    }
    case 5:
    {
      PORTD=0b00010010;
      break;
    }
    case 6:
    {
      PORTD=0b00000010;
      break;
    }
    case 7:
    {
      PORTD=0b01111000;
      break;
    }
    case 8:
    {
      PORTD=0b00000000;
      break;
    }
    case 9:
    {
      PORTD=0b00010000;
      break;
    }
  }
}



Schonmal vielen dank für Hilfe!

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

Bewertung
0 lesenswert
nicht lesenswert
Klar kennt der Compiler die nicht. Schau bitte ins Datenblatt. Da steht, 
wie die Register heißen!

Außerdem kann man so langen Code besser als Anhang posten.

Autor: Thomas D (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Alles klar.

Vielen Dank!

Autor: Thomas D (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Leider will der 16 Bit Timer immernoch nicht.


Hier ist der Quellcode zum testen des Timers. Jede Sekunde soll B0 von 0 
auf 1 bzw 1 auf 0 spring.


#include <avr/io.h>
#include <avr/interrupt.h>


ISR(TIMER1_COMPA_vect){  // 1 Sekunde ist vergangen
  TCNT1L=0;
  TCNT1H=0;
  if((PINB&0)==0) PORTB=0b00000001;
  else PORTB=0b00000000;

}


int main (void)              // Hauptprogramm, startet bei Power ON und 
Reset
{
  DDRB=0b00001111;          // Ziffern 0 bis 3
  DDRD=0b01111111;          // 7 Segmente
  PORTD=0b00001111;

  TCCR1B=(1<<CS11)|(1<<CS10);    // Timer 1: Clock/64
  OCR1AH=15625/256;          // OCR1A = 15625
  OCR1AL=15625%256;

  TIFR=(1<<OCF1A);  // Output Compare Flag Timer1 A aktiviert

  while(1)
  {

  }
  return 0;
}

Autor: Justus Skorps (jussa)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
sei fehlt und
TIFR=(1<<OCF1A);

ist falsch

Autor: Thomas D (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Stimmt. Funktioniert aber leider trotzdem noch nicht.

Ein kleiner Fehler im Interrupt Aufruf:

if((PINB&0)==1) PORTB=0b00000001; muss es heißen.


Hat damit aber nichts zu tun da er da nochnichtmal reingesprungen ist.

Autor: Thomas D (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Bis eben sah ich nur "sei fehlt"


Ich check mal ob ichs check...

Autor: Justus Skorps (jussa)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Thomas D wrote:

>
> Ich check mal ob ichs check...

ein Blick ins Datenblatt oder ins Tut würde helfen...

Autor: Thomas D (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Habs hinbekommen!

Vielen, vielen Dank!!! ;)

Autor: Thomas D (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
So, nächstes Problem: Beide Timer laufen lassen. Timer 1 soll beim 
overflow resetten und Timer1 nach 1 Sekunde zu number einen hinzufügen.


Die main sieht so aus:

int main (void)              // Hauptprogramm, startet bei Power ON und 
Reset
{
  DDRB=0b00001111;          // Ziffern 0 bis 3
  DDRD=0b01111111;          // 7 Segmente
  /* digits berechnen */
  digitNo[0]=number%10;
  digitNo[1]=(number/10)%10;
  digitNo[2]=(number/100)%10;
  digitNo[3]=(number/1000);
  sei();                // Interrupts aktivieren
  TCCR0=(1<<CS01);          // Timer 0: Clock/8
  TIMSK=(1<<TOIE0);          // Timer 0: Interrupt bei Überlauf

  TCCR1B=(1<<CS11)|(1<<CS10);    // Timer 1: Clock/64, Löschen von 
TCNT1L/H bei erreichen von OCR1L/H
  OCR1AH=15625/256;
  OCR1AL=15625%256;

  TIMSK=(1<<OCIE1A);

  while(1)
  {

  }
  return 0;
}



Es leuchtet nichts. Hab das Programm leicht umgeschrieben, sodass jede 
Sekunde etwas leuchten soll und es geht. In den TIMER1_COMPA_vect 
Interrupt spring er also rein. Das gleiche mit dem TIMER0_OVF_vect, da 
springt er nicht rein.

Sobald ich die Zeile TIMSK=(1<<OCIE1A); rausnehme springt er auch in den 
TIMER0_OVF_vect.


Wie kann das sein? Der Interrupt 1 funktioniert immer, Interrupt 0 nur 
wenn TIMSK=(1<<OCIE1A); nicht dabei ist.

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

Bewertung
0 lesenswert
nicht lesenswert
In Deinem letzten Code (von 16:52) ist noch die Zugriffsreihenfolge bei 
TCNT1H/L falsch! Bitte lies im Tutorial den Abschnitt über 
16-Bit-Register. Dein C-Compiler kann Dir die Arbeit der Einzelzugriffe 
nämlich abnehmen (das gilt auch für OCR1AH/L). Die Reihenfolge ist bei 
den Registern essentiell wichtig!

Und einen Timer in einem Compare-Interrupt Handler zurückzusetzen ist 
unsinnig. Dafür gibt es die CTC-Betriebsart. Auch dazu gibt es im 
Tutorial bei der Timer-Beschreibung einen Abschnitt.

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

Bewertung
0 lesenswert
nicht lesenswert
Thomas D wrote:
> Wie kann das sein? Der Interrupt 1 funktioniert immer, Interrupt 0 nur
> wenn TIMSK=(1<<OCIE1A); nicht dabei ist.
Ganz einfach: Weil Du beim Aktivieren des Compare-Interrupt den anderen 
wieder löschst...

Der Artikel zum Thema Bitmanipulation ist in dem Zusammenhang auch 
empfehlenswert.

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

Bewertung
0 lesenswert
nicht lesenswert
Thomas D wrote:
> if((PINB&0)==1) PORTB=0b00000001; muss es heißen.
BTW: Die Bedingung wird nie wahr! Eine VerUNDung mit Null liefert 
immer Null (false) zurück.

Autor: Thomas D (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Jop, es funktioniert jetzt endlich! ;)


Vielen Dank!

Hab den CTC angemacht und aus dem TIMSK=(1<<OCIE1A); ein 
TIMSK|=(1<<OCIE1A); gemacht.


Über die Reihenfolge der Register habe ich so schnell nichts gefunden 
aber es funktioniert auch mit der alten Reihenfolge.

Vielen Dank nochmal. :)

Autor: Thomas D (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
"> if((PINB&0)==1) PORTB=0b00000001; muss es heißen.
BTW: Die Bedingung wird nie wahr! Eine VerUNDung mit Null liefert
immer Null (false) zurück."


Stimmt. if((PINB&1)==1) PORTB=0b00000000; else PORTB=0b00000001; sollte 
es heißen.

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

Bewertung
0 lesenswert
nicht lesenswert
Thomas D wrote:
> Über die Reihenfolge der Register habe ich so schnell nichts gefunden
> aber es funktioniert auch mit der alten Reihenfolge.
http://www.mikrocontroller.net/articles/AVR-GCC-Tu...

Dass es mit der "alten" Reihenfolge in diesem Fall vielleicht klappt, 
ist purer Zufall!

> Stimmt. if((PINB&1)==1) PORTB=0b00000000; else PORTB=0b00000001; sollte
> es heißen.
Auch das ist so nicht richtig. Wenn Du den Portpin umschalten willst, 
dann solltest Du nicht PINB einlesen, sondern PORTB. PINB nur dann, 
wenn der Pin als Eingang konfiguriert ist und der von außen 
vorgegebene Zustand eingelesen werden soll.

Außerdem lässt sich der ganze if-else-Kram da oben als eine einzige 
Anweisung schreiben:
PORTB ^= 1;
oder der besseren Lesbarkeit halber
PORTB ^= PB1;

BTW: Auch Du solltest die Formatierungsmöglichkeit für C-Code hier im 
Forum nutzen!

Autor: Thomas D (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Alles klar. Danke für die Hilfe.

Lesen: Erst Low, dann High
Schreiben: High, dann Low

PIND==... einlesen der Eingänge
PORTD==... einlesen der Ausgänge
PORTD=... schreiben der Ausgänge / (de)aktivieren der Pullup Widerstände 
der Eingänge
PIND=... gibts nicht?


... trotzdem hatte es ja komischerweise funktioniert.

Frage: ^= ist die invertierung der Bits? Dann würde das hier ja nicht 
ganz stimmen: PORTB ^= 1; Ne, kann nicht sein. Was ist ^= ? Sehe es zum 
ersten mal.

Autor: Thomas D (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Test zum Code schreiben:

[CODE]int main(void) {
return 0;
}
[\CODE]

Autor: Thomas D (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Ach, da stehts ja...

[c]int main(void) {
return 0;
}
[\c]

Autor: Thomas D (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Zum 3ten und diesmal mit Vorschau.
int main(void) {
return 0;
}

Okay danke.

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

Bewertung
0 lesenswert
nicht lesenswert
Thomas D wrote:
> PIND=... gibts nicht?
Bei neueren AVRs (z.B. ATMega48/88/168) gibt es auch das. Das toggelt 
dann den Pin.

Autor: Thomas D (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Toggeln heißt invertieren...? Wenn ich dich richtig verstehe invertiert 
es die Ausgangspins?

Also

DDRD=0b00001111;
PORTD=0b00001111;
PIND=0b00001111; <- setzt in diesem Fall D0...D3 auf 0?

Autor: Hannes Lux (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Schau doch einfach mal ins Datenblatt, da sind die I/O-Register sehr 
genau beschrieben.

...

Autor: Thomas D (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Jo, habs gefunden. PINx sind auch bei ATmega48/88/168 nur Leseregister.

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

Bewertung
0 lesenswert
nicht lesenswert
Thomas D wrote:
> PINx sind auch bei ATmega48/88/168 nur Leseregister.
Hatte mich vertan. Die Mega48/... waren mit die letzten AVRs, die das 
noch nicht hatten...

Autor: Thomas D (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
So die nächste Aufgabe ist den Reset auszuschalten, sodass ich den PC6 
benutzen kann.


Im Datenblatthabe ich gefunden: "If the RSTDISBL Fuse is programmed, PC6 
is used as an I/O pin."

Nur leider weiß ich nicht in welchem Register ich den RSTDISBL 
programmieren kann.

Auf Seite 62 Tabelle 26 findet sich dann: "Overriding Signals for 
Alternate Functions in PC6..PC4"

Unter der Spalte Reset gibt es die Möglichkeit "RSTDISBL", "0" oder "1" 
einzustellen. Da RSTDISBL invertierte Logik ist würde ich sagen muss da 
eine "0" rein. Unter PC4 und PC5 muss es denke ich auch auf "0" somit 
ist "Signal Name" = "PVOV"


Jetzt weiß ich leider nicht wie ich einprogrammiere, dass diese 
Einstellung übernommen werden soll.

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

Bewertung
0 lesenswert
nicht lesenswert
Thomas D wrote:
> Nur leider weiß ich nicht in welchem Register ich den RSTDISBL
> programmieren kann.
RSTDISBL ist ein Fusebit und das muss beim Programmieren des Controllers 
separat gesetzt werden. Das geht nicht durch das Anwenderprogramm. Und 
wenn der Reset abgeschaltet ist, ist der AVR nicht mehr per ISP 
programmierbar, nur noch über HV-Programmierung mit einem speziellen 
Programmer (z.B. STK500). Siehe AVR Fuses!

Autor: Thomas D (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Okay. Vielen Dank!

Autor: Hannes Lux (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Übrigens wärst Du nicht der Erste, der sich durch unüberlegtes Spielen 
an den Fuses ausgesperrt hat, bevor er das erste sinnvolle Programm für 
(und in) seinen AVR geschrieben hat. Dieses Forum ist voller solcher 
Hilferufe...

Wenn Du den AVR (und seine Architektur) wirklich kennen lernen und 
verstehen willst, dann mache Deine ersten Schritte in Assembler, denn 
das ist real, da gilt (nur) das Datenblatt und der Instruktionssatz. 
Wenn Du das halbwegs verstanden hast, fällt es Dir in einer Hochsprache 
bedeutend leichter.

...

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.