PDA

Archiv verlassen und diese Seite im Standarddesign anzeigen : PWM und Radencoder mit einem µC



robodriver
03.12.2007, 07:48
Allo Leute,

ich habe mal eine Frage: an meinem Roboter befinden sich 2 Motoren als Antrieb. Diese Werden beide übers Hardware-PWM vom µC angesteuert.
Jetzt würde ich ganz gern jedem Rad noch einen Radencoder verpassen.
HAbe da aber ein Problem: Zur Auswertung der Radencoder benutzt man ja am besten den internen Timer als Counter.
Also bräuchte ich beide Timer zum auswerten der beiden Encoder. Aber ein Timer wird ja bereits zum erzeugen der PWM verwendet.

Wie würdet ihr das ganze Problem lösen?
Einen Controller (in DIP) mit mehr als 2 Timern habe ich noch nicht gefunden.

Gruß Robodriver

Andun
03.12.2007, 08:43
Moin

Wie kommst du denn drauf, dass du zum auswerten beide Timer brauchst? Ich muss zwar gestehen, dass ich mich nicht sooo intensiv damit auseinander gesetzt habe, aber das zählen sollte doch reichen, wenn ein Timer zählt und du mit unterschiedlichen Variablen die Werte speicherst.

Oder wie stellt du dir das vor? Generell hat der Atmega32 aber schon OC0, OC1A, OC1B und einen OC2 eingang ... (das weiß ich auswendig). Ich meine, dass der eine Timer etwas kastriert ist, aber das es schon 3 versch. sind.

(kann auch gut sein, dass ich falsch liege :D)

Andun

pctoaster
03.12.2007, 08:49
Hi,
einen Controller mit mehr als 2 Timern gibt es sehr wohl (ATMega32).
Aber die brauchst Du hier nicht. Es hindert Dich niemand daran, mehrere Encoder mit Hilfe eines Timers nacheinander abzufragen.
Allerdings darfst Du diesen Timer nicht als Counter verwenden, sondern nur als IRQ. In der entsprechenden ISR fragt Du Deine Encoderzustände ab und wertest diese dann in der main() (oder wo immer) aus.

Gruß
pctoaster

robodriver
03.12.2007, 09:29
Hi,
einen Controller mit mehr als 2 Timern gibt es sehr wohl (ATMega32).


Ah okay. In dessen Datenblatt hatte ich nachgesehen und nur auf die Pins geschaut und da gibt es nur T0 und T1. Aber unten im Text seh ich gerade das es noch einen T2 gibt. Also ist der dritte Timer einer der nur intern existiert und nicht nach außen geführt ist?
In der Beschreibung steht "siehe PINOUT beschreibung" aber in dieser existiert kein T2 *verwirrt sei*



Es hindert Dich niemand daran, mehrere Encoder mit Hilfe eines Timers nacheinander abzufragen.

Wie soll das gehen? Nur nochmal zum Verständniss (kann sein das Encoder das falsche Wort ist): Ich erhalte lediglich einzelne Impulse von dem Ding. Genau genommen 256 pro Radumdrehung.

Auswerten will ich wenn beide Räder drehen wie weit in einer bestimmten Zeit x jedes Rad gedreht hat um dann über die PWM die Räder nach zu korrigieren, damit beide immer die gleiche Geschwindigkeit haben egal ob eins auf Teppich und das andere auf Fliesen läuft...



Allerdings darfst Du diesen Timer nicht als Counter verwenden, sondern nur als IRQ. In der entsprechenden ISR fragt Du Deine Encoderzustände ab und wertest diese dann in der main() (oder wo immer) aus.


Oh, IRQ und ISR... davom habe ich ja noch nie was gehört. Was ist das? :-k

