Forum: Mikrocontroller und Digitale Elektronik ISR-Aufruf; kann das klappen? ArduinoMikro/ATmega32U4


von Claude J. (berus)


Lesenswert?

Hallo Beitragsleser,

letztes Mal habe ich den folgende Thread eroeffnet. Man muss ihn nich 
lesen, um mein jetztiges Anliegen zu verstehen. Aber dennoch moechte ich 
darauf verlinken:

Beitrag "Re: PI-Regelschleife fuer Geschwindigkeiteit eines autonomen Roboters"

Im alten Thread hat Jan Heynen mir zu Recht unterstellt, dass ich einen 
Tachogenerator verwenden will fuer meinen Roboter. Diese Annahme ist 
absolute berechtigt, wenn man sich meinen alten Code ansieht. Da ich 
damals aber duemmer war als ich jetzt bin, wusste ich nicht, wie ich 
seine Bemerkung zu verstehen hatte, denn ich wollte und will auch jetzt 
Wheelencoder benutzen fuer mein jetziges Roboterprojekt.

Mein Projekt praegnant zusammengefasst. Ich will einen autonomen Roboter 
bauen, der zwei unabhaenige Raeder/Motoren auf gleiche Geschwindigkeit 
regelt.  Dafuer nutze ich einen Arduino Mikro mit einem ATmega32U4. Das 
nur so am Rande. Ich muss jetzt etwas mit C arbeiten, weil ich keine 
Bibliothek gefunden habe, die mir massgenscheidert weiterhelfen kann. 
Ich benutze also C in der Arduino IDE, soll angeblich gehen. Es ist also 
ein Gemsich aus C- und Arudino-Programmierung.

Meine eigentliche Frage lautet, ist mein nachfolgender Code richtig?

# define F_CPU 16000000 UL //Systemfrequenz 16 MHZ (das gibt der Atmega 
her)

void setup()
{

TCCR1B |= (0<<CS11) | (1<<CS10); // 16-Bit-Timer 1 Prescaler nimmt Wert 
1 an
TCCR3B |= (0<<CS32) | (1<<CS30); // 16-Bit-Timer 3 Prescaler nimmt Wert 
8 an
TCCR0A |= (0<<CS01) | (1<<CS00); // 8-Bit-Timer 0 Prescaler nimmt Wert 1 
an

sei;

}

void loop()
{;}


ISR(Timer1_COMPA_vect)
{;}

ISR(Timer3_COMPA_vect)
{;}

ISR(Timer0_COMPA_vect)
{;}

Was soll der Code bewirken, moechte man jetzt vermutlich wissen? Die 
Idee dahinter ist, das die Timer 1 und 3 62500 Overflows/s ausloesen und 
der Timer 0 244,1406 Overflows/s. Ich glaube, dass ich keine weiteren 
Bits mehr setzen muss, weil ich z.B. den CTC-Modus nicht brauche, glaube 
ich. In den ISR-nen der Timer 1 und 3 werden jetzt die sogenannten Ticks 
oder Klicks der Wheelencoder gezaehlt werden und im Timer 0 wuerde die 
Geschwindigkeit der auf Basis der Klicks/Ticks ermittelt werden. Den 
Inhalt der Tick Zaehlung und Geschwindigkeitsbestimmung habe ich noch 
als Datei angehaengt, vollstaendigkeitshalber. Ich habe meinen Code auf 
das wesentlichste, also das worauf sich meine Frage bezieht, 
eingeschraenkt.

Ich habe also den Systemtakt auf 16 MHz gestellt und die Vorteiler fuer 
die Timer eingestellt. So muessten die ISR doch jetzt nach jedem 
Interrupt aufgerufen werden, ist das richtig? Das scheint mir irgendwie 
zu einfach zu sein.

Tut mir Leid, aber ein Teil meines PCs hat eine gewaltige Macke. Ich 
schicke den Anhang gleich noch hinter her. Ich hab Angst, dass wenn ich 
den Beitrag jetzt nicht losschicke, dann wird er geloescht.

FG,
Berus

von M. W. (rallini94)


Lesenswert?

Claude J. schrieb:
> So muessten die ISR doch jetzt nach jedem
> Interrupt aufgerufen werden, ist das richtig?

