Hallo,
die ISR benötigt minimum 22,8µs, der ATtiny taktet mit 8MHz.
Damit kann ich die Servos nur in Schritten größer 2° einstellen.
Ist es irgendwie möglich den Code schneller zu machen?
> Damit kann ich die Servos nur in Schritten größer 2° einstellen.
Genauer lassen sich die üblichen Modellbau Servos ohnehin nicht
einstellen, egal welche Klimmzüge du mit der Ansteuerung anstellst.
Veit D. schrieb:> die ISR benötigt minimum 22,8µs, der ATtiny taktet mit 8MHz.> Damit kann ich die Servos nur in Schritten größer 2° einstellen.
Schön - was willst Du denn genau? So beschrieben, dass alle es verstehen
:-)
Hallo,
der restliche Code ist sehr umfangreich, liest sich bestimmt niemand
durch.
Die Vergleiche benötigen die meiste Zeit. Darum gehts.
Die K6...ON K7...OFF Anweisungen sind define mit sbi und cbi.
Die count Vergleiche habe ich nun von 16Bit auf 8Bit geändert. Der
Wertebereich bis 255 reicht aus. Sind nur Ganzzahlvergleiche. Macht 4µs
weniger. Damit kann ich schon einmal sicher auf 2° einstellen. Weil die
ISR unter 22µs bleibt. Sonst gibts unnötige Abweichungen wenn die ISR
länger benötigt wie berechnet.
Stefan du meinst weniger wie 2° macht keinen Sinn? Habe das Gefühl das
die sich schon in 1° Schritten einstellen lassen.
Hallo,
der ATtiny taktet mit seinen internen 8MHz. Schneller geht nicht, alle
Pins sind belegt. Das Design ist leider fertig.
Die K6...OFF K7...OFF Anweisungen sind define mit sbi und cbi.
Schneller gehts glaube ich nicht.
Alle Ganzzahlvergleiche sind auf 8Bit Datentyp geändert.
Veit D. schrieb:> Die count Vergleiche habe ich nun von 16Bit auf 8Bit geändert.
Wäre jetzt auch meine Empfehlung gewesen.
Hast du denn im Compiler mal Optimierung eingeschaltet? (Also das
Projekt als "Release" kompilieren und in den Projekteinstellungen den
Compiler auf Geschwindigkeit trimmen)
Es gibt unterschiedliche Servos, unterschiedliche Hysteres. Einige sind
nervöser, andere ruhiger, die 1° sind nicht unrealistisch. Ob das so ist
lässt sich ja einfach testen indem die ISR auf ein Servo reduziert wird.
Hast du schon mit verschiedenen Optimierungen kompiliert?
Hallo,
es ist ein ATtiny841.
vermessen tue ich die ISR mit dem eigentlich 8. Servopin.
Diesen Pin schalte ich am Anfang der ISR ein und am Ende wieder aus.
Gemessen wird mit Oszi und Datalogger.
sbi und cbi sind die üblichen defines.
1
#ifndef sbi
2
#define sbi(sfr, bit) (_SFR_BYTE(sfr) |= _BV(bit)) // setzt das angegebene Bit auf 1
3
#endif
4
#ifndef cbi
5
#define cbi(sfr, bit) (_SFR_BYTE(sfr) &= ~_BV(bit)) // setzt (löscht) das angegebene Bit auf 0
6
#endif
Okay, auch wenn vielleicht unter 2° keinen echten Sinn machen, wäre ich
wenn möglich dennoch interessiert ob die ISR noch schneller gemacht
werden kann.
Hallo,
Compileroptimierung habe ich noch nicht probiert, möchte vorher den Code
optimal wissen. Wenn der Code schon das optimale wäre, kann ich mit dem
Compiler rumspielen. Ja, wäre eine Idee.
Die einzelnen Pulscounts können alle unterschiedlich sein.
Veit D. schrieb:> if (count == MaxPulscount) { // nach aktuell längster Pulsdauer> Timer stoppen> stop_Timer1();> count = 0;> state_running_Timer1 = false;> }>> count++;
Ist es gewollt, dass count mit 1 statt 0 stoppt?
Veit D. schrieb:> Ist es irgendwie möglich den Code schneller zu machen?
Den Code-Inhalt der Funktion
stop_Timer1();
in die ISR aufnehmen. Spart mindestens Call und Return,
vielleicht auch noch Stack-Sicherung und Stack-Restore.
Dieter F. schrieb:> Dieter F. schrieb:>> Schön - was willst Du denn genau? So beschrieben, dass alle es verstehen>> :-)>> Nochmal ...
Das ist eine normale Soft-PWM.
Hallo,
Jungs, habe Mist gemacht, hatte die falsche Oszi Messeinstellung. Ich
muss ja um den ISR zu vermessen die High Pulslänge messen und nicht die
Periodendauer. Dann schwirrten mir noch die Werte mit anderen
Timerberechnungen im Kopf rum sodass ich nicht drauf kam das etwas nicht
stimmt.
Also aktuell benötigt die ISR 8µs.
Großes Entschuldigung.
@ Mawin:
wenn ich zu ISR Beginn count++ mache, dann würde er doch um eins zu kurz
zählen, sprich die Pulsslängen wäre um eine Timerpulslänge zu kurz?
Wenn der Timer selbst einen Umlauf später stoppt ist das nicht schlimm.
Er soll nur nicht ständig laufen.
@Joachim:
wie meinst du das? Momentan weiß ich nicht wie du das meinst. Um die
Timeraktivität so kurz wie möglich zu halten, starten alle zeitgleich.
Damit läuft der Timer max. 2ms und hat 20ms Pause.
sind es nun 7,8 statt 8µs. Danke für den Hinweis.
Edit:
Muss mich revidieren, macht keinen Unterschied. Hängt von der Oszi
Auflösung Einstellung ab. Beide Varianten 7,8µs. Scheinbar greift hier
schon der Compiler ein.
Veit D. schrieb:> Damit läuft der Timer max. 2ms und hat 20ms Pause.
Brauchst du das für irgendwas anderes? Wenn nicht:
Jeder ISR-Aufruf erzeugt genau einen Impuls für einen Servo.
-alle Ausgänge löschen
-den, der dran ist, setzen
-OCR mit aktuellem+gewünschter Impulslänge laden, Überlauf der Addition
stört nicht, wenn der Timer durchläuft
Den meisten Servos ist die Impulspause völlig egal, kann man dann auch
komplett weglassen. Oder aber einen neunten Zyklus einführen.
Hallo,
aha, verstehe wie du das meinst. Dann würde der Timer maximal 8x 2ms
hintereinander laufen. satte 16ms. Kann ich mir leider nicht erlauben.
Genau das würde das restliche Programm stören, vorallendingen meine
schnelle uart Kommunikation mit 250kBaud. Das ist der Nachteil schneller
Kommunikation.
Veit D. schrieb:> wenn ich zu ISR Beginn count++ mache, dann würde er doch um eins zu kurz> zählen, sprich die Pulsslängen wäre um eine Timerpulslänge zu kurz?>> Wenn der Timer selbst einen Umlauf später stoppt ist das nicht schlimm.> Er soll nur nicht ständig laufen.
Der Timerlauf fängt aber bereits mit 1 an, weil der letzte Timerlauf mit
1 aufhört (weil count=0; count++)
?
Das tut doch nicht weh, wenn der läuft :-).
Alle rund 1,5ms kommt ein int mit ein paar wenigen Takten, das ist doch
kein Problem für die Kommunikation.
Nach deinem Verfahren hast du eine viel höhere zeitliche Belastung. Die
ISR selbst dauert viel länger, und du musst sie für vernünftige
Servoauflösung sehr oft aufrufen.
Hallo,
das muss ich mir nochmal durch den Kopf gehen lassen. Im groben kann ich
mir schon vorstellen wie das ablaufen könnte bzw. wie du das im Detail
meinst. Damit wäre die Interruptlast drastisch reduziert, wenn eine ISR
noch nur aller 1 bis 2ms dazwischen funkt. Nur wie ich die 8 Servos
nacheinander dann einzeln steuere ist mir noch nicht so richtig bewusst.
@ Mawin:
habe nochmal nachgedacht. Weil wo ich den Code geschrieben habe ist
schon eine Weile her. Mal angenommen ich möchte eine Pulslänge von einem
count erzeugen, sprich einer Timerlänge von 22µs.
Ich starte in einer anderen Funktion den Timer mittels Prescaler setzen.
Der Timer läuft los und springt in die ISR wenn der Compare Vergleich
gültig ist. Nach 22µs. Dann wird 1 == 1 verglichen, Pulslänge fertig.
Demzufolge haut das hin.
Hallo,
deine obigen Stichpunkte nochmal anders formuliert.
Würde für mich bedeuten, ich setze in einer Funktion, nicht in ISR, den
gewünschten Servopin auf 1. Der Puls beginnt damit. Gleichzeitig, also
sofort danach, lade ich den OCRnx auf den gewünschten Wert für die
geforderte Pulslänge, aktueller Zähler + "Längencount" und die ISR
selbst macht nichts anderes als pauschal alle Servopins wieder auf 0 zu
setzen. Hier muss ich nicht nochmal den aktiven Pin rausfischen.
@ Veit Devil (devil-elec)
>Würde für mich bedeuten, ich setze in einer Funktion, nicht in ISR, den>gewünschten Servopin auf 1. Der Puls beginnt damit. Gleichzeitig, also>sofort danach, lade ich den OCRnx auf den gewünschten Wert für die>geforderte Pulslänge, aktueller Zähler + "Längencount" und die ISR>selbst macht nichts anderes als pauschal alle Servopins wieder auf 0 zu>setzen. Hier muss ich nicht nochmal den aktiven Pin rausfischen.
Nicht ganz. Sinnvollerweise packt man die gesamte Servofunktionalität in
die ISR. Mit der seriellen Servopulserzeugung kann man theoretisch mit
einer Auflösung von 1 CPU-Takt die Servosignale erzeugen, weil dann nur
noch der Jitter der ISR reinspuckt aber nicht mehr die minimale
ISR-Laufzeit.
>meinst. Damit wäre die Interruptlast drastisch reduziert, wenn eine ISR>noch nur aller 1 bis 2ms dazwischen funkt.
Die ISR funkt gar nicht dazwischen, sie macht einfach ihren Job.
> Nur wie ich die 8 Servos>nacheinander dann einzeln steuere ist mir noch nicht so richtig bewusst.
Das ist einfach. Die Servosignale werden zwar zeitlich versetzt
generiert, in der Praxis ist es aus Sicht der übergeordneten Steuerung
aber vollkommen egal! Dein Programm übergibt per Funktion/globale
Variablen die Servo-Pulsbeite an die ISR und fertig. Die Servos
reagieren aber praktisch genau so, wie wenn die Pulse nicht zeitversetzt
wären. Schließlich sind es nur ein paar ms.
1
---****---
2
-------****---
3
-----------****---
Ungefähr so. Bei 8 MHz wird man wohl auf den Prescaler /8 gehen müssen,
das ist aber immer noch mehr als ausreichend.
#define SERVO_PERIOD (F_CPU / (8* SERVO_FRQ)) // Periodendauer der Servosignale in Timer-Takten
4
5
volatileuint16_tpulsbreite[SERVO_CNT];
6
7
ISR(TIMER1_COMPA_vect){
8
staticuint8_tservo;
9
10
switch(servo){
11
0:K6_1_OFF;K6_2_ON;break;
12
1:K6_2_OFF;K6_3_ON;break;
13
2:K6_3_OFF;K6_4_ON;break;
14
3:K6_4_OFF;K6_5_ON;break;
15
4:K6_5_OFF;K6_6_ON;break;
16
5:K6_6_OFF;break;
17
6:K6_1_ON;break;// virtueller Servo
18
}
19
20
servo++;
21
if(servo=>SERVO_CNT)servo=0;
22
OCR1A+=pulsbreite[servo];
23
}
Wenn man dann noch einen zusätzlichen, virtuellen Servokanal einfügt,
welcher als Pulsbreite die Differenz der echten Kanäle zur minimalen
Periodendauer kompensiert, ist es perfekt.
Veit D. schrieb:> Genau das würde das restliche Programm stören, vorallendingen meine> schnelle uart Kommunikation mit 250kBaud.
Das ist Unsinn. Die UART kann 2 Byte puffern, ehe ihr Interrupt
behandelt werden muß. Das ergibt satte 640 Zyklen Zeit bei 8MHz, um die
andere Interrupts verzögern dürfen.
Mit einem zusätzlichen 74HC164 könnte man 8 Servos sogar auf 125ns genau
ansteuern, indem der 74HC164 seinen CLK von einem Compare-Output
bekommt. Und zwischen den Takten sind >1ms (8000 Zyklen) Zeit, um das
nächste Compare vorzubereiten.
Hallo Falk,
mit dazwischen funken meinte ich, dass die ISR Zeit meine serielle
Kommunikation dahingehend stört, dass ich das Byte vom Ringbuffer zu
spät abhole, weil die ISR zu oft und zu lange benötigt. So wie ich den
Timer bzw. ISR derzeit verwende. Das kann ich zwar mit gewissen "Tricks"
umgehen, ist aber ziemlich unschön. Der Code wird auch nicht gerade
lesbarer.
Die Servos müssen und sollen bei mir nicht permanent laufen. 1s reicht
aus. Nur solange das sie sicher ihre Position erreichen. Ihre eigene
Getriebesperrung ist hier ausreichend. Deshalb das einschalten der
Servos in einer anderen Funktion als die ISR. Hat aber den Nachteil, wie
von dir aufgezeigt, gewisse Timingdifferenzen, wenn man nicht alles in
der ISR erledigt.
Ich überlege mir aktuell etwas, wo ich noch eine Art Flag mitgebe ob das
spezielle Servo was jetzt dran wäre überhaupt eingeschaltet werden soll
oder nicht.
Vielen Dank erstmal.
Tschau
Veit
Naja, wenn noch ein UART mit 250kBAud (DMX512?) nebenher läuft, muss man
ein wenig tricksen. D.h. man schalter kurz vor dem eigentlichen
Interrupt den UART-Interrupt ab und nach dem COMPA Interrupt wieder an.
Doch wie macht man das praktisch? Mit dem COMPB Interrupt!
1
#define TIMING_WINDOW 75 // Timer Cycles
2
3
ISR(TIMER1_COMPA_vect){
4
staticuint8_tservo;
5
6
switch(servo){
7
0:K6_1_OFF;K6_2_ON;break;
8
1:K6_2_OFF;K6_3_ON;break;
9
2:K6_3_OFF;K6_4_ON;break;
10
3:K6_4_OFF;K6_5_ON;break;
11
4:K6_5_OFF;K6_6_ON;break;
12
5:K6_6_OFF;break;
13
6:K6_1_ON;break;// virtueller Servo
14
}
15
16
servo++;
17
if(servo=>SERVO_CNT)servo=0;
18
OCR1A+=pulsbreite[servo];
19
OCR1B=OCR1A-TIMING_WINDOW;
20
USCR0B|=(1<<RXCIE0);// USART0 RXC ISR ON
21
}
22
23
ISR(TIMER1_COMPB_vect){
24
USCR0B&=~(1<<RXCIE0);// USART0 RXC ISR OFF
25
}
TIMING_WINDOW muss per #define so groß gewählt sein, daß die UART RX ISR
definitv WENIGER Timer-Takte braucht, gleichzeitig aber innerhalb dieser
Zeit nicht mehr als 2 Datenbytes ankommen können.
Hallo,
aber Peter, die uart kann meinetwegen 2000 Bytes puffern. Wenn die
Hauptschleife keine Zeit hat diese abzuholen gehen auch hier irgendwann
Bytes verloren. Ich kann ja nur vom aktuellen Zustand reden. Und da wird
die ISR aller 22µs aufgerufen und benötigt selbst derzeit 8µs, es
verbleiben für das Hauptprogramm demzufolge nur 14µs für alles, unter
anderem ein Byte vom Ringerbuffer zu holen.
Wie gesagt, ich rede vom aktuellen Programmzustand.
Wenn ich das ändere wie hier gesagt, sieht das alles anders aus.
Das darf man jetzt nicht alles zusammenwürfeln, dann kommen falsche
Meinungen bei raus.
Ich tüftel mal weiter ... :-)
Tschau
Veit
Falk B. schrieb:> Naja, wenn noch ein UART mit 250kBAud (DMX512?) nebenher läuft, muss man> ein wenig tricksen.
Da braucht man gar nicht tricksen. Bei 250kBaud dauert ein Zeichen schon
mal 40µs, also 320 Takte. Das braucht dein obiges Programm bei weitem
nicht, ich schätze mal um die 100, wenn überhaupt. Also selbst bei
gleichzeitigem OCR- und UART-Int gibts keine Probleme.
Hallo,
upps Falk hat schon wieder geantwortet.
Wenn die ISR kurz genug ist, sollte das die uart Kommunikation nicht
stören, wenn das aller 1 bis 2ms auftritt. Wenn alle Stränge reißen,
kann ich notfalls die Baudrate halbieren, möchte ich aber erstmal
vermeiden.
Kein DMX, normale serielle zwischen mehreren Controllern.
Das wird am Ende eine Modelleisenbahnsteuerung.
Im Grunde ist es eine Eigenbau SPS wenn man das nüchtern betrachtet.
Jetzt hat Joachim zwischenzeitlich noch geantwortet.
Geht alles in die richtige Richtung.
Danke euch erstmal.
@ H.Joachim Seifert (crazyhorse)
>> Naja, wenn noch ein UART mit 250kBAud (DMX512?) nebenher läuft, muss man>> ein wenig tricksen.>Da braucht man gar nicht tricksen.
Doch, muss man!
> Bei 250kBaud dauert ein Zeichen schon>mal 40µs, also 320 Takte. Das braucht dein obiges Programm bei weitem>nicht, ich schätze mal um die 100, wenn überhaupt. Also selbst bei>gleichzeitigem OCR- und UART-Int gibts keine Probleme.
Falsch!
Das Problem ist der JITTER! Wenn nämlich ein handvoll Takte VOR dem
COMPA-Interrupt der UART-RXC Interrupt aktiv wird, wird dieser
ausgeführt und verzögert damit die Ausführung des COMPA-Interrupts. Das
ist natürlich zufällig -> Jitter im Servosignal!
@ Veit Devil (devil-elec)
>Wenn die ISR kurz genug ist, sollte das die uart Kommunikation nicht>stören, wenn das aller 1 bis 2ms auftritt.
Schon mal das Ganze anders herum betrachtet? Daß der UART die
Servosignale stört?
>Kein DMX, normale serielle zwischen mehreren Controllern.>Das wird am Ende eine Modelleisenbahnsteuerung.
Mit 250kBaud? Wieviel Millionen Weichen und Licher willst du damit
steuern?
Hallo,
anders betrachtet auch wieder wahr. Anderer Blickwinkel.
Das wird keine einfache Steuerung die nur auf Benutzereingaben wartet.
Die soll in bestimmten Grenzen selbst Intelligenz zeigen. Kommt eine Lok
woher angefahren, muss sowohl dessen Position als auch Richtung erkannt
und abgefragt werden, danach werden entsprechend Weichen gesteuert. Das
muss alles bissel ratz fatz gehen. An zu steuernden Teilen und
vorallendingen Eingangssignalen (Sensoren) kommt schon einiges zusammen.
Auch wenn die Anlage nicht groß wird.
Veit D. schrieb:> Ich kann ja nur vom aktuellen Zustand reden. Und da wird> die ISR aller 22µs aufgerufen und benötigt selbst derzeit 8µs, es> verbleiben für das Hauptprogramm demzufolge nur 14µs für alles, unter> anderem ein Byte vom Ringerbuffer zu holen.
Den Zustand kannst Du leicht ändern. Beim nacheinander Erzeugen der
Servosignale hast Du nur alle 1..2ms einen Timerinterrupt und schon ist
die CPU-Last kaum merkbar.
Und den Jitter durch die UART kannst Du gering halten, indem der
UART-Interrupt sich selbst disabled und global die Interrupts freigibt
(am Ende umgekehrt).
Völlig jitterfrei wäre die 74HC164 Variante.
Hallo,
Peter, darüber reden wir doch schon die ganze Zeit, genau darüber ...
Aber schön das du der gleichen Meinung/Ansicht bist.
Die Falk Variante klingt vielversprechend. Ich versuche mich erstmal
daran.
Habe das Rechtecksignal im Normal- und CTC Mode schon hinbekommen mit
switch-case. Werde wohl bei CTC bleiben.
Wünsche allen schon einmal einen guten Rutsch ...
& vielen Dank
Veit
@ Veit Devil (devil-elec)
>Die Falk Variante klingt vielversprechend. Ich versuche mich erstmal>daran.
Gut.
>Habe das Rechtecksignal im Normal- und CTC Mode schon hinbekommen mit>switch-case. Werde wohl bei CTC bleiben.
Nein! Diese Methode benötigt den Normalmodus des Timers! Der muss von
0-0xFFFF durchlaufen!
Hallo,
warum unbedingt Normal-Mode?
Worin macht sich das bemerkbar?
Im Normal-Mode addieren
OCR1A += pulsweite;
Im CTC Mode zuweisen
OCR1A = pulsweite;
Ich sehe es auf dem Oszi das es erstmal passt.
Alle 3 haben 2ms Puls und 18ms Pause.
@Veit Devil (devil-elec)
>Im Normal-Mode addieren>OCR1A += pulsweite;>Im CTC Mode zuweisen>OCR1A = pulsweite;
Ok, so geht es auch.
>Ich sehe es auf dem Oszi das es erstmal passt.>Alle 3 haben 2ms Puls und 18ms Pause.
In deinem Test ist ja auch alles statisch und gleich.
>CTC, Prescaler 1.
Tja, hier liegt das Problem. Bei 8 MHz und 50 Hz (20ms) Wiederholrate
hat man 160.000 Timer-Takte / Servo-Periode. Ein 16 Bit Timer kann aber
nur bis 65535 zählen bzw. 65536 Zyklen/Umlauf. Also müßte man die
160.000 Takte durch MINDESTENS drei virtuelle Servos auf mindestens 3
ISR-Durchläufe strecken, damit der OCR1A Wert immer <=65535 ist. Kann
man machen, ist aber irgendwo albern. Mit Prescaler /8 und 1us
Zeitauflösung hat man bei 2ms Pulsbreite schon 2000 Schritte/11
Bit/0,18° Auflösung. Das sollte reichen.
Hallo,
korrigiere mich wenn ich falsch liege.
Meine Meinung zum OCR1A Wert, Überlauf usw.
Der Timer muss nur bis 2ms laufen. Die Pulspause erzeugen wir doch mit
den virtuellen Servos. Womit wir die restliche Zeit strecken um auf 18ms
Pause zu kommen. Das ist doch dein Trick, den ich so verstanden hatte
bzw. auch der von Joachim.
Wenn die ISR aller 20ms aufgerufen wird könnte ich doch nie nach 2ms
abschalten.
Oder wir reden aneinander vorbei und du möchtest nur ein einziges
virtuelles Servo verwenden was die restliche Pause erzeugt, dann müßte
ich deinen Einwand beachten, weil dann der Timerwert entsprechend groß
wird.
1
ISR(TIMER1_COMPA_vect)
2
{
3
P_ON;// Sichtbarkeit des Timers zum vermessen
4
5
staticuint8_tservo=0;
6
constuint8_tSERVO_CNT=9;// Anzahl Servos
7
uint16_ttemp;
8
9
switch(servo){
10
case0:Servo0_OFF;Servo1_ON;temp=9999;break;
11
case1:Servo1_OFF;Servo2_ON;temp=11999;break;
12
case2:Servo2_OFF;temp=15999;break;
13
case3:temp=15999;break;// virtuel
14
case4:temp=15999;break;// virtuel
15
case5:temp=15999;break;// virtuel
16
case6:temp=15999;break;// virtuel
17
case7:temp=15999;break;// virtuel
18
case8:temp=15999;break;// virtuel
19
case9:Servo0_ON;temp=7999;break;
20
}
21
22
servo++;
23
24
if(servo>SERVO_CNT)servo=0;
25
26
OCR1A=temp;
27
28
P_OFF;
29
}
Passt immer noch, egal ob CTC oder Normal.
Bis ich die Pulsweitenberechnungen angepasst habe dauert noch, derweile
behelfe ich mir so zum testen und rantasten wie das funktioniert.
@ Veit Devil (devil-elec)
>Meine Meinung zum OCR1A Wert, Überlauf usw.>Der Timer muss nur bis 2ms laufen. Die Pulspause erzeugen wir doch mit>den virtuellen Servos. Womit wir die restliche Zeit strecken um auf 18ms>Pause zu kommen. Das ist doch dein Trick, den ich so verstanden hatte>bzw. auch der von Joachim.
Ja.
>Wenn die ISR aller 20ms aufgerufen wird könnte ich doch nie nach 2ms>abschalten.
Davon war doch auch nie die Rede.
>Oder wir reden aneinander vorbei
Wahrscheinlich.
>und du möchtest nur ein einziges>virtuelles Servo verwenden was die restliche Pause erzeugt,
So der Ansatz.
> dann müßte>ich deinen Einwand beachten, weil dann der Timerwert entsprechend groß>wird.
Das meinte ich.
>Passt immer noch, egal ob CTC oder Normal.
Sicher.
Ob Normalmodus oder CTC ist fast egal. Das Problem des zu schnellen
Taktes bei 8 MHz ist davon unabhängig.
Hallo,
okay, mit nur einen einzigen virtuellen Servo zum Pause strecken sollte
die ISR auch zeitlich kürzer dauern, mit weniger cases.
Gut, Danke für den Einwand.
@Veit Devil (devil-elec)
>okay, mit nur einen einzigen virtuellen Servo zum Pause strecken sollte>die ISR auch zeitlich kürzer dauern, mit weniger cases.
Nein, das tut sie nicht, denn der Compiler ist im Normalfall schlau
genug, die Auswertung eines switch() Konstrukts recht schnell und nahezu
unabhängig von der Anzahl der case Einträge zu machen. Er macht das
NICHT wie du mit einer riesigen Kette von if() Vergleichen! Und selbst
wenn sie geringfügig länger dauern sollte, spielt das keinerlei Rolle,
denn diese ISR wird nur N+1 mal pro Periode für N echte Servos
aufgerufen. Ob das nun 7 oder 10 sind ist praktisch egal.
Falk B. schrieb:> Falsch!>> Das Problem ist der JITTER! Wenn nämlich ein handvoll Takte VOR dem> COMPA-Interrupt der UART-RXC Interrupt aktiv wird, wird dieser> ausgeführt und verzögert damit die Ausführung des COMPA-Interrupts. Das> ist natürlich zufällig -> Jitter im Servosignal!
So herum gesehen ist das ein Problemchen, aber auch nur ein
theoretisches.
Wie lange dauert ein Rx-Int incl. wegschreiben in einen Ringbuffer? 30
Takte? Vielleicht 50, schau ich mir jetzt nicht genau an. Wir reden bei
50 Takten von max. 6µs jitter, eher weniger. Und das nur sporadisch am
selben Servo. Vielleicht kann man es an der Stromaufnahme erkennen,
bewegen wird es sich nicht. Wichtig ist nur, dass keine bytes der
Kommunikation verloren gehen. Und das ist sichergestellt.
@ H.Joachim Seifert (crazyhorse)
>> Das Problem ist der JITTER! Wenn nämlich ein handvoll Takte VOR dem>> COMPA-Interrupt der UART-RXC Interrupt aktiv wird, wird dieser>> ausgeführt und verzögert damit die Ausführung des COMPA-Interrupts. Das>> ist natürlich zufällig -> Jitter im Servosignal!>So herum gesehen ist das ein Problemchen, aber auch nur ein>theoretisches.
NEIN! Weder ein Problemchen noch theoretisch!
>Wie lange dauert ein Rx-Int incl. wegschreiben in einen Ringbuffer? 30>Takte? Vielleicht 50, schau ich mir jetzt nicht genau an. Wir reden bei>50 Takten von max. 6µs jitter, eher weniger.
Richtig! Und ohne diesen Jitter hätte das Signal nur 1-2 CPU Takte,
sprich 125-250ns Jitter, dazwischen liegt der Faktor 24-48!
> Und das nur sporadisch am>selben Servo. Vielleicht kann man es an der Stromaufnahme erkennen,>bewegen wird es sich nicht.
Das weißt du gar nicht. Besonders digitale Servos gelten als sehr agil!
> Wichtig ist nur, dass keine bytes der>>Kommunikation verloren gehen. Und das ist sichergestellt
Du bist und bleibst ein Schwätzer ohne Rückgrat. Ein Mensch mit diesem
hätte einen Irrtum eingestanden und gut. Du aber versuchst mit billgen
Ausreden Recht zu behalten. Es wird dir nicht gelingen!
Hallo Nachtschwärmer,
bitte nicht in dem schönen Thread streiten. Das läuft gerade so schön
interessant mit euch.
Ob der Jitter praktisch stört oder nicht darüber kann man reden.
Wenn man das Problem kennt, kann man es berücksichtigen.
Ich werde auf jeden Fall beide Varianten testen.
Danke an beide.
Was ich eigentlich noch berichten wollte, dass die kürzere 3 cases
Version mit 3,4µs minimal schneller ist wie die 9 cases Version mit
3,7µs.
Die 3 cases Version hat noch einen Vorteil, die ISR hat auch mal eine
längere Pause, nämlich der restlichen Pausenzeit der fehlenden Servos.
Die Pausenfüllzeit. Vorrausgesetzt man nutzt nicht alle die möglich
sind.
Sieht man am unteren 4. Kanal, der Abstand der Spitzen, was dem ISR
Aufruf bedeutet.
Jetzt ist erstmal die Matraze am Zug ...
1
ISR(TIMER1_COMPA_vect)// wird aller >1ms aufgerufen (Prescaler 8)
2
{
3
Servo8_ON;
4
5
staticuint8_tservo=0;
6
constuint8_tSERVO_CNT=9;// Anzahl Servos
7
uint16_ttemp=0;
8
/*
9
switch(servo) { // ISR Zeit 3,4µs
10
case 0: Servo5_OFF; Servo6_ON; temp = 1499; break;
11
case 1: Servo6_OFF; Servo7_ON; temp = 1999; break;
12
case 2: Servo7_OFF; temp = 16999; break; // virtuelle
Das ist doch kein Streit :-)
Ich hatte doch geschrieben, dass Falk prinzipiell Recht hat - damit habe
ich überhaupt kein Problem.
Die Frage ist einfach: stört das ab und zu auftretende Fehlerchen oder
nicht? Servos haben auch einen Totbereich in der Grössenordnung.
Und natürlich kann man es auch von vornherein so exakt wie möglich
machen.
Hallo,
schön, dann bin ich beruhigt. :-)
Zur Zeit teste ich mit permanenter Kommunikation ohne Pause. Die
serielle hat demzufolge keine Pause. Bis jetzt noch kein Fehler, kein
Antwort-Timeout oder sonstige Verschlucker. Sehr schön.
Habe noch den usart receive Interrupt vermessen, diese ISR benötigt
genau 4,24µs, sagen wir 4,3µs.
Damit muss das abzügliche Timing Windows gegen den Jitter mindestens:
4300ns/125ns = 35 Takte/Counts betragen.
Dazu kommt bestimmt noch ein Overhead den ich nicht messen kann, rein-
und rausspringen in die ISR dazu. Also ich schätzte unter 50 darf es
nicht sein. Oder sind die oben besagten 75 Takte schon anderweitig
berechnet wurden?
@Veit Devil (devil-elec)
>Habe noch den usart receive Interrupt vermessen, diese ISR benötigt>genau 4,24µs, sagen wir 4,3µs.
Vorsicht. Die Zeit vor und nach dem Setzen deines IOs kannst du nicht
messen, die sieht man nur im Assemblercode! Das können mal fix 50 Takte
sein, macht bei 8 MHz ca. 6us!
Beitrag "Re: Frage zu IR-Remote+LED-Strips an AVR">Damit muss das abzügliche Timing Windows gegen den Jitter mindestens:>4300ns/125ns = 35 Takte/Counts betragen.
Vorsicht, in meinem Beispiel muss man das in Timer-Takte umrechnen, also
nochmal /8., also eher 5-6.
Hallo,
mit aktiver Jitter Korrektur habe ich immer mal wieder
Kommunikationsaussetzer, sprich der Slave antwortet nicht, weil er das
Datenpaket nicht komplett lesen konnte.
Das Timing-Window kann ich niedriger und höher setzen, ändert leider
nichts.
Konfiguriere ich den Timer auf Normalmode, addiere ich OCR1x oder weise
ich zu, ändert auch nichts an den Aussetzern.
Im CTC Mode sind die Aussetzer sporadisch.
Im Normalmode ist eine Weile Ruhe um dann wieder massiv zu sein und dann
wieder Ruhe, als wenn sich ein Fehler aufaddiert und verschiebt.
Habe eigentlich schon alle Varianten probiert. Normal, CTC, OCR1x
addieren, zuweisen ...
Laut meiner Meinung kann OCR1B Werte zwischen 0 und 65535 annehmen,
demzufolge stört es die uart mit kleinen Werten und mit großen Werten
eher nicht. ???
ATtiny841
Hallo,
Ergänzung:
1 Datenbyte dauert 40µs.
Das komplette Protokoll aus 11 Bytes demzufolge 440µs.
Eigentlich antwortet der Slave nach 120µs.
Mein Antwort Timeoutfenster ist auf 5ms festgelegt.
@ Veit Devil (devil-elec)
>mit aktiver Jitter Korrektur habe ich immer mal wieder>Kommunikationsaussetzer, sprich der Slave antwortet nicht, weil er das>Datenpaket nicht komplett lesen konnte.
Schlecht.
>Das Timing-Window kann ich niedriger und höher setzen, ändert leider>nichts.
Dann liegt der Fehler woanders.
>Im CTC Mode sind die Aussetzer sporadisch.
Das kann eher Zufall sein.
>{> cli(); // Interrupts ausschalten> TCCR1A = 0; // Reset TCCR1A Register> TCCR1B = 0; // Reset TCCR1B Register> TIMSK1 = 0; // Reset TIMSK1 Register (disable Timer Compare Interrupts)
Das ist alles Unsinn, die Register haben definierte Reset-Werte.
> TCNT1 = 0; // Start 0
Dito.
> TIMSK1 = (1<<OCIE1A); // enable Compare Match A ISR> TIMSK1 |= (1<<OCIE1B); // enable Compare Match B ISR
Das kann man in eine Zeile schreiben.
>ISR(TIMER1_COMPA_vect) // wird aller >1ms aufgerufen (Prescaler 8)>{> Servo8_OFF;> static uint8_t servo = 0;> const uint8_t SERVO_CNT = 3; // Anzahl Servos> const uint8_t TIMING_WINDOW = 7; // 6µs 125ns Prescaler 8 = 6,25
Timertakte
Warum änderst du die #defines in echte Variablen? Das ist zwar nicht
schlimm bringt aber auch keine Vorteile.
Der Fehler entsteht im Gesamtzusammenhang. Wir kennen aber weder deinen
vollständigen Quelltext noch deine Testumgebung.
Hmm, ich hab noch ne Vermutung. Wenn dein TIMING_WINDOW zu klein ist,
kann es passieren, daß gerade bei UART RXC Interrupt der COMPB und kurz
danach der COMPA Interrupt aktiv werden. Wird der UART RX Interrupt nun
beendet, wird ZUERST der COMPA Interrupt ausgeführt und DANACH erst
COMPB, denn der hat eine niedrigere Priotität, weil er weiter unten in
der Interruptvektortabell steht, siehe Datenblatt! Und damit wird der
UART für LANGE Zeit ausgeschaltet!! AUTSCH.
Lösung. Setz mal TIMING_WINDOW = 20 oder gar 30.
Hallo,
#defines:
habe gelesen das man wenn möglich const statt #defines verwenden soll,
wegen Datentypprüfung und der Compiler kann damit besser umgehen.
einzelne Zeilen zur Registerkonfig:
damit kann ich beim testen Dinge besser auskommentieren
Register vorher nullen:
alte Angewohnheit, programmiere parallel noch einen Arduino in der
Arduino IDE, da muss man das machen, bei eigener Timerkonfig
Sind jedoch alles Dinge die keine Rolle spielen, jeder schreibt seinen
Syntax etwas anders. :-)
Window 20-30 zeigt leider keine Besserung, würde meiner Logik
widersprechen, je größer der Wert umso länger ist die uart tot gelegt.
Dagegen bringt eine 1 auch keine Besserung. Verhext.
Nochmal zum Verständnis. Der "uart rx ISR" muss nur um die Zeit eher
abgeschaltet werden wie wie der "uart rx ISR" selbst benötigt?
Wenn ich auf den COMPB trigger am Oszi, dann zappelt COMPA zeitlich hin
und her. Das sollte doch eigentlich immer im gleichen Abstand sein?
Hallo,
meine aktuelle These.
OCR1B wird ja im COMPA ISR geändert. Bedeutet doch eigentlich, dass
beide nicht syncron laufen können, weil COMPB immer erst auf den compare
wartet bevor der neue Wert übernommen wird.
Dagegen wird OCR1A sofort übernommen, weil es in der eigenen COMPA
geändert wird.
Ich denke hier müssen wir neu ansetzen?
ich blick zwar grad nicht mehr durch, aber der Thread und die Diskusion
gefällt mir.
Hier kann man entgegen vieler Kritiker auch mal Perlen fischen und was
lernen.
Hallo,
genau meine Meinung. Mir gefällt es auch. Fühl mich wohl.
Habe auch bis jetzt viel gelernt wie man die Servosteuerung besser
machen kann mit weniger CPU Last. Meine alte Variante war zu sehr CPU
lastig am absoluten Rande des machbaren.
Die nackte neue Servosteuerrung funktioniert.
Nur sobald ich einen zweiten Compare Match B dazu nehme, der paar Takte
vor Compare_A den uart receive abschalten soll, kommt die Kommunikation
außer Tritt. Obwohl genügend Zeit wäre.
Ein Byte dauert 40µs. Bedeutet es muss nach 40µs vom Ringbuffer geholt
werden sonst ist es verloren und damit ein Kommunikationsaussetzer.
Oder wir unterschätzen vielleicht das Compare A und Compare B ISR
zusammen 40µs benötigen. ???
Habe auch schon CompA mit CompB vertauscht, aber da funktionierte nichts
mehr. Habe momentan keine Idee mehr.
> Bedeutet es muss nach 40µs vom Ringbuffer geholt> werden sonst ist es verloren und damit ein Kommunikationsaussetzer.
Trotz "The receive buffer consists of a two level FIFO"?
Hallo,
habe meine Kommunikation mal dahin gehend geändert das ich einigermaßen
debuggen bzw. mitloggen kann. Ich frage also den Slave permanent reihum
immer auf TCNT1, OCR1A und OCR1B ab und lasse das im Terminal ausgeben.
Immer wenn die Ausschrift "countErrors" erscheint kam vom Slave keine
Antwort zurück.
Zwischen den Ausschriften "countErrors" habe ich immer paar Zeilen
gelöscht, sonst wird das Ellenlang.
Ich erkenne jedoch das OCR1B manchmal größer OCR1A ist. Was nicht sein
dürfte. Und zwar immer dann, wenn OCR1A einen Wert für einen Puls hat.
Nicht wenn er die Pausenlücke füllt.
Zwischen den Anfragen liegen 1,36ms. Schneller geht mit Ausgabe nicht.
Kann auch daran liegen, weil die Pulse kürzer sind wie das
Abfrageintervall und sich überschneidet.
S. Landolt schrieb:> Trotz "The receive buffer consists of a two level FIFO"?
Trotz. Das hat er ja geschrieben und er hat das Programm in der Realität
laufen...
> Das hat er ja geschrieben
In der Tat. Verstanden habe ich es trotzdem nicht.
Aber eine Frage, ich habe keine konkrete Vorstellung:
> Kommt eine Lok woher angefahren...
Wie schnell fährt eigentlich so ein Modellbahnzug?
S. Landolt schrieb:> Wie schnell fährt eigentlich so ein Modellbahnzug?
gefühlt unrealistisch schnell wenn ich so 50 Jahre zurück denke, die
Teile waren schneller als es dem Maßstab entsprechen dürfte.
Ich weiss ja nicht wie das heute bei Modellbauer gesehen wird, schnell
damit was auf der Anlage passiert und sich Zusände ändern gegen die
Langeweile, oder eher gemütlich um möglichst realitätsnah zu werden?
Hallo,
ich nutze die uart Lib von Peter Fleury, bis jetzt bestens.
Laut meinem Verständnis holt uart0_getc() immer nur ein Byte ab.
Danach muss erneut die Hauptprogrammschleife while(1) durchlaufen
werden.
Das wird scheinbar zu sehr gestört.
Warum und weshalb weiß ich auch nicht.
Ausschnitt:
S. Landolt schrieb:> Wie schnell fährt eigentlich so ein Modellbahnzug?
Schwallgeschwindigkeit :-)
Problem wird sein, die Weichen/Signale/... rechtzeitig auf Kurs zu
bringen. Das würde ich dann aber eher im sensorischen Bereich bzw. der
Auswertung der Sensoren sehen.
Ansonsten - wenn ein Tiny das nicht schafft, dann muss er halt Hilfe
eines seiner Art bekommen oder ein "Mächtigerer" muss eingreifen ...
Veit D. schrieb:> OCR1B wird ja im COMPA ISR geändert. Bedeutet doch eigentlich, dass> beide nicht syncron laufen können
Natürlich nicht.
1) Es gibt nur einen Rechenkern. Damit ist es vollig unmöglich, dass
zwei ISRs "synchron" (eigentlich war wohl eher "parallel" gemeint)
ablaufen. Die Dinger können immer nur nacheinander ablaufen. Die
Reihenfolge kann man als "quasi-zufällig" betrachten, geregelt ist sie
nur dann, wenn die beiden IRQs wirklich exakt gleichzeitig auftreten
oder beide während einer bestehenden Interruptsperre (aus beliebiger
anderer Quelle, insbesondere natürlich auch die exclusive Laufzeit
weiterer konkurrierende ISRs).
Und zwar dann in beiden Fällen durch die wohldokumentierte
Interrupt-Priorität (=Reihenfolge der Interrupt-Vektoren).
2) In den PWM-Modi wird ein neu gesetzter OCR-Wert niemals sofort
übernommen, sondern immer erst bein Eintreten
einer Bedingung, die in der "WGM-Tabelle" steht (Spaltenname :"Update of
OCRx").
> weil COMPB immer erst auf den compare> wartet bevor der neue Wert übernommen wird.> Dagegen wird OCR1A sofort übernommen, weil es in der eigenen COMPA> geändert wird.
OMG. RTFM.
> Ich denke hier müssen wir neu ansetzen?
Ich denke eher DU musst einfach mal das verschissene DB richtig lesen.
WIR (;o) haben das bereits vor langer Zeit getan...
Hallo,
also die Lok Geschwindigkeiten haben ja nun wirklich nichts mit dem hier
zu tun und auch nichts mit der Baudrate.
Ich habe nochwas vermessen, die "read_Ringbuffer_0() Funktions.
Die benötigt gemessen 32µs + unbekannten Overhead. Mit den Zeiten für
die Timer 1 Compares sprengt das bestimmt die 40µs für ein Byte holen.
Könnte gut sein das der Compare B mit seiner benötigten Zeit das
Zünglein an der Waage ist. Ist das möglich oder Quatsch?
@ c Liebhaber:
das ist mir alles klar, nur undeutlich ausgedrückt. Syncron kann man
auch anders verstehen, dass ein Compare immer genau um die exakte Zeit
vor oder nach dem anderen aktiv wird, ist für mich auch syncron.
Veit D. schrieb:> Syncron kann man> auch anders verstehen, dass ein Compare immer genau um die exakte Zeit> vor oder nach dem anderen aktiv wird, ist für mich auch syncron
Ja, wenn dem so ist - nur die Abarbeitung verzögert sich halt in
gleicher Reihenfolge durch den "Jitter". Und wenn zufällig beide
"parallel" auf dem gleichen Zeitpunkt landen "zieht" die
Interrupt-Priorität (wieder zzgl. "Jitter").
Veit D. schrieb:> das ist mir alles klar
Nein, ist es ganz offensichtlich nicht.
> Syncron kann man> auch anders verstehen, dass ein Compare immer genau um die exakte Zeit> vor oder nach dem anderen aktiv wird, ist für mich auch syncron.
Genau das ist aber eben nicht zwingend der Fall. Das klappt zuverlässig
nur ohne sonstige Interruptsperren (also insbesondere weitere
konkurrierende Interrupts) und unter der Bedingung, dass beide
Timer-ISRs schneller abgehandelt sind, als ein Timer-Tick dauert und
niemals exakt gleichzeitig ausgelöst werden.
Lies' endlich dieses verdammte Datenblatt! Diese permanente
Realitätsverweigerung ist ja echt zum Kotzen!
@ Veit Devil (devil-elec)
>OCR1B wird ja im COMPA ISR geändert. Bedeutet doch eigentlich, dass>beide nicht syncron laufen können, weil COMPB immer erst auf den compare>wartet bevor der neue Wert übernommen wird.
Falsche Formulierung. Die sind schon synchron. Aber das mit dem Update
könnte ein Problem sein.
>Dagegen wird OCR1A sofort übernommen, weil es in der eigenen COMPA>geändert wird.>Ich denke hier müssen wir neu ansetzen?
Man sollte es prüfen.
Das Datenblatt sagt, daß im Timer Mode 4 (CTC) OCR1x sofort einen Update
erfährt. Damit sollte das OK sein.
Ich glaube eher, daß sich dein Gesamtprogramm anderswo verhakt.
@ c-hater (Gast)
>Lies' endlich dieses verdammte Datenblatt! Diese permanente>Realitätsverweigerung ist ja echt zum Kotzen!
Deine cholerischen Anfälle ebenso. Nimm die Pillen und sei glücklich.
Wir sind es dann auch.
Falk B. schrieb:> Die sind schon synchron.
Ggf. - nur die Abarbeitung ist (logischerweise) unterschiedlich - je
nach Interrupt-Priorität (falls beide auf genau den gleichen Zeitpunkt
fallen) - ansonsten zählt halt "wer zuerst kommt mahlt zuerst" (auf
ATTiny-Ebene).
Aber das ist hier die falsche "Ebene" - bei der Sensorik und deren
Auswertung muss man ansetzen. Ein Servo schafft es sicher, eine Weiche
in einer 1/2 Sekunde umzustellen - oder?
Die Funktion bool read_Ringbuffer_0() ist ausbaufähig. Die Verarbeitung
der ankommenden Daten sollte man in einer echten Statemachine
machen. Das ist deutlich übersichtlicher und weniger fehleranfällig.
Aber prinzipiell sehe ich erstmal keine grundlegenden Probleme.
Dieter F. schrieb:> Ein Servo schafft es sicher, eine Weiche> in einer 1/2 Sekunde umzustellen - oder?
Das ist aber nicht der Sinn eines Servos zum Stellen der Weiche. Das
soll (wie beim Vorbild) schön langsam und gleichmäßig erfolgen.
Hallo,
Leute, wir lassen das mit der Weiche außen vor, gehört hier nicht rein.
Danke.
So.
Habe mittlerweile die Baudrate auf 125kBaud halbiert. Damit dauert ein
Byte 80µs. Ändert leider nichts am Problem. Auch mit Window bis 30
Timertakte. So langsam wirds echt knifflig woran das liegen könnte.
Falk, nur mal so gefragt, kam dir die Idee mit dem Jitter gestern
spontan oder hast du das schon mal woanders ans laufen bekommen?
1
If the new value written to OCRnA or ICRn is lower than the current value of TCNTn, the counter will miss the compare match. The counter will then have to count to its maximum value (0xFFFF) and wrap around starting at 0x0000 before the compare match can occur.
Im CompA ISR sehe ich da keine Probleme, TCNT1 ist ja in dem Zustand
immer kleiner OCR1A.
Beim OCR1B bin ich mir da im Moment nicht mehr so sicher, weil der wird
nicht in seiner eigenen ISR neu gesetzt. Obwohl es in der Theorie passen
sollte.
Falk B. schrieb:> Deine cholerischen Anfälle ebenso. Nimm die Pillen und sei glücklich.> Wir sind es dann auch.
Ich bin selten seiner Meinung - aber heute:
Nix cholerisch - eher begründet. Problem ist nicht die Schnelligkeit der
Servo-Bewegung - mehr der Auswertung der Sensoren und Steuerung der
Aktoren, von der hier keine Rede ist.
Es bringt gar nichts, mit 250 kB/S irgendetwas durch die Gegend "zu
brüllen" und auf schnelle Reaktion zu vertrauen, wenn ich das auch mit
Vorausschau rechtzeitig und in Ruhe machen kann.
@ Veit Devil (devil-elec)
>Habe mittlerweile die Baudrate auf 125kBaud halbiert. Damit dauert ein>Byte 80µs. Ändert leider nichts am Problem. Auch mit Window bis 30>Timertakte. So langsam wirds echt knifflig woran das liegen könnte.
Geh mal testweise auf eine SEHR niedrige Baudrate, meinetwegen 10kBit/s.
Wenn es dann immer noch klemmt, ist es weniger ein Timing- denn ein
logisches Problem.
>Falk, nur mal so gefragt, kam dir die Idee mit dem Jitter gestern>spontan
Nein.
> oder hast du das schon mal woanders ans laufen bekommen?
Das nicht, aber ähnliche Sachen.
>Im CompA ISR sehe ich da keine Probleme, TCNT1 ist ja in dem Zustand>immer kleiner OCR1A.
Ja.
>Beim OCR1B bin ich mir da im Moment nicht mehr so sicher,
Dort auch, denn OCR1B ist ja immer OCR1A-TIMING_WINDOW, und das sind nur
10-30 Takte.
> weil der wird>nicht in seiner eigenen ISR neu gesetzt. Obwohl es in der Theorie passen>sollte.
Tut es auch. Es ist der gleichen Zähler TCNT1.
@ Dieter F. (jim_quakenbush)
>> Deine cholerischen Anfälle ebenso. Nimm die Pillen und sei glücklich.>> Wir sind es dann auch.>Ich bin selten seiner Meinung - aber heute:>Nix cholerisch - eher begründet.
Der Ton macht die Musik!
>Es bringt gar nichts, mit 250 kB/S irgendetwas durch die Gegend "zu>brüllen" und auf schnelle Reaktion zu vertrauen, wenn ich das auch mit>Vorausschau rechtzeitig und in Ruhe machen kann.
Ist prinzipiell richtig, die 250kBaud sin im Moment eher ein Stresstest
der Software und sportlicher Ehrgeiz. Bei 250kBaud (DMX512) kann so ein
AVR noch GANZ andere Dinge "sychnron" erledigen ;-)
Falk B. schrieb:> Ist prinzipiell richtig, die 250kBaud sin im Moment eher ein Stresstest> der Software und sportlicher Ehrgeiz. Bei 250kBaud (DMX512) kann so ein> AVR noch GANZ andere Dinge "sychnron" erledigen ;-)
Ja, nur hier nicht erforderlich - offensichtlich. Mir sind die "wahren"
Anforderungen und der Gesamt-Zusammenhang nach wie vor nicht bekannt.
Dieter F. schrieb:>Problem ist nicht die Schnelligkeit der> Servo-Bewegung - mehr der Auswertung der Sensoren und Steuerung der> Aktoren, von der hier keine Rede ist.>> Es bringt gar nichts, mit 250 kB/S irgendetwas durch die Gegend "zu> brüllen" und auf schnelle Reaktion zu vertrauen, wenn ich das auch mit> Vorausschau rechtzeitig und in Ruhe machen kann.
Okay, ich gehe mal darauf ein. Mir ist das durchaus bewusst. Nur die
Steuerung der Aktoren und Sensorabfragen lasse ich bewusst in dem Thread
außen vor. Außer die Servopulserzeugung im Moment. Weil das sonst den
Thread unnötig verkompliziert. Das hat auch nichts mit dem aktuellen
Problem zu tun. Völlig andere Baustelle.
Ich habe für Aktoren und Sensoren 2 konkrete Vorstellungen wie ich das
machen werde. Welche Methode zum Zuge kommt hängt davon ab wie schnell
die Kommunikation am Ende sein wird und wieviel Slave-Controller am Ende
benötigt werden. Entweder bin ich schnell genug der Reihe nach alles
abzufragen. Oder ich frage nur gezielt die Slaves und die Werte ab ich
ich gerade benötige. Wahrscheinlich kommt letzte Methode zum Einsatz.
Höchstwahrscheinlich, das zeichnete sich schon länger ab.
Würde aber die Programmierung auf dem Master verkomplizieren. Weil ich
nicht klar getrennt erst alles abfragen, dann alles verarbeiten und am
Ende Steuerbefehle ausgeben kann. Muss dann alles vermischt werden.
Dennoch möchte die Kommunikation so schnell wie möglich haben. Mehrere
Abfragen addieren sich auch zu Verzögerungen. Noch eine andere Idee
schwirrt mir rum. Ich könnte noch gewisse Slaves für sich arbeiten
lassen. Wo zum Aktor die zugehörigen Sensoren an den gleichen Slave
passen. Nur wäre das dann außerhalb der Kontrolle vom Master. Wäre nicht
Sinn der Sache.
So genug abgeschweift.
@ Falk: :-)
erstmal Danke das du mit am Ball bleibst.
Während ich hier tippe läuft es mit Baudrate 10kBit und Window 10
fehlerfrei. Unter 7 kommen wieder sporadisch Fehler. Über 20min sind
locker vorbei. Kann als stabil angenommen werden.
Ich denke das Window darf nicht zu kurz sein, sonst ist es zu dicht am
CompA dran, dessen Aufruf. Zu lang und hoher Baudrate stört es die
Kommunikation.
Man müßte also die Zeit ab CompB ISR Aufruf bis CompA ISR beendet ist
betrachten. Das muss zeitlich mit der Baudrate passen damit kein Byte
verloren geht.
Falk B. schrieb:>>Beim OCR1B bin ich mir da im Moment nicht mehr so sicher,>> Dort auch, denn OCR1B ist ja immer OCR1A-TIMING_WINDOW, und das sind nur> 10-30 Takte.
Mein Gott, noch deutlicher konntest du deine krasse Inkompetenz
bezüglich Interrupts wirklich nicht mehr dokumentieren...
Ja ,die "sheduled time" mag immer in diesem Bereich liegen. Aber was
sagt das über die tatsächliche Ausführungszeit der ISR? Darüber solltest
du mal ernsthaft nachdenken...
Veit D. schrieb:> Weil das sonst den Thread unnötig verkompliziert.
Nö - vereinfachen würde - wenn allen bekannt.
Veit D. schrieb:> Dennoch möchte die Kommunikation so schnell wie möglich haben.
Was Du möchtest und was ggf. sinnvoll ist wäre noch zu verifizieren.
Womit ich wieder zur Eingangsfrage komme:
Dieter F. schrieb:> Schön - was willst Du denn genau? So beschrieben, dass alle es verstehen> :-)
Veit D. schrieb:> Hallo,>> Leute, wir lassen das mit der Weiche außen vor, gehört hier nicht rein.> Danke.>> So.> Habe mittlerweile die Baudrate auf 125kBaud halbiert. Damit dauert ein> Byte 80µs. Ändert leider nichts am Problem. Auch mit Window bis 30> Timertakte. So langsam wirds echt knifflig woran das liegen könnte.
Der Jitter entsteht doch, weil die Uart-ISR manchmal läuft, wenn
eigentlich der Timer wichtiger wäre. Das liegt daran, daß AVR8 (in
Verbindung mit den Default-Werten des ISR-Macros) keine laufende ISR
unterbrechen. Man kann aber die Uart-ISR so schreiben, daß sie die
Interrupt sofort wieder freigibt. Dazu gibt es eine spezielle Version
des ISR-Macros. Die Timer-ISR kann dann schon nach 2..3μs reagieren und
die Uart hat ja 40..80μs Zeit, falls der Timer dazwischen kommt, was ja
inzwischen eher selten passieren sollte. Eine "Disable-Uart-Int"-ISR
bräuchte man dann gar nicht mehr.
Nur mal so zur Gedankenanregung ...
PS:
Falk B. schrieb:> Der Ton macht die Musik!
Harhar. Der erste, der sich in diesem Thread ernsthaft im Ton vergriffen
hat, warst ja wohl mehr als eindeutig du.
Dank der erstaunlicherweise doch eher etwas laschen Löschtätigkeit der
Zensoren bei Regulars (a little bit of sarkasm is unavoidable here)
kann das diesmal wirklich jeder im Detail nachvollziehen...
Ich geh' jetzt pennen. Aber ich werde den Thread morgen ganz sicher noch
einmal besuchen. Bin gespannt, wie sich das weiter entwickelt. Vor allem
eigentlich, ob der Veit sich doch noch entscheidet, uns endlich sein
grausames Gesamtwerk zu präsentieren.
Weil: ich bin ziemlich sicher, dass ich jetzt schon ziemlich genau
weiss, was da falsch ist. Und es wird mir wie immer ein innerlicher
Vorbeimarsch sein, das haarklein aufzudröseln und dabei wieder die
übliche und natürliche Schlussfolgerung zu ziehen...
c-hater schrieb:> und dabei wieder die> übliche und natürliche Schlussfolgerung zu ziehen...
Na ja, da sind nicht alle unbedingt Deiner Meinung - wetten dass?
Guten Rutsch :-)
Welche Servos hast du denn nun überhaupt? Digital, spielarmes Getriebe
und Encoder? Oder eher die analoge Billigvariante mit einfachen
Labberzahnrädern, Plastiklagern und Poti?
Ich tippe auf zweiteres :-)
Unabhängig davon, dass man das Problem lösen sollte (könnte für andere
Sachen interessant sein) - fahr doch mal ohne serielle Kommunikation
immer abwechselnd je 1s 1500µs/1505µs. Reagiert es darauf?
Hallo,
nachdem Gesamtcode hat glaube ich noch niemand gefragt. Zumindestens die
die wirklich an der Problemlösung interessiert sind. Falls ich das
übersehen haben sollte möge man mir verzeihen. Ging ja bisher ganz schön
zu Faden. Also, wenn ihr unbedingt sehen wollt, bitte, aktueller Stand.
Die Formatierung hat beim kopieren leider wieder etwas gelitten.
Vorher noch 3 Screenshots vom Datalogger. Dabei sieht man im Fehlerfall
das CompB nach CompA ausgelöst wurde und damit mitten im Empfang diesen
abschaltet. Kanal "Sender" ist das was der ATtiny empfängt. Erkennt man
auch in der zeitlichen Abfolge denke ich.
Timer 1 läuft im CTC Mode.
@ c Liebhaber:
wenn du wirklich helfen möchtest, dann rede bitte nicht um den heißen
Brei, rede sachlichen Klartext wo du den Fehler vermutest.
1
/*
2
* Projekt -> meinProjekt Properties -> Konfiguration > alle Konfigurationen
3
* Projekt -> meinProjekt Properties -> C/C++ Compiler > Symbols > Defined symbols:
4
* eintragen: F_CPU=8000000UL
5
*
6
* Projekt -> meinProjekt Properties -> C++ Compiler > Miscellaneous:
7
* eintragen: -std=c++11 (wegen constexpr)
8
*
9
*/
10
11
#include<avr/io.h>
12
#include<stdio.h>
13
#include<stdlib.h>
14
#include<string.h>
15
#include<avr/interrupt.h>
16
#include<util/crc16.h>
17
#include<util/atomic.h> // für cli() und sei() mit SREG Sicherung
18
#include"pinDefi.h" // eigene I/O Definitionen
19
#include"usart.h" // rechts > 2. > hinzufügen > vorhandenes Element
20
#include"timer.h" // alles was mit Timern zu tun hat ausgelagert
21
22
#define NOP __asm__ __volatile__ ("nop")
23
24
#ifndef sbi
25
#define sbi(sfr, bit) (_SFR_BYTE(sfr) |= _BV(bit)) // setzt das angegebene Bit auf 1
26
#endif
27
#ifndef cbi
28
#define cbi(sfr, bit) (_SFR_BYTE(sfr) &= ~_BV(bit)) // setzt (löscht) das angegebene Bit auf 0
29
#endif
30
31
32
// UART Berechnungen des Wertes für das Baudratenregister aus Taktrate und gewünschter Baudrate
@ Carl Drexler (jcw2)
>unterbrechen. Man kann aber die Uart-ISR so schreiben, daß sie die>Interrupt sofort wieder freigibt. Dazu gibt es eine spezielle Version>des ISR-Macros. Die Timer-ISR kann dann schon nach 2..3μs reagieren und
Ist totzdem Mist, denn auch das ist mehr Jitter, als technisch nötig.
Die Variante mit COMPB funktioniert. Der Fehler des OP ist im Moment
noch unklar.
>Nur mal so zur Gedankenanregung ...
Lahm!
@ Veit Devil (devil-elec)
>nachdem Gesamtcode hat glaube ich noch niemand gefragt.
Doch ich.
>zu Faden. Also, wenn ihr unbedingt sehen wollt, bitte, aktueller Stand.>Die Formatierung hat beim kopieren leider wieder etwas gelitten.
Geht's noch? Lange Quelltexte gehören in den Anhang! Siehe
Netiquette!
"Wichtige Regeln - erst lesen, dann posten!
Groß- und Kleinschreibung verwenden
Längeren Sourcecode nicht im Text einfügen, sondern als Dateianhang
"
>Vorher noch 3 Screenshots vom Datalogger. Dabei sieht man im Fehlerfall>das CompB nach CompA ausgelöst wurde und damit mitten im Empfang diesen>abschaltet.
Damit kommen wir dem Problem näher.
@ Veit Devil (devil-elec)
>wobei ich soeben auch einmal entdecke das CompB vor CompA ordnungsgemäß>auslöst
Na was denn nun?
Das kann man einfach testen. Schalten direcht nach dem ABSCHALTEN des
UART RXC ein Testpin AUF LOW und ebenso in der anderen ISR nach dem
EINSCHALTEN von RXC ISR das PIN auf HIGH! Damit ieht man am OSZI DIREKT,
wann wie lange die ISR gesperrt ist!
> und dennoch kann er nicht antworten. Ominös.
Ich tippe mal, daß deine MAX487 Ansteuerung Unsinn macht, ich sehe da
komische Dinge.
Das Programm ist ohne die anderen Dateien nich zu vestehen! Wo werden
denn die Interrupts freigeschaltet? Arrrgghhhh. Immer diese
Salamitaktik! Ein Schaltplan wäre auch nicht schlecht, denn du bist
nicht der Erste, der bei RS485 Fehler macht.
Hallo,
sorry.
Die RS485 Geschichte kann ich ausschließen. Sonst hätte ich bis heute
Kommunikationsprobleme anderer Art, unabhängig von diesem neuen Problem,
denke ich.
Im Datalogger Screenshot sieht man die längere Receive Int Abschaltung.
Sollte Deckungsgleich sein mit einem vorherigen wo CompB nach CompA
auslöst.
@Veit Devil (devil-elec)
>Die RS485 Geschichte kann ich ausschließen. Sonst hätte ich bis heute>Kommunikationsprobleme anderer Art, unabhängig von diesem neuen Problem,>denke ich.
VORSICHT! Gerade bei Interrupts wäre ich mir da ganz und gar nicht
sicher! Erst recht nicht beim TXC, denn der kann tricky sein!
>Im Datalogger Screenshot sieht man die längere Receive Int Abschaltung.>Sollte Deckungsgleich sein mit einem vorherigen wo CompB nach CompA>auslöst.
Schon mal ein aussagekräftiges Fehlerbild.
Hmm, es fehlt ein richtiger Pull Up an IC2/Pin1/RO. Die LED +
Vorwiderstand kann das NICHT sauber auf VCC ziehen bzw. halten. Damit
kannst du dir sporadische Fehlpulse an deinem UART Receiver einfangen.
Auf dem Logic Analayzer Bild sieht man, daß der Fehler mit der UART-RXC
Sperrung während des Sendens von Daten auftritt. Das ist zwar immer noch
schlecht, sollte dort aber keine Rolle spielen. Allerdings ist das ja
nur eine Momentaufnahme.
Die vielen if() in handle_Function_UART0_Pins() sind Unsinn, das macht
man mit switch(). Das ist übersichtlicher.
Und man kann es auch mit der Strukturierung übertreiben, wenn man für
eine Zeile Code eine Funktion erfindet 8-0, wie z.B.
void delete_uart0_Transmit_Complete_Flag ()
{
UCSR0A |= (1<<TXC0); // "UART Transmit Complete Flag"
zurücksetzen mit "1"
}
Dein PCINT ISR kann dir auch in die Suppe spucken, vor allem da er höher
als die Timer priorisiert ist. Was macht der eigentlich genau? Und vor
allem, warum greifst du dort DIREKT auf den Speicher des UART-FIFOs zu?
Das ist keine gute Idee! Mal screibest du die Daten in den RX-Buffer,
mal TX-Buffer? Soll das ein Virus sein?
ISR(PCINT0_vect) // Interrupt Handler für PCINTs
{ // dauert ohne "if (index>59)" 3,46µs und mit 5,08µs
uint16_t data = TCNT2;
static uint8_t index = 0; // index = cycle
if ( index < 30 ) { // 0...29
UART0_RxBuf[index] = data; // speichert TNCT2 im Rx Buffer
}
else {
UART0_TxBuf[index-30] = data; // 30...59 im Tx Buffer
}
index++;
if (index > 59) { // nach 60 Pegelwechsel Auswertung
del_PCINT2(); // ISR temporär sperren
state_UART_MODE = CALC;
index = 0;
}
}
Das als ein paar Anmerkungen, wenn gleich ich keine heiße Spur habe.
Falk B. schrieb:> Die vielen if() in handle_Function_UART0_Pins() sind Unsinn, das macht> man mit switch(). Das ist übersichtlicher.
Das sind ja eigentlich else if oder eben switch. ;)
Hoffentlich macht der Compiler daraus eine jump table.
Hallo,
ich dachte du schaust dir das erstmal in Ruhe an. Ganz in Ruhe. Denn ich
finde das voreilige Gemecker erstmal unpassend, wenn man noch nicht weiß
was der Code macht. Ich kann erklären was welche Funktion macht.
Wegen fehlenden Pull-Up an IC2/Pin1/RO. An IC1 Pin 11-13 sind alle 20k
Pullups dran. Genau wegen dem LED Effekt.
Den Code kann man noch aufräumen, ja, aber auch die vielen if in
handle_Function_UART0_Pins() haben ihren Sinn bzw. Ursprung in der
Enstehung. Mit if kann der Code in der Anordnung während der
Kalibrierung gleich zur nächsten Funktion springen an statt aus einem
möglichen switch-case komplett raus und wieder rein.
Was macht der Code überhaupt. Zu beginn wird OSCCAL kalibriert. Die
TX/RX Pins sind Eingänge. Nach erfolgreicher Kalibrierung bleiben sie im
USART Mode.
Der Ringbuffer wird wegen dem kleinen RAM des ATtiny doppelt verwendet
und kommt sich nicht in die Quere. Für die Kalibrierwerte nutze ich
diesen, dafür musste ich den allerdings 16Bit breit machen statt nur
8Bit. Die Timer Counterwerte landen im Ringbuffer und werden am Ende
ausgewertet. Am Ende wird der Ringbuffer vorsorglich gelöscht und steht
ab da der UASRT zur Verfügung. Ganz normal wie von Peter Fleury
vorgesehen.
Da ich mit 60 Kalibrierwerten handiere nutze ich beide, TX und RX
Ringbuffer, die jeweils 32 Bytes groß sind. 32x uint16_t. Ich schreibe
30 TCNT2 Werte in den RX Buffer und die anderen 30 Werte in den TX
Buffer. Den Trick finde ich ziemlich cool. Eigene Erfindung.
Danach bewegt sich die handle_Function_UART0_Pins() Funktion nur noch im
UART Modus. Hier wäre dann switch case von Vorteil. Ja. Oder ich setzte
ein return ein. Deswegen kann der PCINT ISR auch nicht dazwischen
funken, weil der abgeschalten wurde. Die TX/RX Pins sind ja im USART
Modus.
Wegen den Einzeiler Funktionen. Auch hier kennst du die Entstehung
nicht. Meine gesamte Sendefunktion sah vor vielen Monaten noch ganz
anders aus. Da sieht das jetzt damit schon sehr sauber aus. Optimierung
geht natürlich immer. Habe nur lieber sprechende Funktionsnamen wie eine
Zeile kryptischen Code. Da geht jeder anders ran. Ich mach das
schließlich als Hobby.
Lasst uns jetzt nicht über irgendwelchen Syntax streiten bzw.
diskutieren. Das können wir am Ende machen. Echte Fehler ausgenommen.
@Veit Devil (devil-elec)
>Wegen fehlenden Pull-Up an IC2/Pin1/RO. An IC1 Pin 11-13 sind alle 20k>Pullups dran. Genau wegen dem LED Effekt.
Ok, die hab ich übersehen. Beim nächsten Mal den Schaltplan besser als
PDF erstellen, da kann man besser zoomen und navigieren.
>Was macht der Code überhaupt. Zu beginn wird OSCCAL kalibriert. Die>TX/RX Pins sind Eingänge. Nach erfolgreicher Kalibrierung bleiben sie im>USART Mode.
OK.
>Danach bewegt sich die handle_Function_UART0_Pins() Funktion nur noch im>UART Modus. Hier wäre dann switch case von Vorteil. Ja. Oder ich setzte>ein return ein. Deswegen kann der PCINT ISR auch nicht dazwischen>funken, weil der abgeschalten wurde. Die TX/RX Pins sind ja im USART>Modus.
Gut.
>Lasst uns jetzt nicht über irgendwelchen Syntax streiten bzw.>diskutieren. Das können wir am Ende machen. Echte Fehler ausgenommen.
Stimmt, es waren ja auch nur Anmerkungen.
Eine aber eher wichtige Sache ist hier, daß du state_UART_MODE auch in
anderen Funktionen schreibst. Das ist maximal irreführend. Ein
Lesezugiff ist OK, aber SCHREIBEN sollte das nur die eine Statemachine
in dieser Funktion. Sonst wird man irre.
Außerdem sollte man die Sache mit der Kalibrierung und den normalen
UART-Nutzung trennen, das verschafft viel und wichtige Übersicht. Jaja,
ich weiß, historisch gewachsen.
Und wenn am Anfang kalibriert wird, sollte man auch die Interrupts zur
Servosignalerzeugung ausschalten, denn die können da auch irgendwie
reinspucken. Reine Vorsichtsmaßnahme.
Das hier ist aber ein heißer Kandidat
void uart0_flush ()
{
while( !(UCSR0A & (1<<TXC0)) ); // warten bis Flag "TXC0 UART
Transmit Complete" set
}
Das geht im Allgemeinen so NICHT! Denn du kannst nicht sicher sein, daß
vorher immer alle Daten LÜCKENLOS in den USART geschoben wurden und
ZWISCHENDURCH der Sende-FIFO im UART nie leer gelaufen ist und damit TXC
IMMER nur am ENDE auf 1 geht! Damit bin ich schon mal RICHTIG aufs Maul
gefallen und hab fast ne Woche gesucht!
Beitrag "Re: Problem mit Micro-SD-Karte"
Wie kriegt man es WIRKLICH wasserdicht?
Etwa so.
1
voiduart0_flush()
2
{
3
uint8_tcnt;
4
5
// warte auf leeren Software-FIFO, der UDRIE ausschaltet
6
while((UCSR0B&(1<<UART0_UDRIE)));
7
8
UCSR0A|=(1<<TXC0);// TXC0 löschen
9
10
// warte auf leeren Hardware-FIFO, welcher TXC setzt
11
// Timeout nach cnt/2 Zeichen
12
cnt=6;
13
while(cnt>0){
14
if(!(UCSR0A&(1<<TXC0))){
15
_delay_us(20);// 1/2 Zeichen @250kBaud
16
cnt--;
17
}
18
}
19
}
Damit sollte unter ALLEN komischen Umständen immer gewartet werden, bis
deine Daten raus sind, ggf. ein paar Zeichen mehr, wenn vorher sie FIFOs
schon leer waren.
Noch eine Idee. Wie du gemessen hast, scheint das Senden die COMPA ,
COMPB Interrupts durcheinanderzuwürfeln. Du arbeiteste im
Halbduplexbetrieb, also immer nur Senden ODER Empfangen. Da kann das nur
passieren, wenn der UDRE Interrupt sich ungünstig vor COMPB drängelt und
länger als TIMING_WINDOW dauert. Eigentlich ist die ISR eher kurz und
sollte kaum mehr als 10us dauern. Praktisch kommt es aber mitten beim
Senden zu dem COMPA, COMPB Fehler!
OK, Trick! Tausche den Inhalt von COMPA und COMPB bzw. benenne einfach
die ISRs um, mit Anpassung der Zuweisung von OCR1A/B! Denn dann wird,
wenn durch diesen noch unklaren Fehler COMPA und COMPB gleichzeitig
während eines anderen Interrupts aktiv werden, ZUERST COMPA ausgeführt,
welcher die RXC-ISR sperrt und sofort danach COMPB, welcher sie wieder
freigibt! Damit kann sich die Sache nicht mehr temporär verklemmen!
Sperren hat Priorität vor Freigeben, was aber bedeutet, das Freigeben
immer als 2. ausgeführt wird, was "langfristig" für die nächsten 1-2ms
deutlich besser ist.
1
ISR(TIMER1_COMPB_vect)// wird aller >1ms aufgerufen (Prescaler 8)
2
{
3
Servo8_ON;// zum messen mit Oszi/Datalogger Zweck entfremdet
Hallo,
upps das sind ja viele neue Infos. :-)
enum "state_UART_MODE":
reden wir am Ende nochmal drüber ...
Trennung, Kalibrierung <> USART Mode:
der µC ist voll belegt, ich dachte es ist eine geniale Idee gleich die 2
Pins zu nutzen die eh mit dem Master-Controller verbunden sind. Die
RS485 Leitung. Das zur näheren Info.
Die Änderungen probiere ich. Muss jedoch über den Jahreswechsel
arbeiten.
Falls morgen was dazwischen kommt, wir "sehen" uns spätestens nächstes
Jahr, wünsche bis dahin dir und allen anderen Beteiligten und
Neugierigen einen guten feucht fröhlichen Rutsch.
@ Veit Devil (devil-elec)
>Trennung, Kalibrierung <> USART Mode:>der µC ist voll belegt, ich dachte es ist eine geniale Idee gleich die 2>Pins zu nutzen die eh mit dem Master-Controller verbunden sind. Die>RS485 Leitung. Das zur näheren Info.
Mit Trennung meinte ich die Software, nicht die Hardware.
@ Veit Devil
meine Hochachtung das du dich nicht beirren lässt, einige Kommentare
waren ja demotivierend, der Thread gefällt mir immer noch ich hoffe ich
kann irgendwann Nutzen daraus ziehen.
Allen wünsche ich einen guten Rutsch ins neue Ja(h)r
Hi Veit, wenn du eh Multiplexer für "selten benutzte Pin-Funktionen" auf
deinem Board hast, warum nicht noch einen 74c4051er, der die per HW-OC
erzeugten, mit 125ns Auflösung erzeugten Servo-Pulse an den richtigen
Kanal weiterleitet. Dazu braucht es 3+1 Pin (3-Bit Auswahl/1-Bit OC1A/B)
für 8 Servos. Oder wenn man OC1A/B gleichzeitig nutzt 2+2 mit einen
4052er.
Wie gesagt HW erzeugt den Puls (das wichtige Timing) und ISR hat
(gefühlt ewig) Zeit den Multiplexer weiter zu schalten.
BTW, damit ist Falks Vorderungen nach bestmöglicher Realisierung
erfüllt. Wenn ich auch lieber ausprobieren würde, ob mindestens für
fehlerfreie Funktion erforderliche Realisierung nicht doch reicht.
Der Tiny841 kann übrigens die OC-Ausgänge auf die verschiedenen Pins
mappen, d.h. für die bis zu 2 Servos an Timer1 braucht es noch nicht mal
eine HW-Änderung.
Edit:
Timer0/2 können das auch, sind aber wegen ihrer Kürze nicht so leicht
bedienbar wie Timer1, der sowohl hohe Auflösung, als auch selber die zig
ms Zykluszeit selber kann.
125ns Takt 2^16 -> 2^12μs max Zykluszeit
Hallo
@ Carl:
ich habe auf dem Platinenmaß 10x10cm keinen Platz mehr. Die
vermeintlichen Lücken sind alle durch Leiterbahnen "gefüllt".
Damit ihr eine Vorstellung bekommt wie mein Testaufbau z.Z. aussieht,
zeige ich mal 2 Bilder.
So, habe etwas Zeit und habe weiter geforscht. Habe meine verfügbaren 8
Messkanäle neu verkabelt sonst verliere ich den Überblick. Habe das
genutzt um mehr Funktionen zum vermessen anzuzapfen. :-)
Weiterhin alles mit 125kBaud. Timer läuft im Normalmode, weil CompA und
CompB vertauscht habe
Vermutung Fehler in Sendefunktion:
Kann ich selbst immer noch nicht vermuten, weil der µC nicht einmal
versucht zu senden, ich sehe bis jetzt keine verstümmelten Bytes auf der
Leitung oder dergleichen, er sendet im Fehlerfall überhaupt nichts
zurück, die Sendefunktion wird nicht ausgelöst und der MAX487 wird auch
nicht in den Sendemodus geschalten. Irgendwie muss der Fehler beim
einlesen zu suchen sein. So meine Vermutung.
Bild: B
Was ich jedoch sehe ist, dass die "handle_Serial_0_to_Serial_0()", die
immer durchlaufen wird, im Fehlerfall scheinbar nichts zu tun hatte.
Die Pegelwechsel sind fast durchgehend gleichmäßig. Im Gegensatz dazu
wenn alles läuft sind die Pegelwechsel unregelmäßiger. Das kann kein
Zufall sein.
Jetzt dachte ich, lässt den Master etwas später senden, hilft auch
nicht. Würde auch dem Datalogger widersprechen, weil der ATtiny nachdem
er mit antworten fertig ist sofort auf Empfang schaltet, während der
Zeit muss der Master die Daten erstmal verarbeiten und seinerseits
umschalten zum neuen senden seinerseits. Zu schnelles erneutes senden
schließe ich demzufolge aus.
Bild: C
Der letzte Sendemodus vom ATtiny ist auch okay, schaltet erst wieder um
wenn alles raus ist. Wenn hier was faul wäre, könnte der Master nicht
sofort neu senden. Dann schlägt sein Antwort-Timeout von 9ms zu bevor
der Master neue sendet. Die gibts im Datalogger nicht zu sehen.
Bild: D
ohne aktiven Compare_A läuft alles wie am Schnürchen, ich kann
Minutenlang auf die TimeOut LED vom Master schauen, dessen Terminal und
im Datalogger, alles astrein. Es ist verhext. Obwohl der Compare_A
Interrupt ja auch im Fehlerfall vorher nur kurz aktiv ist wie immer. Der
ist auch nicht unbedingt aktiv wenn der ATtiny gerade empfängt. Er
sollte damit nichts zu tun und hat dennoch irgendwas damit zu tun.
Bild: E
Was macht der Compare_A? Er schaltet den uart Empfang ab.
Wird das RXCIE0 Bit danach wirklich wieder eingeschaltet?
Code geändert um dieses eine Bit zu überprüfen ob wirklich immer gesetzt
und gelöscht wird.
Ergebnis, verhält sich genau wie Compare_A.
Lasse ich Compare_A aktiv, kommentiere jedoch das enable/disable des
RXCIE0 Bits jeweils aus, funktioniert auch alles wie am Schnürchen.
Bedeutet, der Timer Interrupt hat keinen negativen Einfluss. Aber dieses
RXCIE0 Bit hat irgendeinen negativen Effekt. Als wenn die uart nicht
wirklich danach Empfangs bereit ist.
Vielleicht darf man den Empfang nicht einfach so knallhart weg und
wieder zuschalten?
Habe zudem paar Einzeilerfunktion rausgenommen.
Kompletter Code ist "Code_A-D"
Letzt Code Änderung im Timer 1 Interrupt ist "Code_E"
@ Veit Devil (devil-elec)
>Leitung oder dergleichen, er sendet im Fehlerfall überhaupt nichts>zurück, die Sendefunktion wird nicht ausgelöst und der MAX487 wird auch>nicht in den Sendemodus geschalten. Irgendwie muss der Fehler beim>einlesen zu suchen sein. So meine Vermutung.
Klingt logisch.
>ist auch nicht unbedingt aktiv wenn der ATtiny gerade empfängt. Er>sollte damit nichts zu tun und hat dennoch irgendwas damit zu tun.
Yep.
>Lasse ich Compare_A aktiv, kommentiere jedoch das enable/disable des>RXCIE0 Bits jeweils aus, funktioniert auch alles wie am Schnürchen.>Bedeutet, der Timer Interrupt hat keinen negativen Einfluss. Aber dieses>RXCIE0 Bit hat irgendeinen negativen Effekt. Als wenn die uart nicht>wirklich danach Empfangs bereit ist.
Scheint so.
Hallo,
habe den Faden weiter gesponnen und die uart Receive ISR mitloggen
lassen. Wann und ob diese immer aktiv wird wenn ein Byte reinkam. Siehe
da, trotz schon lange wieder aktivierten RXCIE0 Bit wird die uart
Receive ISR nicht aktiv. Die komplette Übertragung rauscht teilnahmslos
vorbei, erst danach werden 3 Bytes eingelesen. Was auch immer das ist.
Jetzt bin ich wirklich ratlos was hier schief läuft. Ich weiß auch nicht
mehr was ich sinnvoll noch testen kann.
diese ISR meine ich von der uart Lib.
>Vielleicht darf man den Empfang nicht einfach so knallhart weg und>wieder zuschalten?
Doch, darf man. Zum der UART an sich gar nicht angefaßt wird, es wird
nur der RXC Interrupt kuzzeitig deaktiviert.
Auf dem Bild Logger_E sieht man ja, daß der kurze Abschaltpuls für den
RXC NACH dem Empfang der Daten passiert! Da kann der Empfang doch gar
nicht gestört werden. In deinem LED5 Testsignal gibt es dann auch eine
kurze LOW-Phase. Dort muss man weiter suchen. Mögicherweise ein CRC
Error in den Empfangsdaten?
Veit D. schrieb:> Hallo>> @ Carl:> ich habe auf dem Platinenmaß 10x10cm keinen Platz mehr. Die> vermeintlichen Lücken sind alle durch Leiterbahnen "gefüllt".
100cm^2 und die paar Bauteile -> kein Platz??
Dann lass den ISP-Multiplexer weg und verpass dem Ding einen Bootloader.
Hallo Carl,
von Bootloader Programmierung habe ich keine Ahnung. Bin schon stolz das
ich die Steuerung soweit programmieren konnte. Zudem ich an der Lösung
des Problems interessiert bin. Das möchte ich erstmal verstanden wissen.
Wenn verstanden und klar ist es gibt dafür keine Lösung, kann man immer
noch einen Weg drumherum bauen. Jetzt aber noch nicht. Ich renne nicht
vor einem Problem weg. Dann tappe ich ins Nächste weil das vorherige
noch nicht verstanden wurde. Zudem ich vermute das mit dem
Servo-Multiplexer das Jitterproblem ebenso existiert. Der Takt wäre zwar
astrein, aber die Puls-Schaltzeitpunkte hängen wieder vom Code ab. Die
Slaves sind als Universal-Controller gedacht. Hängt nur von der Programm
ab wofür die dann da sind und was die machen sollen.
@ Falk und Interessierte:
Das hat sich vorhin zeitlich überschnitten. 15:16 Uhr.
Habe CRC zum loggen mit aufgenommen. Ganz unten Led 3.
Kein CRC Fehler. Eigentlich kann da auch keiner sein, wenn die Receive
ISR keine Bytes in den Ringbuffer schiebt.
Ich wette wir stehen mit der Nase davor, aber noch zu viele Bäume. :-)
Meine Frage lautet aktuell, warum ist die uart receive ISR lahm gelegt?
1
boolread_Ringbuffer_0()
2
{
3
// mit Fehlerbehandlung und nichts tun, no data, wird die Funktion nach 0,5 µs verlassen
4
// ohne Fehlerbehandlung und nichts tun, wird die Funktion nach 10,46 µs verlassen
5
6
staticuint8_tindex=0;
7
staticboolstate_Read=false;
8
staticboolstate_Complete=false;
9
staticboolstate_Bypass=false;
10
uint8_tlength=sizeof(Nachricht);
11
12
uint16_tc=uart0_getc();// nächstes Zeichen vom Ringbuffer holen
Kann man machen. Sollte die Optimierung des avr-gcc aber schon seit
Jahren selbst aus einem switch-case-Block machen, wenn es sich lohnt.
Und ändert nicht grundlegend etwas an deinen Problemen, vermute ich :)
Sieht ungefähr so aus (Pseudocode, vermutlich passen Klammern und
Pointer nicht ganz zusammen):
Hallo,
ich meinte, für welches Problem soll das die Lösung sein, dass verstehe
ich nicht. Soll das ein verkapptes Basic goto sein? Ich verstehe immer
noch nicht wo ich hinspringen soll? Deine Ausführungen sind zu kurz. Ich
meine das Programm macht doch jetzt schon nichts anderes. Ob ich nun
Funktionen aufrufe oder in diese anderweitig springe sollte Jacke wie
Hose sein, wenn ich dich richtig verstehe.
@ Veit Devil (devil-elec)
>ich meinte, für welches Problem soll das die Lösung sein, dass verstehe>ich nicht.
Für gar keines.
>Soll das ein verkapptes Basic goto sein?
Ein Gosub.
> Ich verstehe immer noch nicht wo ich hinspringen soll?
Er will switch() neu erfinden.
@ Veit Devil (devil-elec)
>noch einen Weg drumherum bauen. Jetzt aber noch nicht. Ich renne nicht>vor einem Problem weg. Dann tappe ich ins Nächste weil das vorherige>noch nicht verstanden wurde.
Gute Einstellung.
> Zudem ich vermute das mit dem>Servo-Multiplexer das Jitterproblem ebenso existiert. Der Takt wäre zwar>astrein, aber die Puls-Schaltzeitpunkte hängen wieder vom Code ab.
Eben!
>Ich wette wir stehen mit der Nase davor, aber noch zu viele Bäume. :-)>Meine Frage lautet aktuell, warum ist die uart receive ISR lahm gelegt?
Ich hab sie nicht lahm gelegt ;-)
> static bool state_Read = false;> static bool state_Complete = false;> static bool state_Bypass = false;
Das ist schon mal Mist. Drei Variablen zur Zustandsdefinition. Da kann
man sich ggf. schön ins Knie schießen. Aber da es ja sonst auch geht,
liegt das Problem dennoch woanders.
Hallo,
ich merke schon, ich kann hier und da den Code wirklich noch einfacher
gestalten. Ich denke manchmal zu kompliziert, daran bin ich wirklich
gut. :-) Die 3 states brauche ich um das ESC Zeichen aus dem
Datenstrom zu filtern. Jetzt wo du es ansprichts, kann man es auch hier
mit enum und switch case machen.
Wenn ich die uart receive ISR und du auch nicht lahm legst, wer dann?
:-)
Macht bestimmt unser C Liebhaber aus der Ferne. :-)
Nochmal zurück auf Anfang.
UART, nur nochmal zum Verständnis das ich mich nicht verrenne.
---------------------------------------------------------------------
Dieses Flag wird generiert, wenn neue Bytes im uart Empfangsbuffer
liegen. Dieses Bit löst den "ISR (UART0_RECEIVE_INTERRUPT)" aus wenn
"1". Welche ich mit der Led 6 mitgeloggt habe.
1
UCSR0A: Bit7, RXC0 ... USART Receive Complete Flag
2
3
This flag is set when there is unread data in the receive buffer, and
4
cleared when the receive buffer is empty (i.e., does not contain any unread data).
5
If the receiver is disabled, the receive buffer will be flushed and consequently
6
the RXCn flag will become zero. The flag can be used to generate
7
a Receive Complete interrupt (see RXCIEn bit).
Mit diesem Bit steuern wir ob obiges Flag, ob es überhaupt generiert
wird oder nicht. Bytes können dennoch in den uart Buffer landen, werden
nur nicht abgeholt.
Writing this bit to one enables interrupt on the RXCn flag.
4
A USART Receive Complete interrupt will be generated only if the RXCIEn bit,
5
the Global Interrupt Flag, and the RXCn bits are set.
Dieses Bit schaltet generell den Receiver ein und die Pin TX/RX in den
UART Modus überhaupt. Das erfolgt einmalig bei uart Initialisierung.
1
UCSR0B: Bit4, RXEN0 ... Receiver Enable
2
3
Writing this bit to one enables the USART Receiver. When enabled, the receiver
4
will override normal port operation for the RxDn pin. Writing this bit to zero
5
disables the receiver. Disabling the receiver will flush the receive buffer,
6
invalidating FEn, DORn, and UPEn Flags.
-------------------------------------------------------------------
Ich denke wir machen für dieses Jahr erstmal Schluss, ihr habt bestimmt
auch besseres zu tun wie hier zu lesen, für dich/euch hier am Ball zu
bleiben strengt sicherlich auch an.
Ich versuche erstmal den Code laut deinen Hinweisen besser zugestalten.
Das hilft allen. :-)
Ich wünsche auf jeden Fall allen einen guten Rutsch. Denkt dran. Nur mit
Helm, Handschuhen und feuerfesten Overall nach außen treten. :-)
Tschau bis nächstes Jahr
Veit
Vermutlich bin ich einfach nicht schlau genug zu verstehen, warum
Hardware-PWM jittern soll, abgesehen vom Jitter der Taktquelle.
aber sei's drum: viel Spass noch beim basteln.
Nene, das tun sie schon, weil die Servopins nicht direkt von der
Hardware angesprochen werden (mangels ausreichender Anzahl
OCR-Register/direkt daran gekoppelter Pins). Es erfordert also Software
in Form der ISR. Und der Eintritt kann verzögert sein, wenn gerade eine
andere ISR (hier die UART) läuft.
H.Joachim S. schrieb:> Nene, das tun sie schon, weil die Servopins nicht direkt von der> Hardware angesprochen werden (mangels ausreichender Anzahl> OCR-Register/direkt daran gekoppelter Pins). Es erfordert also Software> in Form der ISR. Und der Eintritt kann verzögert sein, wenn gerade eine> andere ISR (hier die UART) läuft.
Und was glaubst du sollte die Aufgabe des Multiplexers in meinem
Vorschlag sein? HW macht einen Puls und feuert dann Compare, worin man
jede Menge Zeit hat, OCR mit dem Wert für den nâchsten Puls zu laden und
den Multiplexer auf den nächsten Ausgang weiter zu drehen. Aber wie
schon gesagt, ich hab ja das Problem nicht und werd dazu auch nichts
mehr schreiben.
Hallo,
Carl:
jetzt spiel mal nicht den Beleidigten, hast gar keinen Grund dazu. Nur
verstehe ich immer noch nicht wie der Multiplexer den Takt genau für die
Servopulse erzeugen soll? Ich weiß auch noch nicht recht wie du das
verschalten wissen möchtest? Mach am besten einen Schaltplan, dann
verstehe ich/wir das besser. Sonst reden wir aneinander vorbei.
Vielleicht haste auch nicht alles lesen können. Kann ja sein. Der Thread
ist schon lang. Im Falle das ein Slave 8 Servos steuern soll, müssen 8
unterschiedliche Pulse erzeugt werden. Wo sollen die OC Ausgänge
herkommen? Kein Timer hat 8 Compare Toggle Pins. Vielleicht liegt hier
das Missverständnis verborgen.
Wenn du immer noch von deiner Idee überzeugt bist, dann erkläre es bitte
deutlicher, sodass ich es verstehe. Danke.
@Veit Devil (devil-elec)
>verstehe ich immer noch nicht wie der Multiplexer den Takt genau für die>Servopulse erzeugen soll?
Nicht direkt. Man erzeugt EIN Servosignal rein per Hardware Timer1 und
einem PWM-Ausgang und schaltet den nach und nach auf mehrere Ausgänge
mit einem Demultiplexer. Das Umschalten macht die Software im Interrupt,
ist aber egal, weil es in den LOW Pausen passiert, da ist Jitter egal.
>unterschiedliche Pulse erzeugt werden. Wo sollen die OC Ausgänge>herkommen?
Ein OC-Ausgang, zeitlich verteilt auf 8 Ausgänge per Demultiplexer.
Kann man machen, ist gut, kostet halt ein wenig Hardware. Die aktuelle
Softwarevariente funktioniert auch, das aktuelle Problem hat damit nicht
direkt was zu tun.
Carl D. schrieb:> Vermutlich bin ich einfach nicht schlau genug zu verstehen, warum> Hardware-PWM jittern soll, abgesehen vom Jitter der Taktquelle.
Hardware-PWM jittert bei konstaktem Takt natürlich nicht, solange man
die Register nicht anfasst, die sie kontrollieren...
Tut man das aber, was für die meisten sinnvollen Awendungen der
Normalfall ist, dann kann ein ungeeigneter Zeitpunkt für
Registeränderungen die Sache aber natürlich mit erheblichem "Jitter"
belasten.
Das Problem dabei ist: Es hängt vom Modus und vom zu ändernden Register
ab, was passieren kann und damit auch, wo man die Änderungen am Besten
schreibt. Praktisch immer ist der beste Moment dafür unmittelbar nach
der Auslösung eines Timer-IRQs (natürlich: nicht immer desselben, auch
wieder PWM-Modus-abhängig).
Blöd bloß: unmittelbar nach Auslösung eines IRQ kann man erstmal
garnix setzen, man kann das leider frühestens nach Ablauf der statischen
Interruptlatenz in der ISR tun. In der Praxis kann man es allerdings im
worst case erst nach Verstreichen von statischer und variabler Latenz
tun.
Und genau letzteres ist das Problem. Die variable Latenz ist nur
berechenbar, wenn man das Timing deterministisch kontrolliert, also in
Assembler programmiert. In Hochsprachen geht das nicht, weil halt kein
Code mit deterministischem Zeitverhalten erzeugt wird (davon, dass er
häufig auch sehr suboptimal ist, mal ganz zu schweigen).
D.h.: man muß entweder beten, dass es reicht oder den Code analysieren,
den der Compiler produziert. Und zwar immer wieder neu und immer wieder
von allen Codeteilen, die die variable Latenz beeinflussen, also
insbesondere den von allen ISRs...
c-hater schrieb:> Die variable Latenz ist nur> berechenbar, wenn man das Timing deterministisch kontrolliert, also in> Assembler programmiert.
Ich denke, man kann sich auch mit passender Hardware beschäftigen. MCs,
die mit "double buffering" arbeiten z.B. :-) - oder?
Dieter F. schrieb:> c-hater schrieb:>> Die variable Latenz ist nur>> berechenbar, wenn man das Timing deterministisch kontrolliert, also in>> Assembler programmiert.>> Ich denke, man kann sich auch mit passender Hardware beschäftigen. MCs,> die mit "double buffering" arbeiten z.B. :-) - oder?
Der ist meist damit beschäftigt seine bekannt guten Manieren zur Schau
zu stellen und offenbar seltener mit Lesen/Nachdenken/Verstehen. Ist ja
auch nicht so, daß Atmel die Timer nicht nur extra wegen genau dem
Problem so gebaut hat. Nein, sie schreiben das sogarn noch für (fast)
alle verständlich in die Doku.
Hallo,
erstmal gesundes Neues an alle. :-)
(auch wenn das in diesem Forum nicht so üblich ist)
Wenn ich ganz ehrlich bin, weiß ich immer noch wie die Pulse mit dem
Multiplexer erzeugt werden soll, wenn nur der Takt an den nächsten
Ausgang geschoben wird.
Zum Hauptproblem uart Receive ISR lahm gelegt.
Ich bin immer noch ratlos und weiß nicht was ich wie gezielt sinnvoll
noch testen soll.
Veit D. schrieb:> Wenn ich ganz ehrlich bin, weiß ich immer noch wie die Pulse mit dem> Multiplexer erzeugt werden soll, wenn nur der Takt an den nächsten> Ausgang geschoben wird.
Es gibt diverse Möglichkeiten, dass spezielle I/O-Pins direkt vom OCR
gesteuert werden können und damit unabhängig von einer ISR werden. So
kann man ein H durch ein Schiebregister takten, dessen Taktsignal direkt
von der Hardware des timers kommt. Zum nachladen des nächsten Wertes und
Steuerung des Dateneingangs hat man dann alle Zeit der Welt, spielt
keine Rolle.
@Veit Devil (devil-elec)
>Wenn ich ganz ehrlich bin, weiß ich immer noch wie die Pulse mit dem
Da fehlt ein nicht, nicht?
>Multiplexer erzeugt werden soll, wenn nur der Takt an den nächsten>Ausgang geschoben wird.
Ganz einfach. Ein 1:N DEMultiplexer gibt EIN (echtes) PWM Signal
zeitlich versetzt auf mehrere seiner Ausgänge. Im Prinzpt so wie du das
jetzt in Software machtst, nur daß eben der Demux ein extra Chip ist die
die Pulserzeugung rein in Hardware erfolgt, die Umschaltung auf den
nächsten Kanal in der unkrtitischen LOW-Phase in Software.
>Zum Hauptproblem uart Receive ISR lahm gelegt.>Ich bin immer noch ratlos und weiß nicht was ich wie gezielt sinnvoll>noch testen soll.
Tja, ich wohl leider auch. Das ist schon sehr mysteriös. Die
Hauptausrede in solchen Situationen ist ein Compilerfehler ;-)
Veit D. schrieb:> Hallo,>> erstmal gesundes Neues an alle. :-)> (auch wenn das in diesem Forum nicht so üblich ist)>>> Wenn ich ganz ehrlich bin, weiß ich immer noch wie die Pulse mit dem> Multiplexer erzeugt werden soll, wenn nur der Takt an den nächsten> Ausgang geschoben wird.
Das ganze kann mit bis zu 2Kanälen ganz ohne HW-Änderungen getestet
werden:
Servos brauchen alle 20ms einen 1,5..2,5ms langen Pulse. 2 davon kann
man mit Timer1 erzeugen.
- Der läuft mit 16MHz Div8 2^16 -> 32ms Zyklus.
- OCR1A/B werden mit den Werten für 1,5..2,5ms geladen 2^16 * (pw/32)
;pw pulse weite in ms
- Comp1A/B ISR (am Ende eines Pulses) setzt den nächsten Wert, der im
PWM-Mode beim Overflow in die echten CompareRegister geladen wird. (Hier
könnte man dann auch den externen 1:8-Multiplexer auf das nächste Servo
umschalten)
> Zum Hauptproblem uart Receive ISR lahm gelegt.> Ich bin immer noch ratlos und weiß nicht was ich wie gezielt sinnvoll> noch testen soll.
Hallo,
entweder bin ich zu blöd das zu raffen oder ich habe derzeit nicht den
Kopf dafür frei. Sorry.
Ich hätte lieber noch paar Ideen zum Receive ISR Problem gehört. Was ich
noch testen könnte, egal wie die Ideen aussehen. Also warum uart Receive
tot ist obwohl enabled wurde.
Hallo,
zeitliche Überschneidung, so langsam dämmert es wie das mit dem
DeMultiplexer abläuft.
Receive ISR:
@Falk
ja ist schon mysteriös.
Ich Danke dir dennoch wie verrückt für deinen Einsatz. Hab viel gelernt.
Hat unser C Liebhaber eine Idee?
Jetzt würde seine große Stunde schlagen. :-)
Edit:
Oder kommt die uart Lib mit ihren Ringbuffer durcheinander wenn das
gerade ungünstig abgeschalten wurde? Oder der Hardware uart Reveice
Buffer erstmal geleert werden muss, bevor wieder sauber empfangen werden
kann?
Edit:
wenn ich mittendrin den uart buffer leere, kann er nie mehr das
Protokoll lesen, weil dann Byte mittendrin verloren gehen, klappt auch
nicht so richtig
Wenn wir die Esotherik mal beiseite lassen, stellen wir ganz sachlich
fest. Es kommt ab und an dazu, daß der UART-RXC Interrupt ab und an NACH
dem Senden einer Messeage deaktiviert wird und erst mit dem nächsten
COMPA/COMPB Interrupt eingeschaltet wird. Dort werden dann die 3
Datenbytes aus dem Hardware-FIFO gelesen und ins Software-FIFO
geschrieben, was anatürlich nicht für eine vollständige Messeage
ausreicht und der Timeout zuschlägt.
Es scheint so, als ob es irgendwo mal einen falschen Zugriff auf USCR0B
gibt und dadurch das RXCIE0 Bit gelöscht würde. Soweit ich es sehe,
gibit es aber nur einen Zugriff auf das Register, in uart0_putc(), dort
wird aber nur UDRIE gesetzt.
Als Pflaster könnte man RXCIE0 am Ende von send_Nachricht () noch einmal
explizit einschalten. Wenn das wirkt, hat man den Fehler weiter
eingegrenzt. Dann muss irgendwo ein versteckter Zugriff auf RXCIE0 sein.
Es könnte auch sein, daß durch Nebeneffekte in der Funktion
handle_Function_UART0_Pins() doch irgendwann mal ein paar andere Zweige
aufgerufen werden und z.B. ein uart0_init() oder uart0_disable()
ausgeführt wird. Man könnte ja mal die anderen Teile der Funktion
auskommentieren und die Kalibierung am Anfang weglassen.
@ Veit Devil (devil-elec)
>Hat unser C Liebhaber eine Idee?>Jetzt würde seine große Stunde schlagen. :-)
In der Tat . . .
>Oder kommt die uart Lib mit ihren Ringbuffer durcheinander wenn das>gerade ungünstig abgeschalten wurde?
Sollte nicht sein, zumal die Funktionen keinen Zugriff auf die
UART-Steuerregister machen, von putc() mal abgesehen.
> Oder der Hardware uart Reveice>Buffer erstmal geleert werden muss, bevor wieder sauber empfangen werden>kann?
Nein.
Hallo,
der Workaround mit dem zusätzlichen Receive enabled in der
send_Nachricht() funktioniert tatsächlich. Jetzt muss ich mir den Code
anschauen, als wenn ich das nicht schon oft genug gemacht hätte ... :-)
Hast den richtigen Riecher.
Die handle Serial 0 baue ich gerade auf switch case um
Hmmmm, ich hab was.
In read_Ringbuffer_0() gibt es
if ( c > UART_NO_DATA) { // irgendein UART Error
state_UART_MODE = ERROR; // Status ändern zur weiteren
Fehlerbehandlung
index = 0;
state_Read = false;
state_Complete = false;
state_Bypass = false;
return false; // es gibt nichts zum lesen
}
Wenn jetzt dieser Fall eintritt, dann gibt es in
handle_Function_UART0_Pins() das hier
if (state_UART_MODE == ERROR) { // UART neu
initialisieren, dauert 7µs
countErrorsUART0++;
uart0_init( UART_BAUD_SELECT(UART_BAUD_RATE,F_CPU) );
state_UART_MODE = UART;
}
UHHH!!!!!
In den UART-Routinen des Herrn Fleury sind ein paar laxe Dinger drin,
die ich so nicht machen würde. Denn dort wird mehrfach auf gobale,
volative Variablen zugegriffen. Jaja, die sind nur 8 Bit, aber der
Zugriff ist NICHT atomar. Da kann sich im Extremfall was
dazwischenmoglen, u.a. bei UART0_LastRxError, da können einzelne Fehler
verschluckt werden! Ich würde die Funktion so schreiben.
Und ich mein, bei einem Empfangsfehler muss man nicht gleich in die
totale Panik verfallen und alles neu initialisieren. Ich würde, vor
allem zum Testen, erstmal einfach ein paar Zähler für die einzelnen
Fehler anlegen und hochzählen. So wie es den schon für countErrorsUART0
gibt.
Falk B. schrieb:> Es scheint so, als ob es irgendwo mal einen falschen Zugriff auf USCR0B> gibt und dadurch das RXCIE0 Bit gelöscht würde. Soweit ich es sehe,> gibit es aber nur einen Zugriff auf das Register, in uart0_putc(), dort> wird aber nur UDRIE gesetzt.
Ich hab jetzt nicht in die Lib reingeschaut, aber wenn der
Read-Modify-Write-Zugriff durch die Compare ISR unterbrochen wird
bekommt das RXCIE-Bit nach Verlassen der ISR wieder den selben
gelöschten Zustand.
Sascha
Hallo,
bei einem reinen Byte Zugriff muss man auch atomic sicher zugreifen? Ich
dachte immer das ist nur ab >=16Bit notwendig. Nun gut, habe sämtliche
Registerzugriffe atomar gemacht, auch meine Timer run stop Funktionen,
man weiß ja aktuell nicht was los ist.
Keine Besserung.
Danach habe ich die uart0_getc Funktion laut Falk geändert.
Keine Besserung.
Danach habe ich den Code radikal abgespeckt.
Dazu muss ich OSCCAL0 von Hand anpassen, weil Kalibrierung rausgenommen.
Ab jetzt arbeitet nur noch die Serielle und Timer 1.
Immer noch Aussetzter wie "gewohnt".
@ Sascha:
wir schalten in Timer 1 CompB ISR uart receive wieder ein.
Oder wie meinst du das?
Der aktuelle Code im Anhang.
Veit D. schrieb:> @ Sascha:> wir schalten in Timer 1 CompB ISR uart receive wieder ein.> Oder wie meinst du das?
das da
UART0_CONTROL |= _BV(UART0_UDRIE);
in uart0_putc
sieht so aus
1) in temp,UCSR0B
2) ori temp,(1<<UDRIE)
3) out UCSR0B,temp
wenn jetzt nach 1 oder 2 die ISR zuschlägt und das RXCIE-Bit setzt, so
wird dieses mit 3 wieder gelöscht!
Sascha
Kennt eigentlich jemand einen validen Grund, warum in der Fleury-Lib der
Puffer der uart0 aus uint16_t-Arrays besteht und beim Puffer der
optionalen uart1 die (erwarteten) unsigned char-Arrays zu finden sind?
Immerhin hat das Ding nur ein halbes k RAM.
Hallo,
also müsste man
UART0_CONTROL |= _BV(UART0_UDRIE);
mit atomic sicher machen?
16 Bit Ringbuffer habe ich geändert, weil ich den mit zum kalibrieren
verwende, am Anfang, sonst habe ich einen 2. Einmessbuffer der nur noch
Speicher sinnlos Speicher wegnimmt.
Entweder hätte ich einen 8 Bit Ringbuffer und einen 16 Bit Einmessbuffer
oder nur einen größeren 16 Bit Ringbuffer den ich für alles verwende.
Die µC RAM Auslastung ist mit kompletten Code laut Atmel Studio nicht
größer 64% gewesen.
Veit D. schrieb:> Hallo,>> also müsste man>> UART0_CONTROL |= _BV(UART0_UDRIE);>> mit atomic sicher machen?
in deinem Fall schon,
oden die Zeile ganz rausnehmen wenn die entsprechende UDRE ISR nicht
benötigt wird.
Sascha
Hallo,
die uart wird doch in ihrer vollen Funktionalität benötigt, warum jetzt
Code rausnehmen?
Übrigens scheint das atomic wirklich zu helfen. Ich werde noch eine
Weile testen und den Code wieder zusammenbauen.
Hallo,
das atomic in uart0_getc() habe ich rausgenommen, jetzt ist nur noch
atomic in uart0_putc() drin.
1
voiduart0_putc(unsignedchardata)
2
{
3
unsignedchartmphead;
4
5
6
tmphead=(UART0_TxHead+1)&UART_TX_BUFFER_MASK;
7
8
while(tmphead==UART0_TxTail){
9
;/* wait for free space in buffer */
10
}
11
12
UART0_TxBuf[tmphead]=data;
13
UART0_TxHead=tmphead;
14
15
/* enable UDRE interrupt */
16
ATOMIC_BLOCK(ATOMIC_RESTORESTATE){// wegen ISR(TIMER1_COMPA_vect)
17
UART0_CONTROL|=_BV(UART0_UDRIE);
18
}
19
20
}
Das läuft jetzt schon viele Minuten ohne Fehler, sodass ich sicher davon
ausgehen kann der Fehler ist gefunden. :-) :-)
Soll ich vorsichtshalber das atomic in uart0_getc() wieder reinnehmen?
Großen Dank an Falk, der hier super mitgezogen und die Hauptarbeit
geleistet hat. Respekt. Danke auch an Sascha der sich eingeklingt hat.
Danke auch an alle anderen die heimlich mitgelesen haben. :-)
Also nehme ich mit, auch bei Zugriffen auf nur Byte Register/Variablen
ist atomic immer angebracht?
Darf ich in dem Thread weitere Fragen stellen zur Code Verbesserung?
Es wurden ja einige Dinge angesprochen die ich ändern
sollte/könnte/müsste.
Veit D. schrieb:>> 16 Bit Ringbuffer habe ich geändert, weil ich den mit zum kalibrieren> verwende, am Anfang, sonst habe ich einen 2. Einmessbuffer der nur noch> Speicher sinnlos Speicher wegnimmt.>> Entweder hätte ich einen 8 Bit Ringbuffer und einen 16 Bit Einmessbuffer> oder nur einen größeren 16 Bit Ringbuffer den ich für alles verwende.>> Die µC RAM Auslastung ist mit kompletten Code laut Atmel Studio nicht> größer 64% gewesen.
uint16_t kann man aber zur Not auch in 2x uint8_t speichern. Dann
braucht man nur einen Puffer.
@ Veit Devil (devil-elec)
>Das läuft jetzt schon viele Minuten ohne Fehler, sodass ich sicher davon>ausgehen kann der Fehler ist gefunden. :-) :-)
Glückwunsch!
>Soll ich vorsichtshalber das atomic in uart0_getc() wieder reinnehmen?
Ja, denn die Funktion greift auch auf UCSR0B zu, getarnt als #define von
#define UART0_CONTROL UCSR0B
UART0_CONTROL |= _BV(UART0_UDRIE);
>Also nehme ich mit, auch bei Zugriffen auf nur Byte Register/Variablen>ist atomic immer angebracht?
Ja, bei ALLEN Variablen oder IO-Registern, auf welche sowohl im
Hauptprogramm als auch in Interrupts zugregriffen wird.
https://www.mikrocontroller.net/articles/Interrupt#Atomarer_Datenzugriff>Darf ich in dem Thread weitere Fragen stellen zur Code Verbesserung?
Sicher
>Es wurden ja einige Dinge angesprochen die ich ändern>sollte/könnte/müsste.
;-)
Carl D. schrieb:> uint16_t kann man aber zur Not auch in 2x uint8_t speichern. Dann> braucht man nur einen Puffer.
Erzähl doch hier nicht so einen Schwachsinn :(
Hallo,
naja, Glückwunsch auch an dich, du musst genauso viel Ehrgeiz besitzen
wie ich um fremde Probleme zu lösen. :-)
Gut, das atomic ist wieder drin.
Habe die Baudrate wieder auf 250kB erhöht und nun fällt mir der
Sendemodus zum erstenmal auf die Füße. Das hattest du schon angesprochen
das das passieren kann. Irgenwie muss der Code insgesamt schneller
gewurden sein. :-)
Das passiert jetzt, weil der uart Transmit Buffer durch die höhere
Datenrate leer läuft und das complete Flag dadurch zu zeitig ausgelöst
wird.
Das Servo Timing Window betrifft ja den uart receive, sollte damit
nichts zu tun haben falls das zu groß sein sollte.
So weit mein Verständnis.
1
voiduart0_flush()
2
{
3
uint8_tcnt;
4
5
// warte auf leeren Software-FIFO, der UDRIE ausschaltet
6
while((UCSR0B&(1<<UART0_UDRIE)));
7
8
UCSR0A|=(1<<TXC0);// TXC0 löschen
9
10
// warte auf leeren Hardware-FIFO, welcher TXC setzt
11
// Timeout nach cnt/2 Zeichen
12
cnt=6;
13
while(cnt>0){
14
if(!(UCSR0A&(1<<TXC0))){
15
_delay_us(20);// 1/2 Zeichen @250kBaud
16
cnt--;
17
}
18
}
19
}
Warum jetzt halbe Zeichenlängen warten?
Ich könnte auch die Sendefunktion ändern.
Vor jeden Byte senden das "UART Transmit Complete Flag" löschen.
Code zum Bild H:
1
voidMAX487_Empfangsmodus()
2
{
3
while(!(UCSR0A&(1<<TXC0)));// uart0_flush, warten bis Flag "TXC0 UART Transmit Complete" set
4
MAX487_empfangen;// MAX487 /RE_DE - umschalten auf empfangen
5
}
6
7
8
voidMAX487_Sendemodus()
9
{
10
Led3_ON;
11
MAX487_senden;// MAX487 /RE_DE - umschalten auf senden
Hallo,
damit sind ehemals 4 Funktionen in eine geschrumpft.
Momentan zeigen sich keine Fehler. Oder lauert der immer noch?
Ich mach erstmal Schluss für heute.
@ Veit Devil (devil-elec)
>Das Servo Timing Window betrifft ja den uart receive,
Es trifft auch den UART UDRE Interrupt, der eigentlich auch kurz durch
COMPA gesperrt werden muss, damit er keinen Jitter verursacht.
>Warum jetzt halbe Zeichenlängen warten?
Einfach so. Man kann auch mehrere Zehntel oder sonstwas warten. Es geht
nur darum, daß man nicht ewig ohne Timeout wartet, denn unter bestimmten
Konstellationen kann es passieren, daß TXC am Ende nie auf HIGH geht,
weil vorher schon alle Daten rausgeschoben wurden. Und Teile eines
Bytes, um im Normalfall möglichst schnell drauf zu reagieren, wenn TXC
gesetzt wird.
>Vor jeden Byte senden das "UART Transmit Complete Flag" löschen.
Das geht so einfach nicht, weil das Ganze durch das Softwarefifo
entkoppelt ist! Probier erstmal meinen Ansatz.
>es müsste doch ausreichend sein wenn ich das Bit vorm letzten ETX Byte>lösche.
Nein!
Glaub mir, ich hab diese Erkenntnis TEUER bezahlt!
Gerhard schrieb:> Carl D. schrieb:>> uint16_t kann man aber zur Not auch in 2x uint8_t speichern. Dann>> braucht man nur einen Puffer.>> Erzähl doch hier nicht so einen Schwachsinn :(
Das ist genau so schwachsinnig wie mit einem 8-Bit Rechner zu versuchen
16-Bit Zahlen zu berechnen. Es scheint nämlich möglich zu sein!
Und im konkreten Fall werden ja in die "Byte-Queue" zwischen ISR und
Hauptprogramm ganze Messagen aus noch mehr als 16Bit gesteckt. Immerhin
werden die von der Uart sogar als Sammlung einzelner Bits übertragen.
// warte auf leeren Software-FIFO, der UDRIE ausschaltet
6
while((UCSR0B&(1<<UART0_UDRIE)));
7
8
UCSR0A|=(1<<TXC0);// TXC0 löschen
9
10
// warte auf leeren Hardware-FIFO, welcher TXC setzt
11
// Timeout nach cnt/2 Zeichen
12
cnt=6;
13
while(cnt>0){
14
if(!(UCSR0A&(1<<TXC0))){
15
_delay_us(20);// 1/2 Zeichen @250kBaud
16
cnt--;
17
}
18
}
19
}
Ich verstehe wie das funktionieren soll. Mir gefällt daran jedoch eine
Kleinigkeit nicht, weil das laut meinem Verständnis immer noch schwammig
ist. Mein Protokoll umfasst 11 Bytes. Jetzt wird in der flush Funktion
festgelegt, dass x-mal das leere data register abgefragt wird. Wenn das
Hauptprogramm aus irgendwelchen Gründen, darum gehts ja um das
abzufangen, die Bytes nicht sofort nacheinander aus dem Ringbuffer an
die uart senden kann, dann liegt das Problem plötzlich umgekehrt vor.
Das empty Flag wird 6x abgefragt und letzten 5 Bytes von 11 können nicht
mehr gesendet werden.
Das Problem ist die Festlegung auf wieviel Bytes (hier 6) gewartet
werden soll. Ich denke man kann keine feste Zahl festlegen. Entweder zu
kurz oder zu lang. Eigentlich müsste das in die Sende ISR verlagert
werden, diese müßte selbst mitzählen wieviel Bytes rausgeschickt wurden.
Die Anzahl 11 steht unverändert fest.
@ Carl:
habe mir auch darüber Gedanken gemacht. 16Bit in zwei Byte. Ich meine,
dass ist schon möglich. Man muss nur mit Bits schieben und Maske drüber
die 16 Bits in 2x 8 Bit aufteilen, ein Buffer speichert alle LSB und der
zweite Buffer bekommt alle MSB zum Bsp.. Beim auswerten alles rückwärts
zusammen friemeln. Das zerlegen kostet im PCINT0 ISR allerdings Zeit.
Gut, ich könnte zur Not das Takteinmesssignal langsamer machen. Will
damit sagen, theoretisch ist das möglich, praktisch sicherlich auch, nur
habe ich derzeit keine Notwendigkeit dafür. RAM ist noch genügend frei.
Und falls der freie RAM schrumpfen sollte, würde ich als erstes den
Ringbuffer von 32 auf 16 reduzieren und schauen ob ich auch mit 30
Einmesstakten klar komme statt mit 60. Der Gedanke ist aber nicht
schlecht, sollte man im Hinterkopf behalten. Diese Art Trickserei geht
dann ans Eingemachte. :-)
@Veit Devil (devil-elec)
>ist. Mein Protokoll umfasst 11 Bytes. Jetzt wird in der flush Funktion>festgelegt, dass x-mal das leere data register abgefragt wird.
Aber erst dann, wenn der Software-FIFO leer ist!
>Wenn das>Hauptprogramm aus irgendwelchen Gründen, darum gehts ja um das>abzufangen, die Bytes nicht sofort nacheinander aus dem Ringbuffer an>die uart senden kann, dann liegt das Problem plötzlich umgekehrt vor.
Nein.
>Das empty Flag wird 6x abgefragt und letzten 5 Bytes von 11 können nicht>mehr gesendet werden.
Falsch.
>Das Problem ist die Festlegung auf wieviel Bytes (hier 6) gewartet>werden soll.
Falsch.! Die Zahl 6 ist NICHT die Anzahl von Bytes sondern die Anzahl
von Wartezyklen!
> Ich denke man kann keine feste Zahl festlegen. Entweder zu>kurz oder zu lang. Eigentlich müsste das in die Sende ISR verlagert>werden, diese müßte selbst mitzählen wieviel Bytes rausgeschickt wurden.
Nein.
>Die Anzahl 11 steht unverändert fest.
Das ist nebensächlich.
Die ganze Sache ist aber schon ein wenig kniffelig. Denn wir greifen mal
wieder auf UCSR0B zu, wenn gleich auch nur lesend. Das Bit UDRIE muss
von COMPA ebenfalls kurz deaktiviert werden, damit die UDR-ISR keinen
Jitter erzeugen kann. Damit wird aber die Abfrage des leeren
Software-FIFOs irritiert! Also muss man diese Abfrage anders machen, so
wie es uart0_getc() macht.
Dieter F. schrieb:> Ich denke, man kann sich auch mit passender Hardware beschäftigen.
Man kann natürlich beliebig komplexe und teuere Hardware mit einem
Problem beschäftigen, was auch mit der vorhandenen Hardware eines µC
lösbar wäre, wenn man sie denn einfach nur wirklich vollständig (also
auch hinsichtlich des Timing) beherrschen würde...
c-hater schrieb:> Man kann natürlich beliebig komplexe und teuere Hardware mit einem> Problem beschäftigen, was auch mit der vorhandenen Hardware eines µC> lösbar wäre, wenn man sie denn einfach nur wirklich vollständig (also> auch hinsichtlich des Timing) beherrschen würde...
Für mich ist z.B. ein ATXMega für ca. 3 € jetzt weder komplex noch
teuer. Aber das ist, wie so oft, Ansichtssache ...
Dieter F. schrieb:> Für mich ist z.B. ein ATXMega für ca. 3 € jetzt weder komplex noch> teuer. Aber das ist, wie so oft, Ansichtssache ...
Nö. Der Punkt ist: Wen du die DMA-Fähigkeiten eines ATXMega benötigst,
um eine Sachen zu realisieren, die man auch ohne DMA mit einem ATMega
machen kann, bist du schlicht nur ein Programmierer mit deutlich
unzureichenden Fähigkeiten.
Das muss nicht mal schlecht sein. Wenn du dein Produkt trotzdem
gewinnbringend verkaufen kannst, spielt es z.B. keinerlei Rolle, wie
Scheisse es eigentlich technisch gesehen ist...
Ich habe nicht alle 177 Beiträge gelesen...
Was ich aus deiner ISR lese ist, dass hinter
>K6_'X'_OFF
warscheinlich ein Makro steht - vielleicht in einer board.h
Das bedeutet, dass es warscheinlich
>K6_'N'_ON ´s
gibt, die du alle zum "selbem" Moment einschaltest.
Du willst warscheinlich die Anzahl der Ausgänge leicht erweiterbar
halten?
>> Mein Vorschlag:
Je nachdem was deine Randbedingungen sind, kannst du auch ein anderes
Konzept anwenden, um die Servos zu steuern. Das PPM wie es im Modelbau
aus historischen Gründen noch meist benutzt wird.
Dann würde der PWM Pin, der zu dem Timer gehört die 7 Sevors
nacheinander bedienen und die Pins die du im Moment benutzt, würden das
Signal multiplexen.
>>> SKEPTISCH
Da ist etwas was mich skeptisch macht, ob du einen sinnvollen Ansatz
verfolgst: In der ISR hast du einen stop_Timer1(); Aufruf.
Initialisiertst und startest du bei jedem "Servozyklus" den Timer neu?
Wenn ja, warum? Bzw. hast du dir etwas konkretes dabei gedacht?
@mr. mistoffelees (Gast)
>Ich habe nicht alle 177 Beiträge gelesen...
Deswegen sind dir wesentliche Dinge entgangen.
>Das bedeutet, dass es warscheinlich>>K6_'N'_ON ´s>gibt, die du alle zum "selbem" Moment einschaltest.
Das Thema ist längst abgehakt.
>Du willst warscheinlich die Anzahl der Ausgänge leicht erweiterbar>halten?
Nein.
>Konzept anwenden, um die Servos zu steuern. Das PPM wie es im Modelbau>aus historischen Gründen noch meist benutzt wird.
Das haben wir schon lange in dieser Diskussion.
>verfolgst: In der ISR hast du einen stop_Timer1(); Aufruf.
Diese Code ist schon lange veraltet.
c-hater schrieb:> Wen du die DMA-Fähigkeiten eines ATXMega benötigst,> um eine Sachen zu realisieren,
Wieso DMA - ich wüsste nicht wozu? Aber Du bist der Profi, wirst schon
Recht haben. Insofern ist der Hinweis auf
c-hater schrieb:> deutlich> unzureichenden Fähigkeiten
wohl korrekt.
c-hater schrieb:> Das muss nicht mal schlecht sein. Wenn du dein Produkt trotzdem> gewinnbringend verkaufen kannst, spielt es z.B. keinerlei Rolle, wie> Scheisse es eigentlich technisch gesehen ist...
Hast Du da Erfahrungswerte? Würde sicher manchen interessieren, wenn Du
bereit bist, diese zu Teilen.
@Veit Devil (devil-elec)
>die Timer Compares ISR ergänzt sehen jetzt so aus, funktioniert soweit.> UCSR0B |= (1<<RXCIE0); // enable USART0 RX Complete Interrupt> UCSR0B |= (1<<UDRIE0); // enable USART0 Data Register Empty Interrupt
Das kann man problemlos in eine Zeile schreiben, ist auch ein paar Takte
schneller.
UCSR0B |= ((1<<RXCIE0) | (1<<UDRIE0));
>ISR(TIMER1_COMPA_vect) // dauert 1,8µs>{> UCSR0B &= ~(1<<RXCIE0); // disable USART0 RX Complete Interrupt> UCSR0B &= ~(1<<UDRIE0); // disable USART0 Data Register Empty
Interrupt
Ebenso hier, man muss nur passende Klammern setzen.
UCSR0B &= ~((1<<RXCIE0) | (1<<UDRIE0));
>Allerdings muss ich dafür die Sichtbarkeit für UART0_RxHead und>UART0_RxTail erweitern.
Ja.
>static volatile unsigned char UART0_TxHead;>static volatile unsigned char UART0_TxTail;
Einfach das static weglassen. Und wenn du schon dabei bist, mach mal aus
unsigned char ein uint8_t, auch wenn es praktisch beim AVR das Gleiche
ist.
>extern static volatile unsigned char UART0_TxHead;>extern static volatile unsigned char UART0_TxTail;
Ist Unsinn. extern und static widersprechen sich ;-)
Hallo,
gut, Einzeiler daraus gemacht, Danke.
Habe in der uart Lib alle unsigned char durch uint8_t und alle unsigned
int durch uint16_t ersetzt. Dabei fiel mir was in der originalen
uart_getc auf.
1
unsignedintuart0_getc(void)
2
{
3
unsignedchartmptail;
4
unsignedchardata;
5
unsignedcharlastRxError;
6
7
8
if(UART0_RxHead==UART0_RxTail){
9
returnUART_NO_DATA;/* no data available */
10
}
11
12
/* calculate buffer index */
13
tmptail=(UART0_RxTail+1)&UART_RX_BUFFER_MASK;
14
15
/* get data from receive buffer */
16
data=UART0_RxBuf[tmptail];
17
lastRxError=UART0_LastRxError;
18
19
/* store buffer index */
20
UART0_RxTail=tmptail;
21
22
UART0_LastRxError=0;
23
return(lastRxError<<8)+data;
24
25
}/* uart_getc */
lastRxError und data sind ein Byte groß, zurückgegeben werden aber ein
uint16_t. Macht der Compiler selbstständig die letzte Addition richtig?
Sonst würde ich lieber vorbeugend lastRxError zum uint16_t machen.
Übrigens läuft das jetzt schon viele Minuten ohne Fehler. :-)
Danke Falk, du hast echt was drauf.
Wäre für
UCSR0A |= (1<<TXC0); // delete_uart0_Transmit_Complete_Flag
ein atomic übertrieben?
Veit D. schrieb:> Danke Falk, du hast echt was drauf.
Irgendwie erinnerst Du mich an meinen Bruder :-). Der bekommt es auch
immer wieder hin, dass andere seine Arbeit machen :-) Nix für ungut -
ist nur eine Erinnerung.
Warum kommt es beim Modelleisenbahnverkehr auf 2 Grad oder wenige µs an?
Das habe ich bisher nicht verstanden. Eine Weiche ist ist offen oder
geschlossen (da machen 2 Grad nichts aus) - gleiches gilt für ein
Signal, eine Lokschuppentür etc.
Mich würde das Konzept dahinter interessieren - kannst Du das bitte mal
bekannt geben?
Warum muss es überhaupt der ATTiny841 sein - nicht die schlechteste Wahl
aus meiner Sicht - aber warum?
Wenn ich Deinen Test-Aufbau sehe kommt mir das schon recht professionell
vor - in welcher Liga spielst du?
Beitrag "Re: ISR Code schneller machen?"
Dieter F. schrieb:> Veit D. schrieb:>> Danke Falk, du hast echt was drauf.>> Irgendwie erinnerst Du mich an meinen Bruder :-).
Ist der auch so ein Kriecher?
@Veit Devil (devil-elec)
> UART0_LastRxError = 0;> return (lastRxError << 8) + data;>lastRxError und data sind ein Byte groß, zurückgegeben werden aber ein>uint16_t. Macht der Compiler selbstständig die letzte Addition richtig?
Ja, weil er unsichtbar die beiden Operanden vor der Operation auf int
erweitert, beim avr gcc sind das 16 Bit.
>Sonst würde ich lieber vorbeugend lastRxError zum uint16_t machen.
Nicht nötig, bestenfalls ein cast.
return ((uint16_t)lastRxError << 8) + data;
Ist aber hier auch eher Gürtel + Hosenträger ;-)
>Übrigens läuft das jetzt schon viele Minuten ohne Fehler. :-)
WOW, Software die mehrere Minuten fehlerfrei läuft sieht man ja nicht
alle Tage! ;-)
>Wäre für>UCSR0A |= (1<<TXC0); // delete_uart0_Transmit_Complete_Flag>ein atomic übertrieben?
Braucht man nicht, da auf UCSR0A nirgendwo in einer ISR zugegriffen
wird. Man kann es aber dennoch per atomic Block schützen, falls man mal
irgendwann eine Erweiterung des Quelltextes vornimmt. Aber halt, da soll
jetzt NICHT dazu führen, daß du ALLE IO-Registerzugriffe in deinem
Programm mit einem atomic block "schützt", das wäre Unsinn^3!
Hast du ein Oszi? Hast du mal den Jitter der Servosignale gemessen? Denn
es können immer noch ein paar einzelne Aussetzer passieren, denn dein
uart_putc() schaltet immer fröhlich UDRIE ein, auch wenn es vorher vom
COMPA ausgeschaltet wurde. Da muss man noch ein bisschen zaubern. Dafür
braucht man entweder eine zusätzliche Variable oder man trickst und
nutzt das ungenutztes Bit TXB80. Mit diesem Bit signalisieren wir, ob
uart_putc() UDRIE einschalten darf oder ob es warten muss.
in COMPA muss dann rein
UCSR0B &= ~((1<<RXCIE0) | (1<<UDRIE0) | (1<<TXB80));
in COMPB muss dann rein
UCSR0B |= ((1<<RXCIE0) | (1<<UDRIE0) | (1<<TXB80));
uart0_putc() sieht dann so aus.
1
voiduart0_putc(unsignedchardata)
2
{
3
unsignedchartmphead;
4
5
tmphead=(UART0_TxHead+1)&UART_TX_BUFFER_MASK;
6
7
while(tmphead==UART0_TxTail){
8
;/* wait for free space in buffer */
9
}
10
11
UART0_TxBuf[tmphead]=data;
12
UART0_TxHead=tmphead;
13
14
/* enable UDRE interrupt */
15
ATOMIC_BLOCK(ATOMIC_RESTORESTATE){// wegen ISR(TIMER1_COMPA_vect)
16
if(UCSR0B&(1<<TXB80)){
17
UART0_CONTROL|=_BV(UART0_UDRIE);
18
return;
19
}
20
}
21
22
// warte auf das Ende der ISR-Sperre
23
// reiner Lesezugriff ist atomar, daher kein atomic block nötig
24
while(!(UCSR0B&(1<<TXB80)));
25
26
ATOMIC_BLOCK(ATOMIC_RESTORESTATE){// wegen ISR(TIMER1_COMPA_vect)
Falk B. schrieb:> WOW, Software die mehrere Minuten fehlerfrei läuft sieht man ja nicht> alle Tage! ;-)
Scheint ein Fieber zu sein :-) Gut für Veit, den "Devil" :-)
Hallo,
@ Dieter
Ich denke ich bin schon alleine ganz schön weit gekommen. Allein schon
die OSCCAL Kalibrierung war ein hartes Stück Arbeit. Damit fing alles
an. Ich wurde nur in der Form korrigiert wie das grundlegend richtig
gemacht wird. Ich hatte mich verrannt. Mit den Hinweisen und Atmel App
Notes habe ich das dann ganz alleine umgesetzt. Das ist auch der erste
Kalibriercode den du öffentlich in einem Forum siehst. ;-)
Von daher, kann ich den Spruch so leider nicht unkommentiert stehen
lassen. Auch wenn du das lustig meinst. Ich denke desweiteren das alles
was hier zum Thema wurde schon ganz schön knifflige Dinge sind/waren,
die nur jemand lösen kann der sich damit in den Tiefen auskennt.
Desweiteren hatte ich ja nichts unfertiges gezeigt, lauffähiges mit
sporadischen Hängern.
Genauigkeit. Ist eine meiner Eigenschaften. Halbe Sachen gibts bei mir
nicht. Dauert zwar alles länger, aber am Ende wird es besser und man
freut sich. Seit über einem Jahr (fast 2) baue ich schon rum.
Programmieren und Leiterplatten erstellen. Die Leiterplatten sehen auch
nur so gut aus, weil ich mir dafür Zeit genommen habe. Der Schaltplan
konnte mit Hilfe des Forums verbessert werden. Den Rest hat mein
Target3001 und elecrow gemacht. :-)
Zurück wegen der Servogenaugigkeit. Wenn die neue Ansteuerungsmethode
mehr Genauigkeit nebenbei mit abwirft, dann nehme ich das gern mit. Die
Weichenzunge soll sauber anliegen. Nicht mit zu viel oder zu wenig
Druck. Die Weichenpulse werden dann noch dahin gehend geändert, dass die
nur 1-2s ans Servo ausgegeben werden. Die Servogetriebe sind soweit gut
selbsthaltend. Dauerhaftes Servo brummen wird auch vermieden.
Konzept. Im Grunde ist das eine selbst gebaute SPS. Mit dieser kann ich
alles steuern was ich möchte. Die Slaves sind die
Eingangs/Ausgangskarten und der Master ist das Herz des Ganzen. Dazu ist
alles komplett universal verwendbar. Oder habe ich die Frage falsch
verstanden?
ATtiny841. Warum nicht? Ich benötigte zum Anfang zwei UARTs. Die zweite
zum debuggen bis die Kommunikation nach der Kalibrierung auf der
eigentlichen uart fertig war.
Zu guter letzt macht es mir einfach Spass zu basteln und zu
programmieren. Man wird davon nicht dümmer. :-)
@ Falk
> WOW, Software die mehrere Minuten fehlerfrei läuft sieht man ja nicht> alle Tage! ;-)
hier musste ich laut lachen. Es läuft nun schon seit 2h ohne Fehler
:-)
Mit Oszi habe ich noch nicht wieder gemessen, der restliche Jitter wird
schwer erkennbar werden. Das geht nur mit Datalogger.
Deine neue Idee schaue ich mir noch genauer an.
Veit D. schrieb:> Auch wenn du das lustig meinst.
Nein.
Veit D. schrieb:> Ich denke desweiteren das alles> was hier zum Thema wurde schon ganz schön knifflige Dinge sind/waren,> die nur jemand lösen kann der sich damit in den Tiefen auskennt.
Ja.
Veit D. schrieb:> Seit über einem Jahr (fast 2) baue ich schon rum.> Programmieren und Leiterplatten erstellen. Die Leiterplatten sehen auch> nur so gut aus, weil ich mir dafür Zeit genommen habe. Der Schaltplan> konnte mit Hilfe des Forums verbessert werden.
Ja - nehme ich mal an.
Veit D. schrieb:> Die> Weichenzunge soll sauber anliegen.
Da spielen 2 oder 4 Grad doch überhaupt keine Rolle - das Material ist
elastisch.
Veit D. schrieb:> Die Weichenpulse werden dann noch dahin gehend geändert, dass die> nur 1-2s ans Servo ausgegeben werden.
Und wir sind hier im µs-Bereich unterwegs - spannend :-)
Veit D. schrieb:> Oder habe ich die Frage falsch> verstanden?
Nein - glaube ich nicht - nur die Antwort scheust Du etwas :-)
Veit D. schrieb:> Man wird davon nicht dümmer. :-)
Da stimmen wir überein :-)
Über den Sinn und zweck derartig genauer Servopulse und 250kBaud für ne
Modellbahn kann man streiten. Als sportliche Herausforderung in Punkto
Software taugt es allemal und man kann es an anderer Stelle irgendwann
sinnvoll einsetzen.
Falk B. schrieb:> ber den Sinn und zweck derartig genauer Servopulse und 250kBaud für ne> Modellbahn kann man streiten. Als sportliche Herausforderung in Punkto> Software taugt es allemal und man kann es an anderer Stelle irgendwann> sinnvoll einsetzen.
Ja.
Hallo,
>>> genau, sehe ich auch so
Dieter, was möchtest du denn wissen? Keine klare Frage - keine klare
Antwort.
ohne TXB80 Bit
Der Jitter beträgt leider bis zu 30µs laut Oszi und Datalogger. Was mich
erstaunt. Was man an 2 Servos hört und sieht.
Auflösung = 90°/1000µs = 0,09° pro Count
90°/1000µs*30µs = 2,7° Jitter
Mit Code für TXB80 Bit funktioniert leider nicht, der ATtiny kann nicht
antworten. Laut Manual ist TXB80 für das 9. Datenbit zuständig. Ich
verwende aber nur 8 Datenbits. ?
Veit D. schrieb:> Die Weichenzunge soll sauber anliegen. Nicht mit zu viel oder zu wenig> Druck. Die Weichenpulse werden dann noch dahin gehend geändert, dass die> nur 1-2s ans Servo ausgegeben werden. Die Servogetriebe sind soweit gut> selbsthaltend. Dauerhaftes Servo brummen wird auch vermieden.
Wenn Du noch etwas mehr basteln willst :)
Das Anliegen der Zunge läßt sich auch detektieren, solange sie irgendwo
in der Mitte ist hat sie keine Verbindung zur Fahrspannung und ist
sozusagen hochohmig. Über einen Timeout ließen sich damit auch Fehler
(klemmen) erkennen.
Hallo,
was mich auch wundert, dass die Kommunikation auch nicht mehr
funktioniert, wenn ich in der uart0_putc() das setzen von UDRIE
rausnehme. Wir setzen und löschen doch in den Timer Compares. Sollte
doch völlig egal sein?
Edit:
ach ne, die
ISR (UART0_TRANSMIT_INTERRUPT)
löscht das ja. Mist.
@ guest, merke ich mir vor, Danke.
@ Veit Devil (devil-elec)
>ohne TXB80 Bit>Der Jitter beträgt leider bis zu 30µs laut Oszi und Datalogger. Was mich>erstaunt. Was man an 2 Servos hört und sieht.
Dein Logicanalyzer kann Jitter messen? Cool!
Aber 30us sind irgendwie zuviel, so lange dauert die UART UDRE ISR
nicht.
Zoom mal beim Oszi auf 10us/DIV rein, da sieht man mehr.
Teste mal ohne Datenverkehr auf dem UART, dann dürften nur noch 1-2
Takte, sprich 125-250ns Jitter sein.
>Mit Code für TXB80 Bit funktioniert leider nicht, der ATtiny kann nicht>antworten. Laut Manual ist TXB80 für das 9. Datenbit zuständig. Ich>verwende aber nur 8 Datenbits. ?
Hmm, keine Ahung, kann ich von hier nicht testen. Hast du ALLE
Änderungen drin? Vielleicht hab ich auch was vergessen, war ja nur so ne
schnelle Idee.
Hallo,
die Software kann über das gesamte aufgezeichnete Signal wie gezeigt
alle min/max Pegellängen analysieren. :-) Das aufgezeichnete Signal
kann man auch speichern und irgendwann genauer anschauen. 30s sind
z.Bsp. 113MB Filegröße. Aufzeichnungsdauer hängt nur vom Speicherplatz
ab. Ist wirklich cool.
In der Zwischenzeit habe ich ohne Datenverkehr gemessen. Da ist der max.
Jitter 10µs. Danach wollte ich wissen wie schlimm es denn wird wenn ich
alle Verbesserungen rausnehme + Datenverkehr. Sprich das uart ISR
aktiveren/deaktiveren in den Timer Compares rausnehme und nur noch
Compare A verwende. Ernüchterung ist, es wird nicht schlechter, bleibt
bei 30µs.
Mit 10µs/DIV am Oszi kann ich nicht messen, der Puls ist außerhalb des
Schirms, also länger wie der Schirm.
@ Veit Devil (devil-elec)
>In der Zwischenzeit habe ich ohne Datenverkehr gemessen. Da ist der max.>Jitter 10µs.
Dann stimmt was nicht. Ohne andere ISRs darf das nicht ansatzweise
soviel jittern! Meßfehler?
>Mit 10µs/DIV am Oszi kann ich nicht messen, der Puls ist außerhalb des>Schirms, also länger wie der Schirm.
Aber sicher, das kann JEDES Digitaloszi. Der Trigger kann nahezu
beliebig weit links liegen (trigger delay). Ob das diese "Billiggurken"
können weiß ich nicht, hab nur gute LeCroys in der Firma ;-)
Probier's einfach mal aus. Trigger auf steigende Flanke, fallende Flanke
in Bildschirmmitte und dann auf 10us/DIV runterdrehen.
Ahhh, Moment! Du hast ja den internen RC-Oszillator! Der ist nicht
sonderlich kurzzeitstabil! Da kann das schon mal bei 1ms Pulsbreite um
10us jittern! Das liegt aber am Oszillator und NICHT an der Software!
Hmm???