pctoaster
03.12.2007, 10:17
Hi,
Ein Timerinterrupt wird, wie der Name schon sagt, über einen Timer ausgelöst. Diesen stellt man auf eine Zeit, die der Abtastzeit des Encoders entspricht. Timer dienen meist zur Auslösung eines IRQs. Deshalb benötigt man dazu die äußeren Anschlüsse nicht. Du verwendest den Timer zur Zeit offensichtlich nur als Counter.
Die ISR (interrupt service routine) wird dann regelmäßig mit diesem Zeitintervall aufgerufen. Hier kann man dann die Encoderausgänge abfragen (Du benötigst also 2 freie Eingänge pro Encoder).
Wenn Du dann diese Eingangswerte Encoder(alt) gegen Encoder(neu) vergleichst, hast Du die Drehrichtung des Encoders erkannt. Alles weitere macht man dann in einem Hauptprogramm.
Soviel zum Prinzip.
Wenn Du nicht weisst, wie man IRQs programmiert, fehlen Dir elementare Grundlagen zur MC Programmierung.
Dann guckst Du hier: http://www.mikrocontroller.net/articles/AVR-Tutorial
wenn es C sein soll (meine Empfehlung): http://www.mikrocontroller.net/articles/AVR-GCC-Tutorial
Und nicht zuletzt: Das Datenblatt des entsprechenden AVRs (das im übrigen während der Entwicklung immer auf den Tisch zu liegen hat ;-) )

Gruß
pctoaster

robodriver
03.12.2007, 10:41
Klar, ich weiß was Interrupts sind und benutze sie auch sehr häufig.
Hab mir aber über Microcontroller alles selbst beigebracht und desshalb fehlen mir solche Fachbegrifft wie IRQ und ISR vollständig. Aber gut, jetzt hab ich sie gehört und kenne sie auch :)
(Und weiß entlich wie das in der Fachsprache heist was ich schon seit Jahren anwende)

So aber trotzdem bin ich insgesamt immer noch nicht schlauer:
Die Drehrichtung der Motoren ist mir eigentlich schnurz egal, weil die gebe ich in der Software vor und der L298 ist da 100% zuverlässig das er so rum dreht wie man ihn ansteuert ;)

Also so weit wie ich da jetzt eine Idee hätte wäre folgendes:
Die Encoderausgänge auf INT0 und INT1 schalten. dann im ISR eine Variable immer +1 Rechnen.
Ein Weiteren Interrupt über einen Timer der z.B. nach 300ms die beiden Variablen miteinander vergleicht und das PWM nach regelt.
Das Problem ist dann nur: Der Encoder bringt z.B. alle 0,3ms einen Impuls.
Dann hängt mein Controller ziemlich häufig in der ISR.
Wie soll er dann die anderen tätigkeiten durchführen ohne impulse vergessen zu zählen? (ich hab ja z.B. noch nen INT für I²C Daten Empfang)
Wenn man den Timer als Counter nimmt ist das egal. Dann springt der Controller nur alle 300ms in ein ISR und wertet aus. aber alle 0,3ms in nen ISR springen, ich weiß nicht dann ist der mir irgendwie zu viel beschäftigt...


Oder hab ich da jetzt immer noch was falsch verstanden?

pctoaster
03.12.2007, 11:02
Hi,
aha, ganz so schlimm ist Dein Unwissen also doch nicht :-)
Wenn Du die INT dafür frei hast, kannst Du das auch so machen. Was ich meinte, war einen Timer IRQ laufen lassen (also die Eingänge praktisch zu pollen). Bei 0,3ms ist aber ein INT wirklich besser.
Wenn der IRQ bei jedem Impuls eine Variable um 1 hochzählt, belastet das den Controller normalerweise mit 0,xxx %.
Bleiben wir mal bei deinem Zahlenbeispiel= 300 us pro Impuls. Wenn Der Controller (mal was langsames) mit 1MHz läuft, brauchts dazu max 10us. Das wären (mal sehr ungünstig gesehen) 3% Prozessorlast.
Da würde ich mir keine Gedanken machen. Ich habe hier eine 3-Achsensteuerung mit 3 richtigen Encodern, I2C und RS232 und der ATMega langweilt sich immer noch.
Wenn Du Dich nicht so lange in einer ISR aufhältst, verlierst Du keinen IRQ. Das passiert nur, wenn Du in der ISR bist und der gleiche IRQ während dieser Zeit wieder ausgelöst wird.

