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.
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:
1
ISR(TIMER0_OVF_vect){
2
deinCode;
3
}
Zu deinem Code:
1
main()// Hauptprogramm, startet bei Power ON und Reset
2
{
3
TCCR1A=(1<<CS00)|(1<<CS00);// Timer 0: Clock/1024
4
TIMSK=(1<<TOIE0)// Timer 0: Interrupt bei Überlauf
5
while(1)
6
{
7
8
}
9
}
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 :)
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!
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.
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;
}
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.
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.
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.
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.
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.
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. :)
"> 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.
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-Tutorial#16-Bit_Register_.28ADC.2C_ICR1.2C_OCR1.2C_TCNT1.2C_UBRR.29
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:
1
PORTB^=1;
oder der besseren Lesbarkeit halber
1
PORTB^=PB1;
BTW: Auch Du solltest die Formatierungsmöglichkeit für C-Code hier im
Forum nutzen!
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.
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?
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...
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.
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!
Ü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.
...