Hallo, ich möchte gerne mit dem AtMega32 ein PWM Signal einlesen und dieses mit einem defnierten PWM Wert vergleichen. Es handelt sich dabei um ein Servo Signal, also 50Hz und 1,5ms im Ruhezustand. Bei einem Wert von 2ms sollte der Mega ein ensprechendes Signal ausgeben, das die Gleichheit drastellt. Wie realisiere ich dies am besten im Assembler? Vielen Dank für die Hilfe Hans
Meine Vorschlag kurz skizziert: - externen Interrupt auf steigende Flanke einstellen - wenn der auslöst, auf fallende Flanke umstellen und passenden Timer starten - wenn der auslöst, Timer ablesen und auswerten
Guter Vorschlag! Ich würde den ext. Interrupt auf jede Pegeländerung konfigurieren und bei Interrupt-Auslösung nur schauen, ab er gerade LOW oder HIGH ist und anschließend mit dem TIMER auswerten
Ich würde eine der vielen Lösungen in der Codesammlung mir angucken und dann die ICP benutzen, wobei man bei einer Impulslängenmessung die Flanke in der ISR umstellen und den aktuellen Timerwert speichern muss. Viel interessanter wäre es mit Hilfe des PinChange-Interrupts mehrere Kanäle auszuwerten.
Erst mal vielen Dank für die schnellen Antworten! Die Idee mit dem ICP ist mir auch schon gekommen, leider habe ich diesen noch nie benutzt! Und wie finde ich heraus was mein Signal, bei dem der Mega schalten soll, für ein Wert hat. Also ich brauche ja einen Wert, mit dem ich das eingelesene Signal im ICP vergleichen und auswerten kann. Dieser Werte müsste die 50Hz und 1.8ms bis 2 ms PW haben und der untere Wert von 1.0ms bis 1.2ms. Kann mir jemand weiter helfen`?
Ob Du die externen Interrupt oder die ICP Methode Verwendest, das sei Dir überlassen. Doch beide Methoden führen zum gleichen Ziel: Der Tastgrad der PWM wird hinreichend genau bestimmt. Du legst dann nur noch fest, ab einer PWM von X geht die Lampe an und bei PWM von Y geht die Lampe aus. Oder denke ich verkehrt ?
Hallo, mir stellt sich halt nur die Frage wie der Mega mir das PWM Signal intern darstellt, ich denke ja er wird irgend ein hex Wert daraus generieren, oder? Oder kann ich in einem Register den genauen PWM Wert finden und den Vergleichen? Mit Lampe an und aus stimmt soweit schon sehr gut :-) Falls ich was falsch verstanden hab, tut es mir leid!
Hallo Hans, mach es Dir nicht zu kompliziert, ich denke die Lösung ist doch relativ einfach. >mir stellt sich halt nur die Frage wie der Mega mir das PWM Signal >intern darstellt, ich denke ja er wird irgend ein hex Wert daraus >generieren, oder? Genau, Du wirst einen Zahlen-Wert schlussendlich erhalten, mehr nicht. >Oder kann ich in einem Register den genauen PWM >Wert finden und den Vergleichen? Nein ganz so einfach ist es nicht ;) Hast Du schon mit Timern und Interrupts in Assembler gearbeitet? Bernhard
Hallo, leider hab ich noch nicht mit Timern und Interuppts im Assembler gearbeitet! Ich kann mich auf jden Fall nicht daran erinnern..... Ich habe immer Zeitschleifen benutzt......... Das heißt der Controller ordnet jedem Einganssignal, bzw. jedem PWM einen festen 8bit Wert zu, kann ich mir so vorstellen wie ein AD Wandlund, oder! Er reagiert also auf die zb. ansteigende Flanke und sagt mir, wie lange er bis zur nächsten Flanke braucht. Da es sich ja um ein Servo Signal handelt, können sich die 50Hz nicht ändern. Also muss ich diese nicht erfassen! Ich müsste dann die Zeit zwischen ansteigender Flanke und abfallender Flanke messen und vergleichen! Sollte ich da dann 2 Interrupts benutzen, als der erste startet den Zähler bei der ansteigenden Flanke, der 2 stoppt ihn mit der abfallenden Flanke. Hoffe das stimmt soweit! Leider weiss ich halt nicht wie ich das Software maßig umsetzten soll, zumindest die Teile mit den Werten und den Interrupts! Danke!
>leider hab ich noch nicht mit Timern und Interuppts im Assembler >gearbeitet! In der Codesammlung findest Du genügend Beispiele, Du wirst sehen so schwierig ist es nicht, ein paar Stunden darüber nachdenken und fluchen und schimpfen und dann hast Du es verstanden. >Sollte ich da dann 2 Interrupts benutzen.... Ich merke schon an Deiner Fragestellung, das es jetzt an der Zeit wäre, dass Du Dich zuerst mit den externen Interrupt beschäftigen solltest. Du brauchst dazu nur einen µC eine TASTE und eine paar LEDs um diese Materie zu verstehen. Wenn Du LEDs per Interrupt an und ausschalten kanst, dann experimentiere mit den Timern. Und wenn Du beides beherrschst, dann kannst Du Dich schwungvoll auf Dein PWM-Projekt stürzen ;) Bernhard
> Ich merke schon an Deiner Fragestellung, das es jetzt an der Zeit > wäre, > dass Du Dich zuerst mit den externen Interrupt beschäftigen > solltest. Ich denke, es wäre an der Zeit, mal das Datenblatt etwas genauer anzusehen. Jedes interne Hardwarefeature, wie Timer, ADC usw. hat entsprechende Register, in denen entsprechende Bits (als Schalter gesehen) entsprechende Funktionalität freischalten oder konfigurieren. Welches Bit in welchem Register für welche Aufgabe zuständig ist, erfährst du im Datenblatt des jeweiligen AVRs aus erster Hand (also verbindlich!). Ich habe dir oben (vierte Antwort) einen Link genannt, in dem du etliche verschiedene Projekte findest, die mit Servoimpulsen zu tun haben. Das geht von der Servoimpulserzeugung über Fahrtregler zu diversen Schaltmodulen bis hin zur internen Servoelektronik. Da wird zwar kein Mega32 verwendet, weil ich nicht mit Kanonen auf Spatzen schieße, aber das Prinzip ist bei allen AVRs etwa gleich. Es ist auch kein ICP verwendet, da die damit erreichbare Genauigkeit nicht benötigt wurde bzw. die verwendeten ATtinys kein ICP unterstützen. Hast du dir diesen Link wenigstens mal angesehen? Zum ICP: Der Interrupt wird durch Pegeländerung am ICP-Pin ausgelöst. Die Software muss vorher einstellen, auf welche Flanke der Int ausgelöst werden soll. Zum Zeitpunkt der relevanten Flanke wird der augenblickliche Zählerstand des Timer1 in die ICP-Register kopiert und kann dann später in der ISR (Interrupt-Service-Routine) in aller Ruhe ausgewertet werden. Dazu merkt man sich immer den Wert der letzten Flanke und subtrahiert ihn vom Wert der aktuellen Flanke, den man sich dann fürs nächste mal merkt. Diese Differenz zweier "Zeitstempel" gibt die Periode oder Impulsbreite in einer 16-Bit-Zahl an. Ein Zahlenwert entspricht dabei einem Timer-Tick, bei Vorteiler 1:1 also einem Controllertakt. Du kannst die Zeit, die der gemessene Zahlenwert repräsentiert, also sehr genau berechnen. Dazu genügen die Regeln des Dreisatzes. Bei 1MHz Controllertakt und Vorteiler 1:1 für Timer1 erreichst du für 1ms den Zahlenwert 1000, für 2ms den Wert 2000 und für 20ms den Wert 20000. Damit du beide Flanken messen kannst, musst du in jeder ISR die zu messende Flanke umschalten. Wenn der Timer1 nicht für weitere Dinge gebraucht wird, kann er auch bei steigender Flanke (Impulsbeginn) gelöscht werden und bei fallender Flanke (Impulsende) wieder ausgelesen. Das erspart die Subtraktion, verschwendet aber Ressourcen, da auf diese Art die beiden Compare-Interrupts und der Überlauf-Inmterrupt nicht mehr nutzbar sind. Also, stecke die Nase ins Datenblatt und versuche die Hardware und deren Register zu verstehen. Das ist die Grundvoraussetzung für erfolgreiche Programmierung. ...
Hallo! Ich habe ein ähnliches Projekt (Selbstbau eines Heli-Gyro-Controllers), möchte als ersten Schritt einen Servokanal einlesen. Die ICP-Methode funktioniert z.B. so: (ATmega8, 8Mhz-Quarz, 64er Vorteiler -->0,000 008 s dauert ein Timerzählen. Der Servokanal hat bei Vollausschlag gemessene 1,1ms=0,0011s. Bedeutet, der ICP Timer müsste dann Werte um die 140 haben...) Hier ein C-Code dazu:
1 | void init_prozedur(void) |
2 | { |
3 | TCCR1B |= (1<<ICNC1)|(1<<ICES1)|(1<<CS11)|(1<<CS10); //vorteiler 64, Mittelung ON, steigende flanke |
4 | TIMSK |= (1<<TICIE1);//INTERRUPT ON |
5 | sei(); |
6 | } |
7 | |
8 | int main (void) |
9 | { |
10 | init_prozedur(); |
11 | DDRC=0xFF; |
12 | DDRB=0b11110000; |
13 | while(1) { |
14 | |
15 | if (aktuell_pwm_l < 140){ |
16 | PORTC|=(1<<PC0); |
17 | } |
18 | if (aktuell_pwm_l > 140){ |
19 | PORTC&=~(1<<PC0); |
20 | } |
21 | } |
22 | |
23 | } |
24 | |
25 | ISR(TIMER1_CAPT_vect){ |
26 | if (status_flanke==0){ |
27 | TCNT1H = 0; //Timer auf null zurück |
28 | TCNT1L = 0; |
29 | TCCR1B &= ~(1<<ICES1); //ICP-Pin auf fallende Flanke einstellen |
30 | status_flanke=1; |
31 | }else{ |
32 | status_flanke=0; |
33 | TCCR1B|=(1<<ICES1); //ICP auf steigende Flanke wieder aktivieren |
34 | aktuell_pwm_l=ICR1L; //Zählerstand speichern |
35 | aktuell_pwm_h=ICR1H; //zählerstand speichern |
36 | } |
37 | } |
Hallo, ich versuche zurzeit ein PWM-Signal (240Hz, mit unterschiedlichem Duty Cycle) mithilfe eines Mega32 auszuwerten. Je nach Duty Cycle des Eingangs-PWM-Signals sollen unterschiedliche LEDs aufleuchten. Könnte bitte wer drüber schaun, ob der Code (Die Basis stammt von Moritzz) so für einen Mega32 funktioniert, da ich mom. leider kein Eval-Board zu verfügung habe, kann ich es nicht selber testen. Beschreibung: Ich habe den Mega32 auf 4Mhz eingestellt, mit einem Vorteiler von 64 ergibt das: 0,000016 Sekunden für einen Timerdurchlauf. Das PWM-Signal hat 240Hz ==> 0.00416667 Sekunden Daraus ergibt sich 0.00416667 / 0,000016 = 260 Die LEDs an PortC 0 - 6 sollen je nach Dutycycle aufleuchten. Nun zum Code:
1 | #include <avr/io.h> |
2 | #include <avr/interrupt.h> |
3 | #define F_CPU 4000000UL
|
4 | |
5 | volatile int flanke=0; |
6 | volatile int aktuell_pwm_l=0; |
7 | volatile int aktuell_pwm_h=0; |
8 | |
9 | ISR(TIMER1_CAPT_vect) |
10 | {
|
11 | |
12 | if (flanke==0) |
13 | {
|
14 | TCNT1H = 0; //Timer auf null zurück |
15 | TCNT1L = 0; |
16 | TCCR1B&= ~(1<<ICES1); //ICP-Pin auf fallende Flanke einstellen |
17 | flanke=1; |
18 | }
|
19 | else
|
20 | {
|
21 | flanke=0; |
22 | TCCR1B|=(1<<ICES1); //ICP auf steigende Flanke wieder aktivieren |
23 | aktuell_pwm_l=ICR1L; //Zählerstand speichern |
24 | aktuell_pwm_h=ICR1H; //zählerstand speichern |
25 | }
|
26 | }
|
27 | |
28 | void init_prozedur(void) |
29 | {
|
30 | TCCR1B |= (1<<ICNC1)|(1<<ICES1)|(1<<CS11)|(1<<CS10); //vorteiler 64, Mittelung ON, steigende flanke |
31 | TIMSK |= (1<<TICIE1);//INTERRUPT ON |
32 | |
33 | sei(); |
34 | }
|
35 | |
36 | int main (void) |
37 | {
|
38 | init_prozedur(); |
39 | DDRC=0xFF; |
40 | DDRB=0b11110000; |
41 | while(1) { |
42 | |
43 | if (aktuell_pwm_l >= 0 && aktuell_pwm_l < 40) |
44 | {
|
45 | PORTC|=(1<<PC0); |
46 | }
|
47 | if (aktuell_pwm_l >= 40 && aktuell_pwm_l < 80) |
48 | {
|
49 | PORTC|=(1<<PC1); |
50 | }
|
51 | if (aktuell_pwm_l >= 80 && aktuell_pwm_l < 120) |
52 | {
|
53 | PORTC|=(1<<PC2); |
54 | }
|
55 | if (aktuell_pwm_l >= 120 && aktuell_pwm_l < 160) |
56 | {
|
57 | PORTC|=(1<<PC3); |
58 | }
|
59 | if (aktuell_pwm_l >= 160 && aktuell_pwm_l < 200) |
60 | {
|
61 | PORTC|=(1<<PC4); |
62 | }
|
63 | if (aktuell_pwm_l >= 200 && aktuell_pwm_l < 255) |
64 | {
|
65 | PORTC|=(1<<PC5); |
66 | }
|
67 | |
68 | |
69 | }
|
70 | |
71 | }
|
Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.