Gruß
pctoaster

robodriver
03.12.2007, 11:17
nun gut, wenn man aber jetzt bedenkt das sagen wir alle 300us ein Interrupt ausgelöst wird, was ist denn wenn der andere Encoder innerhalb dieser 10us auch einen Impuls auslöst?
Während der 10us im ISR sind ja alle weiteren INT deaktiviert. Kann es da nicht passieren das ich dann etwas verliere oder der Controller sich verzählt?
Denn die beiden Encoder laufen ja nicht synchron sondern wahrscheinlich minimal unterschiedlich schnell. Das heist die Impulse der beiden Encoder können sich sehr schnell auch mal überlagern oder sogar gleichzeitig kommen.
Das würde halt alles bei der Timer als Counter methode keine Probleme bereiten.

@pctoaster: Hast du nähere Daten zu deiner 3-Achs Steuerung? Die Umsetzung würde mich interessieren.

PICture
03.12.2007, 11:23
Hallo!

@robodriver

Ich programmiere zwar PICs, kann aber wahrscheinlich dir trotzdem helfen. :)

Du verstehst alles richtig. Weil dein Programm nur in der Pausen zwischen IRQs läuft, muss deine ISR kurz sein. Sie muss aber nur ein Zähler (Variable) erhöhen, deswegen beeinflüsst sie keine Register mehr, die Du "retten" müsstest, und sie kann extrem kurz sein. Wenn du in ASM programmierst, lässt sich alles genau berechnen.

MfG

jeffrey
03.12.2007, 12:57
Hallo,
falls du die Pollingvariante wählst, musst die halt mit der doppelten Frequenz wie deine maximale Frequenz abtasten (Nyquist lässt grüßen).
Du kannst ja auch nen externen Zähler verwenden, den du dann nur alle 300ms oder wie oft du halt willst abfragst.
MfG Jeffrey

wkrug
03.12.2007, 13:03
Das was ich jetzt schreib gilt für einen Controller der ATMEL ATMEGA Reihe, sollte aber prinzipiell auch für andere Controller anwendbar sein.

Ich würd es auch mit dem INT 0 bzw. 1 machen.

Wenn einer deiner Counter frei läuft, also immer von 0...max Wert, dann kannst Du ihn in der INT 0 Routine auslesen und den vorherigen Wert aus der letzten INT 0 abziehen.
Der Wert der Subtraktion ist das Maß für deine Radgeschwindigkeit und kann in einem RAM Speicherplatz abgelegt werden.
So kannst Du für alle INT + ( ICP ? ) Quellen den gleichen Timer verwenden.
Bei einem 8 Bit Timer solltest Du in der Timer Overflow Routine noch zusätzliche Speicherplätze mit hochzählen um mehr als 8 ( 16 ) Bit Auflösung und längere Messzeiten zu erhalten.
Für die PWM hätte ich persönlich den OC1A und OC1B verwendet, die beide nur den Timer 1 belegen, wenn dein PWM Mode das zulässt (ohne CTC).

robodriver
03.12.2007, 13:09
ja gut, das polling ist jetzt neben dem Interrupt und der Timer-methode noch eine dritte.
Auch zu dieser mal noch eine Frage:
angenommen es kommen theoretisch bei der gesetzten Geschwindigkeit 3000 Impulse pro Sekunde (alle 0,3ms).

So, jetzt muss ich den unterschied feststellen können wenn das Rad1 2993 Impulse gemacht hat und das Rad2 z.B. 3002 Impulse.
Das heist das umgerechnet:
Rad1: Impulse alle 0,3341129ms
Rad2: Impulse alle 0,3331112ms
so, wenn ich also die Eingänge alle 0,3ms abfrage (polle) dann gibt es eine überschneidung und ich komme nicht auf die Reellen Werte.
Um genau messen zu können müsste ich also in diesem Fall mindestens alle 0,001ms den Eingang abfragen. das entspricht einer Frequenz von 1MHz...

