Hallo Zusammen,
ich möchte mit dem Timer 1 ein PWM Signal erzeugen und gleichzeitig ein
PPM Signal einer Fernsteuerung über den ICP auswerten. Ist das überhaupt
möglich? Des Weiteren soll die Frequenz und die Pulsbreite einstellbar
sein. Wie kann ich das machen? Einzeln funktionieren die
Programmabschnitte. Wenn ich aber beide Teile benutzen möchte scheint es
am ICR1 zu liegen. Da dieser von beiden genutzt wird.
1
//Bibliotheken einbinden
2
#define F_CPU 8000000
3
#include<avr/io.h>
4
#include<stdlib.h>
5
#include<util/delay.h>
6
#include<avr/interrupt.h>
7
#include"i2cmaster.h"
8
#include"LCD.h"
9
10
intflanke;
11
uint16_tflankeStart;
12
volatileuint16_tstick;
13
floatdeltaStick;
14
floatrechenwert;
15
intsollwert;
16
17
ISR(TIMER1_CAPT_vect){
18
if(flanke==0){
19
flankeStart=ICR1;
20
TCCR1B&=~(1<<ICES1);// fallende Flanke zur Auswertung des ICP
21
flanke=1;
22
}
23
else{
24
stick=ICR1-flankeStart;
25
flanke=0;
26
TCCR1B|=(1<<ICES1);// steigende Flanke zur Auswertung des ICP
Wenn man einen Timer-Modus verwendet, der als TOP-Wert das ICR
verwendet, dann kann man natürlich kein Input Capture mehr betreiben.
Du musst wohl oder übel den Normalmode verwenden und die Ausgabepins
manuell in der Timer-Overflow-ISR setzt.
Das Zurücksetzen kann per OC-Hardware erfolgen.
In der TimerOverflow-ISR kannst/musst du dann auch die OC-Werte setzen,
da in diesem Modus die beiden Register nicht gepuffert sind.
Steffen Ha schrieb:> Hallo Zusammen,>> ich möchte mit dem Timer 1 ein PWM Signal erzeugen und gleichzeitig ein> PPM Signal einer Fernsteuerung über den ICP auswerten. Ist das überhaupt> möglich?
Sicher.
Warum soll das nicht möglich sein?
Nur wirds ein bischen schwierig werden (tm), wenn du das ICR1 Register
gleichzeitig zum Einstellen der PWM-Frequenz im Fast-PWM Modus ...
> TCCR1A = (1<<COM1A1) | (1<<WGM11);> TCCR1B = (1<<WGM13) | (1<<WGM12) | (1<<CS11);> ICR1 = 30000; // = (8MHz / Prescaler 8) / 20000 = 50Hz
... benutzen willst, und du andererseits das ICR1 Register zurm
Ausmessen einer Pulslänge benutzen willst.
> Des Weiteren soll die Frequenz und die Pulsbreite einstellbar> sein. Wie kann ich das machen? Einzeln funktionieren die> Programmabschnitte.
Das sagt dir eigentlich nur eines.
Du hast ein Programm geschrieben, welches Servo-Pulse erzeugen kann
Du hast ein Programm geschrieben, welches Pulse ausmessen kann
Jetzt musst du ein 3-tes, komplett neues Programm schreiben, welches den
Timer so benutzt dass du gleichzeitig Servopulse UND Pulslängen messen
kannst. Die Betonung liegt auf "NEUES". Deine ersten beiden Programme
sind nett, nur helfen sie dir jetzt nur noch bedingt weiter. Es ist ein
Trugschluss zu glauben, man können Teilfunktionalitäten aus 2 Programmen
immer und in jedem Fall einfach so in ein drittes Programm
zusammenmischen. Dein Problem liegt nicht so sehr im Zusammenmischen vom
Code, sondern darin, dass die beiden Einzelkonzepte für sich alleine
zwar funktionieren. Aber miteinander funktionieren sie so eben nicht
mehr. Also muss man sich was neues bzw. anderes überlegen.
(PS: der Timer kennt ja auch noch andere Modi als den Modus 14. Zb. den
Modus 15, bei dem OCR1A die Rolle übernimmt, die in deinem Servo-Code
ICR1 spielte. Dann kannst du zwar OCR1A nicht mehr zur Einstellung der
Pulsbreite der Servopulse benutzen. Aber: Du hast ja noch OCR1B)
@ Karl Heinz Buchegger (kbuchegg) (Moderator)
Wie muss ich das mit OCR1B verstehen? Mit OCR1A das habe ich verstanden.
Der Wert ist dann für die Frequenz verantwortlich.
@ STK500-Besitzer (Gast)
"Finger weg vom TIFR! Darum kümmert sich der Controller ganz alleine."
Benötige ich diese Zeile nicht? Das löscht doch das Input Capture Flag.
1
//Bibliotheken einbinden
2
#define F_CPU 8000000
3
#include<avr/io.h>
4
#include<stdlib.h>
5
#include<util/delay.h>
6
#include<avr/interrupt.h>
7
#include"i2cmaster.h"
8
#include"LCD.h"
9
10
intflanke;
11
uint16_tflankeStart;
12
volatileuint16_tstick;
13
floatdeltaStick;
14
floatrechenwert;
15
intsollwert;
16
17
ISR(TIMER1_CAPT_vect){
18
if(flanke==0){
19
flankeStart=ICR1;
20
TCCR1B&=~(1<<ICES1);// fallende Flanke zur Auswertung des ICP
21
flanke=1;
22
}
23
else{
24
stick=ICR1-flankeStart;
25
flanke=0;
26
TCCR1B|=(1<<ICES1);// steigende Flanke zur Auswertung des ICP
27
}
28
TIFR=(1<<ICF1);
29
}
30
31
intmain(void){
32
sei();//erlaubt globale Interrupts
33
34
i2c_init();
35
i2c_start_wait(0x40|0);
36
lcd_init();
37
lcd_print("Test V1.0");
38
39
DDRD|=(1<<PD5);
40
TCCR1A=(1<<COM1A1)|(1<<WGM11)|(1<<WGM10);
41
TCCR1B=(1<<WGM13)|(1<<WGM12)|(1<<CS11);
42
OCR1A=128;
43
OCR1B=???;
44
45
DDRD&=~(1<<PD6);//Impuls von Fernsteuerung ICP1 = Eingang
46
PORTD|=(1<<PD6);//Pullup aktiviert
47
TCCR1B|=(1<<ICES1);//steigende Flanke zur Auswertung des ICP
48
TCCR1B|=(1<<CS11);//Quelle für Timer/Counter = CPU-Takt/8
49
TIMSK|=(1<<TICIE1);//Capture Interrupt Enable
50
51
while(1)
52
{
53
DDRD&=~(1<<PD7);//als Eingang -> PWM ein / LED blinkt
Steffen Ha schrieb:> @ Karl Heinz Buchegger (kbuchegg) (Moderator)>> Wie muss ich das mit OCR1B verstehen? Mit OCR1A das habe ich verstanden.> Der Wert ist dann für die Frequenz verantwortlich.
Und mit OCR1B kannst dann eine PWM aufziehen.
Modus 14 Modus 15
Die Rolle von ICR1 übernimmt OCR1A
OCR1A -"- OCR1B
Damit ist ICR1 frei geworden und kann die Aufgabe des Capturen
übernehmen.
> "Finger weg vom TIFR! Darum kümmert sich der Controller ganz alleine."> Benötige ich diese Zeile nicht? Das löscht doch das Input Capture Flag.
Das macht der AVR schon ganz von alleine, sobald die ISR aufgerufen
wird.
Und tu dir noch selbst einen klitzekleinen Gefallen
1
intmain(void){
2
sei();//erlaubt globale Interrupts
3
4
...
den sei() macht man als letzte Aktion vor der Haupt-while Schleife.
Zuerst wird alles sauber eingestellt, alle Hardware-Komponenten
konfiguriert, alle Variablen sofern notwendig initialisiert, kurz und
gut das komplette System in einen vernünftigen Ausgangszustand gebracht
und erst dann gehts los, dass Interrupts ihre Arbeit verrichten können.
Aber nicht umgekehrt.
Dann müsste ich ja für OCR1A = 20000 und für OCR1B = 1000 eintragen
damit ich eine Frequenz von 50Hz und eine Pulsbreite von 1,5ms hätte.
Für das Ergebnis des ICP zeigt mir aber das Display manchmal negative
Zahlen wie z.B. -18800 an. Im Grunde kommt aber das richtige bei raus
1100 = 1,1ms für die untere Knüppelposition und 1900 = 1,9ms für die
obere Knüppelposition.
1
//Bibliotheken einbinden
2
#define F_CPU 8000000
3
#include<avr/io.h>
4
#include<stdlib.h>
5
#include<util/delay.h>
6
#include<avr/interrupt.h>
7
#include"i2cmaster.h"
8
#include"LCD.h"
9
10
intflanke;
11
uint16_tflankeStart;
12
volatileuint16_tstick;
13
floatdeltaStick;
14
floatrechenwert;
15
intsollwert;
16
17
ISR(TIMER1_CAPT_vect){
18
if(flanke==0){
19
flankeStart=ICR1;
20
TCCR1B&=~(1<<ICES1);// fallende Flanke zur Auswertung des ICP
21
flanke=1;
22
}
23
else{
24
stick=ICR1-flankeStart;
25
flanke=0;
26
TCCR1B|=(1<<ICES1);// steigende Flanke zur Auswertung des ICP
Tja.
Wenn jetzt der B-Kanal die PWM erzeugt, dann wird das hier
DDRD |= (1<<PD5);
TCCR1A = (1<<COM1A1) | (1<<WGM11) | (1<<WGM10);
wohl nicht mehr stimmen.
Das war ja auf OCR1A als PWM-Erzeuger ausgelegt.
Deine PWM kommt jetzt aber nicht mehr am OC1A Pin raus, sondern am OC1B
Pin.
> sollwert = stick;
da stick ein unsigned int ist, hat der per Definition kein Vorzeichen,
kann also auch nicht negativ sein.
Aber sollwert ist ein int. Wertebereich -32768 bis +32767. Da passt zb
ein stick_wert von 40000 gar nicht mehr rein, sondern es kommt zu einer
Fehlinterpretation. 40000 wird als int als negative Zahl repräsentiert.
Dazu kommt ...
stick = ICR1 - flankeStart;
Das könnte man machen, wenn der Timer von 0 bis 65535 durchlaufen würde.
Nur ... deiner tut das nicht mehr. Und das musst du berücksichtigen.
Wenn der eine Input Capture bei 19990 kommt und der andere bei 10, dann
lagen da keine 45556 Takte dazwischen, sondern nur 20. Weil ja dein
Timer bei 20000 bereits auf 0 resettet wurde. Das berücksichtigst du
hier aber nirgends.
Das Ergebnis des ICP setzt sich doch so zusammen:
Timer zählt vor sich hin. Kommt eine positive Flank wird der Wert
gespeichert. Kommt eine negative Flanke so wird der Wert mit der
positiven Flanke verrechnet. Ein Timer Zählen dauert 1us. Also könnte es
jetzt passieren das z.B. die positive bei 10 kommt und dann die negative
bei 30. Das ganze verrechnet würde dann eine negative Zahl geben?
Warum gibt jetzt OCR1B das PWM Signal aus? Wäre es möglich das OCR1A
trotzdem noch das PWM Signal ausgeben könnte?
Vielen Dank für die Erklärungen und die Geduld.
Steffen Ha schrieb:> Das Ergebnis des ICP setzt sich doch so zusammen:> Timer zählt vor sich hin. Kommt eine positive Flank wird der Wert> gespeichert.
zb bei einem Zählerstand von 19990
> Kommt eine negative Flanke
zb bei einem Zählerstand von 10
> so wird der Wert mit der> positiven Flanke verrechnet. Ein Timer Zählen dauert 1us. Also könnte es> jetzt passieren das z.B. die positive bei 10 kommt und dann die negative> bei 30. Das ganze verrechnet würde dann eine negative Zahl geben?
30 - 10 ergibt doch keine negative Zahl.
> Warum gibt jetzt OCR1B das PWM Signal aus? Wäre es möglich das OCR1A> trotzdem noch das PWM Signal ausgeben könnte?
Wenn du den Timer Modus 14 benutzen würdest/könntest.
Kannst du aber nicht, weil du ICR1 für was anderes brauchst.
Wo liegt denn das Problem den B-Kanal für die PWM zu benutzen? Anstatt
mit den COM-Bits den Compare-Match vom A-Kanal auf den OC1A Ausgang zu
legen, benutzt du die COM-Bits für den B-Kanal und lässt dir am OC1B die
PWM erzeugen. Kabel vom Servo noch an den anderen Anschluss umlöten und
gut ists. Das ist doch alles keine Raketentechnik. Das ist eine Sache
auf 10 Sekunden, wenn du weißt was du tust.
Ein Problem ist es nicht. Später soll das mal einen BLDC Regler geben.
Ein bisschen läuft er schon. Nur will ich keinen Mega 1284p nehmen. Der
hätte ja zwei 16Bit Timer mit denen ich die Sachen getrennt erledigen
kann. Hab nur bei z.B. auf dem Schaltplan vom Mikrokopter BL-Ctrl
gesehen, dass das PWM Signal von OC1A kommt und die den ICP auch
nutzen.
Steffen Ha schrieb:> Hab nur bei z.B. auf dem Schaltplan vom Mikrokopter BL-Ctrl> gesehen, dass das PWM Signal von OC1A kommt und die den ICP auch> nutzen.
:-)
Es gibt ja auch noch andere Möglichkeiten.
Da musst du halt mal deren Programm studieren, wie die das machen.
Sowas, Studium fremder Programme und ergründen wie und warum sie
funktionieren, soll sehr lehrreich sein.
Steffen Ha schrieb:> Hab nur bei z.B. auf dem Schaltplan vom Mikrokopter BL-Ctrl> gesehen, dass das PWM Signal von OC1A kommt und die den ICP auch> nutzen.
Ohne mir das angesehen zu haben oder das Projekt zu kennen, ich weiß von
anderen Copter Projekten (Naze32 baseflight), dass die Kanäle entweder
PWM Ausgang oder InputCapture sein können. Vermutlich wird da also
nichts gleichzeitig gemacht. Ist nur so ein Gedanke.
Bei dem genannten Projekt werden die Kanäle umgemappt, je nachdem, ob
ein Summensignal vom RC-Empfänger kommt, oder jeder Kanal einzeln. Mit
Summensignal kann man dann mehr Motoren (z.B. Octocopter) oder
zusätzliche Servos ansteuern, ansonsten müssen an den Pins per ICP die
Signale der Kanäle eingelesen werden.
Mit ICP kann man die PWM nur 16 bittig benutzen. Ansonsten liest man
Mumpitz bei der Differenz von 2 Capture-Messungen.
OCR1A muß also immer 0xFFFF sein. Die PWM macht OCR1B.
Ich möchte mir meinen eigenen Brushless Regler bauen. Das Signal was ich
von der Fernsteuerung erhalte ist ein PPM Signal.
Die Erfahrung das man mumpitz liest musste ich jetzt auch machen. Bei
50Hz funktioniert die Sache aber bei höheren Frequenzen nicht mehr.
Nehme ich jetzt als Beispiel 8kHz dann müsste ich für OCR1A = 1000 (8Mhz
Clock ohne Prescaler) einstellen. Er zählt dann nur noch bis 1000 und
beginnt dann von vorne. Der Timer braucht dann aber nur noch 0,125us für
einen Zählschritt was bei 1100us (untere Knüppelposition Fernsteuerung)
einem Wert von 8800 entprechen würde.
Die PWM Frequenz möchte ich gerne Variabel haben. 8, 9, 10kHz. Wie kann
ich das jetzt am geschicktesten lösen? Ich brauch später noch Timer für
den Startup Speed und den Kommutierungszeitpunkt.
1
//Bibliotheken einbinden
2
#define F_CPU 8000000
3
#include<avr/io.h>
4
#include<stdlib.h>
5
#include<util/delay.h>
6
#include<avr/interrupt.h>
7
#include"i2cmaster.h"
8
#include"LCD.h"
9
10
intflanke;
11
uint16_tflankepositiv;
12
uint16_tflankenegativ;
13
int16_thilfsnenner;
14
volatileuint16_tstick;
15
floatdeltaStick;
16
floatrechenwert;
17
int16_tsollwert;
18
19
ISR(TIMER1_CAPT_vect){
20
if(flanke==0){
21
flankepositiv=ICR1;// orig flankeStart = ICR1
22
TCCR1B&=~(1<<ICES1);// fallende Flanke zur Auswertung des ICP
Oder könnte ich es auch so machen. Das ich das Programm so lasse und
einen zweiten Timer hinzuziehe. Der in dem Timer1_Capt_Vect gestartet
wird wenn die positive Flanke kommt und wenn die negative kommt gestoppt
wird. So könnte ich vielleicht auch sinnvoll überprüfen ob das PPM
Signal von der Fernsteuerung noch korrekt ist, z.B. wenn der Empfänger
keinen Strom mehr hat. Dieses würde bei der jetzigen Variante keinen
Interrupt mehr auslösen und der letzt gültige Wert würde in der
Variablen verbleiben was in ganz ungünstigem Fall Vollgas ist.
Gruß Steffen
Steffen Ha schrieb:> if (flankepositiv < flankenegativ)
Was soll das denn?
Bloß weil dein Display negative Zahlen anzeigt, und du den Grund dafür
nicht gefunden hast?
Es ist nur ein Interpretationsproblem deiner LCD-Ausgabe.
Warum musst du die Geschichte eigentlich unbedingt mit nur einem Timer
realisieren?
Ein anderer Timer (welche von den beiden weiß ich gerade nicht) des
ATMega8 hat auch eine OnCapture-Funktion. Allerdings mit nur einem
Capture-Register.
Der Timer würde problemlos funktionieren, auch wenn er nur 8Bit
Auflösung hat.
Ich weiss schon warum das Display negative Zahlen anzeigt. Das liegt
daran wenn z.B. der ICP einmal bei der positiven Flanke bei dem
Zählerwert 19900 auslöst und der bei der negativen bei 100 kommt es
durch die Rechnung im ISR zur negativen Zahl. Da der Timer ja nur bis
20000 zählt bei 50Hz.
Nur lässt sich das Problem auch nicht umgehen denn wenn ich jetzt noch
eine andere Frequenz wähle:
Nehme ich jetzt als Beispiel 8kHz dann müsste ich für OCR1A = 1000 (8Mhz
Clock ohne Prescaler) einstellen. Er zählt dann nur noch bis 1000 und
beginnt dann von vorne. Der Timer braucht dann aber nur noch 0,125us für
einen Zählschritt was bei 1100us (untere Knüppelposition Fernsteuerung)
einem Wert von 8800 entprechen würde. Dieses wäre dann noch schlimmer da
der Timer mehr als einmal neu gestartet wird.
Steffen Ha schrieb:> Ich weiss schon warum das Display negative Zahlen anzeigt. Das liegt> daran wenn z.B. der ICP einmal bei der positiven Flanke bei dem> Zählerwert 19900 auslöst und der bei der negativen bei 100 kommt es> durch die Rechnung im ISR zur negativen Zahl. Da der Timer ja nur bis> 20000 zählt bei 50Hz.
In einem begrenzten Zahlenraum wie er hier vorliegt (16bit), gibt es
keine negativen Zahlen, sondern einen Überlauf.
Hier kann man Zahlen zwischen 0 und 65535 darstellen.
Oder man arbeitet mit der Darstellung, dass das höchstwertige Bit das
Vorzeichen beschreibt, und bekommt einen Zahlenraum von -32767 bis
+32768
https://de.wikipedia.org/wiki/DualsystemSteffen Ha schrieb:> Nehme ich jetzt als Beispiel 8kHz dann müsste ich für OCR1A = 1000 (8Mhz> Clock ohne Prescaler) einstellen. Er zählt dann nur noch bis 1000 und> beginnt dann von vorne. Der Timer braucht dann aber nur noch 0,125us für> einen Zählschritt was bei 1100us (untere Knüppelposition Fernsteuerung)> einem Wert von 8800 entprechen würde. Dieses wäre dann noch schlimmer da> der Timer mehr als einmal neu gestartet wird.
Nimm zwei Timer.
Ich habe es jetzt so gelöst das das Timer 1 das PWM Signal erzeugt und
der ICP nur noch zum Starten und Stoppen des Timers 0 genutzt wird. Der
gibt mir dann die Impulsdauer aus. Gleichzeitig nutze ich den Timer0
Overflow um zu überprüfen ob das Empfangssignal noch da ist. Ist das so
ok? Was kann man verbessern?