Hallo, ich möchte für meine Drehgeber das Programm von Peter Dannegger
benutzen. Bin aber in C noch nicht soweit, alles zu verstehen. Lese aber
tagtäglich viel in den Tutorials! Zum Verständnis des Codes würde mir
ein Programmablaufplan helfen. Hat schon jemand einen solchen Plan
erstellt?
Wenn es für dieses Programm von Peter einen Plan gäbe, wäre mir am
liebsten. Mir hilft es aber auch weiter, wenn es einen PAP gibt, der
allgemein das Einlesen des Graycodes mit Interrupt aufzeigt.
Hier noch einmal der Code von Peter, damit ihr nicht solange suchen
müüss
Danke
Udo
1
#include<avr\io.h>
2
#include<avr\interrupt.h>
3
4
#define XTAL 8e6 // 8MHz
5
#define PHASE_A (PINA & 1<<PA1)
6
#define PHASE_B (PINA & 1<<PA3)
7
#define LEDS_DDR DDRC
8
#define LEDS PORTC // LEDs against VCC
9
volatileint8_tenc_delta;// -128 ... 127
10
staticint8_tlast;
11
12
voidencode_init(void)
13
{
14
int8_tnew;
15
new=0;
16
if(PHASE_A)// wenn Bit PORTA1 gesetzt
17
new=3;
18
if(PHASE_B)// wenn Bit PORTA3 gesetzt
19
new^=1;// convert gray to binary
20
last=new;// power on state
21
enc_delta=0;
22
23
TCCR0=1<<WGM01^1<<CS01^1<<CS00;// CTC, XTAL / 64
24
OCR0=(uint8_t)(XTAL/64.0*1e-3-0.5);// 1ms
25
TIMSK|=1<<OCIE0;
26
}
27
28
ISR(TIMER0_COMP_vect)// 1ms for manual movement
29
{
30
int8_tnew,diff;
31
new=0;
32
if(PHASE_A)// wenn Bit PORTA1 gesetzt
33
new=3;//
34
if(PHASE_B)// wenn Bit PORTA3 gesetzt
35
new^=1;// convert gray to binary
36
diff=last-new;// difference last - new
37
if(diff&1)// bit 0 = value (1)
38
{
39
last=new;// store new as next last
40
enc_delta+=(diff&2)-1;// bit 1 = direction (+/-)
41
}
42
}
43
44
int8_tencode_read1(void)// read single step encoders
Udo Scharnitzki wrote:
> Hallo, ich möchte für meine Drehgeber das Programm von Peter Dannegger> benutzen. Bin aber in C noch nicht soweit, alles zu verstehen. Lese aber> tagtäglich viel in den Tutorials! Zum Verständnis des Codes würde mir> ein Programmablaufplan helfen.
Das bezweifle ich.
Der Trick in diesem Code besteht darin, wie sich einzelne Bits des
Drehgebers beim Drehen verändern und wie diese mit logischen Operationen
so miteinander verknüpft werden, dass am Ende das Richtige rauskommt.
Da hilft dir ein Ablaufplan auch nicht viel.
Wenn du den Code verstehen willst, dann besorg dir eine Tabelle mit
Gray-Codes, nimm an dass der Geber auf einem bestimmten Wert steht,
schnapp dir Papier und Bleistift und spiel selbst Computer und arbeite
das Programm ab. Danach denkst du dir, dass der Geber einmal verdreht
wurde und daher einen neuen Code generiert, den du wieder mit Papier und
Bleistift (und deiner bisherigen Variablenbelegung) durchspielst.
Danach heist es: sich zurücklehnen und über das Nachdenken, was man
soeben als menschlicher Copmuter simuliert hat. Schluesse ziehen;
Vorhersagen treffen, was wohl beim nächsten Gray Code passieren müsste;
das ganze dann tatsächlich auf dem Papier anhand des vorhandenen Codes
durchspielen usw. bis man verstanden hat, wie das ganze funktioniert.
Danke Karl-heinz,
habe schon Computer gespielt und genau das gemacht, was du empfohlen
hast. Es war sehr viel Arbeit. Gut, wenn es nur so geht, dann spiele ich
die Graycode Werte durch und beobachte, was sich tut.
Also nochmal Danke für deine schnelle Antwort. Ich werde so verfahren.
Gruß
Udo
ich habe das mal(für ein anderes Problem) so gelöst, das ich mir die
Signale für jeden Ausgang einzeln auf eine Papierschlange gemalt habe -
die konnte ich dann gegeneinander Verschieben und so die möglichen
Bit-Kombinationen sehen - also richtiges Low-Tec.
Viele Grüße,
egberto
Udo Scharnitzki wrote:
> Mir hilft es aber auch weiter, wenn es einen PAP gibt, der> allgemein das Einlesen des Graycodes mit Interrupt aufzeigt.
Hi, man kann die entstehenden Zustände auch über eine kleinen Tabelle
auf den Code abbilden, wie in
http://www.gjlay.de/pub/c-code/drehgeber.html
Ist vielleicht besser zu verstehen, weil es durch die Tabelle nicht zu
Fallunterscheidungen kommt. Es erklärt allerdings nicht den Code, den du
gerne verwenden möchtest...
Ich weiss zwar nicht, was du genau machen möchtest, aber wenn du einfach
nur einen Drehwinkelgeber simulieren willst, dann kann ich Dir den
80C167 empfehlen! Deren Timer haben einen Speziellen
Drehwinkelgeber-Mode.
Gruß
@Johann,
meine Kenntnisse sind noch nicht soweit, das Zustandekommen der Werte
0,1,-1 und DREH_INVALID zu rekonstruieren. Ich glaube 1 ist vorwärts und
-1 ist rückwärts. Aber welche Codezeilen oder logische Verknüpfungen
sind diejenigen, mit denen diese Tabelle erstellt wird. Eine, - für
Anfänger,-- grundlegende Erklärung habe ich bisher noch nicht gefunden.
Vielleicht ist es auch sehr aufwändig einem Anfänger die Generierung zu
erklären. Ich möchte es aber gerne wissen.
Frage:
Kann man das in wenigen Worten erklären? Rest mache (versuche)ich dann
selber. Im Forum gibt es manche Beiträge, ist aber für mich im
Augenblick noch nicht nachvollziehbar.
1
chardrehgeber_step(void)
2
{
3
staticconstchardrehgeber_trainsitions[]PROGMEM=
4
{
5
0,1,-1,DREH_INVALID,
6
-1,0,DREH_INVALID,1,
7
1,DREH_INVALID,0,-1,
8
DREH_INVALID,-1,1,0
9
};
@ein Schlaumeier,
zum Testen habe ich gerade einen manuellen alps Drehgeber angeschlossen,
um das Prizip des Einlesens zu verstehen. Danach werde ich die Encoder
Scheibe mit der 2-Kanal Lichtschranke anschließen. Also die Hardware
optischer Encoder auf der Motorwelle wird zum Einsatz kommen.
Gruß
Udo
Hallo,
habe den Code in meinem MEGA16 und der alps-Drehgeber (Raster24) zeigt
pro Raster 4 Schritte an. Läuft, wie es der Graycode bestimmt.
Jetzt verstehe ich aber eins nicht:
Wenn ich den Geber 1x betätige erzeugen Kanal A und auch Kanal B je 2
Flankenwechsel. In der Variablen val steht dann der binäre Wert 4 und
wird an PORTC ausgegeben. Ich möchte pro Raster aber nur einen Schritt
anzeigen lassen und teile val durch 4. Das ist aber falsch,oder!?
1
val/=teiler;
Ergebnis ist bei jedem Raster immer Null. Ist wahrscheinlich ein grober
Syntax-Fehler? Habe im Tutorial nix gefunden, was mir weiterhilft.
Dividiere ich falsch?
1udo1 wrote:
> Ich möchte pro Raster aber nur einen Schritt> anzeigen lassen und teile val durch 4. Das ist aber falsch,oder!?
Ja, das ist falsch.
Du mußt
1udo1 wrote:
> @Johann,>> meine Kenntnisse sind noch nicht soweit, das Zustandekommen der Werte> 0,1,-1 und DREH_INVALID zu rekonstruieren. Ich glaube 1 ist vorwärts und> -1 ist rückwärts. Aber welche Codezeilen oder logische Verknüpfungen> sind diejenigen, mit denen diese Tabelle erstellt wird. Eine, - für> Anfänger,-- grundlegende Erklärung habe ich bisher noch nicht gefunden.> Vielleicht ist es auch sehr aufwändig einem Anfänger die Generierung zu> erklären. Ich möchte es aber gerne wissen.>> Frage:>> Kann man das in wenigen Worten erklären? Rest mache (versuche)ich dann> selber. Im Forum gibt es manche Beiträge, ist aber für mich im> Augenblick noch nicht nachvollziehbar.
1 ist entgegengesetzt zu -1.
Wenn es an den Ports zB den Übergang gibt
HH -> HL
dann wirs daraus zB eine 1, bei
HH -> LH
wird eine -1 daraus (oder umgekehrt). Und
HL -> LH
etc. ist ungültig, also bei 2 Änderungen. Bei keiner Änderung wie
LH -> LH
passiert eben nix.
Insgesant gibt es 16 der H/L-Kombinationen, die als Index in die Tabelle
dienen. Die hab ich nicht "berechnet", ich hab's mir einfach aufgemalt
und eingetippt.
Wenn der Wert durch 4 zu teilen ist weil pro Raster 4 Flanken gibt, dann
geht das ebenso, wie ich den Wert durch 2 "teile" falls der Geber 2
Flanken/Raster liefert -- eben nur mit einer 4 anstatt einer 2 ;-)
@Johann,
Danke für deine Erläuterung. Ich werde deine Gedankengänge mal
aufzeichnen. Dann wird mir das Prinzip wohl klar sein.
Meine Versuchsanlage läuft ...... aaaber:
Auf der Motorwelle sitzt eine Encoderscheibe, die durch eine 2 Kanal
Lichtschranke geführt wird. Der MEGA16 zählt fehlerfrei alle Impulse,die
die beiden Kanäle generieren. Ab jetzt fehlt mir folgende Info.
Der Motor soll jetzt zum Test 3 Sekunden links laufen, stehen bleiben, 3
Sekunden rechts laufen. Die Zeit definiere ich mit der delay.h --- und
das ist wahrscheinlich falsch: denn solange die delay.h in Aktion ist,
wird mein Timerinterrupt nicht mehr angesprungen, sodass als Folge die
Ports des Encoders nicht mehr abgefragt werden.
Nun würde ich gerne wissen:
Muss ich meine Zeitschleife einzig und allein innerhalb des
Timerinterrupts per Zählregister realisieren? Das heißt jede
Millisekunde ein Register incrementieren. Oder gibt es etwas
Eleganteres. Ich bin sicher, das gibt es. Aber meine bisherigen
C-Kenntnisse lassen mich verhungern. Bitte helft mir doch mal.
Hier mein FALSCHER Versuchscode:
1
intmain(void)
2
{
3
int32_tval=0;
4
5
PORTA=0xFF;// an PORTA2 ist der MOTOR angeschlossen
6
DDRA=0xff;
7
8
DDRD=0x00;// alles auf EINGANG
9
PORTD=0xFF;//Pullups aktiv
10
11
LEDS_DDR=0xFF;// PORT B
12
encode_init();
13
sei();
14
15
// ####### das funktioniert nicht, soll nur zeigen, was ich will#######
16
// ####### Motor soll Drehrichtung wechseln und es sollen auch noch die Schritte gezählt werden ####
1udo1 wrote:
> Der Motor soll jetzt zum Test 3 Sekunden links laufen, stehen bleiben, 3> Sekunden rechts laufen. Die Zeit definiere ich mit der delay.h --- und> das ist wahrscheinlich falsch
Als Faustregel kannst du dir merken.
Die delay Funktionen sind eigentlich fast immer falsch.
Ausser man braucht nur sehr kurze Wartezeiten, wie zb beim Warten auf
ein LCD und diese Wartezeiten stehen in keinem Verhältnis zu den Timing-
Anforderungen des restlichen Programms.
> Muss ich meine Zeitschleife einzig und allein innerhalb des> Timerinterrupts per Zählregister realisieren? Das heißt jede> Millisekunde ein Register incrementieren. Oder gibt es etwas> Eleganteres.
Das ist eigentlich ziemlich elegant.
Der Timerinterrupt realisiert dir innerhalb deines Programms sowas wie
einen gemeinsamen Zeittakt auf den alle Programmkomponenten aufbauen
können.
@1udo1,
man kann "enc_delta" und "encode_read4" auch auf int16_t oder int32_t
erweitern, dann braucht man nicht so oft den Wert abzufragen.
Besser ist allerdings mit nem Timer das Delay zu machen und
zwischendurch den Encoderwert auszulesen.
Peter
1udo1 wrote:
> @Johann,>> Nun würde ich gerne wissen:>> Muss ich meine Zeitschleife einzig und allein innerhalb des> Timerinterrupts per Zählregister realisieren? Das heißt jede> Millisekunde ein Register incrementieren. Oder gibt es etwas> Eleganteres. Ich bin sicher, das gibt es. Aber meine bisherigen> C-Kenntnisse lassen mich verhungern. Bitte helft mir doch mal.
Für solche Timing-Aufgaben verwende ich über meine Projekte ein
Countdown-Modul. Hier die inzwischen etwas angestaubte WEB-Version:
http://www.gjlay.de/pub/c-code/countdown.html
Die Zeiten die es liefert sind nicht sooo genau, der Sekunden-Zähler hat
zB nur eine Auflösung von 1 Sekunde. 2 Sekunden zu warten macht man also
besser über 200*10ms als über 2*1s wenn es genauer sein soll.
Aber für normale Anwendungen wie
-- Taster entprellen (per 10ms)
-- Drehgeber entprellen (per 10ms)
-- LED blinken (per 10ms)
-- Bildschirmschoner (per 1sec oder 1min)
-- DCF-Port abfragen (per 10ms)
tut das prima.
Überleg dir einfach folgendes:
Du hast n LEDs.
No. 1 soll im 1s-Takt blinken,
No. 2 soll im 1.1s-Takt blinken,
No. 3 soll im 1.2s-Takt blinken, etc.
wie würdest du das machen für 5 LEDs?
Hallo, ihr Aktiven
um Praxis zu bekommen, übe ich mit meinen bisherigen Kenntnissen das
eine oder andere aus. Euren Anregungen gehe ich nach. Vielen Dank!! Habe
mich schon mal in folgender Version ausgetobt. Es funktioniert, --- bin
mir aber sicher,dass man mit diesen Programmzeilen keinen Blumentopf
gewinnen kann.
Würde mir jemand mal eine effektivere Lösung aufschreiben?
Noch mal kurz zur Erinnerung:
Auf der Welle des Motors sitzt eine Encoderscheibe, die die Schritte
zählt. Der Motor dreht nach Ablauf von Zeitschleifen links und rechts.
Ich lasse in diesem Programm Timer 2 laufen. Nach 300 Überläufen wird
die switch - Anweisung durchlaufen und dort geprüft, ob der Motor steht,
links läuft oder rechts läuft.
Bin sicher, dass man das einfacher programmieren kann. Möchte gerne
wissen, wie man den Code besser schreiben kann.
Danke für eure Anregungen.
Das komplette Programm habe ich in den vorhergehenden Beiträgen schon
gepostet.
Hier nur die Ergänzung: