Hallo Zusammen, ich bin im Moment dabei, mich in die Welt der µC einzuarbeiten. Habe ein Board mit einem Atmega32 vor mir, an das ich einen Inkrementalimpulsgeber angeschlossen habe. Dieser gibt zwei Rechtecksignale aus, die um 90° verschoben sind. Soweit so gut. Jetzt zum Problem: Wenn ich den Impulsgeber langsam drehe, werden alle Impulse richtig eingelesen. Dreh ich den Impulsgeber schneller, dann werden nicht alle Impulse eingelesen. Meine Ideen dazu sind bis jetzt, dass der Eingang vielleicht nicht mit der hohen Signalfrequenz klar kommt. Im Datenblatt bin ich leider nicht fündig geworden. Nach meiner Berechnung müsste der µC später im Betrieb dann zwei Signale von 40kHz einlesen. Zum Aufbau: Der Impulsgeber (1200 Imp/U) sitzt hinter einem Schrittmotor(1,8°). Der Schrittmotor treibt ein Getriebe (i=4) an. Das Getriebe treibt eine Spindel(p=2mm) an. Die max. Verfahrgeschwindigkeit soll 1000mm/min betragen. Geht das Grundsätzlich? Ich bin für jede Hilfe dankbar!! Matthias Bürkle
@ Matthias Bürkle (matthiasbuerkle) >Wenn ich den Impulsgeber langsam drehe, werden alle Impulse richtig >eingelesen. Dreh ich den Impulsgeber schneller, dann werden nicht alle >Impulse eingelesen. Meine Ideen dazu sind bis jetzt, dass der Eingang >vielleicht nicht mit der hohen Signalfrequenz klar kommt. Der Eingang schon, deine Verarbeitung aber wahrscheinlich nicht. Siehe Drehgeber >Nach meiner Berechnung müsste der µC später im Betrieb dann zwei Signale >von 40kHz einlesen. = 25 us, macht bei 20MHz gerade mal 500 Takte pro Zyklus. Damit dürfte auch ein AVR schon ziemlich gut beschäftigt sein. Das kann man noch per Interrupt machen, besser wäre aber wahrscheinlich Polling. >Geht das Grundsätzlich? Sicher. MFG Falk
Danke für deine Erklärungen. Ich werd mich morgen mal an den Beispielcode auf der Drehgeber-Seite setzen. Wäre es vielleicht bessern, wenn ich vor die Eingänge einen externen 6:1 Teiler aufbaue, damit der Prozessor nicht so ausgelastet ist? Für die Auflösung des Schrittmotors würde das noch reichen. Das Ganze soll nämlich mal einen Achsantrieb für einen CNC-Maschine werden. Also kommt noch die Steuerung für den Schrittmotor dazu. Hierfür brauche ich bestimmt auch noch mal ein wenig Rechenzeit vom Prozessor. Danke schon mal und einen schönen Abend. Matthias
Matthias Bürkle wrote: > Wäre es vielleicht bessern, wenn ich vor die Eingänge einen externen 6:1 > Teiler aufbaue, Darüber solltest du eventuell noch mal ein paar Minuten nachdenken. Nimm mal Zettel und Bleistift und mal dir auf, was dann passiert. :)
Hallo Matthias, 40 kHz sind kein Problem, das geht sogar mit vier Achsen gleichzeitig. Ich habe damals eine Anzeige für Encoder mit einem 8515 und nur 16MHz gebaut, der gleichzeitig noch die Ansteuerung der 7-Seg-Anzeigen (flimmerfrei! ;-) und die Umrechung vornahm. Ach so: und RS-232-Ausgabe und Befehlsverarbeitung ging auch noch (alles mit gcc-avr) Einachsig nahm das Teil sogar über 100kHz ohne Schrittverluste. Mein Tipp dazu: pack das Einlesen der Encoder in eine kleine (!) Interruptroutine, das muss naturgemäß sehr flott erfolgen. Eventuell auch direkt interne Register zuweisen, damit nicht auf dem Stack rumgerödelt wird. Die Anzahl der Rechts/Linksschritte speicherst Du in kleinen 16-Bit-Integern, die dann außerhalb der ISR weiterverarbeitet und wieder genullt werden (achtung: atomare Operationen!). Also, man kann ich 8kByte eine Menge unterbringen ;-) Chris
Das mit dem Interrupt hab ich mir schon überlegt und mal ausprobiert, aber in das Thema muss ich mich noch weiter einarbeiten. Zu dem Tipp mit der 16-bit Integer: Ich muss insgesamt um die 2,4 * 10^6 Impulse einlesen. Sprich, eine 16-bit Integer würde da gar nicht reichen. Kann ich da nicht einfach eine 32-bit Integer nehmen(global angelegt), die ich bei jeder Interrupt-Routine einfach mit 1 addiere: z.B. so: i_counter++; Sollte für den Interrupt ja eigentlich schnell genug gehen nehm ich an. Danke schon mal für eure zahlreichen Tips :-) Matthias
Die Idee mit den 16-Bit-Integern dient der Verkürzung der ISR-Laufzeit. Der "Trick" ist, dass man die 16-Bit-Zahlen außerhalb des Interrupts verarbeitet und dort diese auf 0 setzt. Damit können diese Werte nicht überlaufen - vorausgesetzt, man liest die Register genügend schnell aus. Bei 16 Bit (-32768 bis +32767) hättest Du bei 40 kHz etwa 0,8 Sekunden, um zumindest einmal die Werte auszulesen und die Zähler zurückzusetzen. Außerhalb kannst Du natürlich mit 32 Bit arbeiten (ich hatte damals sogar long long - geht also alles :-) Ich musste das damals so machen, weil bei vier Achsen und 32-Bit-Werten die Laufzeit zu groß geworden wäre. Wenn Du nur eine Achse auslesen musst, kannst Du vermutlich auch mit 32 Bit und Deiner beschriebenen Addition arbeiten. Chris P.S.: Ich konnte damals bei 16MHz eine "Interruptfrequenz" von über 120kHz bei einer Achse nehmen, das waren irgendwas um die 120 Takte für den gesamten Zyklus - und die darf man sich ja nicht voll genehmigen, weil außerhalb der ISR ja auch noch etwas passieren soll :-)
Also wenn ich dich richtig verstanden hab, könnte ich z.B. in der Interruptroutine eine 16bit-Integer einfach aufzählen lassen. Und im Hauptprogramm dann den Wert der 16bit zu dem Wert der 32bit-Integer addieren und eine 0 in die 16bit-Integer schreiben. Das mit der einen Achse ist nur im Moment beim Testaufbau. Die Maschine hat nacher 4 Achsen, die (im extremsten Fall) alle gleichzeitig bewegt werden sollen. Ich muss jetzt mal schauen, wie alles mit einer Achse funktioniert. Sobald diese eine läuft, kopier ich das Programm einfach für die anderen Achsen. Sollten da Probleme mit der Laufzeit auftreten, dachte ich mir, dass ich die Achsen auf mehrere Controller aufteile. Aber ich bleib erstmal bei der einen Achse, jeder fangt ja mal klein an. ;-) Matthias
Matthias Bürkle wrote: > Also wenn ich dich richtig verstanden hab, könnte ich z.B. in der > Interruptroutine eine 16bit-Integer einfach aufzählen lassen. Und im > Hauptprogramm dann den Wert der 16bit zu dem Wert der 32bit-Integer > addieren und eine 0 in die 16bit-Integer schreiben. Genau :-) Du musst nur darauf achten, während der Addition den Interrupt zu verhindern, weil der sonst "dazwischenhauen" könnte, also etwa so: cli (); counter32 += (signed long) isr16_counter; isr16_counter = 0; sei (); > Das mit der einen Achse ist nur im Moment beim Testaufbau. Die Maschine > hat nacher 4 Achsen, die (im extremsten Fall) alle gleichzeitig bewegt > werden sollen. Ich muss jetzt mal schauen, wie alles mit einer Achse > funktioniert. Sobald diese eine läuft, kopier ich das Programm einfach > für die anderen Achsen. Sollten da Probleme mit der Laufzeit auftreten, > dachte ich mir, dass ich die Achsen auf mehrere Controller aufteile. > > Aber ich bleib erstmal bei der einen Achse, jeder fangt ja mal klein an. > ;-) Eben - bis damals alles so lief, wie es sollte, vergingen auch einige Wochen. Wenn Du vier Controller einsetzen kannst, solltest Du das tun - die Arbeitszeit für die Optimierungen hast Du sofort wieder drin :-) Chris.
Hallo Chris, ich hab jetzt mal probiert über den INT1 Eingang eine Interruptroutine auszuführen. Leider klappts nicht so wie ich will :-(. Vielleicht kannst du mal schnell über den Code schauen. Er ist auf ein Minimum reduziert. (Wahrscheinlich schon zu wenig ;-) ). Hier ist der Code:
1 | #include <stdlib.h> |
2 | #include <avr/io.h> |
3 | #include <avr/interrupt.h> |
4 | |
5 | |
6 | int8_t i=0; |
7 | |
8 | |
9 | ISR(INT1_vect) |
10 | {
|
11 | if(i=0) |
12 | {
|
13 | i=1; |
14 | }
|
15 | else
|
16 | {
|
17 | i=0; |
18 | }
|
19 | }
|
20 | |
21 | |
22 | |
23 | int main(void) |
24 | {
|
25 | DDRA |= (1<<PA5); //PA5 als Ausgang deklarieren |
26 | PORTA |= (1<<PA5); //LED2 ausschalten |
27 | |
28 | GIMSK = 0b10000000; |
29 | MCUCR = 0b00001000; |
30 | |
31 | sei(); |
32 | |
33 | while(1) |
34 | {
|
35 | if(i=1) |
36 | {
|
37 | PORTA &= ~(1<<PA5); |
38 | }
|
39 | else
|
40 | {
|
41 | PORTA |= (1<<PA5); |
42 | }
|
43 | }
|
44 | |
45 | return(0); |
46 | }
|
Ich wäre dir sehr dankbar, wenn du mir sagen könntest was ich falsch mache. Matze
Also, ohne das Programm getestet zu haben: Die Bedingung if (i=0) ist immer wahr, ich würde es auf jeden Fall mal mit if (i==0) versuchen ;-) Beliebter Fehler - eigentlich sollte der GCC da eine Warnung rauswerfen (kommt vermutlich auf die eingestellte Warnstufe an). Chris
Voll peinlich :-|. Anfängerfehler!! Naja, hab ich korrigiert, aber geht immer noch nicht. Die LED bleibt nach dem Einschalten der Stromversorgung aus und reagiert auch nicht auf die Tastendrücke. Danke schon mal Matze
Du soll auch noch die Interrupts activieren. Bei mega32 : // Initialize External interrupts - all disabled: MCUCR = (1 << ISC11) | (1 << ISC10) | (1 << ISC01) | (1 << ISC00); GICR = (0 << INT2) | (0 << INT1) | (0 << INT0); MCUCSR = (0 << ISC2); Jetzt wird der Interrupt bei jeden niveau Wechsel activiert.
Ok, aber hab ich das nicht schon mit den Befehlen "GIMSK = 0b10000000" und "MCUCR = 0b00001000;" gemacht? Ich hab deinen Code mal bei mir eingesetzt, aber leider geht immer noch nix. Irgentwie blick das mit dem Interrupt nicht wirklich. Ich hab nochmal den abgeänderten Code, vielleicht sieht ja jemand den Bug:
1 | #include <stdlib.h> |
2 | #include <avr/io.h> |
3 | #include <avr/interrupt.h> |
4 | |
5 | |
6 | int8_t i; |
7 | |
8 | |
9 | ISR(INT1_vect) |
10 | {
|
11 | if(i==0) |
12 | {
|
13 | i=1; |
14 | }
|
15 | else
|
16 | {
|
17 | i=0; |
18 | }
|
19 | }
|
20 | |
21 | |
22 | |
23 | int main(void) |
24 | {
|
25 | DDRA |= (1<<PA5); //PA5 als Ausgang deklarieren |
26 | PORTA |= (1<<PA5); //LED2 ausschalten |
27 | |
28 | |
29 | i=0; |
30 | |
31 | while(1) |
32 | {
|
33 | MCUCR = (1 << ISC11) | (1 << ISC10) | (1 << ISC01) | (1 << ISC00); |
34 | GICR = (0 << INT2) | (0 << INT1) | (0 << INT0); |
35 | MCUCSR = (0 << ISC2); |
36 | |
37 | |
38 | |
39 | sei(); |
40 | |
41 | if(i==1) |
42 | {
|
43 | PORTA &= ~(1<<PA5); |
44 | }
|
45 | else
|
46 | {
|
47 | PORTA |= (1<<PA5); |
48 | }
|
49 | |
50 | }
|
51 | |
52 | return(0); |
53 | }
|
Matze
Matthias Bürkle wrote: > Ich hab nochmal den abgeänderten Code, vielleicht sieht ja jemand den > Bug: Ich hab jetzt die Registerinitialisierungen nicht mit dem Datenblatt verglichen, aber du musst i auf jeden Fall mal volatile machen (auch ein beliebter Anfängerfehler) >
1 | > #include <stdlib.h> |
2 | > #include <avr/io.h> |
3 | > #include <avr/interrupt.h> |
4 | >
|
5 | >
|
6 | > volatile int8_t i; |
7 | > ... |
Wenns danach immer noch nicht tut, dann schiebst du die LED ein/ausschalterei mal in die ISR hinein. Wenn sich danach immer noch nichts tut, dann stimmt an deiner Registerkonfigurierung was nicht. Oh. Und lass um Himmels willen die Konfiguration in Ruhe. Das macht man einmal(!) und danach lässt man sie in Ruhe (es sei denn man muss umkonfiguerieren). Aber es ist im schlimmsten Fall kontraproduktiv, wenn du innerhalb der while Schleife ständig an den Konfigurationsregistern herumfummelst (selbiges für den sei)
Hallo Karl Heinz, danke für deine Tipps, aber es geht immer noch nicht. Ich hab jetzt das "volatile" vor "int8_t i" gesetzt, die Ein- & Ausschalterei in die Routine geschrieben und die Konfiguration und das sei() vor die while-Schleife gezogen. Aber es geht immer noch nicht. Trotzdem vielen Dank Matze
Matthias Bürkle wrote: > Hallo Karl Heinz, > danke für deine Tipps, aber es geht immer noch nicht. Ich hab jetzt das > "volatile" vor "int8_t i" gesetzt, die Ein- & Ausschalterei in die > Routine geschrieben und die Konfiguration und das sei() vor die > while-Schleife gezogen. Aber es geht immer noch nicht. Noch ein Tipp: Bevor du dich in die Arbeit stürzt und deinen Code hier gross beschreibst, geh in deinen Programmier-Editor, selektier den ganzen Code, machen einen Copy, wechsle hier ins Fenster und mach einen paste. Wenn du freundlich bist, setzt du noch ein [ C ] davor und ein [ / C ] dahinter (jeweils ohne die Leerzeichen). Das hat viele Vorteile: * zum einen sehen wir hier was du wirklich programmiert hast * zum anderen ist es für dich viel weniger Arbeit * und zu guter letzt läufst du nicht Gefahr einen Tippfehler zu machen Sollst du das immer machen? Ja, immer! Zu deinem Problem: Dann wird wohl bei der Initialisierung der Interrupts was nicht stimmen. Also: Nochmal Datenblatt rausholen und alle gesetzten Bits vergleichen. Danach nochmal Datenblatt studieren, ob was vergessen wurde.
1 | GICR = (0 << INT2) | (0 << INT1) | (0 << INT0); |
2 | MCUCSR = (0 << ISC2); |
Was denkst du, was das wohl machen wird?
In dem Register GICR aktiviere bzw. deaktiviere ich die einzelnen Interrupts. So hab ichs zumindest verstanden. Das MCUCSR kenn ich nicht, ich kenn nur MCUCR mit dem man einstellt, welche Ereignisse das Interrupt auslösen.
Ich glaube der Herr Moderator meint eher die vielen Nullen da, die da bestimmt nicht hinsollen ;)
OK, steht ja auch drüber, dass damit alle "disabled" sind. >// Initialize External interrupts - all disabled: > MCUCR = (1 << ISC11) | (1 << ISC10) | (1 << ISC01) | (1 << ISC00); > GICR = (0 << INT2) | (0 << INT1) | (0 << INT0); > MCUCSR = (0 << ISC2); Aber wenn ich jetzt den INT1=1, ISC11=1 und ISC10=0 (für die Auswertung der negativen Flanke), macht der µC immer noch nix :-(.
Hallo, ich bin mal wieder am probieren. Ich bin endlich weitergekommen und wollte nur den momentanen Code online stellen, mit dem das bis jetzt ganz zuverlässigt klappt. Ich hab aber noch vor die Auswertung zu überprüfen, in dem ich einen Absolutdrehgeber(mit fertiger Auswertung) starr mit dem Inkrementalgeber verbinde und die Anzeigen vergleiche. Sobald ich da was weis, meld ich mich wieder. Aber hier erstmal mein Code:
1 | #include <avr\io.h> |
2 | #include <avr\interrupt.h> |
3 | #include "lcd.h" |
4 | |
5 | |
6 | volatile int8_t interrupt_i; |
7 | |
8 | SIGNAL(INT0_vect) |
9 | {
|
10 | if(PIND & (1<<PD3)) |
11 | {
|
12 | interrupt_i++; |
13 | }
|
14 | }
|
15 | |
16 | SIGNAL(INT1_vect) |
17 | {
|
18 | if(PIND & (1<<PD2)) |
19 | {
|
20 | interrupt_i--; |
21 | }
|
22 | }
|
23 | |
24 | |
25 | int main(void) |
26 | {
|
27 | int16_t wert1=0; |
28 | int16_t wert2=0; |
29 | char ausgabe1[8]; |
30 | |
31 | |
32 | DDRD &= ~(1<<PD2); |
33 | DDRD &= ~(1<<PD3); |
34 | PORTD |= (1<<PD2); |
35 | PORTD |= (1<<PD3); |
36 | |
37 | MCUCR = (1 << ISC11) | (1 << ISC10) | (1 << ISC01) | (1 << ISC00); |
38 | GICR = (0 << INT2) | (1 << INT1) | (1 << INT0); |
39 | MCUCSR = (1 << ISC2); |
40 | |
41 | lcd_init(LCD_DISP_ON); |
42 | lcd_clrscr(); |
43 | lcd_gotoxy(0,0); |
44 | lcd_puts("Wert:"); |
45 | lcd_gotoxy(7,0); |
46 | lcd_puts(ausgabe1); |
47 | |
48 | sei(); |
49 | |
50 | while(1) |
51 | {
|
52 | wert1 = wert1 + interrupt_i; |
53 | interrupt_i=0; |
54 | |
55 | itoa(wert1,ausgabe1,10); |
56 | if(wert2 != wert1) |
57 | {
|
58 | lcd_gotoxy(7,0); |
59 | lcd_puts(" "); |
60 | lcd_gotoxy(7,0); |
61 | lcd_puts(ausgabe1); |
62 | wert2 = wert1; |
63 | }
|
64 | }
|
65 | }
|
Bis dann Matze
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.