Nein, die Timer anzustellen reicht nicht aus. WEnn eine ISR ausgelöst 
werden soll, wenn der Timer überläuft, musst du den entsprechenden 
Interrupt in den entsprechenden Registern auch aktivierten.

beim Atmega325A wäre das z.B. das Bit TOIE0 (Timer 0 Overflow Interrupt 
enable) im Register TIMSK0. Dann wäre die ISR aber auch TIMER0_OVF_vect.
Oder eben das Bit OCIE0A (Timer 0 Output Compare Match A Interrupt 
enable) und du kannst die ISR lassen

von Claude J. (berus)


Angehängte Dateien:

Lesenswert?

Hallo an alle Leser,

@Ralini: danke fuer deine Hilfe. Fuer dein schnelle Antwort.

Ich habe jetzt zwei Dokumente angehaengt, musste gestern erst meinen PC 
wieder in Gang kriegen. Im ersten Dokument ist der ganze Code, im 
zweiten nur ein Teil davon, also das, worauf sich meine noch folgenden 
Fragen beziehen.

Ich bin mir ziemlich unsicher, was die Funktionalitaet meines Codes 
angibt. Syntaktisch ist er einwandfrei, sagt der Debugger. Meine 
Bedenken sind aber folgende.

Ich habe zwei Raeder mit jeweils einen Wheelencoder. Ich will die Ticks 
von  beiden Wheelencodern moeglichst gleichzeitig lesen. Fuer jeden 
Wheelencoder habe ich eine ISR. Die ISR fuer den rechten Wheelencoder 
wird vom Timer3 und die ISR fuer den linken Wheelencoder wird vom Timer 
1 ausgeloest.

Die Inkremente beider Wheelencoder sollen von derjenigen ISR ausgewertet 
werden, die vom Timer0 ausgeloest wird. Daraus soll sich die 
Geschwindigkeit ergeben.

Diese Geschwindigkeit soll dann im Hauptprogramm innerhalb einer PI- 
Regelung verwertet werden.

Ich bin skeptisch, weil ich mir unsicher bin. Ich habe folgende 
Bedenken.

Momentan verwende ich zwei ISRen, jeweils eine fuer jeden Wheelencoder. 
Beide ISRen werden mit 62,5 kHz ausgefuehrt bzw. jeder Wheelencoder wird 
mit 62,5 kHz abgetastet.

Bedenken Nummer 1:

Kann es sein, dass bei einer solchen hohen Abastrate fuer das Zaehlen 
der Inkremente eines Wheelencoders eine inkrementeale Drehung des Motors 
mehrfach erfasst wird. Ich meine, dass ich z.B. drei Inkremente zaehle, 
obwohl sich der Motor nur um ein Inkrement gedreht hat und somit zwei 
Inkremente zu viel gezaehlt weren

Bedenken Nummer 2:

Sollte ich die Inkremente der beiden Wheelencoder nicht besser in einer 
ISR zaehlen, statt wie bisher in zwei ISR? Bleibe ich bei zwei ISR, dann 
wuerde ich doch theoretisch die Geschwindikeit des linken Motors 
zeitlich nach der Geschwindigkeit des rechten Motors erfassen oder 
umgekehrt, aber nicht gleichzeitig, was ich ja eigentlich erreichen 
will.

Bedenken Nummer 3:

Kommt mein Hauptprogramm, was die PI-Regelung sein soll, ueberhaupt zum 
Zuge, wenn ich die ganze Zeit die ISR am laufen habe? Ich meine, da die 
ISR durch den Timer getriggert wird, bleibt zwischen zwei Overflows nur 
eine paar uS Zeit zum Durchlaufen des Hauptprogramms bis die ISR ein 
naechstes Mal durchlaufen werden kann. Ich glaube, dass ich die Zeit 
zwischen zwei Interrupt verlaengern muss. Es muesste also so sein, dass 
das Haupprogramm lange dauert, die ISR fuer die Auswertung der Inkrement 
bzw. die Bestimmung der Geschwindigkeiten mittel lang dauer und die ISR 
fuer die Zaehlung der Inkremente kurz dauer. Ist das richtig?

Ehrlich gesagt, glaube ich, dass mein Konzept nicht richtig durchdacht 
ist. Das wird mir beim Schreiben gerade noch deutlicher. Koenntet ihr 
mir Anregungen und Hinweise geben, wie ich das Konzept verbessern kann?

