Hallo an alle!
Ich besitze ein ATmega64A Board (Takt auf internen 8MHz RC-Oszillator
gestellt), an dem ich ein paar Bus-Systeme auspobieren möchte. Gestern
habe ich es das erste Mal in Betrieb genommen und jetzt ist ein äußerst
merkwürdiger Fehler aufgetreten, vlt. findet ja eine von euch den
Fehler...
Ich habe folgenden code programmiert, der einen Timer-Interrupt auslöst
(sicherheitshalber sind nicht verwendete Bits auf 0 gestetzt) und
zugleich blinkt eine LED im 1s-Takt in der While(1)-Schleife:
1
#define F_CPU 8000000UL // define system clock with 8MHz
2
3
#include<avr/io.h>
4
#include<avr/interrupt.h>
5
#include<util/delay.h>
6
7
8
intmain(void)
9
{
10
TCNT0=0;
11
TCCR0&=(~(1<<COM00))&(~(1<<COM01));// normal operation mode, OC0 not connected to a pin
12
TCCR0|=(1<<WGM01);// clear on compare matching
13
TCCR0&=(~(1<<WGM00));// with OCR0
14
TCCR0|=(1<<CS00);// set prescaler
15
TCCR0&=(~(1<<CS01))&(~(1<<CS02));// to 1 -> 12MHz
16
17
OCR0=200;// set compare value for timer0 to 200 (25µs)
18
19
TIMSK|=(1<<OCIE0);// enable interrupt for timer0
20
21
DDRA|=(1<<PA0);
22
DDRD|=(1<<PD1);
23
24
sei();
25
26
while(1)
27
{
28
PORTA^=(1<<PA0);
29
_delay_ms(500);
30
}
31
32
return0;
33
}
34
35
36
ISR(TIMER0_COMP_vect)
37
{
38
PORTD^=(1<<PD1);
39
}
Mit diesem Code bekomme ich am Pin PD1 mit dem Oszi wie erwartet ein
20kHz Rechteck-Signal. Jedoch blinkt die LED am Pin PA0 nicht mehr,
sondern leuchtet dauerhaft!
Deaktiviere ich den Timer durch Auskommentieren der Zeile "CCR0 |=
(1<<CS00);" toggelt der Pin PD1 logischerweise nichtmehr, aber dafür
blinkt die LED an Port PA0.
Beides gleichzeitig scheint nicht zu funktionieren, bzw. befindet sich
das Programm wsl. dauerhaft im ISR...
Was kann dieses eigenartige Verhalten auslösen oder worin liegt der
Fehler?
Habe bereits Timer2 ausprobiert -> gleiches Ergebnis
Neues Projekt erstellt -> gleiches Ergebnis
Ich hoffe jemand kann mir helfen.
Danke und lG
Florian
.lss Datei erzeugt/angeschaut?
Darin steht, was wirklich passiert. Leider in Assembler, aber das lesen
zu können und zu verstehen, ist nie verkehrt. Schreiben muß man's ja
nicht unbedingt selber ;-)
Der Atmega läuft mit 8MHz, deine ISR wird alle 1us, also mit 1MHz Takt
aufgerufen.
Das lässt dem Atmega nur 8 Takte Zeit um in die ISR zu springen, die
Register auf dem Stack zu sichern, read-modify-write auf PORTD
auszuführen, Register vom Stack zurückholen und wieder ins Hauptprogramm
zu springen.
Der Atmega hängt einfach ununterbrochen im Interrupt weil er keine Zeit
fürs Hauptprogramm mehr hat.
Schau dir mal an wie man mit Atmega Timern eine PWM ausgibt, das
funktioniert komplett in Hardware und verbraucht keine CPU-Takte.
Musst du den Timer Interrupt(Flag) nicht löschen?
Andererseits müsste natürlich die Frequenz viel höher als 20kHz sein,
falls das Programm immer wieder in den IR-Vector springen würde.
(Sorry, kenn mich mit AVR IR-Handling nicht aus)
Vielen Dank für die schnellen Antworten!
@Carl Drexler:
Die Datei wird erzeugt, aber auf den ersten Blick erkenne ich garnix,
habe leider nur Grundahnung in Sachen ASM...
@ Christoph S:
Es war ein Fehler im Kommentar, es sind 25µs, habe es ausgebessert...
Florian C. schrieb:> TCCR0 &= (~(1<<COM00)) & (~(1<<COM01)); // normal operation mode, OC0> not connected to a pin> TCCR0 |= (1<<WGM01); // clear on compare matching> TCCR0 &= (~(1<<WGM00)); // with OCR0> TCCR0 |= (1<<CS00); // set prescaler> TCCR0 &= (~(1<<CS01)) & (~(1<<CS02)); // to 1 -> 12MHz
Und da blickst du noch durch? Was hältst du davon?:
> Der Atmega läuft mit 8MHz, deine ISR wird alle 1us, also mit 1MHz Takt> aufgerufen.
Die Interruptroutine wir 40.000 mal pro Sekunde aufgerufen.
> Der Atmega hängt einfach ununterbrochen im Interrupt weil er keine Zeit> fürs Hauptprogramm mehr hat.
Ununterbrochen nicht, da mindesten 1 Befehl nach dem RETI ausgeführt
wird.
Das Hauptprogramm dürfte trotzdem nur sehr langsam ausgeführt werden
(40.000 x Zeit in der IR ist verdammt lang).
Fazit: Das Programm ist ungeeignet für die zu lösende Aufgabe.
@Matthias S.
Ich finde es einfacher wenn ich es nach Bit-Typ unterteile, aber daran
dürfte es nicht scheitern...
@Kamest:
Ich habe auch bereits den Prescaler auf 8 gesetzt -> gleiches Ergebnis
Das Programm dient nur zum Austesten des Timer-Interrupts, muss also
keinen Sinn Ergeben, sondern dient lediglich zum Beheben des Fehlers...
Kamest schrieb:> Ununterbrochen nicht, da mindesten 1 Befehl nach dem RETI ausgeführt> wird.> Das Hauptprogramm dürfte trotzdem nur sehr langsam ausgeführt werden> (40.000 x Zeit in der IR ist verdammt lang).
Ja stimmt, das _delay_ms(500) Macro wird wohl sehr viel läääääänger
brauchen.
Volker S. schrieb:> Kamest schrieb:>> Ununterbrochen nicht, da mindesten 1 Befehl nach dem RETI ausgeführt>> wird.>> Das Hauptprogramm dürfte trotzdem nur sehr langsam ausgeführt werden>> (40.000 x Zeit in der IR ist verdammt lang).>> Ja stimmt, das _delay_ms(500) Macro wird wohl sehr viel läääääänger> brauchen.Florian C. schrieb:> Das Programm dient nur zum Austesten des Timer-Interrupts, muss also> keinen Sinn Ergeben, sondern dient lediglich zum Beheben des Fehlers...
Muss der wirklich in so kleinen Zeitabständen kommen?
Florian C. schrieb:> @Matthias S.> Ich finde es einfacher wenn ich es nach Bit-Typ unterteile, aber daran> dürfte es nicht scheitern...
Es geht hier nicht um den Bittyp, sondern darum, das du den Timer in
Häppchen initialisierst, was manchmal zu lustigen Effekten führen kann.
Ein atomarer Zugriff, der alle Bits so setzt, wie sie sein sollen,
bewahrt einen davor.
Das ganze geht übrigens auch komplett ohne Interrupt und läuft im
Hintergrund, wenn du 'Toggle On Compare Match' benutzt und PB4 (OC0) als
Ausgang.
Christoph S. schrieb:> Der Atmega läuft mit 8MHz, deine ISR wird alle 1us, also mit 1MHz Takt> aufgerufen.
Das steht aber nur im Kommentar. Das Compare-Register wird auf 200
gesetzt.
Hier mal eine überarbeitete Timer-Initialisierung:
1
TCCR0=
2
(0b00<<COM00)// normal operation mode, OC0 not connected to pin
3
|(0b10<<WGM00)// clear on compare matching with OCR0
4
|(0b001<<CS00);// set prescaler to /1 8MHz -> 125ns Tick
5
6
OCR0=200;// 200*125ns -> Interrupt alle 25μs
Bei 8MHz sollte das 25μs-Pinwackeln nicht zu 5-stelliger Verlängerung
des delay() führen.
Hatte dadurch bisher noch nie Probleme, kann es aber gerne auch ander
versuchen.
Ich habe jetzt mal den zweiten Kanal des Oszis an den Ausgang der LED
geklemmt:
Die LED toggelt mit der gleichen Frequenz wie der
Timer-Interrupt-Ausgang, lediglich um 180° phasenverschoben! Wie kann
das sein?
Florian C. schrieb:> Und als Entwicklungsumgebung verwende ich das AVR-Studio 7.0
Kannst du damit nicht debuggen?
Müsste man doch leicht erkennen könne , was da schief läuft.
Kurzes OT
Matthias S. schrieb:> Und da blickst du noch durch? Was hältst du davon?:TCCR0 = (1 << WGM01)> | (1 << CS00);
Danke, jetzt bestätigen die avr Jünger selbst, dass die 1 <<
Schreibweise nicht zu lesen ist.
Und noch besser die Variante von CD (0b00<<COM00). ?
Florian C. schrieb:> @Carl Drexler:> Habe deine Initialisierung probiert -> selbes Ergebnis
Ja hoffentlich. Ich hab ja die selben Werte übergeben, nur anders
(durchschaubarer) formuliert. Nur die Kommentare waren im Original
"leicht veraltet".
Anständiger Definer schrieb:> Danke, jetzt bestätigen die avr Jünger selbst, dass die 1 <<> Schreibweise nicht zu lesen ist.> Und noch besser die Variante von CD (0b00<<COM00). ?
Aha? Dann erklär doch bitte mal, was du meinst.
Die Schreibweise
S. Landolt schrieb:>> Wie kann das sein?> Ständiger Neustart nach erstmaligem ISR-Aufruf? Aber warum?
Vielleicht weil die HW nicht die Interruptvektoren hat, die der Compiler
vermutet. Deshalb .lss-File anschauen.
Matthias S. schrieb:> Anständiger Definer schrieb:>> Danke, jetzt bestätigen die avr Jünger selbst, dass die 1 <<>> Schreibweise nicht zu lesen ist.>> Und noch besser die Variante von CD (0b00<<COM00). ?>> Aha? Dann erklär doch bitte mal, was du meinst.> Die Schreibweise>
1
>TCCR0=(1<<WGM01)|(1<<CS00);
2
>
> setzt genau die Bits WGM01 und CS00 im TCCR0 Register. Alle anderen> werden auf 0 gesetzt. Wenn du das nicht verstehst:> https://www.mikrocontroller.net/articles/Bitmanipulation
Er meint, daß ich die Bits zusammengeschrieben hab und dann auf das
unterste Bit ausrichte. Damit sieht man direkt den Wert aus dem DB.
@MS
Nimm erst den Stock aus deinem ...
CD schrieb über die unübersichtliche Initialisierung. Und warum
unübersichtlich! Weil durch die 1 << das Auge abgelenkt wird. Viel
zuviele unnötige Zeichen.
Man kann ein C Programm auch in einer Zeile schreiben. Warum macht man
das nicht? Denk darüber nach, bevor du wieder mit Puls antwortest.
@A. K.: was bedeutet das und wie kann dies vermieden werden?
Jeder hat andere Vorlieben und am Ende kann man alle anderen
Schreibweisen auch nachvollziehen, daher soll doch jeder programmieren
wie es ihm beliebt, hauptsache man hält sich an Ansi C...
Vektor passt.
Ist das mit -Os übersetzt?
Die ISR scheint mir etwas lang. Aber das ist eben auch ein Chip mit
H-Kennzeichen. Der kann nicht mal per PIN-Write mit einen Ausgang
wackeln.
Aber 8*25=200 Takte braucht der nicht für die ISR.
Langsameres delay() ja, aber höchstens 2..3x.
@Carl Drexler:
Ich denke auch, dass das Delay nicht deutlich länger sein müsste...
@S. Landolt:
Ok, kann ich diese M103-Fuse einfach in den Fusebits deaktivieren?
Im Anhang befindet sich ein Foto vom Oszillogramm: oben der LED-Ausgang,
unten der Timer-Interrupt Ausgang...
> Ok, kann ich diese M103-Fuse einfach in den Fusebits deaktivieren?
Ja. Da sie, M103C um korrekt zu sein, im Extended Fuse Byte nur noch
zusammen mit dem WDTON liegt, kann zumindest nichts schief gehen.
Anständiger Definer schrieb:> @MS> Nimm erst den Stock aus deinem ...>> CD schrieb über die unübersichtliche Initialisierung. Und warum> unübersichtlich! Weil durch die 1 << das Auge abgelenkt wird. Viel> zuviele unnötige Zeichen.>> Man kann ein C Programm auch in einer Zeile schreiben. Warum macht man> das nicht? Denk darüber nach, bevor du wieder mit Puls antwortest.
Das ist aber auch nicht wirklich schwer, wenn man will. Seit C11
(spätestens) sind Bit-Konstanten sogar Standard.
Und eine wie auch immer Abneigung gegen eine Chip-Familie an der Frage
Bit-Nummer oder Bit-Maske festzumachen, dafür bin ich ca 40 Jahre zu
alt.
Ich benutze selbst lieber mehr als C und hab dann
Avr::Timer0::Wgm = Avr::Timer0::WGM::CTC;
da stehen. Sogar wenn Wgm über 2 HW-Register verteilt ist.
Im Anhang befinden sich meine Fuse-Bits.
Hier gibt es nirgens das M103 Fusebit... Nach Aktivierung des
WDTON-Fusebit kam es zu unvorhergesehenem Verhalten im Oszillogramm,
daher habe ich es wieder deaktiviert, aber das Ergebnis war das Selbe...
Carl D. schrieb:> Und eine wie auch immer Abneigung gegen eine Chip-Familie an der Frage> Bit-Nummer oder Bit-Maske festzumachen, dafür bin ich ca 40 Jahre zu> alt.
Der Chip kann nix dafür, das liegt schon an den Codern. ?
Florian C. schrieb:> Im Anhang befindet sich ein Foto vom Oszillogramm: oben der LED-Ausgang,> unten der Timer-Interrupt Ausgang...
Sieht aus als wäre der Wechsel beim Timer etwas früher.
Der IR killt (wegen der M103C Kompatibilität?) irgendwie die
Zählvariable des delay Macros?
Die nennen das 'CompMode'!
Egal wie es ausgeht, mein Dank an A.K., dass er meiner Erinnerung auf
die Sprünge geholfen hat.
(Und gleich morgen gehe ich in die Apotheke, vielleicht haben sie ja
etwas gegen akuten Gedächtnisschwund)
S. Landolt schrieb:> Die nennen das 'CompMode'!
Ja, ziemlich idiotisch.
> Egal wie es ausgeht, mein Dank an A.K., dass er meiner Erinnerung auf> die Sprünge geholfen hat.
Hätte er den M128 genommen, wärst du sicherlich gleich drauf gekommen -
bei dem stolpert(e) wohl jeder mal drüber. Der M64 ist technisch
gleich, aber halb getestet, folglich hat er auch diese in diesem Fall
sinnlose Fuse.
WOW, ES FUNKTIONIERT!!
Vielen vielen Dank, es lag tatsächlich am "CompMode" Fusebit!
Nachdem ich es deaktiviert und gesetzt habe geht jetzt beides
einwandfrei!
Da wäre ich ja NIE drauf gekommen!
Wieso ist das nur standardmäßig gsetzt, ist ja idiotisch -.-
Florian C. schrieb:> Wieso ist das nur standardmäßig gsetzt, ist ja idiotisch -.-
Der ATmega128 ist dadurch ein funktionskompatibler Nachfolger vom
ATMega103. So konnte man den M103 direkt durch den M128 ersetzen, ohne
etwas ändern zu müssen. Beim M64 ist das bloss ein Schmutzeffekt.
Florian C. schrieb:> Trotzdem sollte das eher opt-in sein als standardmäßig gesetzt...
Dann wäre er nicht mehr plug-in-compatible gewesen. Sie hätten natürlich
die Logistik um einen Pseudo-Typ M103A bereichern können, der in der
abschliessenden Typ-Programmierung anders eingestellt wird als der M128.
Könnte mir vorstellen, dass es bei Atmel danach Bisspuren am Arsch gab.
Jeder der hier schon länger unterwegs ist kennt das eigentlich. Also
dass der M128 beim ersten Return nach einem Unterprogrammaufruf
wegfliegt. Hier wars eben ein Interrupt.
Ok macht etwas Sinn, aber hätte ich trotzdem nicht so gemacht...
Bin leider selten in diesem Forum und das war mein erster Thread hier,
also war mir das neu und alle bisherigen µCs die ich programmiert habe
hatten da keinerlei Probleme...
Florian C. schrieb:> Wieso ist das nur standardmäßig gsetzt, ist ja idiotisch -.-
Vermutlich gab es einen Kunden, der richtig große Stückzahlen des
Mega103 abgenommen hat und der hat es Atmel diktieren können.
Und damit auch diese krude SPI-Programmierung über die UART-Pins, wo
auch viele drauf reinfallen.