Oder andersrum ausgedrückt (weil ich manchmal nicht so gut sagen kann was ich meine):
Wenn ich die beiden Eingänge alle 0,3ms polle dann wird im oberen Beispiel nach 100 Messungen (30ms) das Ergebniss lauten das beide Motoren gleich schnell drehen. Weil ich beim Abfragen ja nie weiß ob ich jetzt am Ende eines Impuls oder am Anfang bin...

Oder habe ich hier einen Denkfehler?

robodriver
03.12.2007, 13:16
Das was ich jetzt schreib gilt für einen Controller der ATMEL ATMEGA Reihe, sollte aber prinzipiell auch für andere Controller anwendbar sein.


genau die benutze ich auch ;)



Für die PWM hätte ich persönlich den OC1A und OC1B verwendet, die beide nur den Timer 1 belegen, wenn dein PWM Mode das zulässt (ohne CTC).

Ja genau das mach ich doch. Hab ich doch im ersten Post geschrieben, das mein PWM mir einen Timer weg nimmt. genau da liegt ja mein Problem, das ich desshalb nur noch einen Timer für die Radencoder übrig habe was natürlich nicht reicht.

@all:
generelles weiteres Problem ist, das der Mega8 auch nur 2 Interrupts hat und ich einen schon für so ne Art Not-Stop verwende.
Also hätte ich für die beiden Encoder noch einen Timer Eingang und einen Interrupt Eingang.
Was haltet ihr davon einen Encoder über den Counter auszuwerten und dan anderen über den Interrupt (Variable +1). Müsste doch eigentlich genauso genau sein oder?

jeffrey
03.12.2007, 13:18
mit der doppelten Frequenz wie deine maximale Frequenz abtasten
du musst alle 0,15ms abtasten, dann kannst du jeden impuls zuverlässig erkennen.
mfg jeffrey

thewulf00
03.12.2007, 14:49
mit der doppelten Frequenz wie deine maximale Frequenz abtasten
du musst alle 0,15ms abtasten, dann kannst du jeden impuls zuverlässig erkennen.
mfg jeffrey
Selbst wenn Nyquist das sagt, kannst Du mit 0,15ms Abtastrate nicht zwischen 0,32 und 0,34ms unterscheiden.

jeffrey
03.12.2007, 15:14
hi,

Selbst wenn Nyquist das sagt, kannst Du mit 0,15ms Abtastrate nicht zwischen 0,32 und 0,34ms unterscheiden.
das ist ja auch egal. du zählst einfach die anzahl an impulsen, die in den 300ms auftreten.
mfg jeffrey

thewulf00
03.12.2007, 15:23
Das Problem ist aber, dass der Unterschied von 0,32 zu 0,34 ausgeglichen werden muss. Es geht darum, dass das linke Rad 0,32 und das rechte 0,34 gemessen hat. Das Problem ist, dass er auf einmal schief fährt und deshalb muss da nachgeregelt werden.

wkrug
03.12.2007, 15:34
Ja genau das mach ich doch. Hab ich doch im ersten Post geschrieben, das mein PWM mir einen Timer weg nimmt. genau da liegt ja mein Problem, das ich desshalb nur noch einen Timer für die Radencoder übrig habe was natürlich nicht reicht.
Warum den nicht ?
Du kannst doch den einen Timer als Zähler für beide Radsensoren verwenden.
Für dein Not Aus wäre doch auch der Analog Komperator (Interrupt) geeignet, dann hättest Du wieder beide Interrupts frei für deine Radsensoren.

Wenn die Frequenz und der Modus des Timer 1 passt, kannst Du sogar den als Zähler für deine Radsensoren verwenden.

Die Impulse der Radsensoren dienen als Torzeitquelle für einen Timer.
Je mehr Impulse von den Radsensoren kommen um so kleiner wird der Unterschied zwischen vorheriger Messung und aktueller Messung.