Freundliche Gruesse,
Berus

von Claude J. (berus)


Lesenswert?

Kann es sein, dass dies gar nicht so geht. Muesste ich nicht eventuell 
einen zweiten Chip verwenden?


Den ersten Chip zum messen der Inkremente der beiden Motoren und zur 
Auswertung der Inkremente bzw. Bestimmung der Geschwindigkeit

und

den zweiten Chip zum Durchfuehren des Hauptprogrammes. Das Hauptprogramm 
wuerde dann ununterbrochen laufen und nur dann auf die Geschwindikeit 
vom anderen Chip  zugreifen, wenn es die Geschwindigkeit zur PI-Regelung 
wirklich braucht.

FG,
Berus

von Felix F. (wiesel8)


Lesenswert?

Claude J. schrieb:
> TCCR1B |= (0<<CS11) | (1<<CS10); // 16-Bit-Timer 1 Prescaler nimmt Wert
> 1 an
> TCCR3B |= (0<<CS32) | (1<<CS30); // 16-Bit-Timer 3 Prescaler nimmt Wert
> 8 an
> TCCR0A |= (0<<CS01) | (1<<CS00); // 8-Bit-Timer 0 Prescaler nimmt Wert 1
> an
>
> sei;
>
> }
>
>
> Was soll der Code bewirken, moechte man jetzt vermutlich wissen? Die
> Idee dahinter ist, das die Timer 1 und 3 62500 Overflows/s ausloesen und
> der Timer 0 244,1406 Overflows/s.

Wie kommst du auf diese Werte???

16-Bit-Timer 1 mit Prescaler 1 ergibt 244 OVF/s
16-Bit-Timer 3 mit Prescaler 8 ergibt 244/8 OVF/s
8-Bit-Timer 0 mit Prescaler 1 ergibt 62500 OVF/s

Wenn man jetzt davon ausgeht, dass du die ISRs sehr kurz hältst, bist du 
schon bei 5-6 Mio. Instruktionen (von theoretisch 16 Mio., praktisch 
natürlich weniger, da einige Instuktionen mehr Cycles brauchen). Fügst 
du jetzt noch ein paar (float) Berechnungen hinzu oder verwendest die 
aufgeblähten Arduino Libs, kommt dein MC nicht mehr mit.

mfg

von Nico W. (nico_w)


Lesenswert?

Felix F. schrieb:
> Wie kommst du auf diese Werte???

Einen Overflow von nem ISR hat nix mit der Anzahl der Instruktionen zu 
tun!

Bei 16MHz und einem 'vollen' Zyklus bei 16bit zählt der µC 244 mal pro 
Sekunde bis 65535. Also läuft der Zähler da über und erzeugt einen 
Interrupt.

von Felix F. (wiesel8)


Lesenswert?

Nico W. schrieb:
> Einen Overflow von nem ISR hat nix mit der Anzahl der Instruktionen zu
> tun!

Nein, aber du willst mit dem Overflow eine ISR aufrufen, welche eine 
bestimmte Anzahl an Instruktionen benötigt. Und eine ISR läuft nich 
parallel ab, sondern blockiert alles andere während der Rechenzeit.


> Bei 16MHz und einem 'vollen' Zyklus bei 16bit zählt der µC 244 mal pro
> Sekunde bis 65535. Also läuft der Zähler da über und erzeugt einen
> Interrupt.

Du hast den 16-Bit-Timer mit Prescaler 1 konfiguriert, es liegen also 
die vollen 16 MHz an. Der Timer zählt bis 65535, dann gibt es einen 
Overflow. Das passiert genau 244x. Somit wird 244x eine ISR aufgerufen.
Der 8-Bit-Timer läuft nur bis 255 und löst dementsprechend über 62000x 
aus.

mfg

von Berus (Gast)


Lesenswert?

Danke fuer deine Antwort. Ich soll also die Overflows pro Sekunde 
drastisch reduzieren, meinst du.

Ich habe bei der Einstellung von 62,5 khz nicht nachgedacht, ehrlich 
gesagt. Mein letztes uC Projekt ist einige jahre her. Meine Errinnrungn 
an das schon damals lueckenhafte Wissen ueber uC sind starkt 
ausgepraegt, wie man sehen kann.

