ich bin am verzweifeln, weil ich es nicht hinbekomme, bei einem ATtiny 85 einen PWM Output mit dem timer0 zu erzeugen. Ich bin seit Tagen am Recherchieren und Debuggen, komme aber nicht weiter. An den PB0/pin 5 habe ich eine LED angeschlossen und mit einem Widerstand mit Masse verbunden. Die Schaltung funktioniert. Mein Code läuft bis sei(); und resetet dann.
Hat jemand eine Idee?
Folgende Dinge habe ich bereits ausprobiert:
Mit Atmel ICE debuggen. Ich habe im allgemeinen den Eindruck, dass die Register in der IDE teils etwas verzögert aktualisiert werden bzw. in diesem Fall der ATtiny sich resetet, bevor da die finalen Werte drin stehen.
Anderen ATtiny 85 verwendet
Der ATtiny kann die angeschlossene LED steuern, z.b. CTC mode und OCR0A interrupt
mit _delay_ms() an diversen Stellen gehofft, dass irgend ein bit "zu langsam" gesetzt wird
mit while(!(register & (1 << bit)){} das setzen aller bits abgewartet
timer0 als CTC zu configurieren funktioniert problemlos (Überprüfung der Hardware und meines Verständnisses des Datenblatts;) )
Hier ein paar Infos:
Ich nutze Mplab X IDE, Atmel ICE und avr-g++ (der Code soll, wenn er tut, in einer c++ Applikation laufen)
Spannungsversorgunge über USB
die Schaltung habe ich aktuell in einem Breadboard gesteckt
der ATtiny läuft mit 8 MHz ohne clock divider
Watchdog ist deaktiviert (siehe config bits, WDTCR = 0x00; hat auch nichts gebracht)
Hier ein Minimalbeispiel. Das Setzen der LED vor dem Aktivieren der Interrupts zeigt, dass der ATtiny danach resetet. Mit dem Debugger konnte ich es auf sei(); eingrenzen.
1
#include<avr/io.h>
2
#include<util/delay.h>
3
#include<avr/interrupt.h>
4
5
intmain(void){
6
//set output pin
7
DDRB|=(1<<DDB0);//set pin output
8
PORTB|=(1<<PB0);//set pin high
9
10
//config fast PWM , Top = 0xff data sheet p 79
11
TCCR0B&=~(1<<WGM02);
12
TCCR0A|=(1<<WGM01);
13
TCCR0A|=(1<<WGM00);
14
15
//configure overflow interrupt
16
TIMSK|=(1<<TOIE0);
17
18
//prescaler = 1024
19
TCCR0B|=(1<<CS02);
20
TCCR0B&=~(1<<CS01);
21
TCCR0B|=(1<<CS00);
22
23
//toggle LED manually
24
PORTB|=(1<<PB0);
25
_delay_ms(500);
26
PORTB&=~(1<<PB0);
27
_delay_ms(1000);
28
29
sei();//muc resets here ???
30
OCR0A=20;
31
_delay_ms(100);
32
OCR0A=120;
33
_delay_ms(100);
34
OCR0A=255;
35
_delay_ms(100);
36
return0;
37
}
Wo ich hier schon eure Aufmerksamkeit habe: Aus dem Datenblatt geht für mich nicht eindeutig hervor, welche Interrupts ich in TIMSK setzen muss. In anderen Codes habe ich immer nur den overflow interrupt gesehen. Wieso muss ich den output compare match interrupt nicht setzen? Für mich wäre entweder logisch, dass ich beide setzen muss oder dass das in Hardware umgesetzt wird und ich keinen setzen muss.
Vielen Dank für eure Unterstützung!
Grüße
Johannes
Mein Code läuft bis sei(); und
resetet dann.
Hat jemand eine Idee?
Das passiert üblicherweise, wenn man einen Interrupt freigibt, aber keinen Handler dafür im Code hat. Also entweder Handler schreiben oder den Interrupt halt nicht freigeben.
Für die PWM-Ausgabe ist der auch nicht nötig! Die passiert rein in Hardware. Allerdings nur an genau dem/den Pin(s), die in Hardware mit dem Timer verbunden sind und auch nur dann, wenn die entsprechende Verbindung freigeschaltet ist.
Für mich
wäre entweder logisch, dass ich beide setzen muss oder dass das in
Hardware umgesetzt wird und ich keinen setzen muss.
Wie schon gesagt: Grundsätzlich braucht man erstmal überhaupt keinen Interrupt. Sie können aber nützlich sein in folgenden Fällen:
Man will die PWM an einem anderen Pin ausgeben, als durch die reine Hardware möglich. Dann braucht man beide Interrupts.
Man will irgendeine Datenquelle mit der PWM-Ausgabe synchronisieren. Dann braucht man nur den Overflow-Interrupt.
vielen Dank für die super schnellen Antworten. Das sei(); zum Reset führt, wenn kein ISR implementiert ist, werd ich mir merken. Das war schon mal interessant. Dass ich den Interrupt garnicht setzen muss beruhigt mich, weil das hat so eigentlich keinen Sinn gemacht. Jetzt läuft der Code auch über sei(); hinaus, aber die LED lässt sich nicht dimmen. Sie blinkt einmal auf (Zeile 24) und geht dann aus. Der ATtiny geht aber nicht in Reset.
Mit _delay_ms() hab ich schon relativ schlechte Erfahrungen gesammelt;) Ich wollt die Funktion hier nur verwenden, um das Codebeispiel klein zu halten und keinen Timer zu implementieren. _delay_ms() beschäfftigt soweit ich weiß den CPU. Hardware PWM müsste aber ja trotzdem parallel weiterlaufen. Ist das richtig?
Alternative Ansätze wie
1
OCR0A=20;
2
uint32_ti=1;
3
while(i){
4
i++;
5
}
6
OCR0A=120;
oder
1
while(1){
2
OCR0A=20;
3
}
führen aber auch nicht dazu, dass die LED überhaupt leuchtet, geschweige denn gedimmt ist.
Habe zu _delay_ms() nur den hinweise gefunden, dass die Optimierung des Compilers auf 1 gesetzt sein muss, was sie tut. _delay_ms() funktioniert auch hervorragend beim manuellen Schalten der LED...
Ja. Die läuft auch weiter, wenn du nach der (korrekten!) Initialisierung
des Timers einfach in eine Endlosschleife gehst.
Das mit der korrekte Initialisierung ist wohl der springende Punkt... Gegenüber dem Listing oben habe ich lediglich Zeile 16 TIMSK |= (1 << TOIE0); auskommentiert. Aber irgendwas scheint ja immer noch falsch zu sein:(
Das mit der korrekte Initialisierung ist wohl der springende Punkt...
Gegenüber dem Listing oben habe ich lediglich Zeile 16 TIMSK |= (1 <<
TOIE0); auskommentiert. Aber irgendwas scheint ja immer noch falsch zu
sein:(
Da fehlt schlicht was. Ich zitiere mich mal selber:
Die passiert rein in Hardware. Allerdings nur an genau dem/den
Pin(s), die in Hardware mit dem Timer verbunden sind und auch
nur dann, wenn die entsprechende Verbindung freigeschaltet ist.
HeyDas sei(); zum Reset
führt, wenn kein ISR implementiert ist, werd ich mir merken.
Der linker setzt alle unbenutzten ISR-Vectoren auf eine default-ISR, in der dann ein Rücksprung auf Adresse 0 stattfindet. Da ist also kein echter reset, das Programm läuft nur von ganz vorne wieder los.
Die passiert rein in Hardware. Allerdings nur an genau dem/den
Pin(s), die in Hardware mit dem Timer verbunden sind und auch
nur dann, wenn die entsprechende Verbindung freigeschaltet ist.
Beachte insbesondere den letzten Halbsatz.
Laut Datenblatt ist das PB0:
"PortB, Bit 0
OC0A: Output Compare Match output. The PB0 pin can serve as an external output for the Timer/Counter0 Compare Match A when configured as output (DDB0 set (one)). The OCA0 pin is also the output pin for the PWM mode timer function."
Bei Timer steht dann:
"The actual OC0x value will only be visible on the port pin if the data direction for the port pin ist set as output:"
In meinem Code ist das Zeile 7: DDRB |= (1 << DDB0);. Das meinst du vermutlich mit "Verbindung freigeschaltet"?
Meine LED ist an PB0 angeschlossen. Und weil mir bei sowas schon oft Fehler unterlaufen sind habe ich an PB1 auch eine LED angeschlossen, PB1 auch als output gesetzt und in OCR0B auch etwas eingetragen. Keine der beiden LEDs leuchtet:(
@Oliver: Das erklärt, dass beim Debuggen der grüne Pfeil in der IDE sofort auf Zeile 5 int main(void) gesprungen ist. Normalerweise dauert ein Reset etwas länger.
In meinem Code ist das Zeile 7: DDRB |= (1 << DDB0);. Das meinst du
vermutlich mit "Verbindung freigeschaltet"?
Nein. Da du offensichtlich nicht in der Lage bist, das selbst aus dem DB zu extrahieren: es geht (in deinem Fall mit PB0) um die COM0Ax-Bits. Die musst du passend setzen für deinen TimerModus und deinen Anwendungsfall.
Details, wie genau, entnimmst du aber bitte dann doch selber dem DB. Ich bin kein DB-Vorleser.
Danke für den Hinweis. Die sind beim hin und her kopieren leider irgendwann unter den Tisch gefallen. Über die bits bin ich im Datenblatt schon gesolpert. Aber klar, wenn ich das Thema mit der ISR und dem TIMSK-Register mit dem Datenblatt verstanden hätte, hätte ich nicht hier im Forum nachgefragt;)
Danke für die Unterstützung. Jetzt kann ich mich wieder spannenderen Themen widmen, als den Bugs in den paar Zeilen Code.
ich hab gelernt, dass int main(void) den Rückgabewert int hat und dass ich deswegen in die letzte Zeile return 0; schreibe. Dass das nichts bringt, ist mir vollkommen klar.
Wieso brauch ich zwangsweise eine Endlosschleife? Ich schreibe gerade Bibliotheken, damit ich mich nicht jedes mal durch das komplette Datenblatt wälzen muss, wen ich den timer konfigurieren möchte. Der Code oben ist Teil einer Testprozedur, die genau einmal durchlaufen wird. Dann brauche ich auch keine Endlosschleife.
Bist du dir sicher, dass du mir schonmal irgendwas in diesem Forum erklärt hast? Ich hab mir für den Post hier einen neuen Useraccount anlegen müssen... Einzige Möglichkeit wäre, dass ich jetzt zwei Accounts habe;)
Dass man Register auf einmal setzten kann, weiß ich. Danke für den Tipp, ich finde allerdings die von mir gewählte Schreibweise übersichtlicher. Auch wenn das wahrscheinlich dazu führt, dass ich im Assembler-Code dann drei einzelne Anweisungen habe. Ich setze ja sogar Werte auf 0, die per default als 0 initialisiert sind.