Beispiel:
INT 0 (alt) Zählerstand 1111.
INT 1 (alt) Zählerstand 2222.
INT 1 (neu) Zählerstand 2322 -> 2322 - 2222 =100 = Geschwindigkeitsmaß 0
INT 0 (neu) Zählerstand 2611 -> 2611 - 1111 = 1300 = Geschwindigkeitsmaß 1

Ganz klar ist natürlich, das der verwendete Timer in diesem Modus frei laufen muß. Also das TCNT Register nicht angerührt wir und auch der CTC oder fast PWM Mode nicht !! aktiviert ist.

Die Zählerstände "neu" werden nach der Berechnung, für den nächsten Zyklus, in den Zählerstand "alt" übernomen.
Die berechneten Werte werden in einem Speicherplatz zur Übernahme durch das Hauptprogramm abgelegt. Ein Flag wird gesetzt, das es neue Drehzahlinformationen gibt. Dieses Flag wird dann nach der Abarbeitung im Hauptprogramm wieder gelöscht.

Dabei gibt es noch zwei Kröten, die eine ist, das Du dich um einen Overflow kümmern musst, wenn der Zähler den Maximalstand überschreitet = Der Roboter steht.
Die zweite Kröte ist das was ich persönlich als "Overtake Effekt" bezeichne.
Die INT x Routine wird kurz vor dem erreichen des Timer Overflow gestartet.
Vor der Abfrage der Zählerstände kommt es zu einem 0 Durchlauf des Timers. Weil schon eine Interrupt Routine läuft kann die Overflow Routine des Timers nicht ausgeführt werden und eventuell vorhandene Überlaufzähler werden nicht Upgedatet.
( Deshalb ist es günstig hier den ICP zu verwenden ).
Eine mögliche Lösung wäre auch im INT x Interrupt weitere Interrupts mit "SEI" freizugeben, oder das Interrupt Flag des entsprechenden Timers zu überwachen, oder die Takte bis zum Auslesen des Timers vom Zählerstand des TCNT abzuziehen. Das Abziehen geht aber nur, wenn der Timer dann mit Taktteiler 1 läuft, was aber bei einem 8Bit Timer alle 256 Prozessortakte eine Interrupt zur Folge hätte ( ungünstig ! ). Bei anderen Verhältnissen weisst Du ja nicht in welchem Teilerzyklus der Timer gerade ist.

jeffrey
03.12.2007, 15:43
hi,

Das Problem ist aber, dass der Unterschied von 0,32 zu 0,34 ausgeglichen werden muss. Es geht darum, dass das linke Rad 0,32 und das rechte 0,34 gemessen hat. Das Problem ist, dass er auf einmal schief fährt und deshalb muss da nachgeregelt werden.
das ist kein problem, das wird ausgeglichen. 0,32ms führt zu 938 impulse und 0,34 zu 882 Impulsen in 300ms, Es können also noch viel kleinere unterschiede erkannt werden.
MfG Jeffrey

thewulf00
03.12.2007, 15:51
Achso meinst Du das. Aber warum reichen dann keine 0,3ms?

robodriver
03.12.2007, 15:55
@wkrug:

danke für die Ausführliche beschreibung. Wenn ich 2 Interrupts zur Verfügung habe weiß ich ja wie es umzusetzen geht. Das ist dann ja ganz easy. Meine Frage war aber wie man das mit nur EINEM Interrupt-Eingang und EINEM Timer machen kann.
Aber mit dem Not-Aus Interrupt auf den Analog Comparator ist ne klasse Idee. Daran habe ich ja noch gar nicht gedacht. Wenn man es so macht ist wäre dann ja alles klar, weil man dann 2 Interrupts für die Encoder zur Verfügung hat.