Wie bestimme ich die maximal zulaessige Anzahl an Overflows bzw. 
Interrupts pro Sekunde?

Sind es fuer das Hauptprogramm zu viele Interrupts, wenn beide Encoder 
(jeweils ein Encoder an einem Motor) bei jedem Inkrement einen Interrupt 
ausloesen? Eine realistische Annahme ist womoeglich, dass ein Encoder 
pro Sekunde 100 Mal inkrementiert wird. Das waeren dann 200 Interrupts 
pro Sekunde.

Koenntest du mir bitte erklaeren, wie du auf die 5 Millionen 
Instruktionen gekommen bist?

Ist es die Rechnung (16^2*244)*2+62500*8^2 = 4124928?

FG,
Berus

von Berus (Gast)


Lesenswert?

Oh, da war ich zu langsam. Danke fuer die Erklaerung.

von Felix F. (wiesel8)


Lesenswert?

Wie viele Interrupts du in deinem Programm haben darfst, hängt davon ab 
was du sonst noch machst. Bis jetzt (ohne große Berechnungen) ist alles 
noch in Ordnung.

Aktuell hast du ~63000 INTs/s. Wenn man von ca. 50 Instruktionen pro ISR 
ausgeht, hast du somit über 3 Mio. Instruktionen. Da nicht jede 
Instruktion mit 1 Cycle auskommt kannst du das mal grob auf 5 Mio. 
aufrunden. Kommt letztendlich auch auf dein Programm an. 
Lese/Schreibbefehle auf Speicher oder Gleitkommaberechnungen brauchen 
sehr viel länger als z.B. einfache Additionen.

Aber da Mechanik sehr langsam ist (im Vergleich zur Elektronik) 
benötigst du vermutlich keine so hohe Auflösungen. Ich würden den 
8-Bit-Timer auf jeden Fall mal um die Hälfte reduzieren, damit gewinnst 
du schon sehr viel. Und wenn am Ende noch genug Ressourcen übrig sind, 
kannst du die Timer immer noch hochschrauben.

mfg

von Felix F. (wiesel8)


Lesenswert?

Claude J. schrieb:
> Bedenken Nummer 2:
>
> Sollte ich die Inkremente der beiden Wheelencoder nicht besser in einer
> ISR zaehlen, statt wie bisher in zwei ISR? Bleibe ich bei zwei ISR, dann
> wuerde ich doch theoretisch die Geschwindikeit des linken Motors
> zeitlich nach der Geschwindigkeit des rechten Motors erfassen oder
> umgekehrt, aber nicht gleichzeitig, was ich ja eigentlich erreichen
> will.

Mit einer ISR bist du vmtl sogar "mehr" parallel als mit 2.
Für jede ISR muss der Controller bestimmte Instruktionen durchführen 
(z.B. sichern von Registerinhalten) bevor er überhaupt mit dem 
tatsächlichen Code anfängt. Und da 2 ISRs sowieso nie parallel sein 
können, werden die Encoder sowieso hintereinander eingelesen.
Mit einer ISR sparst du dir aber zusätzlichen Code, der lediglich für 
das Ausführen der ISR gedacht ist und kannst die Encoder in einer ISR 
direkt hintereinander einlesen.
1
Mit 1 ISR:
2
- Zu ISR springen
3
- Register sichern etc.
4
- Encoder 1 einlesen
5
- Encoder 2 einlesen
6
- ISR verlassen
7
8
Mit 2 ISRs
9
- Zu ISR 1 springen
10
- Register sichern etc.
11
- Encoder 1 einlesen
12
- ISR verlassen
13
- Zu ISR 2 springen
14
- Register sichern etc.
15
- Encoder 2 einlesen
16
- ISR verlassen

mfg

von Berus (Gast)


Lesenswert?

Ok, also nur eine ISR. Ich bitte fuer meine uebertrieben Vorsicht um 
Nachsicht. Lieber frage ich aber noch Mal nach.

Kannst du bestaetigen, dass es fuer das Hauptprogramm keine 
Schwierigkeiten gibt, wenn ein Inkrement eines jeden Encoders ein 
Interrupt ausloest? Du meintest bereits, dass Mechanik viel langsamer 
ist also Elektrotechnik.

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.