Forum: Mikrocontroller und Digitale Elektronik PWM einlesen beim mega32


von Hans A. (hans08)


Lesenswert?

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

von Jan M. (mueschel)


Lesenswert?

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

von Bernhard S. (bernhard)


Lesenswert?

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

von Rahul (Gast)


Lesenswert?

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.

von Hannes L. (hannes)


Lesenswert?


von Hans A. (hans08)


Lesenswert?

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`?

von Bernhard S. (bernhard)


Lesenswert?

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 ?

von Hans A. (hans08)


Lesenswert?

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!

von Bernhard S. (bernhard)


Lesenswert?

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

von Hans A. (hans08)


Lesenswert?

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!

von Bernhard S. (bernhard)


Lesenswert?

>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

von Hannes L. (hannes)


Lesenswert?

> 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.

...

von Y. T. (moritzz)


Lesenswert?

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
}

von Christian D. (Gast)


Lesenswert?

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
Noch kein Account? Hier anmelden.