thewulf00
03.12.2007, 15:59
Ich denke, das könntest Du so machen:
Mach beide über ein ODER-Baustein (evtl. auch einfach Transistoren) auf die Interruptleitung und dann beide an einen eigenen Port. Dann wird bei jedem Signal, egal ob links oder rechts, ein Interrupt ausgelöst und Du kannst über die Pins den Zustand auslesen, d.h. auch wenn die Interrupts gleichzeitig kommen, funktioniert das. So machts nämlich der PC: Erstmal die allgemeine Interruptleitung aktivieren und dann gucken, welcher es war.

robodriver
03.12.2007, 16:05
@thewulf:
aber auch da können noch Daten verloren gehen:
folgender Fall angenommen:
Encoder1 geht von 0-->1
Encoder2 ist auf 0
Interrupt wird ausgelöst alles super
und jetzt geht der Encoder1 von 1-->0
und zeitgleich der Encoder2 von 0-->1
das bedeutet unter umständen wechselt das ODER-Glied seinen Ausgangspegel gar nicht (die haben ja auch noch eine kleine Verzögerung)

thewulf00
03.12.2007, 16:08
Du musst das anders sehen: Der Kontakt auf einer Seite ist schon längst wieder auf 0, noch bevor Du die ISR verlassen hast. Denn der Encoder bleibt nicht auf 1, sondern geht gleich wieder auf null. Ich würds auf nen Versuch ankommen lassen... ;-)

Ansonsten mach doch einfach pro Seite einen Hochfrequenzzähler (also ich meine schneller als Atmel-Geschwindigkeit, z.B. einen 100MHz-Zähler) und Du guckst selbstständig alle paar Timeraufrufe auf den gezählten Wert.

robodriver
03.12.2007, 16:12
Naja, aber die Spur eines Encoders bleibt doch 180° auf high und ist dann für 180° low.
Wenn sich die Spuren der beiden Encoder dann ungünstig überschneiden wechseln sie sich exakt ab und somit hat man hinter dem ODER 360° high, also immer high

jeffrey
03.12.2007, 17:32
hi,
des mit einem interrupt nennt sich glaub daisy chain. da besteht aber ser wohl die gefahr, dass einzelne impulse vergessen werden.
mfg jeffrey

robodriver
03.12.2007, 17:36
ja, genau das war meine Befürchtung. Das hatte ich ja schonmal geschrieben:



Während der 10us im ISR sind ja alle weiteren INT deaktiviert. Kann es da nicht passieren das ich dann etwas verliere oder der Controller sich verzählt?



Aber sagt mal, so ausergewöhnlich ist doch diese Anwendung nicht.
Irgendwie beißen wir uns hier grad total fest hab ich das gefühl...
Hat denn noch nie jemand sowas umgesetzt?

pctoaster
03.12.2007, 19:13
Hi,
teilweise wurde ja schon einiges geklärt. Trotzdem gebe ich noch meinen Senf dazu ;-)

Wenn sich die IRQs überlappen, stört das nicht, solange nicht der gleiche nochmals kommt. Die IRQs, die nicht sofort bedient werden können, werden zwischengespeichert, sofern sie aus einer anderen Quelle stammen.
Du kannst natürlich alle möglichen verschiedenen Methoden für die Auswertung der Encoder gleichzeitig verwenden. So habe ich das auch gemacht. Die zeitkritischen über INT (bei mir ca.8kHz). Das habe ich jetzt so gelassen, obwohl ich den in die Polling Routine noch mit rein nehmen könnte, aber das muß ich noch austesten.)
Wenn es etwas langsamer sein darf: Pollen (und zwar mit der doppelten Abtastfrequenz, wie oben schon korrekt von jeffrey angemerkt wurde. Bei mir werden mit 28kHz 2 Encoder per Polling gleichzeitig ausgelesen, wobei bis jetzt nur einer getestet wurde.)
Je mehr Encoder Du gleichzeitig auslesen willst, desto eher lohnt sich das Pollen, da Du zwar mit der doppelten Abtastfrequenz arbeiten musst, aber dafür nur einen Timer benötigst.

Gruß
pctoaster