Moin moin,
ich versuche mich gerade daran, mit einem ATMega8L ein PPM-Signal
auszugeben, um das Transmittermodul einer RC-Funke anzusprechen. Bisher
hatte ich das ganze "blind" programmiert, gestern hatte ich Zugriff auf
ein Oszilloskop um das ganze zu testen - und hatte natürlich keinen
Erfolg. Ich kann mir jedoch noch ganz erklären, wo der Fehler liegt.
Ich habe den Timer nun verlangsamt, um das ganze auch mit einer LED
anzeigen zu können, komme aber dennoch der Sache nicht auf die Schliche.
Die Grundidee ist folgende: Um einen PPM-Pulszug ("frame") zu erzeugen,
benutze ich Timer 1, der mit einer Auflösung von 1/2µS pro Tick läuft.
Zu Beginn wird der Ausgangspegel auf HIGH gestellt, nach 300µs erreicht
der Timer den Wert von OCR1B und löst den Interrupt aus: hier wird der
Pegel abgesenkt, der erste Stop-Puls ist damit gesendet, und
nachgeschaut, wie lange der erste Puls dauern soll - dieser Wert wird
dann nach OCR1A geschrieben (Im Debugging-Beispiel soll der gesamtpuls
zwischen den steigenden Flanken 2500µs dauern).
Erreicht der Timer diesen Wert, setzt er sich zurück und feuert den
OC1A-IRQ: Hier wird der PPM-Ausgang wieder hochgerissen, das Spiel
wiederholt sich mit dem nächsten Channel.
Da ein Pulszug stets 20ms dauern soll, führt eine Variable Buch über die
durchgeführten Pulse (frame_time_remaining) und hängt die verbleibende
Zeit als letzten Puls an.
Hier der Quelltext, den ich verwende:
/* set compare value for the stop pulse to 300µs */
41
OCR1B=STOP_MS*2;
42
/* set pulse width to max for now */
43
OCR1A=0xFFFF;
44
/* set Timer 1 to clk/8, giving us ticks of 1/2 µs */
45
// TCCR1B = 1<<CS11;
46
TCCR1B|=(0<<CS11|1<<CS12);
47
48
/* initialize channel data */
49
start_ppm_frame();
50
set_ppm(1);
51
52
/* enable interrupts */
53
sei();
54
55
while(1){}
56
return0;
57
}
58
59
/* the timer has reached OCR1A, so the current PPM pulse has been completed */
60
ISR(TIMER1_COMPA_vect){
61
set_ppm(1);
62
}
63
64
/* finished sending the stop pulse */
65
ISR(TIMER1_COMPB_vect){
66
set_ppm(0);
67
if(current_channel<N_CHANNELS){
68
/* get the pulse width for the current channel */
69
uint16_ttimeout=(1000+1500)*2;
70
OCR1A=timeout;
71
frame_time_remaining-=timeout;
72
}else{
73
/* we already transmitted the last channel, only wait for the frame to finish */
74
OCR1A=frame_time_remaining;
75
start_ppm_frame();
76
}
77
}
Dadurch, dass der Timer jetzt wesentlich langsamer läuft, sollte man das
ja auf der LED verfolgen können; diese lecuhtet aber eigentlich
permanent, nur kurz unterbrochen. Erwartet hätte ich eigentlich, dass
die LED nur für einen kurzen Moment aktiv ist und ansonsten dunkel.
Sieht jemand den Fehler?
Stefan Tomanek schrieb:> Da ein Pulszug stets 20ms dauern soll, führt eine Variable Buch über die> durchgeführten Pulse (frame_time_remaining) und hängt die verbleibende> Zeit als letzten Puls an.
Die Zeit ist absolut unkritisch. Irgendetwas im Bereich 10..100 ms
tut's. Großartige Restzeitberechnungen sind da überflüssig.
Michael schrieb:> Die Zeit ist absolut unkritisch. Irgendetwas im Bereich 10..100 ms> tut's. Großartige Restzeitberechnungen sind da überflüssig.
Ja, das habe ich schon gelesen, ich wollte aber erstmal nah am Original
bleiben :-)
Hast du sonst eine Idee, warum das Signal nicht korrekt generiert wird?
@ Stefan Tomanek (tommie)
>permanent, nur kurz unterbrochen. Erwartet hätte ich eigentlich, dass>die LED nur für einen kurzen Moment aktiv ist und ansonsten dunkel.
Jo.
>Sieht jemand den Fehler?
Nein.
Naja, ich würde es etwas anders machen. Nämlich den Timer1 durchlaufen
lassen und nur OCR1A jeweils erhöhen. Dann braucht man auch keine zwei
OCR Interrupts. Prinzipiell so.
Beitrag "Re: Soft-PWM mit Atmega16"
Ausserdem kann und sollte man die Bitmuster als auch die OCR Zahlen der
einzelnen Kanäle vorher berechnen, das spart einiges an Zeit in der ISR.
Und möglichst keine Funktinsaufrufe in der ISR, das erzeugt unnötig
viele push/pops und kostet Zeit. Vor allem für so eine einfache, kleine
Funktion.
MFG
Falk
Stefan Tomanek schrieb:> Dadurch, dass der Timer jetzt wesentlich langsamer läuft, sollte man das> ja auf der LED verfolgen können; diese lecuhtet aber eigentlich> permanent, nur kurz unterbrochen. Erwartet hätte ich eigentlich, dass> die LED nur für einen kurzen Moment aktiv ist und ansonsten dunkel.
Du könntest dein Programm auch mit Originaltakteinstellungen im
Simulator laufen lassen und in den Registern gucken, was anders läuft,
als du es dir das gedacht hast. Dafür gibt es den Debugger.
Falk Brunner schrieb:> Und möglichst keine Funktinsaufrufe in der ISR, das erzeugt unnötig> viele push/pops und kostet Zeit. Vor allem für so eine einfache, kleine> Funktion.
die set_ppm-Funktion ist auch dem Debugging entsprungen, allerdings wird
der gcc das wohl inlinen (müsste ich mal nachprüfen).
Ich habe nochmal experimentiert: Wenn ich das set_ppm(1) in der
TIMER1_COMPA_vect auskommentiere, blitzt die LED nur kurz auf - ich kann
mir aber gerade nicht erklären, wo der Port PB0 wieder eingeschaltet
wird, nachdem der OCR1A-IRQ ihn deaktiviert hat; habe ich evtl.
irgendein Bit gesetzt, das den Timer1 in Hardware an PB0 (ist ja auch
ICR0) schalten und walten lässt?
Michael schrieb:> Du könntest dein Programm auch mit Originaltakteinstellungen im> Simulator laufen lassen und in den Registern gucken, was anders läuft,> als du es dir das gedacht hast. Dafür gibt es den Debugger.
simulavr unterstützt leider die Timer-Register (noch) nicht :-/
Stefan Tomanek schrieb:> warum das Signal nicht korrekt generiert wird?
Es gibt kein WGM12 in TCCR1A.
> TCCR1A = (1<<WGM12);
Außerdem ist's schlecht Interrupts zu erlauben, für die man keine ISR
definiert.
> TIMSK = (1<<OCIE1B | 1<<OCIE1A | 1<<TOIE1);
MWS schrieb:> Stefan Tomanek schrieb:>> warum das Signal nicht korrekt generiert wird?>> Es gibt kein WGM12 in TCCR1A.>>> TCCR1A = (1<<WGM12);
Argh, Danke. Das erklärt auch obiges Verhalten, damit habe ich halt den
Output-Compare-Modus eingeschaltet. Manchmal sieht man den Wald vor
Bäumen nicht, vor allem, wenn die WGM-Bits sich ber zwei Register
erstrecken.
> Außerdem ist's schlecht Interrupts zu erlauben, für die man keine ISR> definiert.>>> TIMSK = (1<<OCIE1B | 1<<OCIE1A | 1<<TOIE1);
Der Overflow-IRQ ist auch nur ein Debugging-Relikt, ich hatte
zwischendurch so einiges ausprobiert... :-)
Stefan Tomanek schrieb:> damit habe ich halt den Output-Compare-Modus eingeschaltet.
Nein, das war nicht der Fall, Du hast nur einmal einen Compare Match A
erzwungen, der sich aufgrund unbesetzter COM1A1..0 nicht ausgewirkt hat.
Das tatsächliche Problem entstand dadurch, dass mangels gesetztem WGM12
der CTC-Mode nicht eingeschaltet war, damit lief der Timer im normalen
Modus und beim Overflow hast Du dann auch noch wegen fehlender ISR 'nen
Reset bekommen.
Stimmt, danke für den Hinweis. Jetzt scheint das Teil zu laufen, ich
muss jetzt doch nochmal ein Oszilloskop organisieren, um das genau zu
untersuchen.
Stefan Tomanek schrieb:> stefan@exa:~$ sudo apt-get install avr-studio> Paketlisten werden gelesen... Fertig> Abhängigkeitsbaum wird aufgebaut> Statusinformationen werden eingelesen... Fertig> E: Paket avr-studio kann nicht gefunden werden> stefan@exa:~$
Ich schmeiss mich weg lol...
Chris schrieb:> ähm AVR-Studio gibts nur für Windows :-)
Achso! War mit gar nicht bewusst! Ich habe mich schon gewundert, warum
ich das Programm mit meiner Paketverwaltung nicht installieren konnte!
(https://de.wikipedia.org/wiki/Ironie)