Forum: Mikrocontroller und Digitale Elektronik ISR Code schneller machen?


von Veit D. (devil-elec)


Lesenswert?

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?

1
ISR(TIMER1_COMPA_vect)    
2
{
3
  static uint16_t count = 0;
4
  
5
    if (count == servo[0].pulscount) {    // Impulsdauer Servo 1
6
      K6_1_OFF;
7
    }
8
  if (count == servo[1].pulscount) {    // Impulsdauer Servo 2
9
    K6_2_OFF;
10
  }
11
  if (count == servo[2].pulscount) {    // Impulsdauer Servo 3
12
    K6_3_OFF;
13
  }
14
  if (count == servo[3].pulscount) {    // Impulsdauer Servo 4
15
    K6_4_OFF;
16
  }
17
  if (count == servo[4].pulscount) {    // Impulsdauer Servo 5
18
    K7_5_OFF;
19
  }
20
  if (count == servo[5].pulscount) {    // Impulsdauer Servo 6
21
    K7_6_OFF;
22
  }
23
  if (count == servo[6].pulscount) {    // Impulsdauer Servo 7
24
    K7_7_OFF;
25
  }
26
  
27
  if (count == MaxPulscount) {    // nach aktuell längster Pulsdauer Timer stoppen
28
    stop_Timer1();
29
    count = 0;
30
    state_running_Timer1 = false;
31
  }
32
  
33
  count++;
34
}

von MaWin O. (Gast)


Lesenswert?

Veit D. schrieb:
> Ist es irgendwie möglich den Code schneller zu machen?

Ohne den restlichen Code zu kennen nicht.

von Stefan F. (Gast)


Lesenswert?

> 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.

von S. Landolt (Gast)


Lesenswert?

182 Takte, erscheint mir etwas viel für das bisschen Programm - was 
verbirgt sich hinter Kn_m_OFF?
Lässt sich vielleicht der ATtiny schneller machen?

von Dieter F. (Gast)


Lesenswert?

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 
:-)

von Veit D. (devil-elec)


Lesenswert?

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.

von Veit D. (devil-elec)


Lesenswert?

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.
1
ISR(TIMER1_COMPA_vect)    
2
{
3
  static uint8_t count = 0;
4
  
5
    if (count == servo[0].pulscount) {    // Impulsdauer Servo 1
6
      K6_1_OFF;
7
    }
8
  if (count == servo[1].pulscount) {    // Impulsdauer Servo 2
9
    K6_2_OFF;
10
  }
11
  if (count == servo[2].pulscount) {    // Impulsdauer Servo 3
12
    K6_3_OFF;
13
  }
14
  if (count == servo[3].pulscount) {    // Impulsdauer Servo 4
15
    K6_4_OFF;
16
  }
17
  if (count == servo[4].pulscount) {    // Impulsdauer Servo 5
18
    K7_5_OFF;
19
  }
20
  if (count == servo[5].pulscount) {    // Impulsdauer Servo 6
21
    K7_6_OFF;
22
  }
23
  if (count == servo[6].pulscount) {    // Impulsdauer Servo 7
24
    K7_7_OFF;
25
  }
26
  
27
  if (count == MaxPulscount) {    // nach aktuell längster Pulsdauer Timer stoppen
28
    stop_Timer1();
29
    count = 0;
30
    state_running_Timer1 = false;
31
  }
32
  
33
  count++;
34
}

von Stefan F. (Gast)


Lesenswert?

> Stefan du meinst weniger wie 2° macht keinen Sinn?

Ja. Aber vielleicht hast du ja ganz besondere Servos vorliegen.

von Einer K. (Gast)


Lesenswert?

Veit D. schrieb:
> der ATtiny taktet mit 8MHz.

Welcher?
Einer Mit PLL?
Dann kannste den Takt verdoppeln.

von S. Landolt (Gast)


Lesenswert?

> der ATtiny taktet mit seinen internen 8MHz
Um welchen geht es denn, wenn ich fragen darf?

von jz23 (Gast)


Lesenswert?

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)

von Johannes S. (Gast)


Lesenswert?

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?

von Veit D. (devil-elec)


Lesenswert?

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.

von Löser (Gast)


Lesenswert?

Wenn die servos nach ihrer pulsecount Variable aufsteigend in dem Array 
stehen, kannst du den längsten Pfad auch halbieren.

von Veit D. (devil-elec)


Lesenswert?

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.

von Dieter F. (Gast)


Lesenswert?

Dieter F. schrieb:
> Schön - was willst Du denn genau? So beschrieben, dass alle es verstehen
> :-)

Nochmal ...

von MaWin O. (Gast)


Lesenswert?

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?

von H.Joachim S. (crazyhorse)


Lesenswert?

Die beste Variante ist, wenn du einem nach dem anderen bedienst. Also 
so, wie es im Fernsteuerprotokoll eigentlich vorgesehen war.

von Mitlesa (Gast)


Lesenswert?

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.

von S. Landolt (Gast)


Lesenswert?

> Compileroptimierung habe ich noch nicht probiert
Liegt es daran?

In Assembler sollte der Kern doch etwa so aussehen:
1
  lds  r16,count
2
  .
3
  .
4
  lds  r17,servo_n.pulscount
5
  cp   r16,17
6
  brne 
7
   cbi
8
  .
9
  .
Das sind also 8* 6 Takte, ganz grob gerechnet, plus Overhead von 
vielleicht 40, ergeben rund 90 Takte = 11 us.

von Matthias S. (Firma: matzetronics) (mschoeldgen)


Lesenswert?

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.

Beitrag #5257094 wurde von einem Moderator gelöscht.
von Veit D. (devil-elec)


Lesenswert?

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.

von Veit D. (devil-elec)


Lesenswert?

Hallo,

mit
1
//stop_Timer1();
2
TCCR1B &= ~( (1<<CS12)|(1<<CS11)|(1<<CS10) );

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.

: Bearbeitet durch User
von H.Joachim S. (crazyhorse)


Lesenswert?

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.

von Veit D. (devil-elec)


Lesenswert?

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.

von MaWin O. (Gast)


Lesenswert?

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++)

von Veit D. (devil-elec)


Lesenswert?

Hallo Mawin,

stimmt, erzeugt eine kleine ungewollte Abweichung. Danke.

von H.Joachim S. (crazyhorse)


Lesenswert?

?
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.

von Veit D. (devil-elec)


Lesenswert?

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.

von MaWin O. (Gast)


Lesenswert?

Veit D. schrieb:
> Demzufolge haut das hin.

Dann stimmt aber die statische Initialisierung mit 0 nicht. Denn dann 
ist der erste Durchlauf falsch.

von Veit D. (devil-elec)


Lesenswert?

Hallo,

okay, stimmt, jetzt sollte daran alles korrekt sein.   :-)

von H.Joachim S. (crazyhorse)


Lesenswert?

Veit D. schrieb:
> Damit wäre die Interruptlast drastisch reduziert

Genau.

von Veit D. (devil-elec)


Lesenswert?

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.

von Falk B. (falk)


Lesenswert?

@ 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.
1
#define SERVO_CNT 7  // 6 echte + 1 virtueller Servo
2
#define SERVO_FRQ 50  // Servo Widerholfrequenz /Hz
3
#define SERVO_PERIOD (F_CPU / (8* SERVO_FRQ)) // Periodendauer der Servosignale in Timer-Takten 
4
5
volatile uint16_t pulsbreite[SERVO_CNT];
6
7
ISR(TIMER1_COMPA_vect) {
8
  static uint8_t servo;
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.
1
calc_vservo(void){
2
  uint16_t i, j;
3
4
  for (i=0, j=0; i<SERVO_CNT-1; i++) {
5
    j += pulsbreite[i];
6
  }
7
  pulsbreite[SERVO_CNT-1] = SERVO_PERIOD - j;
8
}

: Bearbeitet durch User
von Peter D. (peda)


Lesenswert?

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.

von Veit D. (devil-elec)


Lesenswert?

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

: Bearbeitet durch User
von Falk B. (falk)


Lesenswert?

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
  static uint8_t servo;
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.

von Veit D. (devil-elec)


Lesenswert?

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

von H.Joachim S. (crazyhorse)


Lesenswert?

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.

von Veit D. (devil-elec)


Lesenswert?

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.

von Falk B. (falk)


Lesenswert?

@ 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!

von Falk B. (falk)


Lesenswert?

@ 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?

von Veit D. (devil-elec)


Lesenswert?

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.

von Peter D. (peda)


Lesenswert?

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.

von Veit D. (devil-elec)


Lesenswert?

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

von Falk B. (falk)


Lesenswert?

@ 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!

von Veit D. (devil-elec)


Lesenswert?

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.

1
ISR(TIMER1_COMPA_vect)    
2
{
3
  P_ON;                   // Sichtbarkeit des Timers zum vermessen
4
    
5
  static uint8_t servo = 0;
6
  const uint8_t SERVO_CNT = 9;        // Anzahl Servos
7
8
  switch(servo) {
9
    case 0: Servo0_OFF; Servo1_ON;  break;
10
    case 1: Servo1_OFF; Servo2_ON;  break;
11
    case 2: Servo2_OFF;             break;    
12
    case 3:                         break;    // virtueller Servo
13
    case 4:                         break;    // virtueller Servo
14
    case 5:                         break;    // virtueller Servo
15
    case 6:                         break;    // virtueller Servo
16
    case 7:                         break;    // virtueller Servo
17
    case 8:                         break;    // virtueller Servo
18
    case 9:             Servo0_ON;  break;    
19
  }
20
21
  servo++;
22
  
23
  if (servo > SERVO_CNT)  servo = 0;
24
  
25
  OCR1A = 15999;
26
  
27
  P_OFF;
28
}

CTC, Prescaler 1.

von Falk B. (falk)


Lesenswert?

@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.

von Veit D. (devil-elec)


Lesenswert?

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
  static uint8_t servo = 0;
6
  const uint8_t SERVO_CNT = 9;        // Anzahl Servos
7
  uint16_t temp;
8
  
9
  switch(servo) {
10
    case 0: Servo0_OFF; Servo1_ON; temp =  9999; break;
11
    case 1: Servo1_OFF; Servo2_ON; temp = 11999; break;
12
    case 2: Servo2_OFF;            temp = 15999; break;    
13
    case 3:                        temp = 15999; break;    // virtuel
14
    case 4:                        temp = 15999; break;    // virtuel
15
    case 5:                        temp = 15999; break;    // virtuel
16
    case 6:                        temp = 15999; break;    // virtuel
17
    case 7:                        temp = 15999; break;    // virtuel
18
    case 8:                        temp = 15999; break;    // virtuel
19
    case 9:             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.

von Falk B. (falk)


Lesenswert?

@ 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.

von Veit D. (devil-elec)


Lesenswert?

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.

von Falk B. (falk)


Lesenswert?

@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.

von H.Joachim S. (crazyhorse)


Lesenswert?

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.

von Falk B. (falk)


Lesenswert?

@ 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!

von Veit D. (devil-elec)


Angehängte Dateien:

Lesenswert?

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
  static uint8_t servo = 0;
6
    const uint8_t SERVO_CNT = 9;        // Anzahl Servos
7
    uint16_t temp = 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
13
      case 3:              Servo5_ON;  temp =   999; break;
14
    }
15
    */
16
  switch(servo) {    // ISR Zeit 3,7µs
17
    case 0: Servo5_OFF; Servo6_ON;  temp =  1499; break;
18
    case 1: Servo6_OFF; Servo7_ON;  temp =  1999; break;
19
    case 2: Servo7_OFF;             temp =  1999; break;  // virtuelle
20
    case 3:                         temp =  1999; break;
21
    case 4:                         temp =  1999; break;
22
    case 5:                         temp =  1999; break;
23
    case 6:                         temp =  1999; break;
24
    case 7:                         temp =  1999; break;
25
    case 8:                         temp =  1999; break;
26
    case 9:             Servo5_ON;  temp =   999; break;
27
  }  
28
  
29
    servo++;
30
    
31
    if (servo > SERVO_CNT)  servo = 0;
32
    
33
    OCR1A = temp;
34
  
35
  // Count   999 =  1ms
36
  // Count  1999 =  2ms
37
  // Count 19999 = 20ms
38
  
39
  Servo8_OFF;
40
}

von H.Joachim S. (crazyhorse)


Lesenswert?

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.

von Veit D. (devil-elec)


Lesenswert?

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?

von Falk B. (falk)


Lesenswert?

@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.

von Veit D. (devil-elec)


Lesenswert?

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
1
void set_Timer1 ()    
2
{
3
  cli();      // Interrupts ausschalten
4
  TCCR1A = 0;    // Reset TCCR1A Register
5
  TCCR1B = 0;    // Reset TCCR1B Register
6
  TIMSK1 = 0;    // Reset TIMSK1 Register (disable Timer Compare Interrupts)
7
  TCNT1 = 0;    // Start 0
8
  OCR1A = 19999;  // TOP Wert & Prescaler 8 = 20ms 
9
  TIMSK1  = (1<<OCIE1A);  // enable Compare Match A ISR
10
  TIMSK1 |= (1<<OCIE1B);  // enable Compare Match B ISR
11
  //TCCR1B = (1<<WGM12);  // CTC
12
  TCCR1B |= (1 << CS11);  // Prescaler 8, Timer starten
13
  sei();          // Interrupts einschalten
14
}
1
ISR(TIMER1_COMPA_vect)    // wird aller >1ms aufgerufen (Prescaler 8)
2
{
3
  Servo8_OFF;
4
  
5
  static uint8_t servo = 0;
6
  const uint8_t SERVO_CNT = 3;        // Anzahl Servos
7
  const uint8_t TIMING_WINDOW = 7;    // 6µs / 125ns / Prescaler 8 = 6,25 Timertakte
8
  uint16_t temp = 0;
9
    
10
  switch(servo) {    // ISR Zeit 4,75µs
11
    case 0: Servo5_OFF;  Servo6_ON;  temp =  1499; break;
12
    case 1: Servo6_OFF;  Servo7_ON;  temp =  1999; break;
13
    case 2: Servo7_OFF;              temp = 16999; break;  // virtuelle
14
    case 3:              Servo5_ON;  temp =   999; break;
15
  }
16
    
17
  servo++;
18
    
19
  if (servo > SERVO_CNT)  servo = 0;
20
    
21
  OCR1A += temp;
22
  OCR1B = OCR1A-TIMING_WINDOW;
23
  UCSR0B |= (1<<RXCIE0);    // enable USART0 RX Complete Interrupt
24
  
25
  // Count   999 =  1ms
26
  // Count  1999 =  2ms
27
  // Count 19999 = 20ms
28
  
29
  Servo8_ON;
30
}
1
ISR(TIMER1_COMPB_vect)      // dauert 1,13µs
2
{  
3
  UCSR0B &= ~(1<<RXCIE0);    // disable USART0 RX Complete Interrupt
4
}

: Bearbeitet durch User
von Veit D. (devil-elec)


Lesenswert?

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.

: Bearbeitet durch User
von Falk B. (falk)


Lesenswert?

@ 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.

von Veit D. (devil-elec)


Lesenswert?

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?

von Veit D. (devil-elec)


Lesenswert?

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?

von Joachim B. (jar)


Lesenswert?

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.

von Veit D. (devil-elec)


Lesenswert?

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.

von S. Landolt (Gast)


Lesenswert?

> 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"?

von Veit D. (devil-elec)


Lesenswert?

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.
1
T 14039  A 16999  B 16990  
2
T 38     A 1499   B 1990  
3
T 243    A 16999  B 16990  
4
T 4123   A 16999  B 16990  
5
T 11960  A 16999  B 16990  
6
T 15932  A 999    B 
7
countErrors 61
8
9
16990  
10
T 3247  A 16999  B 16990  
11
T 7178  A 16999  B 16990  
12
T 11109  A 16999  B 16990  
13
T 15094  A 16999  B 990  
14
T 7788   A 16999  B 16990  
15
T 11732  A 16999  B 16990  
16
T 15717  A 999    B 
17
countErrors 62
18
19
16990  
20
T 3085   A 16999  B 16990  
21
T 10942  A 16999  B 16990  
22
T 14927  A 16999  B 990  
23
T 820    A 1999   B 1990  
24
T 1088   A 16999  B 16990  
25
T 14937  A 16999  B 990  
26
T 817    A 1999   B 1990  
27
T 1073   A 16999  B 16990  
28
T 16816  A 1499   B 1490  
29
T 
30
countErrors 63
31
32
3950     A 16999  B 16990  
33
T 11815  A 16999  B 16990  
34
T 15788  A 999    B 1490  
35
T 87     A 1999   B 16990  
36
T 606    A 1499   B 1990  
37
T 1851   A 16999  B 16990  
38
T 11660  A 16999  B 16990  
39
T 15631  A 16999  B 
40
countErrors 64
41
42
16990  
43
T 3613   A 16999  B 16990  
44
T 15435  A 16999  B 1490  
45
T 1380   A 1999   B 16990  
46
T 13730  A 16999  B 16990  
47
T 693    A 1499   B 1990  
48
T 1963   A 16999  B 16990  
49
T 11762  A 16999  B 16990  
50
T 15747  A 999   B 
51
countErrors 65
52
53
16990  
54
T 3064   A 16999  B 16990  
55
T 6981   A 16999  B 16990  
56
T 10912  A 16999  B 16990  
57
T 14897  A 16999  B 990  
58
T 777    A 1999   B 1990

von Some Kind of Schniedelwutz (Gast)


Lesenswert?

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...

von S. Landolt (Gast)


Lesenswert?

> 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?

von Joachim B. (jar)


Lesenswert?

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?

von Veit D. (devil-elec)


Lesenswert?

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:
1
/*************************************************************************
2
Function: uart0_getc()
3
Purpose:  return byte from ringbuffer  
4
Returns:  lower byte:  received byte from ringbuffer
5
          higher byte: last receive error
6
**************************************************************************/
7
unsigned int uart0_getc(void)
8
{    
9
    unsigned char tmptail;
10
    unsigned char data;
11
    unsigned char lastRxError;
12
13
14
    if ( UART0_RxHead == UART0_RxTail ) {
15
        return UART_NO_DATA;   /* no data available */
16
    }
17
    
18
    /* calculate buffer index */
19
    tmptail = (UART0_RxTail + 1) & UART_RX_BUFFER_MASK;
20
    
21
    /* get data from receive buffer */
22
    data = UART0_RxBuf[tmptail];
23
    lastRxError = UART0_LastRxError;
24
    
25
    /* store buffer index */
26
    UART0_RxTail = tmptail; 
27
    
28
    UART0_LastRxError = 0;
29
    return (lastRxError << 8) + data;
30
31
}/* uart_getc */
1
ISR (UART0_RECEIVE_INTERRUPT)    // dauert 4,24µs @ 8MHz
2
/*************************************************************************
3
Function: UART Receive Complete interrupt
4
Purpose:  called when the UART has received a character
5
**************************************************************************/
6
{     
7
    unsigned char tmphead;
8
    unsigned char data;
9
    unsigned char usr;
10
    unsigned char lastRxError;
11
 
12
 
13
    /* read UART status register and UART data register */
14
    usr  = UART0_STATUS;
15
    data = UART0_DATA;
16
    
17
    /* get FEn (Frame Error) DORn (Data OverRun) UPEn (USART Parity Error) bits */
18
  #if defined(FE) && defined(DOR) && defined(UPE)
19
    lastRxError = usr & (_BV(FE)|_BV(DOR)|_BV(UPE) );
20
  #elif defined(FE0) && defined(DOR0) && defined(UPE0)
21
    lastRxError = usr & (_BV(FE0)|_BV(DOR0)|_BV(UPE0) );
22
  #elif defined(FE1) && defined(DOR1) && defined(UPE1)
23
    lastRxError = usr & (_BV(FE1)|_BV(DOR1)|_BV(UPE1) );
24
  #elif defined(FE) && defined(DOR)
25
    lastRxError = usr & (_BV(FE)|_BV(DOR) );
26
  #endif
27
28
    /* calculate buffer index */ 
29
    tmphead = ( UART0_RxHead + 1) & UART_RX_BUFFER_MASK;
30
    
31
    if ( tmphead == UART0_RxTail ) {
32
        /* error: receive buffer overflow */
33
        lastRxError = UART_BUFFER_OVERFLOW >> 8;
34
    } else {
35
        /* store new index */
36
        UART0_RxHead = tmphead;
37
        /* store received data in buffer */
38
        UART0_RxBuf[tmphead] = data;
39
    }
40
    UART0_LastRxError |= lastRxError;   
41
}

Mein Hauptprogramm macht nichts weiter wie Serielle bedienen.
1
while (1)  // Hauptprogramm
2
{             
3
      handle_Serial_0_to_Serial_0();
4
}
1
bool read_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
  static uint8_t index = 0;
7
  static bool state_Read = false;
8
  static bool state_Complete = false;
9
  static bool state_Bypass = false;
10
  uint8_t length = sizeof(Nachricht);
11
  
12
  uint16_t c = uart0_getc();        // nächstes Zeichen vom Ringbuffer holen
13
  
14
  // UART Lib Fehlercodes  (dezimal)
15
  //      4096        2048          1024         512                   256
16
  // UART_FRAME_ERROR, UART_OVERRUN_ERROR, UART_PARITY_ERROR, UART_BUFFER_OVERFLOW, UART_NO_DATA
17
  
18
  if ( c & UART_NO_DATA) {  
19
    return false;            // Abbruch nach 500ns, es gibt nichts zum lesen
20
  }
21
  
22
  if ( c > UART_NO_DATA) {        // irgendein UART Error
23
    state_UART_MODE = ERROR;      // Status ändern zur weiteren Fehlerbehandlung
24
    index = 0;
25
    state_Read = false;
26
    state_Complete = false;
27
    state_Bypass = false;
28
    return false;            // es gibt nichts zum lesen
29
  }
30
  
31
  // ------------------------------------------------------------------
32
  c &= 0xFF;                // High Byte nullen vor Weiterverarbeitung
33
    
34
  if (state_Bypass == false) {            // Byte speziell auswerten
35
    if (c == ESC) {            // ESC Kennung ?
36
      state_Bypass = true;
37
      return false;
38
    }
39
    else if (c == ETX) {        // ENDE Kennung ?
40
      state_Read = false;
41
      state_Complete = true;
42
      allows_updateEncoder = YES;
43
    }
44
    else if (c == STX) {        // START Kennung ?
45
      index = 0;
46
      state_Read = true;
47
      state_Complete = false;
48
      allows_updateEncoder = NO;
49
      return false;
50
    }
51
  }  
52
53
  if (state_Read == true && (index < length)) {  // Bytes einsortieren
54
    empfDaten.asArray[index++] = (uint8_t)c;
55
    state_Bypass = false;
56
  }
57
  
58
  if (state_Complete == true && index >= length ) {    // Übertragungsende
59
    state_Complete = false;
60
    index = 0;
61
    if ( calc_CRC16(empfDaten.asArray, length) == 0) {  // Checksumme korrekt ?
62
      if (empfDaten.toAddr == myAddr) {        // bin ich gemeint?
63
        return true;
64
      }
65
    }
66
  }    
67
  return false;        // noch nicht fertig oder irgendwas ging schief
68
}
1
void handle_Serial_0_to_Serial_0 ()      // 0,5ms
2
{  
3
  if( read_Ringbuffer_0() == true)  {
4
    sendDaten.cmd = empfDaten.cmd;
5
    
6
    switch (empfDaten.cmd) {
7
      
8
      case   1:  sendDaten.data = TCNT1;            break;
9
      case   2:  sendDaten.data = OCR1A;            break;
10
      case   3:  sendDaten.data = OCR1B;            break;
11
      default:  sendDaten.data = last_MasterRequestTime;  break;
12
    }
13
    
14
    send_Nachricht();
15
    
16
  }
17
}

von Dieter F. (Gast)


Lesenswert?

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 ...

von Veit D. (devil-elec)


Lesenswert?

Hallo,

die Modelleisenbahn fährt mit Maßstabsgerechter Geschwindigkeit, keine 
Sorge.  :-)    Hat jedoch nichts mit dem Problem zu tun.   ;-)

: Bearbeitet durch User
von S. Landolt (Gast)


Lesenswert?

> Hat jedoch nichts mit dem Problem zu tun.
Vielleicht doch - wenn man von den fünfundzwanzigtausend Bytes pro 
Sekunde wegkäme.

von c-hater (Gast)


Lesenswert?

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...

von Veit D. (devil-elec)


Lesenswert?

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.

: Bearbeitet durch User
von Dieter F. (Gast)


Lesenswert?

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").

von c-hater (Gast)


Lesenswert?

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!

von Falk B. (falk)


Lesenswert?

@ 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.

von Falk B. (falk)


Lesenswert?

@ 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.

von Dieter F. (Gast)


Lesenswert?

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?

Beitrag #5259347 wurde von einem Moderator gelöscht.
Beitrag #5259349 wurde von einem Moderator gelöscht.
Beitrag #5259353 wurde von einem Moderator gelöscht.
von Falk B. (falk)


Lesenswert?

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.

von Some Kind of Schniedelwutz (Gast)


Lesenswert?

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.

von Dieter F. (Gast)


Lesenswert?

Some Kind of Schniedelwutz schrieb:
> Das
> soll (wie beim Vorbild) schön langsam und gleichmäßig erfolgen.

Schön, erzähl das mal Veit ...

von Veit D. (devil-elec)


Lesenswert?

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.

von Dieter F. (Gast)


Lesenswert?

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.

von Falk B. (falk)


Lesenswert?

@ 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.

von Falk B. (falk)


Lesenswert?

@ 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 ;-)

von Dieter F. (Gast)


Lesenswert?

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.

von Veit D. (devil-elec)


Lesenswert?

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.

: Bearbeitet durch User
von c-hater (Gast)


Lesenswert?

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...

von Dieter F. (Gast)


Lesenswert?

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
> :-)

von Carl D. (jcw2)


Lesenswert?

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:
1
ISR(xyz_vect, ISR_NOBLOCK) {}

von c-hater (Gast)


Lesenswert?

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...

von Dieter F. (Gast)


Lesenswert?

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 :-)

von H.Joachim S. (crazyhorse)


Lesenswert?

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?

von Veit D. (devil-elec)


Angehängte Dateien:

Lesenswert?

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
33
#define UART_BAUD_RATE  125000UL    // gewünschte Baudrate
34
#define UBRR_VAL ((F_CPU+UART_BAUD_RATE*8)/(UART_BAUD_RATE*16)-1)  // sauber runden
35
#define BAUD_REAL (F_CPU/(16*(UBRR_VAL+1)))              // reale Baudrate
36
#define BAUD_ERROR ((BAUD_REAL*1000)/UART_BAUD_RATE)        // Fehler in Promille, 1000 = kein Fehler.
37
#if ((BAUD_ERROR<995) || (BAUD_ERROR>1005))
38
  #error Systematischer Fehler der Baudrate groesser 0,5% und damit zu hoch! 
39
#endif
40
41
42
// Definitionen und Initialisierungen
43
44
uint32_t last_MasterRequestTime;
45
46
uint8_t org_OSCCAL;
47
bool done_cal_OSC = false;      // Merker ob 8MHz Oszillator syncronisiert wurde
48
typedef enum {STOP, RUN, CALC, REPEAT, FINISH, WAITSYNC, SYNC, UART, FREE, READ, BUSY, ERROR} Zustand;    // Steuerzustände
49
volatile Zustand state_UART_MODE = WAITSYNC;
50
51
uint32_t uart_Timeout;
52
extern volatile uint16_t UART0_RxBuf[UART_RX_BUFFER_SIZE];
53
extern volatile uint16_t UART0_TxBuf[UART_TX_BUFFER_SIZE];
54
uint16_t countCalibration;
55
uint32_t countErrorsUART0;    // uart0 Fehlerzähler
56
57
const uint8_t STX = '\xCC';    // Startkennzeichen der Nachricht
58
const uint8_t ETX = '\xEE';    // Endkennzeichen der Nachricht
59
const uint8_t ESC = '\xDD';    // 
60
const uint8_t MasterAddr = 0;  // #0 Adresse vom Master
61
const uint8_t myAddr = 3;    // #3 meine Slave Adresse
62
63
// definiere Nachricht
64
union Nachricht
65
{
66
  struct
67
  {
68
    uint8_t toAddr;
69
    uint8_t fromAddr;
70
    uint8_t cmd;
71
    int32_t data;
72
    uint16_t crc;
73
  };
74
  uint8_t asArray[9];      // Summe aller struct Datentypen, für Zugriff über Index
75
} empfDaten, sendDaten;      // zwei gleiche union Buffer anlegen
76
77
78
uint32_t global_ms;
79
80
// *** Funktion Deklarationen *** //
81
// *** USART *** //
82
void Txd0Rxd0_normalPortPins();
83
void uart0_disable();
84
bool read_Ringbuffer_0();
85
void delete_uart0_Transmit_Complete_Flag();
86
void uart0_flush();
87
void MAX487_Empfangsmodus ();
88
void MAX487_Sendemodus ();
89
void sendStruct(uint8_t *structPtr, uint8_t length);
90
void handle_Serial_0_to_Serial_0();
91
void handle_Function_UART0_Pins();
92
void send_Nachricht();
93
uint16_t calc_CRC16 (uint8_t feld[], uint8_t length);
94
// *** allgemeine *** //
95
void set_PCINT2();
96
void del_PCINT2();
97
void Calibration_RunMode();
98
void Calibration_Calculation();
99
100
101
int main(void)
102
{          
103
  Pin_TxD0_OUT;  // TxD0 Ausgang
104
  Pin_RxD0_IN;  // RxD0 Eingang
105
  Pin_RxD0_PULL;  // RxD0 Pullup aktiv
106
  
107
  MAX487_TXRX_OUT;  // PA0 Ausgang, MAX487 /RE_DE 
108
  MAX487_empfangen;  // MAX487 /RE_DE - umschalten auf empfangen
109
  
110
  Servo1_OUT;  // Pin auf Ausgang konfigurieren
111
  Servo2_OUT;
112
  Servo3_OUT;
113
  Servo4_OUT;
114
  Servo5_OUT;
115
  Servo6_OUT;
116
  Servo7_OUT;
117
  Servo8_OUT;
118
    
119
  uart0_disable();
120
  Txd0Rxd0_normalPortPins();
121
  set_Timer0();        // millis, seconds
122
  preSet_Timer1();
123
  preSet_Timer2();      // Timer zum einmessen/kalibrieren
124
         
125
  sendDaten.toAddr = MasterAddr;   
126
  sendDaten.fromAddr = myAddr;
127
  sendDaten.data = 123;
128
  org_OSCCAL = OSCCAL0;    // Debug
129
  
130
    while (1)  // Hauptprogramm
131
    {             
132
    handle_Function_UART0_Pins();
133
    
134
    global_ms = millis();
135
  }
136
}
137
138
139
/* *** Funktionen *** */
140
141
void handle_Function_UART0_Pins()      // im UART Modus max. 29µs
142
{   
143
  if (state_UART_MODE == UART) {                // UART0 wird benutzt
144
    handle_Serial_0_to_Serial_0();
145
  }
146
  
147
  if (state_UART_MODE == FINISH) {              // schaltet UART0 Funktion ein
148
    del_PCINT2();
149
    run_Timer0();
150
    run_Timer1();
151
    memset((void *)UART0_TxBuf, '\0', sizeof(UART0_TxBuf));  // Messbuffer löschen
152
    memset((void *)UART0_RxBuf, '\0', sizeof(UART0_RxBuf));  //
153
    uart0_init( UART_BAUD_SELECT(UART_BAUD_RATE,F_CPU) );
154
    state_UART_MODE = UART;
155
  }
156
  
157
  if (state_UART_MODE == SYNC) {                // schaltet UART0 Funktion ab
158
    uart0_disable();
159
    Txd0Rxd0_normalPortPins();
160
    state_UART_MODE = WAITSYNC;
161
  }
162
    
163
  if (state_UART_MODE == REPEAT) {              // Kalibrierung wiederholen
164
    set_PCINT2();
165
    countCalibration++;
166
    state_UART_MODE = WAITSYNC;
167
  }
168
  
169
  if (state_UART_MODE == WAITSYNC) {              // OSCCAL Calibration ready, dauert 85µs
170
    memset((void *)UART0_TxBuf, '\0', sizeof(UART0_TxBuf));  // Messbuffer löschen
171
    memset((void *)UART0_RxBuf, '\0', sizeof(UART0_RxBuf)); //
172
    Calibration_RunMode();
173
  }
174
175
  if (state_UART_MODE == CALC) {                // OSCCAL neu berechnen
176
    Calibration_Calculation();
177
  }
178
  
179
  if (state_UART_MODE == ERROR) {                // UART neu initialisieren, dauert 7µs
180
    countErrorsUART0++;
181
    uart0_init( UART_BAUD_SELECT(UART_BAUD_RATE,F_CPU) );
182
    state_UART_MODE = UART;
183
  }
184
}
185
186
187
void set_PCINT2()
188
{
189
  GIMSK = (1<<PCIE0);         // PCINT 7:0 Gruppe Trigger aktiv
190
  PCMSK0 = (1<<PCINT2);    // PCINT2, Pin Einzelauswahl
191
}
192
193
194
void del_PCINT2()
195
{
196
  GIMSK = 0;      // PCINT 7:0 Gruppe deaktivieren
197
  PCMSK0 = 0;    // PCINT2 deaktivieren
198
}
199
200
201
void Calibration_RunMode ()  
202
{
203
  stop_Timer0();
204
  stop_Timer1();
205
  set_PCINT2();
206
  run_Timer2();            // Timer 2 starten mit Prescaler 1
207
  state_UART_MODE = RUN;
208
}
209
210
211
void Calibration_Calculation ()      // dauert komplett max. 357µs
212
{
213
  const uint16_t Target = 128;    // gültig für Baudrate 62500 und 8MHz
214
  const uint16_t min = Target-8;    // falsche Messwerte filtern
215
  const uint16_t max = Target+8;
216
  uint16_t summe = 0;
217
  uint16_t diff = 0;
218
  uint16_t count_fail = 0;
219
  uint16_t Ticks = 0;
220
    
221
  // TCNT2 auswerten ... 59 Differenzen bilden
222
    ATOMIC_BLOCK (ATOMIC_RESTORESTATE) {        // dauert 270µs
223
      for (uint8_t i=0; i<29; i++) {          // 0...28           
224
        diff = (UART0_RxBuf[i+1] - UART0_RxBuf[i]);  // 29 Differenzen
225
        if (min < diff && diff < max) {        // filtert falsche Werte
226
          summe = summe + diff;
227
        }
228
        else {
229
          count_fail++;        // Anzahl ungültiger Messwerte zählen
230
        }
231
      }
232
233
      diff = (UART0_TxBuf[0] - UART0_RxBuf[29]);    // 30. Differenz
234
      if (min < diff && diff < max) {          // filtert falsche Werte
235
        summe = summe + diff;
236
      }
237
      else {
238
        count_fail++;          // Anzahl ungültiger Messwerte zählen
239
      }
240
241
      for (uint8_t i=0; i<29; i++) {          // 0...28
242
        diff = (UART0_TxBuf[i+1] - UART0_TxBuf[i]);  // weitere 29 Differenzen
243
        if (min < diff && diff < max) {        // filtert falsche Werte
244
          summe = summe + diff;
245
        }
246
        else {
247
          count_fail++;        // Anzahl ungültiger Messwerte zählen
248
        }
249
      }
250
    }
251
  
252
    Ticks = (summe*1.0/(59-count_fail))+0.5;  // Durchschnitt berechnen und runden    
253
            
254
    if (Ticks > Target) {
255
      OSCCAL0--;
256
      state_UART_MODE = REPEAT;
257
    }
258
    
259
    if (Ticks < Target) {
260
      OSCCAL0++;
261
      state_UART_MODE = REPEAT;
262
      
263
    }
264
      
265
    if (Ticks == Target) {      // Ticks auf Ziel ?
266
      stop_Timer2();        
267
      state_UART_MODE = FINISH;  // Kalibrierung beenden
268
      done_cal_OSC = true;        // Merker Oszillator syncronisiert
269
    }
270
    
271
    if (count_fail > 9) {      // zu viele ungültige Messwerte
272
      state_UART_MODE = REPEAT;
273
      done_cal_OSC = false;       // Merker Oszillator nicht syncronisiert
274
    }    
275
}
276
277
278
ISR(PCINT0_vect)  // Interrupt Handler für PCINTs
279
{           // dauert ohne "if (index>59)" 3,46µs und mit 5,08µs
280
  uint16_t data = TCNT2;
281
  static uint8_t index = 0;    // index = cycle
282
  
283
  if ( index < 30 ) {        // 0...29
284
    UART0_RxBuf[index] = data;  // speichert TNCT2 im Rx Buffer
285
  }
286
  else {
287
    UART0_TxBuf[index-30] = data;  // 30...59 im Tx Buffer
288
  }
289
290
  index++;
291
  
292
  if (index > 59) {        // nach 60 Pegelwechsel Auswertung
293
    del_PCINT2();        // ISR temporär sperren
294
    state_UART_MODE = CALC;
295
    index = 0;
296
  }
297
}
298
299
300
void Txd0Rxd0_normalPortPins()
301
{
302
  Pin_TxD0_OUT;    // TxD0 Ausgang
303
  Pin_RxD0_IN;    // RxD0 Eingang
304
  Pin_RxD0_PULL;    // RxD0 Pullup aktiv
305
}
306
307
308
void uart0_disable()
309
{
310
  UBRR0H = 0;
311
  UBRR0L = 0;
312
  UCSR0B = 0;
313
  UCSR0C = 0;
314
}
315
316
317
bool read_Ringbuffer_0()
318
{   
319
  //  mit Fehlerbehandlung und nichts tun, no data, wird die Funktion nach  0,5 µs verlassen 
320
  // ohne Fehlerbehandlung und nichts tun, wird die Funktion nach 10,46 µs verlassen 
321
  
322
  static uint8_t index = 0;
323
  static bool state_Read = false;
324
  static bool state_Complete = false;
325
  static bool state_Bypass = false;
326
  uint8_t length = sizeof(Nachricht);
327
  
328
  uint16_t c = uart0_getc();        // nächstes Zeichen vom Ringbuffer holen
329
  
330
  // UART Lib Fehlercodes  (dezimal)
331
  //      4096        2048          1024         512                   256
332
  // UART_FRAME_ERROR, UART_OVERRUN_ERROR, UART_PARITY_ERROR, UART_BUFFER_OVERFLOW, UART_NO_DATA
333
  
334
  if ( c & UART_NO_DATA) {  
335
    return false;            // Abbruch nach 500ns, es gibt nichts zum lesen
336
  }
337
  
338
  if ( c > UART_NO_DATA) {        // irgendein UART Error
339
    state_UART_MODE = ERROR;      // Status ändern zur weiteren Fehlerbehandlung
340
    index = 0;
341
    state_Read = false;
342
    state_Complete = false;
343
    state_Bypass = false;
344
    return false;            // es gibt nichts zum lesen
345
  }
346
  
347
  // ------------------------------------------------------------------
348
  c &= 0xFF;                // High Byte nullen vor Weiterverarbeitung
349
    
350
  if (state_Bypass == false) {            // Byte speziell auswerten
351
    if (c == ESC) {            // ESC Kennung ?
352
      state_Bypass = true;
353
      return false;
354
    }
355
    else if (c == ETX) {        // ENDE Kennung ?
356
      state_Read = false;
357
      state_Complete = true;
358
    }
359
    else if (c == STX) {        // START Kennung ?
360
      index = 0;
361
      state_Read = true;
362
      state_Complete = false;
363
      return false;
364
    }
365
  }  
366
367
  if (state_Read == true && (index < length)) {  // Bytes einsortieren
368
    empfDaten.asArray[index++] = (uint8_t)c;
369
    state_Bypass = false;
370
  }
371
  
372
  if (state_Complete == true && index >= length ) {    // Übertragungsende
373
    state_Complete = false;
374
    index = 0;
375
    if ( calc_CRC16(empfDaten.asArray, length) == 0) {  // Checksumme korrekt ?
376
      if (empfDaten.toAddr == myAddr) {        // bin ich gemeint?
377
        return true;
378
      }
379
    }
380
  }    
381
  return false;        // noch nicht fertig oder irgendwas ging schief
382
  
383
}
384
385
386
void handle_Serial_0_to_Serial_0 ()      
387
{  
388
  if( read_Ringbuffer_0() == true)  {
389
    sendDaten.cmd = empfDaten.cmd;
390
    
391
    switch (empfDaten.cmd) {
392
      case   1:  sendDaten.data = TCNT1;            break;
393
      case   2:  sendDaten.data = OCR1A;            break;
394
      case   3:  sendDaten.data = OCR1B;            break;
395
      case   4:  sendDaten.data = countErrorsUART0;      break;  
396
      default:  sendDaten.data = last_MasterRequestTime;  break;
397
    }
398
    
399
    send_Nachricht();
400
    last_MasterRequestTime = seconds();      // Zeitstempel letzter Kommunikation merken
401
    
402
  }
403
  
404
}
405
406
407
void delete_uart0_Transmit_Complete_Flag ()
408
{
409
  UCSR0A |= (1<<TXC0);        // "UART Transmit Complete Flag" zurücksetzen mit "1" 
410
}
411
412
413
void uart0_flush ()
414
{
415
  while( !(UCSR0A & (1<<TXC0)) );    // warten bis Flag "TXC0 UART Transmit Complete" set
416
}
417
418
419
void MAX487_Empfangsmodus ()
420
{    
421
  uart0_flush();
422
  MAX487_empfangen;          // MAX487 /RE_DE - umschalten auf empfangen
423
}
424
  
425
426
void MAX487_Sendemodus ()
427
{
428
  MAX487_senden;            // MAX487 /RE_DE - umschalten auf senden
429
  delete_uart0_Transmit_Complete_Flag();
430
}
431
432
433
void send_Nachricht ()
434
{   
435
  uint8_t data = 0;
436
  sendDaten.crc = calc_CRC16(sendDaten.asArray, (sizeof(Nachricht)-sizeof(sendDaten.crc)) ) ;  
437
  MAX487_Sendemodus();
438
  uart0_putc(STX);
439
  for (uint8_t i=0; i < sizeof(Nachricht); i++) {
440
    data = sendDaten.asArray[i];
441
    if (data == ESC || data == ETX || data == STX) {
442
      uart0_putc(ESC);
443
    }
444
    uart0_putc(data);        // Bytes rausschieben
445
  }
446
  uart0_putc(ETX);
447
  MAX487_Empfangsmodus();
448
}
449
450
451
uint16_t calc_CRC16 (uint8_t feld[], uint8_t length)
452
{
453
  uint16_t crc = 0xFFFF;        // initial CRC value
454
  uint8_t b = 0;
455
  
456
  while (length--) {
457
    crc = _crc16_update(crc, feld[b++]);
458
  }
459
  return crc;
460
}
461
462
463
ISR(TIMER1_COMPA_vect)    // wird aller >1ms aufgerufen (Prescaler 8)
464
{
465
  Servo8_ON;  // zum messen mit Oszi/Datalogger Zweck entfremdet
466
  
467
  static uint8_t servo = 0;
468
  const uint8_t SERVO_CNT = 3;        // Anzahl Servos
469
  const uint8_t TIMING_WINDOW = 10;    // 6µs / 125ns / Prescaler 8 = 6,25 Timertakte
470
  uint16_t temp = 0;
471
    
472
  switch(servo) {    // ISR Zeit 4,75µs
473
    case 0: Servo5_OFF;  Servo6_ON;  temp =  4999; break;
474
    case 1: Servo6_OFF;  Servo7_ON;  temp =  4999; break;
475
    case 2: Servo7_OFF;        temp =  4999; break;  // virtuelle
476
    case 3:             Servo5_ON;  temp =  4999; break;
477
  }
478
    
479
  servo++;
480
    
481
  if (servo > SERVO_CNT)  servo = 0;
482
    
483
  OCR1A = temp;
484
  OCR1B = temp-TIMING_WINDOW;
485
  UCSR0B |= (1<<RXCIE0);    // enable USART0 RX Complete Interrupt
486
  
487
  // Count   999 =  1ms
488
  // Count  1999 =  2ms
489
  // Count 19999 = 20ms
490
  
491
  Servo8_OFF;
492
}
493
494
495
ISR(TIMER1_COMPB_vect)      // dauert 1,13µs
496
{  
497
  Servo1_ON;  // zum messen mit Oszi/Datalogger Zweck entfremdet
498
  UCSR0B &= ~(1<<RXCIE0);    // disable USART0 RX Complete Interrupt
499
  Servo1_OFF;
500
}

von Joachim B. (jar)


Lesenswert?

als Anhang wäre es echt besser als so lang

als Anhang gibt es auch die Codeansicht und würde mein Mausrad nicht 
beleidigen ;)

von Veit D. (devil-elec)


Angehängte Dateien:

Lesenswert?

Hallo,

bitte schön. Habe noch in die Bilder einen Pfeil zur Markierung 
eingezeichnet. Bild c zeigt eins ohne Fehler, CompB vor CompA.

von Veit D. (devil-elec)


Angehängte Dateien:

Lesenswert?

Hallo,

wobei ich soeben auch einmal entdecke das CompB vor CompA ordnungsgemäß 
auslöst und dennoch kann er nicht antworten. Ominös.

von Falk B. (falk)


Lesenswert?

@ 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!

von Falk B. (falk)


Lesenswert?

@ 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.

von Falk B. (falk)


Lesenswert?

@ 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.

von Falk B. (falk)


Lesenswert?

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.

: Bearbeitet durch User
von Veit D. (devil-elec)


Angehängte Dateien:

Lesenswert?

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.

: Bearbeitet durch User
von Falk B. (falk)


Lesenswert?

@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.

von Veit D. (devil-elec)


Lesenswert?

Hallo,

dann bin gespannt was die komplette Offenlegung für Erkenntnisse bringt.

Tschau
Veit

von Falk B. (falk)


Lesenswert?

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.

von absolute (Gast)


Lesenswert?

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.

von Veit D. (devil-elec)


Lesenswert?

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.

von Falk B. (falk)


Lesenswert?

@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
void uart0_flush ()
2
{
3
  uint8_t cnt;
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
4
  
5
  static uint8_t servo = 0;
6
  const uint8_t SERVO_CNT = 3;        // Anzahl Servos
7
  const uint8_t TIMING_WINDOW = 10;    // 6µs / 125ns / Prescaler 8 = 6,25 Timertakte
8
  uint16_t temp = 0;
9
    
10
  switch(servo) {    // ISR Zeit 4,75µs
11
    case 0: Servo5_OFF;  Servo6_ON;  temp =  4999; break;
12
    case 1: Servo6_OFF;  Servo7_ON;  temp =  4999; break;
13
    case 2: Servo7_OFF;        temp =  4999; break;  // virtuelle
14
    case 3:             Servo5_ON;  temp =  4999; break;
15
  }
16
    
17
  servo++;
18
    
19
  if (servo > SERVO_CNT)  servo = 0;
20
    
21
  OCR1B = temp;
22
  OCR1A = temp-TIMING_WINDOW;
23
  UCSR0B |= (1<<RXCIE0);    // enable USART0 RX Complete Interrupt
24
  
25
  // Count   999 =  1ms
26
  // Count  1999 =  2ms
27
  // Count 19999 = 20ms
28
  
29
  Servo8_OFF;
30
}
31
32
33
ISR(TIMER1_COMPA_vect)      // dauert 1,13µs
34
{  
35
  Servo1_ON;  // zum messen mit Oszi/Datalogger Zweck entfremdet
36
  UCSR0B &= ~(1<<RXCIE0);    // disable USART0 RX Complete Interrupt
37
  Servo1_OFF;
38
}

von Veit D. (devil-elec)


Angehängte Dateien:

Lesenswert?

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.

von Falk B. (falk)


Lesenswert?

@ 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.

von Joachim B. (jar)


Lesenswert?

@ 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

von Carl D. (jcw2)


Lesenswert?

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

: Bearbeitet durch User
von Veit D. (devil-elec)


Angehängte Dateien:

Lesenswert?

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"

: Bearbeitet durch User
von Falk B. (falk)


Lesenswert?

@ 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.

von Veit D. (devil-elec)


Angehängte Dateien:

Lesenswert?

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.
1
ISR (UART0_RECEIVE_INTERRUPT)    
2
/*************************************************************************
3
Function: UART Receive Complete interrupt
4
Purpose:  called when the UART has received a character
5
**************************************************************************/
6
{     
7
  Led6_ON;
8
    unsigned char tmphead;
9
    unsigned char data;
10
    unsigned char usr;
11
    unsigned char lastRxError;
12
 
13
    /* read UART status register and UART data register */
14
    usr  = UART0_STATUS;
15
    data = UART0_DATA;
16
    
17
    /* get FEn (Frame Error) DORn (Data OverRun) UPEn (USART Parity Error) bits */
18
  #if defined(FE) && defined(DOR) && defined(UPE)
19
    lastRxError = usr & (_BV(FE)|_BV(DOR)|_BV(UPE) );
20
  #elif defined(FE0) && defined(DOR0) && defined(UPE0)
21
    lastRxError = usr & (_BV(FE0)|_BV(DOR0)|_BV(UPE0) );
22
  #elif defined(FE1) && defined(DOR1) && defined(UPE1)
23
    lastRxError = usr & (_BV(FE1)|_BV(DOR1)|_BV(UPE1) );
24
  #elif defined(FE) && defined(DOR)
25
    lastRxError = usr & (_BV(FE)|_BV(DOR) );
26
  #endif
27
28
    /* calculate buffer index */ 
29
    tmphead = ( UART0_RxHead + 1) & UART_RX_BUFFER_MASK;
30
    
31
    if ( tmphead == UART0_RxTail ) {
32
        /* error: receive buffer overflow */
33
        lastRxError = UART_BUFFER_OVERFLOW >> 8;
34
    } else {
35
        /* store new index */
36
        UART0_RxHead = tmphead;
37
        /* store received data in buffer */
38
        UART0_RxBuf[tmphead] = data;
39
    }
40
    UART0_LastRxError |= lastRxError;   
41
  Led6_OFF;
42
}

von Falk B. (falk)


Lesenswert?

>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?

von Carl D. (jcw2)


Lesenswert?

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.

von Veit D. (devil-elec)


Angehängte Dateien:

Lesenswert?

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
bool read_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
  static uint8_t index = 0;
7
  static bool state_Read = false;
8
  static bool state_Complete = false;
9
  static bool state_Bypass = false;
10
  uint8_t length = sizeof(Nachricht);
11
  
12
  uint16_t c = uart0_getc();        // nächstes Zeichen vom Ringbuffer holen
13
  
14
  // UART Lib Fehlercodes  (dezimal)
15
  //      4096        2048          1024         512                   256
16
  // UART_FRAME_ERROR, UART_OVERRUN_ERROR, UART_PARITY_ERROR, UART_BUFFER_OVERFLOW, UART_NO_DATA
17
  
18
  if ( c & UART_NO_DATA) {  
19
    return false;            // Abbruch nach 500ns, es gibt nichts zum lesen
20
  }
21
  
22
  if ( c > UART_NO_DATA) {        // irgendein UART Error
23
    state_UART_MODE = ERROR;      // Status ändern zur weiteren Fehlerbehandlung
24
    index = 0;
25
    state_Read = false;
26
    state_Complete = false;
27
    state_Bypass = false;
28
    return false;            // es gibt nichts zum lesen
29
  }
30
  
31
  // ------------------------------------------------------------------
32
  c &= 0xFF;                // High Byte nullen vor Weiterverarbeitung
33
    
34
  if (state_Bypass == false) {            // Byte speziell auswerten
35
    if (c == ESC) {            // ESC Kennung ?
36
      state_Bypass = true;
37
      return false;
38
    }
39
    else if (c == ETX) {        // ENDE Kennung ?
40
      state_Read = false;
41
      state_Complete = true;
42
    }
43
    else if (c == STX) {        // START Kennung ?
44
      index = 0;
45
      state_Read = true;
46
      state_Complete = false;
47
      return false;
48
    }
49
  }  
50
51
  if (state_Read == true && (index < length)) {  // Bytes einsortieren
52
    empfDaten.asArray[index++] = (uint8_t)c;
53
    state_Bypass = false;
54
  }
55
  
56
  if (state_Complete == true && index >= length ) {    // Übertragungsende
57
    state_Complete = false;
58
    index = 0;
59
    if ( calc_CRC16(empfDaten.asArray, length) == 0) {  // Checksumme korrekt ?
60
      if (empfDaten.toAddr == myAddr) {        // bin ich gemeint?
61
        return true;
62
      }
63
    }
64
    else {
65
      Led3_ON;
66
      NOP; NOP; NOP; NOP; NOP; NOP; NOP; NOP;   // 1µs
67
      Led3_OFF;
68
    }
69
  }    
70
  return false;        // noch nicht fertig oder irgendwas ging schief
71
  
72
}

: Bearbeitet durch User
von Sebastian S. (amateur)


Lesenswert?

Gibt es eigentlich die gute, alte Sprungtabelle nicht mehr?
Eine Abfrage ob erlaubt (<  >)
und dann einfach Indexhüpfen...

von Veit D. (devil-elec)


Lesenswert?

Hallo Sebastian,

wie meinst du das?
Worauf beziehst du dich?

von Sebastian S. (amateur)


Lesenswert?

@Veit Devil
Du legst eine Tabelle mit den Anfängen der Routinen an und machst dann 
einen Indexsprung, abhängig vom Wert.
Geht seit ewigen Zeiten in C.

von Arno (Gast)


Lesenswert?

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):
1
void f(void)
2
{
3
}
4
5
void g(void)
6
{
7
}
8
9
void h(void)
10
{
11
}
12
13
void main(void)
14
{
15
  void * sprungtabelle[] = {&f, &g, &h};
16
  int i;
17
  if ( i >= 0 && i < 3 )
18
  {
19
    *(sprungtabelle[i])();
20
  }
21
}

MfG, Arno

von Veit D. (devil-elec)


Lesenswert?

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.

von Falk B. (falk)


Lesenswert?

@ 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.

von Falk B. (falk)


Lesenswert?

@ 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.

von Veit D. (devil-elec)


Lesenswert?

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.
1
UCSR0B: Bit7, RXCIE0 ... RX Complete Interrupt Enable
2
3
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

von Carl D. (jcw2)


Lesenswert?

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.

von H.Joachim S. (crazyhorse)


Lesenswert?

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.

von Carl D. (jcw2)


Lesenswert?

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.

von Carl D. (jcw2)


Lesenswert?

Veit D. schrieb:
> Ich denke manchmal zu kompliziert, daran bin ich wirklich gut.  :-)

!!!

von Veit D. (devil-elec)


Lesenswert?

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.

von Falk B. (falk)


Lesenswert?

@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.

von c-hater (Gast)


Lesenswert?

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...

von Dieter F. (Gast)


Lesenswert?

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?

von Carl D. (jcw2)


Lesenswert?

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.

von Veit D. (devil-elec)


Lesenswert?

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.

von H.Joachim S. (crazyhorse)


Lesenswert?

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.

von Falk B. (falk)


Lesenswert?

@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 ;-)

von Carl D. (jcw2)


Lesenswert?

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.

von Veit D. (devil-elec)


Lesenswert?

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.

von Veit D. (devil-elec)


Lesenswert?

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

: Bearbeitet durch User
von Falk B. (falk)


Lesenswert?

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.

von Falk B. (falk)


Lesenswert?

@ 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.

von Veit D. (devil-elec)


Lesenswert?

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

von Falk B. (falk)


Lesenswert?

@Veit Devil (devil-elec)

>Hast den richtigen Riecher.

Und das trotz massiver Erkältung!
SCHNIEEEEF ;-)

von Veit D. (devil-elec)


Lesenswert?

Hallo,

mehr Nebenluft schadet also nicht wie man feststellt ...   :-)
Wünsche gute Besserung.

von Falk B. (falk)


Lesenswert?

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.
1
unsigned int uart0_getc(void)
2
{    
3
    unsigned char tmptail;
4
    unsigned char data;
5
    unsigned char lastRxError;
6
7
8
    if ( UART0_RxHead == UART0_RxTail ) {
9
        return UART_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
    /* store buffer index */
18
    UART0_RxTail = tmptail; 
19
 
20
    ATOMIC_BLOCK (ATOMIC_RESTORESTATE) {
21
      lastRxError = UART0_LastRxError;
22
      UART0_LastRxError = 0;  
23
    }
24
25
    return (lastRxError << 8) + data;
26
27
}/* uart_getc */

von Falk B. (falk)


Lesenswert?

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.

von Sascha W. (sascha-w)


Lesenswert?

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

von Veit D. (devil-elec)


Angehängte Dateien:

Lesenswert?

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.

von Sascha W. (sascha-w)


Lesenswert?

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

von Carl D. (jcw2)


Lesenswert?

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.

: Bearbeitet durch User
von Veit D. (devil-elec)


Lesenswert?

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.

: Bearbeitet durch User
von Sascha W. (sascha-w)


Lesenswert?

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

von Veit D. (devil-elec)


Lesenswert?

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.
1
void uart0_putc(unsigned char data)
2
{
3
    unsigned char tmphead;
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) {
17
    UART0_CONTROL    |= _BV(UART0_UDRIE);
18
  }
19
20
}

von Falk B. (falk)


Lesenswert?

@Veit Devil (devil-elec)

>Übrigens scheint das atomic wirklich zu helfen.

Welches? Das bei uart0_putc()?

von Veit D. (devil-elec)


Lesenswert?

Moment, z.Z. sind beide drin, deins und das von Sascha

: Bearbeitet durch User
von Veit D. (devil-elec)


Lesenswert?

Hallo,

das atomic in uart0_getc() habe ich rausgenommen, jetzt ist nur noch 
atomic in uart0_putc() drin.
1
void uart0_putc(unsigned char data)
2
{
3
    unsigned char tmphead;
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.

von Carl D. (jcw2)


Lesenswert?

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.

von Falk B. (falk)


Lesenswert?

@ 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.

;-)

von Gerhard (Gast)


Lesenswert?

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 :(

von Veit D. (devil-elec)


Angehängte Dateien:

Lesenswert?

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
void uart0_flush ()
2
{
3
  uint8_t cnt;
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
void MAX487_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
void MAX487_Sendemodus ()
9
{  
10
  Led3_ON;
11
  MAX487_senden;      // MAX487 /RE_DE - umschalten auf senden
12
  UCSR0A |= (1<<TXC0);  // delete_uart0_Transmit_Complete_Flag            
13
              // "UART Transmit Complete Flag" zurücksetzen mit "1" 
14
}
15
16
17
void send_Nachricht ()
18
{   
19
  uint8_t data = 0;
20
  sendDaten.crc = calc_CRC16(sendDaten.asArray, (sizeof(Nachricht)-sizeof(sendDaten.crc)) ) ;  
21
  MAX487_Sendemodus();
22
  uart0_putc(STX);
23
  for (uint8_t i=0; i < sizeof(Nachricht); i++) {
24
    data = sendDaten.asArray[i];
25
    if (data == ESC || data == ETX || data == STX) {
26
      uart0_putc(ESC);
27
    }
28
    uart0_putc(data);        // Bytes rausschieben
29
  }
30
  uart0_putc(ETX);
31
  MAX487_Empfangsmodus();
32
  Led3_OFF;
33
}

von Veit D. (devil-elec)


Lesenswert?

Hallo,

es müsste doch ausreichend sein wenn ich das Bit vorm letzten ETX Byte 
lösche.
1
void send_Nachricht ()
2
{   
3
  uint8_t data = 0;
4
  sendDaten.crc = calc_CRC16(sendDaten.asArray, (sizeof(Nachricht)-sizeof(sendDaten.crc)) ) ;  
5
  MAX487_Sendemodus();
6
  uart0_putc(STX);
7
  for (uint8_t i=0; i < sizeof(Nachricht); i++) {
8
    data = sendDaten.asArray[i];
9
    if (data == ESC || data == ETX || data == STX) {
10
      uart0_putc(ESC);
11
    }
12
    uart0_putc(data);        // Bytes rausschieben
13
  }
14
  UCSR0A |= (1<<TXC0);        // delete_uart0_Transmit_Complete_Flag  
15
  uart0_putc(ETX);
16
  MAX487_Empfangsmodus();
17
}

von Veit D. (devil-elec)


Lesenswert?

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.
1
void send_Nachricht ()
2
{
3
  uint8_t data = 0;
4
  sendDaten.crc = calc_CRC16(sendDaten.asArray, (sizeof(Nachricht)-sizeof(sendDaten.crc)) ) ;
5
  
6
  Led3_ON;
7
  MAX487_senden;          // MAX487 /RE_DE - umschalten auf senden
8
  
9
  uart0_putc(STX);
10
  for (uint8_t i=0; i < sizeof(Nachricht); i++) {
11
    data = sendDaten.asArray[i];
12
    if (data == ESC || data == ETX || data == STX) {
13
      uart0_putc(ESC);
14
    }
15
    uart0_putc(data);      // Bytes rausschieben
16
  }
17
  UCSR0A |= (1<<TXC0);      // delete_uart0_Transmit_Complete_Flag
18
  uart0_putc(ETX);
19
  
20
  while( !(UCSR0A & (1<<TXC0)) );  // uart0_flush, warten bis Flag "TXC0 UART Transmit Complete" set
21
  MAX487_empfangen;        // MAX487 /RE_DE - umschalten auf empfangen
22
  Led3_OFF;
23
}

von Falk B. (falk)


Lesenswert?

@ 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!

von Carl D. (jcw2)


Lesenswert?

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.

: Bearbeitet durch User
von Veit D. (devil-elec)


Lesenswert?

Hallo,
1
void uart0_flush ()
2
{
3
  uint8_t cnt;
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
}

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.  :-)

von Falk B. (falk)


Lesenswert?

@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.
1
// warte auf leeren Software-FIFO
2
while (UART0_RxHead != UART0_RxTail);

von c-hater (Gast)


Lesenswert?

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...

von Dieter F. (Gast)


Lesenswert?

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 ...

von c-hater (Gast)


Lesenswert?

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...

von mr. mistoffelees (Gast)


Lesenswert?

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?

von Falk B. (falk)


Lesenswert?

@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.

von Dieter F. (Gast)


Lesenswert?

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.

von Veit D. (devil-elec)


Lesenswert?

Hallo,

die Timer Compares ISR ergänzt sehen jetzt so aus, funktioniert soweit.
1
ISR(TIMER1_COMPB_vect)    // dauert 6,2µs, wird aller >1ms aufgerufen
2
{
3
  Led2_ON;              // für Datalogger, debuggen
4
  
5
  static uint8_t servo = 0;
6
  const uint8_t SERVO_CNT = 3;        // Anzahl Servos
7
  const uint8_t TIMING_WINDOW = 7;    // 6µs / 125ns / Prescaler 8 = 6,25 Timertakte
8
  uint16_t temp = 0;
9
    
10
  switch(servo) {    // ISR Zeit 4,75µs
11
    // Count   999 =  1ms
12
    // Count  1999 =  2ms
13
    // Count 19999 = 20ms
14
    case 0: Led7_OFF;  Led8_ON;  temp =  1999; break;
15
    case 1: Led8_OFF;        temp =  1999; break;  // virtuelle
16
    case 2:             temp = 13999; break;  // virtuelle
17
    case 3:             Led7_ON;  temp =  1999; break;
18
  }
19
    
20
  servo++;
21
    
22
  if (servo > SERVO_CNT)  servo = 0;
23
    
24
  OCR1B += temp;
25
  OCR1A = OCR1B-TIMING_WINDOW;
26
  UCSR0B |= (1<<RXCIE0);    // enable USART0 RX Complete Interrupt
27
  UCSR0B |= (1<<UDRIE0);    // enable USART0 Data Register Empty Interrupt
28
    
29
  Led1_OFF;          
30
  Led2_OFF;
31
}
32
33
34
ISR(TIMER1_COMPA_vect)      // dauert 1,8µs
35
{  
36
  UCSR0B &= ~(1<<RXCIE0);    // disable USART0 RX Complete Interrupt
37
  UCSR0B &= ~(1<<UDRIE0);    // disable USART0 Data Register Empty Interrupt
38
  Led1_ON;          // für Datalogger, debuggen
39
}


Wegen dem uart flush, ist wirklich glịtschig.   :-)
Eigentlich würde ich das nun so formulieren.
1
void send_Nachricht ()
2
{
3
  uint8_t data = 0;
4
  sendDaten.crc = calc_CRC16(sendDaten.asArray, (sizeof(Nachricht)-sizeof(sendDaten.crc)) ) ;
5
  
6
  Led3_ON;            // für Datalogger, debuggen
7
  MAX487_senden;          // MAX487 /RE_DE - umschalten auf senden
8
  
9
  uart0_putc(STX);
10
  for (uint8_t i=0; i < sizeof(Nachricht); i++) {
11
    data = sendDaten.asArray[i];
12
    if (data == ESC || data == ETX || data == STX) {
13
      uart0_putc(ESC);
14
    }
15
    uart0_putc(data);      // Bytes rausschieben
16
  }
17
  uart0_putc(ETX);
18
  
19
  while (UART0_TxHead != UART0_TxTail);  // warte auf leeren TX-Ringbuffer
20
  UCSR0A |= (1<<TXC0);          // delete_uart0_Transmit_Complete_Flag
21
  while( !(UCSR0A & (1<<TXC0)) );      // uart0_flush, warten bis Flag "TXC0 UART Transmit Complete" set
22
  
23
  MAX487_empfangen;            // MAX487 /RE_DE - umschalten auf empfangen
24
  Led3_OFF;
25
}


Allerdings muss ich dafür die Sichtbarkeit für UART0_RxHead und 
UART0_RxTail erweitern. Damit habe ich Probleme. Aktuell gibts die nur 
in der uart.cpp
1
static volatile unsigned char UART0_TxHead;
2
static volatile unsigned char UART0_TxTail;
Jetzt dachte ich, die muss ich nur in meiner main.cpp mit extern bekannt 
machen, so wie ich das mit dem Ringbuffer gemacht habe, klappt aber 
nicht.
1
extern static volatile unsigned char UART0_TxHead;
2
extern static volatile unsigned char UART0_TxTail;
1
conflicting specifiers in declaration of 'UART0_TxHead'  
2
conflicting specifiers in declaration of 'UART0_TxTail'  
3
'UART0_TxHead' was not declared in this scope
4
'UART0_TxTail' was not declared in this scope

Edit:
moment, wir wollen ja den Sendebuffer überwachen, alles Rx zu Tx 
geändert, der der Form halber.

: Bearbeitet durch User
von Falk B. (falk)


Lesenswert?

@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 ;-)

von Veit D. (devil-elec)


Lesenswert?

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
unsigned int uart0_getc(void)
2
{    
3
    unsigned char tmptail;
4
    unsigned char data;
5
    unsigned char lastRxError;
6
7
8
    if ( UART0_RxHead == UART0_RxTail ) {
9
        return UART_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?
1
void send_Nachricht ()
2
{
3
  uint8_t data = 0;
4
  sendDaten.crc = calc_CRC16(sendDaten.asArray, (sizeof(Nachricht)-sizeof(sendDaten.crc)) ) ;
5
  
6
  Led3_ON;            // für Datalogger, debuggen
7
  MAX487_senden;          // MAX487 /RE_DE - umschalten auf senden
8
  
9
  uart0_putc(STX);
10
  for (uint8_t i=0; i < sizeof(Nachricht); i++) {
11
    data = sendDaten.asArray[i];
12
    if (data == ESC || data == ETX || data == STX) {
13
      uart0_putc(ESC);
14
    }
15
    uart0_putc(data);      // Bytes rausschieben
16
  }
17
  uart0_putc(ETX);
18
  
19
  while (UART0_TxHead != UART0_TxTail);  // warte auf leeren TX-Ringbuffer
20
  UCSR0A |= (1<<TXC0);          // delete_uart0_Transmit_Complete_Flag
21
  while( !(UCSR0A & (1<<TXC0)) );      // uart0_flush, warten bis Flag "TXC0 UART Transmit Complete" set
22
  
23
  MAX487_empfangen;            // MAX487 /RE_DE - umschalten auf empfangen
24
  Led3_OFF;
25
}

: Bearbeitet durch User
von Dieter F. (Gast)


Lesenswert?

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?"

von Thomas (Gast)


Lesenswert?

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?

von Dieter F. (Gast)


Lesenswert?

Thomas schrieb:
> Ist der auch so ein Kriecher?

Gottseidank ähnelt er Dir nicht :-) - er ist nämlich nicht feige :-)

von Falk B. (falk)


Lesenswert?

@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
void uart0_putc(unsigned char data)
2
{
3
    unsigned char tmphead;
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)
27
      UART0_CONTROL    |= _BV(UART0_UDRIE);
28
    }
29
    
30
}/* uart_putc */

von Dieter F. (Gast)


Lesenswert?

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" :-)

von Veit D. (devil-elec)


Lesenswert?

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.

von Dieter F. (Gast)


Lesenswert?

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 :-)

von Falk B. (falk)


Lesenswert?

Ü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.

von Dieter F. (Gast)


Lesenswert?

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.

von Veit D. (devil-elec)



Lesenswert?

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. ?

: Bearbeitet durch User
von guest (Gast)


Lesenswert?

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.

von Veit D. (devil-elec)


Lesenswert?

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.

: Bearbeitet durch User
von Falk B. (falk)


Lesenswert?

@ 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.

von Veit D. (devil-elec)


Lesenswert?

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.

von Falk B. (falk)


Lesenswert?

@ 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???

von Veit D. (devil-elec)


Lesenswert?

Hallo,

Ergänzung.
Ohne Datenverkehr.
Das Oszi zeigt für das 1. Servo 0µs Jitter an.
Für das 2. Servo 10µs.
Nur das 2. Servo zuckt selten sporadisch.

von Falk B. (falk)


Lesenswert?


von Veit D. (devil-elec)


Lesenswert?

Oszi Trigger:

mit steigender Flanke Trigger außerhalb des Schirms kann es nicht mehr 
die Pegellängen messen, aber ich sehe die fallende Flanke zittern. Das 
sind weniger wie 5µs. Ohne Datenverkehr.

Mit Datenverkehr zittert es ca. 20µs, so gut man das eben erkennen kann.

LeyCroy kann ich mir nicht leisten.  :-)
Ich wäre auch Rohde & Schwarz Fan.   :-)

von Veit D. (devil-elec)


Lesenswert?

Falk B. schrieb:
> https://scienceprog.com/avr-internal-oscillator-jitter-research/
>
> Früher war alles besser . . .

Das ist natürlich traurig zu sehen, weil ich annahm das der interne sehr 
stabil ist laut Manual. Ist ja Temperatur stabilisiert. Ich hatte auch 
zu Beginn der ganzen Geschichte mit Haarfön und Eiswürfel den ATtiny 
geärgert. Das Timer Taktsignal von damals 100kHz veränderte sich 
überhaupt nicht. Seitdem nahm ich an alles ist dufte.

von Falk B. (falk)


Lesenswert?

@Veit Devil (devil-elec)

>mit steigender Flanke Trigger außerhalb des Schirms kann es nicht mehr
>die Pegellängen messen,

Das ist egal.

> aber ich sehe die fallende Flanke zittern.

Das ist der Punkt!

> Das
>sind weniger wie 5µs. Ohne Datenverkehr.

Ist immer noch zuviel, wenn man von einer idealen Taktquelle ausgeht.

>Mit Datenverkehr zittert es ca. 20µs, so gut man das eben erkennen kann.

Immerhin ist ein Unterschied sichtbar

Letze Idee für heute. Generiere ein Servosignal rein in Hardware per 
OCR1A Pin ohne ISR etc, dazu die COMA1x Bits passend setzen. Damit kann 
man den Jitter des RC-Oszillators sicher messen.

von Falk B. (falk)


Lesenswert?

@ Veit Devil (devil-elec)

>Das ist natürlich traurig zu sehen, weil ich annahm das der interne sehr
>stabil ist laut Manual.

Das ist relativ.

> Ist ja Temperatur stabilisiert.

Die Frequenz ja, aber Jitter ist, je nach Messung, ein Kurzzeiteffekt.

von Veit D. (devil-elec)


Lesenswert?

Hallo,

der RC Jitter beträgt laut Link ca. 100ns, was ca. 1 Takt entspricht. 
Sollte sich das nicht permanent ausgleichen weil er ständig zittert?

Hardware OCR1A Messung mach ich morgen ... gute Nacht

von mr. mistoffelees (Gast)


Lesenswert?

>@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.

Im Ganzen wäre es viel einfacher einem Thread zu folgen, wenn nicht 
grundsätzlich zwischendrin jemand anfangen würde dem TO eine andere 
Hardware zu empfehlen, woraufhin dann erstmal eine Diskussion über die 
vorgeschlagene Hardwareänderung entbrennt (in diesem Fall XMega) und das 
dann noch gemischt mit persönlichen Meinungen zwischen verschiedenen 
"Lagern".
Vielleicht sollten sich die Mods angewöhnen, solche Beitraäge ebenfalls 
zu löschen.
DESHALB lese ich nicht alle vorigen Beiträge weil die ganz schnell am 
Eröffnungsthred vorbeigehen.

von Dieter F. (Gast)


Lesenswert?


von Äxl (geloescht) (Gast)


Lesenswert?

HI,
nachdem ja alles läuft (ich wollte nicht auch noch "mitmachen")...
Was macht deine crc16-Geschichte. Wie lange dauert denn das alles so?
Die Debug-LED wird ja erst danach eingeschaltet.
Da würde mich ja mal das asm Listing interessieren. Ist es hinderlich, 
mit dem "sizeof" Operator zu arbeiten oder sendDaten als Struct 
anzulegen, statt eine eigene Variable zu verwenden? Ich frag nur aus 
neugierde, ich bin eher in der Hardware zu Hause.
1
sendDaten.crc = calc_CRC16(sendDaten.asArray, (sizeof(Nachricht)-sizeof(sendDaten.crc)) ) ;

aus der Referenz:
http://www.microchip.com/webdoc/avrlibcreferencemanual/group__util__crc_1ga95371c87f25b0a2497d9cba13190847f.html


Ist das Pseudocode zu erklären oder warum muss die Zählvariable "i" ein 
16 bittiges int sein? Dann die ganzen Parameter-Übergaben an die 
Funktion. Das kommt mir alles total "viel" vor. Aber - ich hab nicht die 
Mega-Ahnung, was den ganzen Pointer-Kram angeht und weis auch nicht, wie 
weit der Compiler hier optimiert.
1
uint16_t
2
crc16_update(uint16_t crc, uint8_t a)
3
{
4
    int i;
5
6
    crc ^= a;
7
    for (i = 0; i < 8; ++i)
8
    {
9
        if (crc & 1)
10
            crc = (crc >> 1) ^ 0xA001;
11
        else
12
            crc = (crc >> 1);
13
    }
14
15
    return crc;
16
}


Ebenso möchte ich mich vorsichtig der oben gestellten Frage anschließen, 
ob man bei einer Modellbahn mit RS485-Bus, 250KBaud und CRC arbeiten 
muss.

Schade, das das Projekt für mich total undurchsichtig ist.
Ich lege immer alles in ein Verzeichns und zippe mir das weg.
Wenn dann jemand nach dem Code fragt, um mir helfen zu können, dann lade 
ich das ZIP-File hoch und gut.

Aber: schön, wenn jetzt alles (länger als 2Stunden lang?) läuft.

Grüße Äxl

von Falk B. (falk)


Lesenswert?

@Veit Devil (devil-elec)

>der RC Jitter beträgt laut Link ca. 100ns, was ca. 1 Takt entspricht.

Aber nur nach 10us trigger delay. bei 2ms sieht das anders aus.

>Sollte sich das nicht permanent ausgleichen weil er ständig zittert?

Das hängt von den Details ab, genauer, von der Amplitude der 
Phasenmodulation. Wenn man will, kann man die Phase über große Zeiträume 
sehr stark driften lassen und sie dann wieder genausoviel in die 
Gegenrichtung driften lassen. im Langzeitmittel ist die Frequenz sehr 
genau, kurzzeitig ist die Phase sonstwo 8-0

>Hardware OCR1A Messung mach ich morgen ... gute Nacht

Tu das. Danach solltest du deine Taktauswahl überdenken und dir einen 8 
MHz Quarzoszillator besorgen und damit deinen AVR takten. Das kostet nur 
ein IO-Pin, bringt dir aber einen ASTREINEN Takt. Für deine Zwecke ist 
ein RC-Oszillator weder technisch noch ökonomisch sinnvoll bzw. nötig.

https://www.mikrocontroller.net/articles/AVR-Tutorial:_Equipment#Erg.C3.A4nzende_Hinweise_zur_Taktversorgung_.28kann_.C3.BCbersprungen_werden.29

von Falk B. (falk)


Lesenswert?

@ Äxl (geloescht) (Gast)

>Da würde mich ja mal das asm Listing interessieren.

Warum? Das macht der Compiler schon ganz gut.

>Ist es hinderlich,
>mit dem "sizeof" Operator zu arbeiten

Keine Sekunde, denn das ist praktisch eine Konstante.

>oder sendDaten als Struct
>anzulegen, statt eine eigene Variable zu verwenden?

Auch kein Thema, das dröselt der Compiler normal auf und fertig.

>http://www.microchip.com/webdoc/avrlibcreferencema...

>Ist das Pseudocode zu erklären

Nö, echtes C.

> oder warum muss die Zählvariable "i" ein
>16 bittiges int sein?

Weil int halt Standard ist. Die Optimierer schreiben dort uint8_t hin, 
die High Speed Optimierer uint_fast8_t.

> Dann die ganzen Parameter-Übergaben an die
>Funktion. Das kommt mir alles total "viel" vor.

Was ist an ZWEI Parametern viel?

>Aber - ich hab nicht die
>Mega-Ahnung, was den ganzen Pointer-Kram angeht und weis auch nicht, wie
>weit der Compiler hier optimiert.

Weit genug. Wenn gleich man eine CRC nicht derartig krampfhaft noch in 
eine Unterfunktion zerlegt. Im Normalfall kann man das direkt in einer 
Funktion für einen Datenblock machen.

>Ebenso möchte ich mich vorsichtig der oben gestellten Frage anschließen,
>ob man bei einer Modellbahn mit RS485-Bus, 250KBaud und CRC arbeiten
>muss.

Muss man nicht, aber man kann.

von Veit D. (devil-elec)


Lesenswert?

Hallo,

möchte wirklich jemand eine Steuerung bauen ohne Netz und doppelten 
Boden? Wenn ich einen Befehl auf Reise schicke, dann möchte ich sicher 
sein das genau dieser Befehl an der richten Adresse unverstümmelt 
ankommt. Stell euch mal vor ich baue keine Sicherheiten ein. Ich möchte 
zum Bsp. eine Weiche stellen. Jetzt läuft die Übertragung wegen 
irgendwas gegen den Baum. Danach fühlt sich der falsche Slave 
angesprochen und der stellt dann keine Weiche, was sowieso die falsche 
wäre, sondern schickt einen 2. Zug auf die Strecke. Möchte das wirklich 
jemand ernsthaft von vornherein als Restrisiko einplanen? Ich denke 
nicht.

Ich wundere mich ehrlich gesagt über die IoT Scheunentor offenen 
Gerätschaften nicht mehr wirklich, wenn man an solche Dinge schlaksig 
rangeht.

Die CRC Funktion wird mehrfach benötigt, deshalb als Funktion, zudem 
einfacher wartbar.

sizeof wurde schon geändert, nur nicht in diesem Code der dann zum 
Testcode wurde. Mache ich mit constexpr.

So, zurück zum Thema, RC Jitter. Ja der zappelt durchaus rum   :-(
https://youtu.be/ts-UkvS9csw
Am Ende gehe ich auf 10µs/DIV bis 5µs/DIV runter.

Auf den Quarz hatte ich verzichtet, weil ich das Servoproblem damals 
noch nicht kannte. Außerdem habe ich ohne Quarz genau 8 Pins frei, 
perfekt für Byte Übertragung. Zudem der Platinenplatz mit den Steckern 
auch gut ausgenutzt wird. Damit ich die Bauteile nicht umsonst gekauft 
habe, werde ich nun für die Servos eigene Platinen bauen, mit Quarz. Ich 
werde dann gleich einen 16MHz Quarz nutzen. Ich denke, dass klingt nach 
einem Plan.  :-)

von Veit D. (devil-elec)


Lesenswert?

Hallo,

wegen deinem LeyCroy:
Kann das Pulse etc. auch vermessen wenn diese außerhalb des Schirms 
liegen?

zur Frage crc16 Funktion, wenn ich das so messe dauert es 28,2µs.
1
Led3_ON;
2
sendDaten.crc = calc_CRC16(sendDaten.asArray, (sizeof(Nachricht)-sizeof(sendDaten.crc)) ) ;
3
Led3_OFF;


wegen Jitter vs. Servo:
auch wenn das bis zu 2° ausmacht, bei genauerer Überlegung reduziert 
sich das wieder bis zur Weiche, durch Hebel ist da eine Untersetzung 
drin usw. Ich denke vorerst auf größere Neubauten kann ich erstmal 
Abstand nehmen. Wenn mich doch etwas stört, weiß ich gleich was ich 
machen muss. Umsonst war die Übung jedenfalls nicht.

Der nächste Punkt war die tausend if in der handle Serial Funktion. 
Umgebaut auf switch-case.
1
void handle_Function_UART0_Pins()
2
{
3
  switch (state_UART_MODE) {
4
  case RUN:  // Dummy, virtueller Zustand, Takteinmesszyklus läuft mittels ISR(PCINT0_vect)
5
        break;
6
    
7
  case UART:  // UART0 wird benutzt
8
        handle_Serial_0_to_Serial_0();
9
        break;  // minimal 7,7µs ... maximal 18,2µs
10
    
11
  case FINISH:  // schaltet UART0 Funktion ein
12
        del_PCINT2();
13
        run_Timer0();
14
        run_Timer1();
15
        memset((void *)UART0_TxBuf, '\0', sizeof(UART0_TxBuf));  // Messwerte löschen
16
        memset((void *)UART0_RxBuf, '\0', sizeof(UART0_RxBuf));
17
        uart0_init( UART_BAUD_SELECT(UART_BAUD_RATE,F_CPU) );
18
        state_UART_MODE = UART;
19
        break;
20
    
21
  case SYNC:  // schaltet UART0 Funktion ab
22
        uart0_disable();
23
        Txd0Rxd0_normalPortPins();
24
        state_UART_MODE = WAITSYNC;
25
        break;
26
    
27
  case REPEAT:  // Kalibrierung wiederholen
28
        set_PCINT2();
29
        countCalibration++;
30
        state_UART_MODE = WAITSYNC;
31
        break;
32
    
33
  case WAITSYNC:  // OSCCAL Calibration ready, dauert 85µs
34
        memset((void *)UART0_TxBuf, '\0', sizeof(UART0_TxBuf));  // Messwerte löschen
35
        memset((void *)UART0_RxBuf, '\0', sizeof(UART0_RxBuf)); //
36
        stop_Timer0();
37
        stop_Timer1();
38
        set_PCINT2();            // ISR(PCINT0_vect) scharf schalten
39
        run_Timer2();            
40
        state_UART_MODE = RUN;
41
        break;
42
        
43
  case CALC:  // OSCCAL neu berechnen
44
        Calibration_Calculation();
45
        break;
46
    
47
  case ERROR:  // Fehlerbehandlung noch nicht komplett
48
        countErrorsUART0++;
49
        //uart0_init( UART_BAUD_SELECT(UART_BAUD_RATE,F_CPU) );
50
        state_UART_MODE = UART;
51
        break;
52
        
53
  default:  break;
54
  }
55
}

Was würdest du wegen dem enum "state_UART_MODE" ändern?
In der ISR(PCINT0_vect) wird der nach 60 Messungen auf CALC gesetzt.
In der Berechungsfunktion je nach Ergebnis auf REPEAT oder FINISH.
In der Ringbuffer Funktion bei uart Fehler auf ERROR.
Ich wüßte jetzt nicht was man daran verbessern oder trennen sollte?
Das hängt doch alles zusammen bzw. voneinander ab.

von Falk B. (falk)


Lesenswert?

@Veit Devil (devil-elec)

>wegen deinem LeyCroy:
>Kann das Pulse etc. auch vermessen wenn diese außerhalb des Schirms
>liegen?

Nein. Aber für die Messung einer einfachen Pulsbreite reicht es, die 2. 
Flanke exakt auf die Bildschirmmitte zu legen. Dann entspricht die 
Triggerverzögerung (horizontal delay) exakt der Pulsbreite).


>drin usw. Ich denke vorerst auf größere Neubauten kann ich erstmal
>Abstand nehmen. Wenn mich doch etwas stört, weiß ich gleich was ich
>machen muss. Umsonst war die Übung jedenfalls nicht.

Ja, aber um den Effekt der Jitterreduzierung durch die clevere COMPA 
Abschaltung zu messen, solltest du dennoch testweise ein Board mit einem 
externen Quarzoszillator betreiben. Seeing is believing!

>Der nächste Punkt war die tausend if in der handle Serial Funktion.
>Umgebaut auf switch-case.

Fast gut.

>  case UART:  // UART0 wird benutzt
>        handle_Serial_0_to_Serial_0();
>        break;  // minimal 7,7µs ... maximal 18,2µs

Das break setze ich immer in die gleiche Einrückung wie das case, denn 
es ist praktisch mit einer schließenden Klammer identisch.

>  case CALC:  // OSCCAL neu berechnen
>        Calibration_Calculation();
>        break;

Hier fehlt der Übergang des States, der sollte/darf NICHT unsichtbar an 
anderen Stellen erfolgen. Das Gleiche gilt für deine anderen States. 
Dort müssen die Übergangsbedingen rein, damit sie direkt sichtbar sind! 
Die dürfen nicht im restlichen Programm verstreut sein, sonst wird man 
wahnsinnig!

>Was würdest du wegen dem enum "state_UART_MODE" ändern?

Siehe oben.

>In der ISR(PCINT0_vect) wird der nach 60 Messungen auf CALC gesetzt.

Schlecht. Dort setzt man ein Flag ala calc_buffer_full und wertet dies 
in der FSM aus und macht DORT den Zustandswechsel!

>In der Berechungsfunktion je nach Ergebnis auf REPEAT oder FINISH.
>In der Ringbuffer Funktion bei uart Fehler auf ERROR.
>Ich wüßte jetzt nicht was man daran verbessern oder trennen sollte?

Siehe oben. Man kann den Ablauf nicht nachvollziehen. Das Hin- und 
Herspringen in den Funktionen um den Ablauf nachzuvollziehen ist Murks, 
da würden dir die Kollegen in der Firma an die Gurgel gehen ;-)

>Das hängt doch alles zusammen bzw. voneinander ab.

Ja, aber es muss auch einfach und überschaubar dargestellt sein.
1
    case CALC:  // OSCCAL neu berechnen
2
      if (Calibration_Calculation() == true) {
3
        state_UART_MODE = FINISH;
4
      }
5
    break;

Außerdem ist es kosmetisch vorteilhaft, die States in der Reihenfolge 
aufzuschreiben, wie sie im Normalfall durchlaufen werden, das 
vereinfacht das Nachvollziehen ein wenig mehr.

von Falk B. (falk)


Lesenswert?

>  case RUN:  // Dummy, virtueller Zustand, Takteinmesszyklus läuft
>        break;

Das würde im Normalfall bedeuten, daß dieser Zustand nie wieder 
verlassen wird. Das ist aber bei dir nicht so. Ergo, der Leser wird 
verarscht.

Regel: Schreibzugriffe auf die State-Variable erfolgen nur in der FSM 
selber. Lesezugriffe von anderswo sind erlaubt, besonders von anderen 
FSMs, damit die wissen, was ab geht.

von Veit D. (devil-elec)


Lesenswert?

Hallo,

jetzt wird mir das Problem für andere Codeleser klarer. Danke.

Wegen dem Quarz, mal sehen ob ich einen Quarz habe, irgendwo muss einer 
rumliegen. Dann teste ich das nochmal.

: Bearbeitet durch User
von Falk B. (falk)


Lesenswert?

@Veit Devil (devil-elec)

>Wegen dem Quarz, mal sehen ob ich einen Quarz habe, irgendwo muss einer
>rumliegen. Dann teste ich das nochmal.

Quarzoszillator, das ist ein aktives Bauteil mit VCC, GND und Ausgang. 
Ein Quarz allein ist rein passiv mit 2 Anschlüssen.

von Veit D. (devil-elec)


Lesenswert?

Hallo,

Tüte gefunden, sind 8MHz Quarze und die passenden Kondensatoren sind 
auch dabei.

Code aufgeräumt:
nur das break komplett vorrücken gefällt mir optisch nicht    :-)
Eine neue Zeile mit case ist mir für mich klar genug, zudem es farblich 
unterschiedlich dargestellt wird. Die Einrückung ist in AS noch ein TAB 
mehr, wird hier leider nicht dargestellt. Die enum Variable habe ich 
auch gleich lokal gemacht. Die Reihenfolge ist angepasst, bis auf UART, 
habe ich auf Anfang belassen, weil das der Standardzustand ist.

Die Nachrichtenlänge wird überall gleich als Konstante übergeben.
1
// definiere das Datenprotokoll
2
union Message
3
{
4
  struct
5
  {
6
    uint8_t toAddr;
7
    uint8_t fromAddr;
8
    uint8_t cmd;
9
    int32_t data;
10
    uint16_t crc;
11
  };
12
  uint8_t asArray[9];      // Summe aller struct Datentypen, für Zugriff über Index
13
} empfDaten, sendDaten;      // zwei gleiche union Buffer anlegen
14
15
constexpr uint8_t length_Message = sizeof(Message);

1
void handle_Function_UART0_Pins()
2
{
3
  static state_t state_UART_MODE = SYNC;      // Startbedingung
4
   
5
  switch (state_UART_MODE) {
6
    
7
  case UART:  // UART0 wird benutzt
8
        handle_Serial_0_to_Serial_0();
9
        if (countErrorsUART0 > 50000) {    // erstmal ne wilde Hausnummer
10
          state_UART_MODE = ERROR; }
11
      break;    // minimal 7,7µs ... maximal 18,2µs
12
  
13
  case SYNC:  // schaltet UART0 Funktionalität ab
14
        uart0_disable();
15
        Txd0Rxd0_normalPortPins();
16
        state_UART_MODE = WAITSYNC;
17
      break;
18
    
19
  case WAITSYNC:  // OSCCAL Calibration ready, dauert 85µs
20
        memset((void *)UART0_TxBuf, '\0', sizeof(UART0_TxBuf));  // Messwerte löschen
21
        memset((void *)UART0_RxBuf, '\0', sizeof(UART0_RxBuf)); //
22
        stop_Timer0();
23
        stop_Timer1();
24
        set_PCINT2();            // ISR(PCINT0_vect) scharf schalten
25
        run_Timer2();            
26
        state_UART_MODE = RUN;
27
      break;
28
  
29
  case RUN:  // Takteinmesszyklus läuft mittels ISR(PCINT0_vect)
30
        if (state_measurement_complete == true) {
31
          state_UART_MODE = CALC; }
32
      break;
33
        
34
  case CALC:  // OSCCAL neu berechnen
35
        if (Calibration_Calculation() == true) {
36
           state_UART_MODE = FINISH; }
37
        else { state_UART_MODE = REPEAT; }
38
        state_measurement_complete = false;    // Reset für ISR(PCINT0_vect) einmessen
39
      break;
40
        
41
  case REPEAT:  // Kalibrierung wiederholen
42
        set_PCINT2();
43
        countCalibration++;
44
        state_UART_MODE = WAITSYNC;
45
      break;
46
  
47
  case FINISH:  // schaltet UART0 Funktion ein
48
        del_PCINT2();
49
        run_Timer0();
50
        run_Timer1();
51
        memset((void *)UART0_TxBuf, '\0', sizeof(UART0_TxBuf));  // Messwerte löschen
52
        memset((void *)UART0_RxBuf, '\0', sizeof(UART0_RxBuf));
53
        uart0_init( UART_BAUD_SELECT(UART_BAUD_RATE,F_CPU) );
54
        state_UART_MODE = UART;
55
      break;
56
  
57
    
58
  case ERROR:  // Fehlerbehandlung noch nicht komplett
59
        // uart0_init( UART_BAUD_SELECT(UART_BAUD_RATE,F_CPU) );
60
        // oder 
61
        // state_UART_MODE = SYNC;
62
        state_UART_MODE = UART;
63
        countErrorsUART0 = 0;
64
      break;
65
        
66
  default:  break;
67
  }
68
}

Wenn das so näher an irgendwelchen Stil Standards ist, würde ich mich 
schon zufrieden geben. Dann würde ich die Ringbuffer Funktion angehen.

von Falk B. (falk)


Lesenswert?

@Veit Devil (devil-elec)

>nur das break komplett vorrücken gefällt mir optisch nicht    :-)

Naja, ist halt nicht perfekt.

>Eine neue Zeile mit case ist mir für mich klar genug, zudem es farblich
>unterschiedlich dargestellt wird. Die Einrückung ist in AS noch ein TAB
>mehr, wird hier leider nicht dargestellt.

Weil TABs immer ungünstig sind. Man sollte seinen Editio so einstellen, 
daß der immer gleich echte Leerzeichen draus macht. Oder wenigststens 
vor dem Kopieren ins einen Beitrag.

Die Nachrichtenlänge wird überall gleich als Konstante übergeben.

>constexpr uint8_t length_Message = sizeof(Message);

Was soll der High Tec Unsinn it constexpr? Braucht hier kein Mensch.
K.I.S.S.!

>  switch (state_UART_MODE) {

>  case UART:  // UART0 wird benutzt

case muss eingerückt werden.

>        handle_Serial_0_to_Serial_0();
>        if (countErrorsUART0 > 50000) {    // erstmal ne wilde Hausnummer
>          state_UART_MODE = ERROR; }

DAS ist GANZ schlechter Stil, die schließende Klammer ans Ende zu 
setzen! Das macht man nicht! Es gibt 2 wesentliche Formate, die 
allgemein akzeptiert sind. Entweder so
1
// Klammer auf jeweils eigener Zeile
2
if (bla)
3
{
4
  // yes
5
}
6
else
7
{
8
  // no
9
}
10
11
//Oder mein Favorit mit etwas kompakterer Schreibweise mit
12
//öffnender Klammer auf der gleichen Zeile wie das einleitende Statement.
13
14
if (bla) {
15
  // yes
16
} else {
17
  // no
18
}
Damit sieht man IMMMER die schließende Klammer zum öffnenen Statement in 
einer Ebene!

Alle diversen Mischformen haben wenig bis keine Akzeptanz unter echten 
Softwerkern. Das Gleiche gilt natürlich auch für andere Kontrukte wie 
for(), while() oder switch().

>  case CALC:  // OSCCAL neu berechnen
>        if (Calibration_Calculation() == true) {
>           state_UART_MODE = FINISH; }
>        else { state_UART_MODE = REPEAT; }

Wenn schon Klammern, welche prinzipiell besser sind, dann auch die 
Anweisung auf eine neue Zeile. Ist übersichlicher und leicher zu 
erweitern.

>Wenn das so näher an irgendwelchen Stil Standards ist, würde ich mich
>schon zufrieden geben.

Ist schon ganz OK.

von Veit D. (devil-elec)


Angehängte Dateien:

Lesenswert?

Hallo,

gut, die Klammersetzung nehme ich zurück, wollte paar Zeilen einsparen. 
Schlechter Stil, okay.

Bevor ich nun die Fuse setze für den Quarz frage ich lieber nochmal 
nach, weil das Auswahlmenü in AS für mich nicht ganz klar ist was 
gemeint ist, ich würde das blau ausgewählte nehmen. Richtig?
Quarz ist ein 8MHz, HC49.

von Falk B. (falk)


Lesenswert?

Ext. Crystal Osz. 8.0- MHz, ganz unten auf deiner Liste.

von Joachim B. (jar)


Lesenswert?

ich bevorzuge diesses, macht es einfach übersichtlicher

Falk B. schrieb:
> // Klammer auf jeweils eigener Zeile
> if (bla)
> {
>   // yes
> }
> else
> {
>   // no
> }

um Zeilen einzusparen geht auch

> { // yes
> }
> else
> { // no
> }

aber man sieht immer wo es beginnt und wo es endet
1
vor allem wenn es sowas gibt
2
{ // yes
3
  { // yes2
4
  }
5
}
6
else
7
{ // no
8
  { // no2
9
  }
10
}

von Veit D. (devil-elec)


Lesenswert?

Hallo,

bist du dir sicher?
Laut der Einstellungen rückwärts geschaut sind alle 4 CKSEL Bits 
gesetzt, was laut Datenblatt für einen Quarz > 8MHz sein müßte.

Müsste ich nicht laut Datenblatt S.30 auf die Bitfolge 1101 (SUT 0) 
kommen? Das wäre dann die Auswahl über des blau markierten. Hab bammel 
...

: Bearbeitet durch User
von Falk B. (falk)


Lesenswert?

@ Veit Devil (devil-elec)

>Laut der Einstellungen rückwärts geschaut sind alle 4 CKSEL Bits
>gesetzt, was laut Datenblatt für einen Quarz > 8MHz sein müßte.

So kritisch ist das nicht. Ob man nun die Einstellung 3-8 MHz oder 
8-16MHz wählt.

>Müsste ich nicht laut Datenblatt S.30 auf die Bitfolge 1101 (SUT 0)
>kommen? Das wäre dann die Auswahl über des blau markierten. Hab bammel
>...

Wovor? Verfusen? Mein Gott, trag's wie ein Mann!

von Veit D. (devil-elec)


Lesenswert?

Hallo,

is ja gut   :-)   ich ändere zum erstenmal die Fuse bezüglich der 
Taktquelle. Ansonsten hatte ich schon rumgefummelt. Es wäre Schade um 
den Aufwand falsche Takt-Fuse zu korrigieren. Das war mein Anliegen.

Gesagt getan, läuft mit reinen Timertakt astrein ohne zittern. Selbst 
mit 1µs/DIV nichts zu sehen.  :-)   Ich bau mal um und teste den 
"Servocode".

von Falk B. (falk)


Angehängte Dateien:

Lesenswert?

@Joachim B. (jar)

>ich bevorzuge diesses, macht es einfach übersichtlicher

Meine Meinung basiert hierauf.

Strukturierte Programmierung auf Mikrocontrollern

ftp://ftp.idsoftware.com/idstuff/doom3/source/CodeStyleConventions.doc

Wenn DIE Jungs das so machen und auch sinnvoll begründen, kann es nicht 
falsch sein. Und ich bin dann genau so cool wie die ;-)

Siehe Anhang, der Originallink ist im Moment nicht erreichbar.

von S. Landolt (Gast)


Lesenswert?

> Mein Gott, trag's wie ein Mann!

Und das zu einem selbsternannten Teufel...
Überhaupt entbehrt der ganze Thread nicht einer gewissen Komik.

von Joachim B. (jar)


Lesenswert?

Falk B. schrieb:
> Meine Meinung basiert hierauf.

also offen und zu Klammer in einer Spalte gefällt mir ja auch, das mit 
der offenen Klammer am Ende hat mich schon öfter aufs Glatteis geführt, 
das lehne ich weiterhin rundum ab!

wer guckt denn immer am Ende, doch nur Hunde ;)
ausserdem ist mein Monitor nicht UHD 27"

und wenn man ans Ende scrollt sieht man vorne nicht.

: Bearbeitet durch User
von Veit D. (devil-elec)


Lesenswert?

Hallo,

ich habs jetzt so und so bleibt das jetzt. Punkt.   :-)
1
void handle_Function_UART0_Pins()
2
{
3
  //static state_t state_UART_MODE = SYNC;      // Startbedingung
4
  static state_t state_UART_MODE = FINISH;       // mit externen 8MHz Quarz
5
   
6
  switch (state_UART_MODE) {
7
    
8
  case UART:  // UART0 wird benutzt
9
              handle_Serial_0_to_Serial_0();
10
              if (countErrorsUART0 > 50000) {  // erstmal ne wilde Hausnummer
11
                state_UART_MODE = ERROR;
12
              }
13
      break;    // minimal 7,7µs ... maximal 18,2µs
14
  
15
  case SYNC:  // schaltet UART0 Funktionalität ab
16
           uart0_disable();
17
           Txd0Rxd0_normalPortPins();
18
           state_UART_MODE = WAITSYNC;
19
      break;
20
    
21
  case WAITSYNC:  // OSCCAL Calibration ready, dauert 85µs
22
           memset((void *)UART0_TxBuf, '\0', sizeof(UART0_TxBuf));  // Messwerte löschen
23
           memset((void *)UART0_RxBuf, '\0', sizeof(UART0_RxBuf)); //
24
           stop_Timer0();
25
           stop_Timer1();
26
           set_PCINT2();               // ISR(PCINT0_vect) scharf schalten
27
           run_Timer2();            
28
           state_UART_MODE = RUN;
29
      break;
30
  
31
  case RUN:  // Takteinmesszyklus läuft mittels ISR(PCINT0_vect)
32
           if (state_measurement_complete == true) {
33
             state_UART_MODE = CALC;
34
           }
35
      break;
36
        
37
  case CALC:  // OSCCAL neu berechnen
38
           if (Calibration_Calculation() == true) {
39
              state_UART_MODE = FINISH;
40
           }
41
           else {
42
              state_UART_MODE = REPEAT;
43
           }
44
           state_measurement_complete = false; // Reset für ISR(PCINT0_vect) einmessen
45
      break;
46
        
47
  case REPEAT:  // Kalibrierung wiederholen
48
           set_PCINT2();
49
           countCalibration++;
50
           state_UART_MODE = WAITSYNC;
51
      break;
52
  
53
  case FINISH:  // schaltet UART0 Funktion ein
54
           del_PCINT2();
55
           run_Timer0();
56
           run_Timer1();
57
           memset((void *)UART0_TxBuf, '\0', sizeof(UART0_TxBuf));  // Messwerte löschen
58
           memset((void *)UART0_RxBuf, '\0', sizeof(UART0_RxBuf));
59
           uart0_init( UART_BAUD_SELECT(UART_BAUD_RATE,F_CPU) );
60
           state_UART_MODE = UART;
61
      break;
62
  
63
    
64
  case ERROR:  // Fehlerbehandlung noch nicht komplett
65
           // uart0_init( UART_BAUD_SELECT(UART_BAUD_RATE,F_CPU) );
66
           // oder 
67
           // state_UART_MODE = SYNC;
68
           state_UART_MODE = UART;
69
           // countErrorsUART0 = 0;
70
      break;
71
        
72
  default:  break;
73
  }
74
}

von Veit D. (devil-elec)



Lesenswert?

Hallo,

die Servopulse haben ein Zittern von 10µs-20µs laut Datalogger und 20µs 
laut Oszi. Egal ob uart Kabel angesteckt sind oder nicht. Das kleine 
Servo ist für mich jedoch praktisch ruhig. Das größere Servo zuckt noch 
ein klein wenig.

Laut Gehör zuckt das große Servo jedoch deutlich weniger, also fast 
nicht mehr, wenn die uart unterbrochen wird. Einen kleinen Unterschieds 
gibts laut Gehör noch.

von Falk B. (falk)


Lesenswert?

@Veit Devil (devil-elec)

>die Servopulse haben ein Zittern von 10µs-20µs laut Datalogger und 20µs
>laut Oszi.

Dann stimmt was nicht.

> Egal ob uart Kabel angesteckt sind oder nicht.

Dann erst recht nicht. 8-(

Hast du die letzte Änderung mit dem TXB80 Bit drin? Beim letzten Mal 
ging die ja noch nicht. Aber auch ohne diese Änderung darf ohne 
UART-Datenverkehr keine 10-20us Jitter passieren! Hast du die Fuses 
richtig gesetzt?

von Veit D. (devil-elec)


Angehängte Dateien:

Lesenswert?

Hallo,

das mit dem TXB80 Bit funktioniert nicht. Das ist das 9. Datenbit vom 
Frame. Ich habe kein 9. Databit. Spielt auch erste eine Geige wenn ich 
Unterschiede mit/ohne uart habe.

Ich habe nun auch die Compare A ISR rausgenommen, wird ohne uart nicht 
benötigt. An den Messungen ändert das nichts. So eine Sch ...

Fuse, was soll falsch sein, der µC läuft doch.

Morgen kürze ich den Code mal wieder radikal ein, sodass nur noch die 
Pulserzeugung stur läuft.

von Wilhelm M. (wimalopaan)


Lesenswert?

Veit D. schrieb:

> Die Nachrichtenlänge wird überall gleich als Konstante übergeben.
>
1
> // definiere das Datenprotokoll
2
> union Message
3
> {
4
>   struct
5
>   {
6
>     uint8_t toAddr;
7
>     uint8_t fromAddr;
8
>     uint8_t cmd;
9
>     int32_t data;
10
>     uint16_t crc;
11
>   };
12
>   uint8_t asArray[9];      // Summe aller struct Datentypen, für Zugriff 
13
> über Index
14
> } empfDaten, sendDaten;      // zwei gleiche union Buffer anlegen
15
> 
16
> constexpr uint8_t length_Message = sizeof(Message);
17
>

An constexpr sehe ich, dass Du das ganze mit einem C++-11/14/17-Compiler 
übersetzt. War mir bislang gar nicht aufgefallen - habe aber auch nicht 
alles hier gelesen. Allerdings greifst Du dann sicher C-like in Deiner 
union auch über das nicht-aktive Member zu (das habe ich jetzt aber 
nicht in Deinem Code überprüft). Dies ist in C++ UB im Gegensatz zu C. 
Ich habe zwar wirklich noch nie bei primtiven DT in der union hier 
jemals ein Problem erlebt bei C++, aber es ist trotzdem UB und dem 
sollte man sich bewusst sein.

: Bearbeitet durch User
von Veit D. (devil-elec)


Lesenswert?

Hallo,

ich habe die Option "-std=c++11" gesetzt.
Zu welchen Problem kann das führen mit dem nicht aktiven Member?
Das wird in der read_Ringbuffer  Funktion verwendet.

Den Code konnte ich noch kürzen, geht ja schnell. Was soll ich sagen, 
kein einziger Jitter. Obwohl der nichts anderes macht wie der komplette 
Code mit auskommentierten Funktionen.
1
#include <util/delay.h>
2
#include <avr/io.h>
3
#include <stdio.h>
4
#include <stdlib.h>
5
#include <avr/interrupt.h>
6
#include <util/atomic.h>      // für cli() und sei() mit SREG Sicherung
7
#include "pinDefi.h"          // eigene I/O Definitionen
8
#include "timer.h"         // alles was mit Timern zu tun hat ausgelagert
9
10
#define NOP __asm__ __volatile__ ("nop")
11
12
#ifndef sbi
13
#define sbi(sfr, bit) (_SFR_BYTE(sfr) |= _BV(bit))   // setzt das angegebene Bit auf 1
14
#endif
15
#ifndef cbi
16
#define cbi(sfr, bit) (_SFR_BYTE(sfr) &= ~_BV(bit))  // setzt (löscht) das angegebene Bit auf 0
17
#endif
18
19
20
int main(void)
21
{            
22
  Led1_OUT;  // Pin auf Ausgang konfigurieren
23
  Led2_OUT;
24
  Led3_OUT;
25
  Led4_OUT;
26
  Led5_OUT;
27
  Led6_OUT;
28
  // Led7_OUT;   Quarz
29
  // Led8_OUT;   Quarz
30
    
31
  
32
  preSet_Timer1();
33
  run_Timer1();
34
         
35
    while (1)  // Hauptprogramm
36
    {             
37
    
38
  }
39
}
40
41
42
/* *** Funktionen *** */
43
ISR(TIMER1_COMPB_vect)    // dauert 6,2µs, wird aller >1ms aufgerufen
44
{  
45
   Led1_ON;
46
  static uint8_t servo = 0;
47
  const uint8_t SERVO_CNT = 3;        // Anzahl Servos
48
  const uint8_t TIMING_WINDOW = 7;    // 6µs / 125ns / Prescaler 8 = 6,25 Timertakte
49
  static uint16_t temp = 0;
50
    
51
  switch(servo) {    // ISR Zeit 4,75µs
52
    // Count   999 =  1ms
53
    // Count  1999 =  2ms
54
    // Count 19999 = 20ms
55
    case 0: Led2_OFF;  Led5_ON;  temp =  1499; break;
56
      case 1: Led5_OFF;  Led6_ON;  temp =  1499; break; 
57
    case 2: Led6_OFF;          temp = 13999; break; // virtuelle
58
    case 3:           Led2_ON;  temp =  1499; break;
59
  }
60
    
61
  OCR1B += temp;
62
  OCR1A = OCR1B-TIMING_WINDOW;
63
  UCSR0B |= ((1<<RXCIE0) | (1<<UDRIE0));  // enable USART0 RX Complete Interrupt
64
                      // enable USART0 Data Register Empty Interrupt
65
  servo++;
66
  if (servo > SERVO_CNT)  servo = 0;
67
                      
68
  Led1_OFF;          
69
}
70
71
72
ISR(TIMER1_COMPA_vect)      // dauert 1,8µs
73
{  
74
  UCSR0B &= ~((1<<RXCIE0) | (1<<UDRIE0));  // disable USART0 RX Complete Interrupt
75
                      // disable USART0 Data Register Empty Interrupt
76
  Led1_ON;          // für Datalogger, debuggen
77
}


der Timer:
1
void preSet_Timer1 ()    // Normal Mode 4, Servo Pulserzeugung
2
{
3
  cli();      // Interrupts ausschalten
4
  TCCR1A = 0;    // Reset TCCR1A Register
5
  TCCR1B = 0;    // Reset TCCR1B Register
6
  TIMSK1 = 0;    // Reset TIMSK1 Register (disable Timer Compare Interrupts)
7
  TCNT1 = 0;    // Start 0
8
  OCR1A = 19979;  // Compare & Prescaler 8 = 20µs eher 
9
  OCR1B = 19999;  // Compare & Prescaler 8 = 20ms 
10
  TIMSK1  = (1<<OCIE1A);  // enable Compare Match A ISR
11
  TIMSK1 |= (1<<OCIE1B);  // enable Compare Match B ISR
12
  sei();          // Interrupts einschalten
13
}  
14
15
16
void run_Timer1 ()      
17
{
18
  ATOMIC_BLOCK (ATOMIC_RESTORESTATE) {
19
    TCCR1B |= (1 << CS11);  // Prescaler 8, Timer starten
20
  }
21
}

von Falk B. (falk)


Lesenswert?

@Veit Devil (devil-elec)

>das mit dem TXB80 Bit funktioniert nicht.

Mist.

>Das ist das 9. Datenbit vom
>Frame. Ich habe kein 9. Databit.

Ja eben darum kann man es für andere Dinge nutzen ;-)
Soweit die Theorie ;-)

>Spielt auch erste eine Geige wenn ich
>Unterschiede mit/ohne uart habe.

Ja.

>Ich habe nun auch die Compare A ISR rausgenommen, wird ohne uart nicht
>benötigt. An den Messungen ändert das nichts. So eine Sch ...

Du sagst es.

>Fuse, was soll falsch sein, der µC läuft doch.

Er könnte aber auch noch mit dem RC-oszillator laufen!
Die Fuses sehen aber OK aus! Quarz und beide Kondensatoren ordentlich 
kontaktiert?

>Morgen kürze ich den Code mal wieder radikal ein, sodass nur noch die
>Pulserzeugung stur läuft.

Falscher Ansatz. Für den reinen Pulstest schreibt man ein einfaches, 
extra Testprogramm und lädt das einfach. Immer dermaßen im Quelltext 
rumfuhrwerken ist nicht sinnvoll.

von Wilhelm M. (wimalopaan)


Lesenswert?

Veit D. schrieb:

Noch eine Kleinigkeit:

>
1
>            memset((void *)UART0_TxBuf, '\0', sizeof(UART0_TxBuf)); 
2
>            memset((void *)UART0_RxBuf, '\0', sizeof(UART0_RxBuf));
3
>

Ich hoffe, dass an dieser Stelle z.B. UART0_TxBuf noch nicht zu einem 
Zeigertyp zerfallen (decay) ist, denn dann liefert UART0_TxBuf was 
falsches;-)

Wenn Du schon C++ nimmst, dann solltest Du für so etwas dann auch 
std::array verwenden.

von Falk B. (falk)


Lesenswert?

@Veit Devil (devil-elec)

>Den Code konnte ich noch kürzen, geht ja schnell. Was soll ich sagen,
>kein einziger Jitter.

Schon mal ein Ansatz. Jetzt musst du den Rest schrittweise wieder 
aktivieren.

von Falk B. (falk)


Lesenswert?

@ Wilhelm M. (wimalopaan)

>Wenn Du schon C++ nimmst, dann solltest Du für so etwas dann auch
>std::array verwenden.

Falsch. Er sollte von C++ die Finger lassen und vernünftiges C ohne 
unsinnige Stunts programmieren! Das reicht für seine Anwendung als 
Hobbyprogrammierer vollkommen aus! Er braucht keine Sekunde C++11, 
bestenfalls C99.

von Wilhelm M. (wimalopaan)


Lesenswert?

Falk B. schrieb:
> @ Wilhelm M. (wimalopaan)
>
>>Wenn Du schon C++ nimmst, dann solltest Du für so etwas dann auch
>>std::array verwenden.
>
> Falsch. Er sollte von C++ die Finger lassen und vernünftiges C ohne
> unsinnige Stunts programmieren! Das reicht für seine Anwendung als
> Hobbyprogrammierer vollkommen aus! Er braucht keine Sekunde C++11,
> bestenfalls C99.

Deswegen sage ich das ja!
Ich vermute, dass er constexpr nur an Stellen verwendet, wo es nicht 
notwendig ist und sonst nichts von C++ einsetzt. Dann kann er den Code 
auch einfach als C99/C11 mit einem C-Compiler übersetzen und er ist das 
UB los.

(Trotzdem ist m.E. std::array wesentlich besser rohe Arrays ...)

von Wilhelm M. (wimalopaan)


Lesenswert?

Wilhelm M. schrieb:
> Veit D. schrieb:
>
> Noch eine Kleinigkeit:
>
>>
1
>>            memset((void *)UART0_TxBuf, '\0', sizeof(UART0_TxBuf));
2
>>            memset((void *)UART0_RxBuf, '\0', sizeof(UART0_RxBuf));
3
>>
>
> Ich hoffe, dass an dieser Stelle z.B. UART0_TxBuf noch nicht zu einem
> Zeigertyp zerfallen (decay) ist, denn dann liefert UART0_TxBuf was
> falsches;-)

Sorry, verschrieben: gemeint war ... dann liefert sizeof(UART0_TxBuf) 
was falsches ...

von Veit D. (devil-elec)


Lesenswert?

Hallo,

alleine für bool benötigt man schon C++. Was ist denn an C++ nun wieder 
falsch? Ich kam auch darauf, weil ich bestimmte Berechnungen zur 
Compilerzeit erledigt habe, deswegen kenne ich constexpr. Ihr wollt mir 
das jetzt nicht verbieten?

Das Buffer löschen kann ich eigentlich rausnehmen, weil uart_init tail 
und Head sowieso gleich setzt. Meine Messwerte sollte es nicht 
verändern. Probiere ich aus. Wobei das alles keinen Einfluss haben 
sollte, uart Fehler habe ich keinen einzigen.

Hauptthema:
Habe den Kondensator an Pin 2 rausgenommen, lief immer noch. Kondensator 
an Pin 3 rausgenommen, zappelte alles, Quarz entfernt, es funktionierte 
nichts mehr, ich schlussfolgere Fuse passen.

Morgen bau ich die Funktionen Schrittweise wieder ein.

Erstmal Danke bis hier hin.

Edit:
Was meinst du mit UB?

: Bearbeitet durch User
von Falk B. (falk)


Lesenswert?

@Veit Devil (devil-elec)

>alleine für bool benötigt man schon C++.

Keine Sekunde. C funktioniert seit über 40 Jahren ohne "echtes" bool. 
Wenn es tausende andere C-User können, kannst du das auch.

> Was ist denn an C++ nun wieder falsch?

Wer fliegen will, sollte erstmal laufen lernen. Man kann nicht mit dem 
Fliegen anfangen.
Lern erstmal SOLIDES C, dann kannst du dich VIELLEICHT mit C++ 
beschäftigen, denn das ist ein GANZ anderes Kaliber!

> Ich kam auch darauf, weil ich bestimmte Berechnungen zur
>Compilerzeit erledigt habe, deswegen kenne ich constexpr. Ihr wollt mir
>das jetzt nicht verbieten?

Doch, denn du brauchst es keine Sekunde. Das ist nur akademischer 
Unsinn.
Du hast keine hochdynamischen Datenstrukturen, nur ein triviales 
Datenpaket!

>Habe den Kondensator an Pin 2 rausgenommen, lief immer noch. Kondensator
>an Pin 3 rausgenommen, zappelte alles, Quarz entfernt, es funktionierte
>nichts mehr, ich schlussfolgere Fuse passen.

OK.

von 2⁵ (Gast)


Lesenswert?

Veit D. schrieb:
> Was meinst du mit UB?

Undefined Behavior -> 
https://de.wikipedia.org/wiki/Undefiniertes_Verhalten

von Wilhelm M. (wimalopaan)


Lesenswert?

Veit D. schrieb:
> Hallo,
>
> alleine für bool benötigt man schon C++.

Nein, hat C auch.

> Was ist denn an C++ nun wieder
> falsch? Ich kam auch darauf, weil ich bestimmte Berechnungen zur
> Compilerzeit erledigt habe, deswegen kenne ich constexpr. Ihr wollt mir
> das jetzt nicht verbieten?

Ich möchte Dir gar nichts verbieten, und bin hier ganz klar von der 
C++-Fraktion ;-)

Aber in Deinem Code, den ich aber nur sehr flüchtig angesehen habe, habe 
ich weder klassische OOP Ansätze (dyn. Polymorphie) (also eher 
Arduino-like und wie man es m.E. auf µC nicht machen sollte) noch stat. 
Polymorphie (also templates und TMP) noch eine starke Verwendung des 
Typsystems (domänenspezifische DT) gesehen. Insofern schließe ich mich 
der Vermutung von Falk an, dass vielleicht hier die Kenntnisse fehlen 
und es deswegen besser ist, sich auf C zu konzentrieren. Dies soll bitte 
keine Wertung sein ... Aber schon der Einsatz von rohen Arrays und ihren 
Problemen deuten eben darauf hin.

von Wilhelm M. (wimalopaan)


Lesenswert?

Falk B. schrieb:

> Doch, denn du brauchst es keine Sekunde. Das ist nur akademischer
> Unsinn.

Sorry Falk: aber das ist jetzt ... Unsinn!

von Wilhelm M. (wimalopaan)


Lesenswert?

Falk B. schrieb:
> @Veit Devil (devil-elec)
>
>>alleine für bool benötigt man schon C++.
>
> Keine Sekunde. C funktioniert seit über 40 Jahren ohne "echtes" bool.

Falsch: Seit C99 in C enthalten.

von Falk B. (falk)


Lesenswert?

@ Wilhelm M. (wimalopaan)

>>>alleine für bool benötigt man schon C++.
>
>> Keine Sekunde. C funktioniert seit über 40 Jahren ohne "echtes" bool.

>Falsch: Seit C99 in C enthalten.

Erwischt, meine Aussage stimmt trotzdem. Es geht auch ohne echtes bool, 
wenn gleich es bisweilen etwas schwammig wird. Ich bin pragmatischer 
Hardwerker, noch Fragen? ;-)

von Joachim B. (jar)


Lesenswert?

darüber lohnt nicht zu streiten
https://de.wikipedia.org/wiki/Varianten_der_Programmiersprache_C#C99

bool ist drin, vor 1995 wird ja heute keiner mehr Compiler hier im 
Thread nutzen (wollen)

von Falk B. (falk)


Lesenswert?

Ein schneller Test scheint die freie Verwendbarkeit des TXB80 Bits zu 
bestätigen. Wenn es also in der Software nicht funktioniert, liegt der 
Fehler woanders.
1
/*
2
 * UART test for TXB80 bit use as software control bit
3
 */
4
5
 #include <util/atomic.h>
6
 
7
void setup() {
8
  Serial.begin(9600);
9
  Serial.println(F("UART TXB80 test."));
10
}
11
12
void loop() {
13
14
  ATOMIC_BLOCK(ATOMIC_RESTORESTATE) {
15
    UCSR0B ^= (1<<TXB80);   // toggle TXB80
16
  }
17
  
18
  Serial.print(F("TXB80 is "));
19
  if ( UCSR0B & (1<<TXB80) ) {
20
    Serial.print(F("HIGH"));
21
  } else {
22
    Serial.print(F("LOW"));
23
  }
24
  Serial.println();
25
  delay(500);
26
}

: Bearbeitet durch User
von Veit D. (devil-elec)


Lesenswert?

Hallo,

können wir das mit C vs. C++ auf später verschieben? Sonst kämpfe ich an 
mehreren Fronten gleichzeitig. Ihr seit ja alle angemeldet und bekommt 
neue Beiträge gemeldet. Ich greife das am Ende wieder auf. Nur eine 
Frage brennt mir unter den Nägeln, memset ist doch Standard C, was soll 
da schief gehen? Mit sizeof verhindere ich auch ein schreiben ins 
Nirwarna.

Test:
habe den Timer 0 aktiviert und die Pulse zittern wieder - aber anders. 
Nicht ständig sondern alle Sekunde nach Gefühl. Mit Timer 0 habe ich mir 
einen ms und s Timer gebaut. Sekundenvariable rausgenommen. Pulse zucken 
dennoch ca. aller einer Sekunde. Wobei ich millis() noch nicht verwende, 
nur der Timer 0 läuft für sich alleine.

Ich baue den Code mal weiter zusammen, mal sehen wann es schlimmer wird 
oder der Timer 0 später noch mehr Störung verursacht. Der Timer 0 
alleine kann es nicht sein, laut meinem Gefühl.    ;-)

Auszug aus der timer.cpp
1
static volatile uint32_t millis_count;    // ISR Timer 0 Variable
2
3
void preSet_Timer0 ()    // millis, seconds
4
{
5
  cli();          
6
  TCCR0A = (1 << WGM01);      // CTC Modus
7
  TCCR0B = 0;
8
  OCR0A = 124;                // TOP, (F_CPU/PRESCALER)/1000-1
9
  TIMSK0 = (1 << OCIE0A);      // Compare A enable
10
  sei();
11
}
12
13
14
void run_Timer0 ()    // millis, seconds
15
{  
16
  ATOMIC_BLOCK (ATOMIC_RESTORESTATE) {
17
    TCCR0B |= (1 << CS01) | (1 << CS00);  // Prescaler 64
18
  }
19
}
20
21
22
void stop_Timer0 ()    // millis, seconds
23
{    
24
  ATOMIC_BLOCK (ATOMIC_RESTORESTATE) {              
25
    TCCR0B &= ~( (1<<CS02)|(1<<CS01)|(1<<CS00) );  
26
  }
27
}
28
29
30
ISR(TIMER0_COMPA_vect)
31
{  
32
  millis_count++;      // Zähler für Millisekunden
33
}
34
35
36
uint32_t millis()
37
{
38
  uint32_t value = 0;
39
  ATOMIC_BLOCK (ATOMIC_RESTORESTATE) {
40
    value = millis_count;
41
  }
42
  return value;
43
}

von Falk B. (falk)


Lesenswert?

@Veit Devil (devil-elec)

>können wir das mit C vs. C++ auf später verschieben? Sonst kämpfe ich an

Sicher. Hat mit dem aktuellen Problem auch gar nichts zu tun.

>Frage brennt mir unter den Nägeln, memset ist doch Standard C, was soll

Ja.

>da schief gehen?

Nix.

>habe den Timer 0 aktiviert und die Pulse zittern wieder - aber anders.

Was macht denn Timer 0? Ich dachte, der ist nur während der Kalibrierung 
aktiv?

>Nicht ständig sondern alle Sekunde nach Gefühl. Mit Timer 0 habe ich mir
>einen ms und s Timer gebaut. Sekundenvariable rausgenommen. Pulse zucken
>dennoch ca. aller einer Sekunde. Wobei ich millis() noch nicht verwende,
>nur der Timer 0 läuft für sich alleine.

Ja klar! Es müssen ALLE Interrupts außer COMPB im COMPA gesperrt werden, 
das ist ja der Trick! Daß Timer 0 da im Hintergrund noch läuft war mir 
bis jetzt nicht klar!

>Ich baue den Code mal weiter zusammen, mal sehen wann es schlimmer wird
>oder der Timer 0 später noch mehr Störung verursacht. Der Timer 0
>alleine kann es nicht sein, laut meinem Gefühl.    ;-)

FALSCH! Das hast du doch selber gerade bewiesen, denn ohne Timer 0 gibt 
es keinen Jitter.

von Carl D. (jcw2)


Lesenswert?

Brauchst du wirklich Millisekunden oder nur etwas "in dem Bereich"?
Was ist der Bereich den du auswertest/auf den du wartest? __ms...__ms

Falls sich das irgendwie mit dem Timer1-Zyklus deckt, dann nimm Comp1A 
zum millis zählen.
BTW, wie groß ist millis? Mit 16Bit kann man bis zu 65,x Sekunden 
warten. Die 2^32/1000/60/60/24 oder 49 Tage der Arduino-Lib braucht man 
nicht wirklich.

von Wilhelm M. (wimalopaan)


Lesenswert?

Falk B. schrieb:
> @Veit Devil (devil-elec)
>
>>können wir das mit C vs. C++ auf später verschieben? Sonst kämpfe ich an
>
> Sicher. Hat mit dem aktuellen Problem auch gar nichts zu tun.
>
>>Frage brennt mir unter den Nägeln, memset ist doch Standard C, was soll
>
> Ja.
>
>>da schief gehen?
>
> Nix.

Doch. Das kommt ganz darauf an.

Anhand des zitierten Beispiels konnte ich nicht sagen, wie / wo das 
Ziel(Array) deklariert / definiert ist.
Wenn der Compiler die vollständige Definition gesehen hat und der 
Array-Bezeichner nicht zu einem Zeiger zerfallen ist, wird die Größe 
korrekt durch sizeof() ermittelt. In diesem Fall wird also das ganze 
Array(?) genullt.

Ist es aber kein Array-Bezeichner mehr, sondern nur noch ein Zeiger 
(etwa weil Du den Array-Bezeichner als Zeiger an eine Funktion 
übergibst), so bestimmt sizeof() die Größe des Zeigers (wohl 2 Bytes auf 
einem AVR8). Dann werden nur die ersten zwei Bytes des Ziels genullt.

von Wilhelm M. (wimalopaan)


Lesenswert?

Nur so zur Info: ich habe so einen RC-Controller mit dem 
AtMega328PB@20MHz. Der bedient einen Uart mit 115KB/VollDuplex, einen 
Uart mit 19200B/VollDuplex, eine SoftPPM mit 8-Kanälen per ISR wie bei 
Dir, eine I2C, eine OneWire, zwei HardPWM, zwei Drehzahlsensoren und 
8-AD-Kanäle. Relevant sind hier die beiden im Interrupt betriebenen 
Uarts und die SoftPPM per Timer-Interrupt. I2C läuft im Polling-Mode und 
OneWire arbeitet mit kurzen ISR-Sperren. Hard-PWM braucht keine CPU und 
die A/D-Kanäle werden gepollt.

Da habe ich 1 - 1,5 µs Jitter auf den SoftPPM-Kanälen und meine analogen 
Servos bleiben ruhig. Rechnet man linear auf 8MHz runter wären das 2,5 - 
3,75µs Jitter.

von Veit D. (devil-elec)


Angehängte Dateien:

Lesenswert?

Hallo,

ich nutze Timer 2 zum kalibrieren. Steht in der ISR(PCINT0_vect) drin, 
habe daran nie etwas geändert. Tut mir leid wenn das jemand nicht 
gesehen hat. Ich weiß wie schwer es ist fremden Code zu lesen.

Kurzer Umriss, ich habe mit Arduino vor paar Jahren angefangen. Beim 
nackten ATtiny möchte ohne Arduino IDE zurechtkommen. Allein schon wegen 
Speicherplatz. Deswegen Atmel Studio und die millis Funktion nachgebaut. 
Damit wollte ich später noch einen TimeOut realisieren wenn was seitens 
ATtiny schief geht. Kann ich aber wie ich sehe und ihr gesehen habt nun 
auch mit Timer 1 machen und ein 1ms Intervall benötige ich sicherlich 
auch nicht.

Auf Grund von Timer 0 habe ich den dann auch flux mit in der Timer 1 
Compare ISR getoppt und gestartet. Brachte leider noch nichts. Erst als 
ich das Timing-Window erhöht habe wurde es besser. Hatte bis jetzt immer 
7 Timercounts.
Mit 10 Counts (Led1 15µs) immer noch leichter Jitter aber mit starken 
sporadischen Ausreißern.
Ab 15 Counts (Led1 20µs) wird es besser, nur noch minimaler Jitter ohne 
Ausreißer. Eine weitere Erhöhung bringt nichts.

Erst wenn ich in der Hauptschleife die millis Abfrage auskommentiere ist 
fast absolute Jitterruhe. Ich denke ein Restjitter wird immr bleiben, 
weil das Programm ja immer ertst wohin springen muss. Oder? Kann ja 
nicht alles gleichzeitig machen.

Das Timingwindow muss doch nur soweit vorher aktiv werden, für die Zeit 
im voraus, wie alle Maßnahmen in der Timer 1 Compare A benötigen? Also 
so lange wie ein uart Byte abholen dauern würde und zusätzlich wie lange 
die Timer 0 Compare ISR benötigt. So war das doch gedacht?

Nur wird Timer 1 nicht regelmäßig aufgerufen. Das schwankt ja zwischen 
1ms und 20ms bis zur kompletten Abschaltung was ich ganz am Ende machen 
möchte. Oder ich muss gänzlich auf einen Zeitzähler verzichten. Ich 
mache erstmal das Timer 0 Aufrufintervall größer 1ms.

Aktueller Stand hängt dran.

von Wilhelm M. (wimalopaan)


Lesenswert?

Warum ist das nicht in "usart.h"?

Zudem sind das weder Definitionen noch Initialisierungen.
1
// Definitionen und Initialisierungen
2
3
extern volatile uint16_t UART0_RxBuf[UART_RX_BUFFER_SIZE];
4
extern volatile uint16_t UART0_TxBuf[UART_TX_BUFFER_SIZE];
5
extern volatile uint8_t UART0_TxHead;
6
extern volatile uint8_t UART0_TxTail;

Zu memset(): mit den o.g. Deklarationen ist es in Ordnung, da 
UART_Tx_Buf ein Array-Bezeichner ist und bei memset() nicht zu einem 
Zeiger zerfallen ist. Trotzdem ist sowas gefährlich. Warum benutzt Du 
nicht gleich UART_RX_BUFFER_SIZE?

von Veit D. (devil-elec)


Lesenswert?

Wir kommen der Sache jedoch schon näher, dass stimmt bestimmt nicht nur 
mich glücklich ...    :-)

von Carl D. (jcw2)


Lesenswert?

Wilhelm M. schrieb:
>
> Ist es aber kein Array-Bezeichner mehr, sondern nur noch ein Zeiger
> (etwa weil Du den Array-Bezeichner als Zeiger an eine Funktion
> übergibst), so bestimmt sizeof() die Größe des Zeigers (wohl 2 Bytes auf
> einem AVR8). Dann werden nur die ersten zwei Bytes des Ziels genullt.

Was noch fehlt:
Würde man statt
  xy_t array[32]
schreiben
  std::array<xy_t,32> array;
dann könnte man dieses als Referenz an eine Funtion übergeben
  void func( std::array<xy_t,32>& array);
und hätte die komplette Typ-Info.

BTW, std::array macht das mit Zero-Overhead. Keine extra Daten, kein 
extra Code, kein malloc.

@Wilheml: ich benutze das bei AVR in einer eigenen 
(minimalst)Implementierung. Kennst du eine für AVR?

von Veit D. (devil-elec)


Lesenswert?

Hallo,

@Wilhelm:
ich nutze den uart Ringbuffer mit zum kalibrieren. Damit ich keinen 2. 
Buffer anlegen muss der danach sinnlos Speicher frist. Deswegen muss ich 
die Sichtbarkeit erweitern. Die letzten beiden Zeilen nutzen wir zum 
uart ISR steuern. Eben wegen dem Jitter.

von Wilhelm M. (wimalopaan)


Lesenswert?

Carl D. schrieb:
> Wilhelm M. schrieb:
>>
>> Ist es aber kein Array-Bezeichner mehr, sondern nur noch ein Zeiger
>> (etwa weil Du den Array-Bezeichner als Zeiger an eine Funktion
>> übergibst), so bestimmt sizeof() die Größe des Zeigers (wohl 2 Bytes auf
>> einem AVR8). Dann werden nur die ersten zwei Bytes des Ziels genullt.
>
> Was noch fehlt:
> Würde man statt
>   xy_t array[32]
> schreiben
>   std::array<xy_t,32> array;
> dann könnte man dieses als Referenz an eine Funtion übergeben
>   void func( std::array<xy_t,32>& array);
> und hätte die komplette Typ-Info.

Das hatte ich doch oben schon vorgeschlagen ...

> @Wilheml: ich benutze das bei AVR in einer eigenen
> (minimalst)Implementierung. Kennst du eine für AVR?

Ja, meine eigene ;-)

von Wilhelm M. (wimalopaan)


Lesenswert?

Veit D. schrieb:
> Hallo,
>
> @Wilhelm:
> ich nutze den uart Ringbuffer mit zum kalibrieren. Damit ich keinen 2.
> Buffer anlegen muss der danach sinnlos Speicher frist. Deswegen muss ich
> die Sichtbarkeit erweitern. Die letzten beiden Zeilen nutzen wir zum
> uart ISR steuern. Eben wegen dem Jitter.

Ja, aber man will doch die Deklaration nicht in jeder 
Implementierungsdatei wiederholen, deswegen gehört das in die 
korrespondierende Header-Datei.

von Falk B. (falk)


Lesenswert?

@Veit Devil (devil-elec)

>Erst wenn ich in der Hauptschleife die millis Abfrage auskommentiere ist
>fast absolute Jitterruhe.

Die Abfrage ist ein Kopieren unter ISR Sperre und das mit maximaler 
CPU-Last, wenn nix zu tun ist! Das ist Irrsinn! Und es wirkt ebenso wie 
eine ISR, sie erzeugt Jitter.

> Ich denke ein Restjitter wird immr bleiben,
>weil das Programm ja immer ertst wohin springen muss. Oder?

Oder! Wenn im normalen Programm KEINE (kurzzeitge) ISR-Sperre genutzt 
wird, wie in deiner millis() Funktion, dann wird bei einerem Interrupt 
nur der aktuelle Assemblerbefehl zu Ende ausgeführt und sofort die ISR 
ausgeführt. Der dadurch entstehende Jitter liegt bei max. 1-2 
CPU-Takten!

>Kann ja nicht alles gleichzeitig machen.

Muss es auch nicht, aber Interrupts

>Das Timingwindow muss doch nur soweit vorher aktiv werden, für die Zeit
>im voraus, wie alle Maßnahmen in der Timer 1 Compare A benötigen?

Nein, wie alle anderen ISRs im Extremfall (worst case) dauern können).

>Also
>so lange wie ein uart Byte abholen dauern würde und zusätzlich wie lange
>die Timer 0 Compare ISR benötigt. So war das doch gedacht?

Ja.

>Nur wird Timer 1 nicht regelmäßig aufgerufen. Das schwankt ja zwischen
>1ms und 20ms bis zur kompletten Abschaltung was ich ganz am Ende machen
>möchte. Oder ich muss gänzlich auf einen Zeitzähler verzichten. Ich
>mache erstmal das Timer 0 Aufrufintervall größer 1ms.

Musst du nicht, wenn du auch Timer 0 im COMPA kurz deaktiverst und in 
COMPB wieder einschaltest. Aber du mußt die unsinnige und wahnsinnige 
Kopierei mit deiner millis() Funktion rausnehmen.

von Falk B. (falk)


Lesenswert?

>ich nutze Timer 2 zum kalibrieren. Steht in der ISR(PCINT0_vect) drin,
>habe daran nie etwas geändert. Tut mir leid wenn das jemand nicht
>gesehen hat. Ich weiß wie schwer es ist fremden Code zu lesen.

In gut strukturiertem Quelltext würde man das direkt sehen . . .

von Dieter F. (Gast)


Lesenswert?

Falk B. schrieb:
> Der dadurch entstehende Jitter liegt bei max. 1-2 > CPU-Takten!

Sicher?

z.B. (max. Dauer)

RCALL  3
ICALL  3
RET    4
RETI   4
CPSE   1/2/3
SBRC   1/2/3
SBRS   1/2/3
SBIC   1/2/3
SBIS   1/2/3

von Veit D. (devil-elec)



Lesenswert?

Hallo,

@ Wilhelm:
das ist doch in der uart Lib deklariert, nur muss ich ja irgendwie 
darauf zugreifen wegen der Doppelnutzung, deswegen musste ich die 
Sichtbarkeit erweitern. Ich weiß im Moment nicht worauf du hinaus 
möchtest.
kompletter Code >> Beitrag >> Datum: 29.12.2017 23:37
Beitrag "Re: ISR Code schneller machen?"

@ Falk:
Habe das Timer 0 Intervall auf 10ms erweitert. Ändert nichts.
Wie auch, millis() wird ja dennoch ständig aufgerufen. Ich weiß nicht 
wie ich sonst den aktuellen Zählerstand von millis abfragen soll? 
Irgendwie muss ich ja auf zugreifen und wegen >Byte atomar.

Habe mal vermessen.
ohne uart, keine Kabel dran, ohne ohne millis() ... 1µs Jitter
ohne uart, keine Kabel dran, mit millis() ......... 3µs Jitter
mit uart, Kabel dran, ............... 16µs Jitter, egal ob mit/ohne 
millis()

von Falk B. (falk)


Lesenswert?

@Dieter F. (jim_quakenbush)

>> Der dadurch entstehende Jitter liegt bei max. 1-2 > CPU-Takten!

>Sicher?

Nicht ganz ;-)

>RCALL  3
>ICALL  3
>RET    4

OK, ist einer der längsten Befehle.

>RETI   4

Kann nicht durch eine ISR unterbrochen werden, es sei denn man macht 
verschachtelte Interrupts per Software.

>CPSE   1/2/3
>SBRC   1/2/3
>SBRS   1/2/3
>SBIC   1/2/3
>SBIS   1/2/3

Das sind meistens nur 1-2 Takte, weil selten eine 2-Wort Anweisung 
danach kommt.

von Dieter F. (Gast)


Lesenswert?

Falk B. schrieb:
> Das sind meistens nur 1-2 Takte, weil selten eine 2-Wort Anweisung
> danach kommt.

Meistens <> max. - darum ging es mir :-) (und hier speziell um RET)

von Falk B. (falk)


Lesenswert?

@ Veit Devil (devil-elec)

>das ist doch in der uart Lib deklariert,

Aber nur lokal (static), damit man es von außen nicht sieht.

> nur muss ich ja irgendwie
>darauf zugreifen wegen der Doppelnutzung, deswegen musste ich die
>Sichtbarkeit erweitern.

Eben das static weglassen ;-)

>Habe das Timer 0 Intervall auf 10ms erweitert. Ändert nichts.
>Wie auch, millis() wird ja dennoch ständig aufgerufen.

Und warum? Was soll der Unsinn?

> Ich weiß nicht
>wie ich sonst den aktuellen Zählerstand von millis abfragen soll?

Deine "Abfrage" ist im Moment 100% sinnlos!

>Irgendwie muss ich ja auf zugreifen und wegen >Byte atomar.

Dazu muss man aber nicht STÄNDIG 32 Bit Variablen kopieren! Das macht 
man nur dann, wenn es WIRKLICH nötig ist. Ist es in deinem Programm im 
Moment nie!

Und wenn man das nur für einen Timeout braucht, zählt man den in der ISR 
runter. Dann braucht man immer nur eine direkte Abfrage im normalen 
Programm, im Idealfall direkt atomar mit nur 8 Bit. Die reichen für 
normale Timeouts locker. Dann braucht man NIRGENDWO eine ISR-Sperre und 
alles ist gut und es gibt keinen zusätzlichen Jitter.
1
volatile uint8_t timeout;
2
3
...
4
5
isr(timer0) {
6
...
7
  if (timeout) timeout--;
8
}
9
10
...
11
12
im Programm
13
14
timeout = 100;
15
16
irgendwo anders 
17
18
if (!timeout) { // timeout }

>ohne uart, keine Kabel dran, ohne ohne millis() ... 1µs Jitter

Das muss weniger sein, aber dein Logicanalyzer zeigt die Zeiten nur mit 
1us Auflösung an, dem kann man da nicht mehr vertrauen. Miss mit dem 
Oszi.

>ohne uart, keine Kabel dran, mit millis() ......... 3µs Jitter

Siehe oben, das millis() ist Unsinn.

>mit uart, Kabel dran, ............... 16µs Jitter, egal ob mit/ohne
>millis()

Weil das mit dem UDRIE im usart0_putc() noch nicht funktioniert, da mußt 
du nochmal ran. Das sollte so wie von mir skizziert eigentlich laufen. 
Wenn GAR NICHTS gesendet wird, gibt es irgendwo einen grundlegenden 
Fehler, den findet man aber. Das TXB80 ist frei verfügbar, wenn man 
nicht im 9 Bit UART-Modus arbeitet.

von Arno (Gast)


Lesenswert?

Falk B. schrieb:
> @Veit Devil (devil-elec)
>
>>Erst wenn ich in der Hauptschleife die millis Abfrage auskommentiere ist
>>fast absolute Jitterruhe.
>
> Die Abfrage ist ein Kopieren unter ISR Sperre und das mit maximaler
> CPU-Last, wenn nix zu tun ist! Das ist Irrsinn! Und es wirkt ebenso wie
> eine ISR, sie erzeugt Jitter.

Jetzt haben wir den Übeltäter endlich :) Respekt Falk, dass du so lange 
drangeblieben bist.

Was dagegen tun?

1) millis() seltener aufrufen (so wie aktuell hast du geschätzt 2/3 der 
Zeit die Interrupts deaktiviert, kein Wunder, dass das dauernd Jittert) 
dann tritt der Jitter immerhin seltener auf
2) in millis() nicht alle Interrupts deaktivieren, sondern nur 
denjenigen, der deine >1Byte-Variable ändern kann - dann wird sich den 
Servosignal nicht mehr daran stören
3) andere Infrastruktur, z.B. im Millisekunden-Timer keine 
32Bit-Variable hochzählen, sondern ein Flag setzen oder eine 
8Bit-Variable weiterzählen und das im Hauptprogramm auswerten -> bietet 
allerdings das Risiko, dass du Millisekunden verlierst

Dass du mit uart+Kabel noch mehr Jitter erhältst - lass mich raten, in 
irgendeinem der case-Äste in handle_Function_UART0_Pins(); schaltest du 
auch für "lange" Zeit Interrupts ab? Und zwar in einem, der nur aktiv 
ist, wenn du Daten empfängst? Oder dein Receive-ISR ist "zu lang", 
länger als dein Timing-Window?

MfG, Arno

von Veit D. (devil-elec)


Lesenswert?

Hallo,

die in main verwendeten uart Variablen sind nicht static.

in uart.cpp
1
volatile uint16_t UART0_TxBuf[UART_TX_BUFFER_SIZE];
2
volatile uint16_t UART0_RxBuf[UART_RX_BUFFER_SIZE];
3
volatile uint8_t UART0_TxHead;
4
volatile uint8_t UART0_TxTail;

in main.cpp
1
extern volatile uint16_t UART0_RxBuf[UART_RX_BUFFER_SIZE];
2
extern volatile uint16_t UART0_TxBuf[UART_TX_BUFFER_SIZE];
3
extern volatile uint8_t UART0_TxHead;
4
extern volatile uint8_t UART0_TxTail;


das andere lass ich mir durch den Kopf gehen ...

von Falk B. (falk)


Lesenswert?

@Veit Devil (devil-elec)

>die in main verwendeten uart Variablen sind nicht static.

Ach?

>in uart.cpp
>volatile uint16_t UART0_TxBuf[UART_TX_BUFFER_SIZE];

>in main.cpp
>extern volatile uint16_t UART0_RxBuf[UART_RX_BUFFER_SIZE];

Merkt er was?

von Veit D. (devil-elec)


Lesenswert?

Hallo,

ich sehe aktuell nicht worauf du hinaus möchtest. Man muss es doch 
extern bekannt machen.

von Falk B. (falk)


Lesenswert?

@Veit Devil (devil-elec)

>Man muss es doch extern bekannt machen.

Ja, aber die Deklaration von Variablen in uart.cpp gehört in uart.h, 
welche dann wo auch immer per #include eingefügt wird.

von Veit D. (devil-elec)


Lesenswert?

Hallo,

du machst mich jetzt total irre. Wenn ich das in die uart.h verschiebe, 
dann meckert die main.cpp und uart.cpp rum. Dann geht gar nichts mehr. 
Angeblich ist alles nicht deklariert.
In main.cpp und uart.cpp ist jedoch schon immer    #include "usart.h" 
inkludiert.

Anfangs dachte ich ich hätte den Mechanismus dahinter verstanden, je 
länger der Thread dauert umso weniger verstehe ich.

In der main.cpp sind die 4 Zeilen raus und in der uart.h steht
1
#include <avr/pgmspace.h>
2
3
#if (__GNUC__ * 100 + __GNUC_MINOR__) < 405
4
#error "This library requires AVR-GCC 4.5 or later, update to newer AVR-GCC compiler !"
5
#endif
6
7
8
// module global variables
9
10
volatile uint16_t UART0_TxBuf[UART_TX_BUFFER_SIZE];
11
volatile uint16_t UART0_RxBuf[UART_RX_BUFFER_SIZE];
12
volatile uint8_t UART0_TxHead;
13
volatile uint8_t UART0_TxTail;
14
volatile uint8_t UART0_RxHead;
15
volatile uint8_t UART0_RxTail;
16
volatile uint8_t UART0_LastRxError;

von Veit D. (devil-elec)


Lesenswert?

Hallo,

habe das erstmal wieder rückgängig gemacht und mich danach nochmal ans 
TXB80 Bit gemacht. Ich hatte übersehen das ich das auch von Hand setzen 
soll. Ich dachte die uart soll das setzen und löschen und hatte mich die 
ganze Zeit gewundert wie das funktionieren soll wenn ich das 9. Bit 
nicht nutze im Datenframe. Jetzt gehts damit.

Laut Datenlogger hat Servo 1 einen 5µs Jitter. Die anderen beiden 2µs. 
Muss ein Messfehler sein, weil laut Oszi alle gleich zucken. Alles mit 
Kommunikation aber ohne millis() Abfrage. Mit millis Abfrage zuckt es am 
Oszi wieder etwas wilder.

Jetzt mal eine grundlegende Frage. Ohne millis. Dieser Restjitter ist 
doch jetzt System bedingt oder muss der auch noch weg? Also ist das 
überhaupt möglich?

Das Timer 0 Intervall hatte ich erhöht, damit nicht aller 1ms sondern 
erst aller 10ms dessen ISR zuschlägt.

von Veit D. (devil-elec)


Lesenswert?

Hallo,

Nachfrage
1
/* enable UDRE interrupt */
2
ATOMIC_BLOCK (ATOMIC_RESTORESTATE) {  // wegen ISR(TIMER1_COMPA_vect)
3
   if (UCSR0B & (1<<TXB80)) {
4
      UART0_CONTROL    |= _BV(UART0_UDRIE);  // enable UDRE interrupt
5
      return;
6
   }
7
}

Wenn man den atomic Block mittendrin verlässt, wird dann das SREG 
Register noch richtig behandelt?

von Jan K. (jan_k)


Lesenswert?

Veit D. schrieb:
> Hallo,
>
> du machst mich jetzt total irre. Wenn ich das in die uart.h verschiebe,
> dann meckert die main.cpp und uart.cpp rum. Dann geht gar nichts mehr.
> Angeblich ist alles nicht deklariert.
> In main.cpp und uart.cpp ist jedoch schon immer    #include "usart.h"
> inkludiert.
>
 #include "usart.h"  oder  #include "uart.h"?
> Anfangs dachte ich ich hätte den Mechanismus dahinter verstanden, je
> länger der Thread dauert umso weniger verstehe ich.
>

Auch wenn es manchmal noch anders gemacht wird, die extern Deklaration 
einer Variable kommt in den Header (foo.h oder foo.hh) des selben Moduls 
(foo.c oder foo.cpp), wo die Variable definiert wird.

von Veit D. (devil-elec)


Lesenswert?

Hallo,

die Dateien heißen alle usart.xxx und werden auch so mit usart.h 
inkludiert, sonst hätte das noch nie funktioniert. Das war hier im 
Thread ein Tippfehler. Geläufig sagt man uart obwohl es eine usart ist.

Auch wenn ich die in der usart.h als extern volatile deklariere werden 
diese nicht gefunden.

von Veit D. (devil-elec)


Lesenswert?

Hallo,

der aktuelle Stand ist jedenfalls der, ohne millis() Abfrage, dass die 
Servos selbst nicht zucken, auch wenn ich am Oszi das restliche zittern 
sehe mit 1µs/DIV bzw. Datalogger. Das ist schon einmal sehr sehr schön 
das mit Euch (Falk) geschafft zu haben. Vielen Dank.

Was nun mit der geänderten usart.h falsch ist weiß ich nicht. Könnt ihr 
mir da helfen?

Die millis() Funktion gehe ich an.

von Falk B. (falk)


Lesenswert?

@ Veit Devil (devil-elec)

>TXB80 Bit gemacht. Ich hatte übersehen das ich das auch von Hand setzen
>soll.

OMG!

> Ich dachte die uart soll das setzen und löschen und hatte mich die
>ganze Zeit gewundert wie das funktionieren soll wenn ich das 9. Bit
>nicht nutze im Datenframe. Jetzt gehts damit.

AHA!

>Laut Datenlogger hat Servo 1 einen 5µs Jitter. Die anderen beiden 2µs.
>Muss ein Messfehler sein, weil laut Oszi alle gleich zucken.

Bist du sicher, daß dein Logicanalyzer immer mit voller Abtastrate von 
24 MHz arbeitet und nicht irgendwann mal auf eine geringere Rate 
runterschaltet?

> Alles mit
>Kommunikation aber ohne millis() Abfrage. Mit millis Abfrage zuckt es am
>Oszi wieder etwas wilder.

Klingt schon mal gut, wenn gleich da theoretisch noch etwas weniger 
Jitter drin sein müßte.

>Jetzt mal eine grundlegende Frage. Ohne millis. Dieser Restjitter ist
>doch jetzt System bedingt oder muss der auch noch weg?

Muss nicht, aber kann.

> Also ist das
>überhaupt möglich?

Ich meine ja. Hast du Timer 0 deaktiviert bzw. schaltest du ihn im COMPA 
kurzzeitig aus?

Man sollte auf ca. 0,5us Jitter runterkommen, das sind 4 Takte bei 8 
MHz.

>Das Timer 0 Intervall hatte ich erhöht, damit nicht aller 1ms sondern
>erst aller 10ms dessen ISR zuschlägt.

Ist weniger oft aber immer noch oft genug. Siehe oben.

von Falk B. (falk)


Lesenswert?

@ Veit Devil (devil-elec)

>>ATOMIC_BLOCK (ATOMIC_RESTORESTATE) {  // wegen ISR(TIMER1_COMPA_vect)
>>   if (UCSR0B & (1<<TXB80)) {
>>      UART0_CONTROL    |= _BV(UART0_UDRIE);  // enable UDRE interrupt
>>      return;
>>   }
>>}

>Wenn man den atomic Block mittendrin verlässt, wird dann das SREG
>Register noch richtig behandelt?

Ja, darum kümmern sich das Macro und diverse Compilertricks hinter 
ATOMIC_BLOCK.

von Falk B. (falk)


Lesenswert?

@ Veit Devil (devil-elec)

>du machst mich jetzt total irre. Wenn ich das in die uart.h verschiebe,
>dann meckert die main.cpp und uart.cpp rum.

Weil irgendwelche anderen Abhängigeiten nicht passen. Aber das kann man 
korrigieren.


> Dann geht gar nichts mehr.
>Angeblich ist alles nicht deklariert.

Ist wahrscheinlich auch so.

Lange Rede, kurzer Sinn. Lad mal dein GESAMTES Projekt als Zip-File 
hoch, dann kann ich das anschauen und gerade rücken. Das alles per 
Beitrag zu kommentieren ist irgendwann ermüdend.

>Anfangs dachte ich ich hätte den Mechanismus dahinter verstanden, je
>länger der Thread dauert umso weniger verstehe ich.

;-)

Eigentlich (tm) ist es einfach. In jeder Headerdatei steht drin, was der 
Rest der Welt von der .c Datei wissen muss.

- Funktionsdeklarationen von Funktion, die von außen sicht- und nutzbar 
sein sollen
- Variablendeklarationen von Funktion, die von außen sicht- und nutzbar 
sein sollen
- #define
- Typdesfinitonen
- Inline-Funktionen mit echtem C-Code (nicht nur Deklarationen)

https://www.mikrocontroller.net/articles/FAQ#Header_File_-_wie_geht_das

von Veit D. (devil-elec)


Angehängte Dateien:

Lesenswert?

Hallo,

okay soweit. Timer 0 wird in Timer 1 Compare aus- und eingeschalten.
Der Datalogger wird sicherlich mit 4 Kanälen nicht mehr mit 24MHz 
abtasten.

Zurück wegen der usart.h und extern volatile

Ich halte mich aktuell genau an das Bsp., in einem c++ Forum gesehen.

in main.c
int var=5;
und in header.h
extern int var;

usart.h
nach der UART_TX_BUFFER_SIZE / UART_RX_BUFFER_SIZE  Definition
1
extern volatile uint16_t UART0_TxBuf[UART_TX_BUFFER_SIZE];
2
extern volatile uint16_t UART0_RxBuf[UART_RX_BUFFER_SIZE];
3
extern volatile uint8_t UART0_TxHead;
4
extern volatile uint8_t UART0_TxTail;

in main.cpp
[c]
volatile uint16_t UART0_TxBuf[UART_TX_BUFFER_SIZE];
volatile uint16_t UART0_RxBuf[UART_RX_BUFFER_SIZE];
volatile uint8_t UART0_TxHead;
volatile uint8_t UART0_TxTail;
/c]

>> multiple definition

Lasse ich das in main.cpp weg, so wie ihr sagt, dann ist alles komplett 
undefined reference. Ich blicke nicht mehr durch was vorne und hinten 
ist.

Ich lade das mal hoch Danke für die Unterstützung. Ich bringe es vorher 
in den Urzustand wie es bis jetzt immer funktionierte ...........

Möchtest du als Dank für die sportliche Herausforderung ein oder zwei 
ATtiny841 haben? Unbenutzt. Kann den Quarz mit reinlegen. Kann die auch 
noch auf eine Adapterplatine löten.

: Bearbeitet durch User
von Falk B. (falk)


Lesenswert?

@ Veit Devil (devil-elec)

>    ATtiny841_MAX487_032_Slave3.zip (128 KB, 0 Downloads)

Schau ich mir gleich mal an.

>Der Datalogger wird sicherlich mit 4 Kanälen nicht mehr mit 24MHz
>abtasten.

Doch, aber nicht unbedingt bei jeder Zeitauflösung. Das machen 
Digitaloszis auch nicht.

>Ich halte mich aktuell genau an das Bsp., in einem c++ Forum gesehen.

Was zum Geier hast du immer mit deinem C++? Du kannst nicht mal richtig 
gut C.

>in main.c
>int var=5;
>und in header.h
>extern int var;

Stimmt.

>usart.h
>nach der UART_TX_BUFFER_SIZE / UART_RX_BUFFER_SIZE  Definition

>extern volatile uint16_t UART0_TxBuf[UART_TX_BUFFER_SIZE];
>extern volatile uint16_t UART0_RxBuf[UART_RX_BUFFER_SIZE];
>extern volatile uint8_t UART0_TxHead;
>extern volatile uint8_t UART0_TxTail;


>in main.cpp
>volatile uint16_t UART0_TxBuf[UART_TX_BUFFER_SIZE];
>volatile uint16_t UART0_RxBuf[UART_RX_BUFFER_SIZE];
>volatile uint8_t UART0_TxHead;
>volatile uint8_t UART0_TxTail;
>/c]

Ist Murks, weil man nicht quer durch Dateien Deklarationen schreibt.

>Lasse ich das in main.cpp weg, so wie ihr sagt, dann ist alles komplett
>undefined reference. Ich blicke nicht mehr durch was vorne und hinten
>ist.

Das wird wieder. Dauert nicht allzu lang.

>Ich lade das mal hoch Danke für die Unterstützung. Ich bringe es vorher
>in den Urzustand wie es bis jetzt immer funktionierte ...........

Gut.

>Möchtest du als Dank für die sportliche Herausforderung ein oder zwei
>ATtiny841 haben? Unbenutzt. Kann den Quarz mit reinlegen.

Mach mal. Leg noch ne Flasche Martini Bianco rein und alles wird gut ;-)

>Kann die auch noch auf eine Adapterplatine löten.

Nein, wenn dann beides getrennt. Löten kann ich im Fall der Fälle 
selber.

von Veit D. (devil-elec)


Lesenswert?

>>Möchtest du als Dank für die sportliche Herausforderung ein oder zwei
>>ATtiny841 haben? Unbenutzt. Kann den Quarz mit reinlegen.
>
> Mach mal. Leg noch ne Flasche Martini Bianco rein und alles wird gut ;-)
>
>>Kann die auch noch auf eine Adapterplatine löten.
>
> Nein, wenn dann beides getrennt. Löten kann ich im Fall der Fälle
> selber.

Das mach ich doch glatt.  :-)   Den Rest per PN/Mail.

von Carl D. (jcw2)


Lesenswert?

Arno schrieb:
> Falk B. schrieb:
>> @Veit Devil (devil-elec)
>>
>>>Erst wenn ich in der Hauptschleife die millis Abfrage auskommentiere ist
>>>fast absolute Jitterruhe.
>>
>> Die Abfrage ist ein Kopieren unter ISR Sperre und das mit maximaler
>> CPU-Last, wenn nix zu tun ist! Das ist Irrsinn! Und es wirkt ebenso wie
>> eine ISR, sie erzeugt Jitter.
>
> Jetzt haben wir den Übeltäter endlich :) Respekt Falk, dass du so lange
> drangeblieben bist.
.
> Was dagegen tun?
>
> 1) millis() seltener aufrufen (so wie aktuell hast du geschätzt 2/3 der
> Zeit die Interrupts deaktiviert, kein Wunder, dass das dauernd Jittert)
> dann tritt der Jitter immerhin seltener auf

Wenn millis() aufgerufen wurde und noch weiter gewartet werden soll, 
dann braucht man nicht weiter nach millis() zu fragen, sondern kann in 
IDLE Sleep gehen, denn erst beim nächsten Interrupt besteht die Chance 
daß sich millis() geändert haben könnte. Und selbst wenn viele andere 
Interrupts kommen, dann sorgt dieses Vorgehen dafür, daß mills() erst 
NACH einem Int die INT's global für kurze Zeit sperrt.

von Veit D. (devil-elec)


Lesenswert?

Hallo,

irgendein Sleep Mode kommt nicht in Frage. Wenn ich auf einem Slave 
nicht alle freien Pins für Servos benötige, dann werden das wohl 
Eingänge werden um Sensoren abzufragen. Entweder ich frage im 
Hauptprogramm so oft es geht ab oder aller paar ms (mit millis) so wie 
es angedacht war. Eigentlich benötige ich einen globalen Zeitzähler den 
ich dann wie von Arduino gewohnt in den Funktionen lokal verarbeiten 
kann. Um damit irgendwelche Funktionen zum Bsp. aller 20 oder 30ms 
aufzurufen. Dort macht man das so. Was ja nicht dumm ist.
1
void update ()
2
{
3
  static unsigned long last_ms = 0;
4
  const byte zeit = 20;           // intervall
5
  
6
  if (millis() - last_ms > zeit) {
7
    last_ms = millis();
8
    ...
9
  mach was sinnvolles
10
  ...
11
  }
12
}

oder
1
void update ()
2
{ 
3
  static unsigned long last_ms = 0;
4
  const byte zeit = 20;           // intervall
5
  
6
  if (millis() - last_ms < zeit) return; 
7
  last_ms += zeit; 
8
  
9
  ...
10
  mach was sinnvolles
11
  ...
12
}


Ich denke erstmal über den Vorschlag von Falk nach, wie man millis 
anders macht. Wenn man hier wie aktuell verstanden jedoch nur auf ein 
Zeitintervall festgelegt ist, dann ist das auch blöd. Dann kann man 
nicht eine Funktion aller 10ms und eine andere aller 50ms aufrufen. Was 
ich mit den beiden Bsp. jedoch locker machen könnte.

von Wilhelm M. (wimalopaan)


Lesenswert?

Veit D. schrieb:
> Hallo,
>
> Nachfrage
>
1
> /* enable UDRE interrupt */
2
> ATOMIC_BLOCK (ATOMIC_RESTORESTATE) {  // wegen ISR(TIMER1_COMPA_vect)
3
>    if (UCSR0B & (1<<TXB80)) {
4
>       UART0_CONTROL    |= _BV(UART0_UDRIE);  // enable UDRE interrupt
5
>       return;
6
>    }
7
> }
8
>
>
> Wenn man den atomic Block mittendrin verlässt, wird dann das SREG
> Register noch richtig behandelt?

Wenn Du Dir das Macro anschaust (oder die Doku dazu liest) stellst Du 
fest, dass dort eine Gcc C-Extension verwendet wird, die in C etwas 
nachbildet, was in C++ ein ganz einfaches und ganz oft genutztes Idiom 
ist: RAII. Sprich: wenn der Block auf irgendeinem Weg verlassen wird, 
wird in C eine spezielle cleanup-function aufgerufen. Das erreicht man 
in C++ durch einen DTor (eines lokalen Objektes) ohne jede 
non-Standard-Spracherweiterung ;-)

von Wilhelm M. (wimalopaan)


Lesenswert?

Falk B. schrieb:

>>in main.cpp
>>volatile uint16_t UART0_TxBuf[UART_TX_BUFFER_SIZE];
>>volatile uint16_t UART0_RxBuf[UART_RX_BUFFER_SIZE];
>>volatile uint8_t UART0_TxHead;
>>volatile uint8_t UART0_TxTail;
>>/c]
>
> Ist Murks, weil man nicht quer durch Dateien Deklarationen schreibt.

Das obige sind keine Deklarationen, sondern Definitionen. Also schlägt 
hier die ODR (one-definition-rule) zu und Du erhälst eine multiple 
definition.

Deklarieren kann man eine Entität sooft wie man will, solange alle 
Deklarationen übereinstimmen. Genau deswegen darf / sollte man das in 
Header-Dateien machen.

>>Lasse ich das in main.cpp weg, so wie ihr sagt, dann ist alles komplett
>>undefined reference. Ich blicke nicht mehr durch was vorne und hinten
>>ist.

Ist nicht nachvollziehbar.
Du solltest Dir vielleicht mal an einem Beispiel klar machen, was 
Programm-globale oder Übersetzungseinheit-globale Variable sind, wie man 
sie definiert und deklariert.

von Falk B. (falk)


Angehängte Dateien:

Lesenswert?

@Veit Devil (devil-elec)

So, ich hab mal deinen Code gesichtet. Ich hab kein Atmelstudio 7, nur 
6.2, d.h. ich mußte ein neues Projekt erstellen und die Quelltexte rüber 
kopieren (die Projekte sind nicht rückwärtskompatibel, naja)
Dann hab ich mal deine .cpp Endungen wieder in .c umbenannt, den mit C++ 
hat dein Projekt rein gar nichts zu tun!

Dabei kommen beim 1. Kompilieren 3 Fehler.

1. bool ist unbekannt, wird durch #include <stdbool.h> behoben
2. PCINT2 kennt er nicht, fehlt auch in iotn841.h, hab ich mal manuell 
korrigiert
3. 'Message' undeclared here (not in a function),

Das ist dein Eiertanz mit den Deklarationen. Hab ich geändert, indem ich 
sizeof(empfDaten) geschrieben habe. Denn messeage ist weder eine 
Variable noch ein Datentyp, deswegen kann sizeof nichts berechnen. Die 
restlichen, verwirrenden Details dürfen andere erklären ;-)

uart0_putc() hab ich noch ein bisschen aufgebohrt, um die kurze 
Interruptsperre zu vermeiden, wenn die "kritische Phase" zwischen COMPA 
und COMPB ISR durchlaufen wird. Sieht komisch aus (doppelt gemoppelt), 
sollte aber funktionieren und muss so gemacht werden!

In read_Ringbuffer_0() hast du die UART-Fehlercodes dezimal drin, das 
ist Unsinn. In Hex ist das besser sichtbar, welches Bit was 
signalisiert. Außerdem sind die schon in uart.h definiert, das reicht. 
Doppelt aufschreiben bringt nix, eher Fehler. Außerdem besteht der Witz 
von enums oder auch einfachen #define darin, daß man sich mit den 
kodierten Zahlen NICHT beschäftigen muss und nur mit den 
selbsterklärenden Worten arbeitet.

Wenn du Timer 0 in COMPA abschaltest, solltest du alle CSxy Bit auf 0 
setzen, damit es auch bei anderen Prescalern ohne Änderung funktioniert, 
so wie du es schon in stop_Timer0() gemacht hast.

In main hab ich am meisten aufgeräumt und sämtliche Deklarationen und 
typedefes in main.h verschoben. Die UART-Deklarationen natürlich in 
uart.h
War eigentlich unkompliziert. Schau's dir mal an.

von Arno (Gast)


Lesenswert?

Falk B. schrieb:
> Wenn du Timer 0 in COMPA abschaltest, solltest du alle CSxy Bit auf 0
> setzen, damit es auch bei anderen Prescalern ohne Änderung funktioniert,
> so wie du es schon in stop_Timer0() gemacht hast.

Halt, der Plan war doch, da nur den IRQ abzuschalten, oder?

MfG, Arno

von Joachim B. (jar)


Lesenswert?

#pragma once
wieder was gelernt

ich mag aber weiterhin
#ifndef usart_h
#define usart_h

weil
https://msdn.microsoft.com/de-de/library/4141z1cx.aspx
Wir empfehlen das #include-Schutz-Idiom, wenn Code für Compiler portabel 
sein muss, die die #pragma once-Richtlinie nicht implementieren, um die 
Konsistenz mit vorhandenem Code aufrechtzuerhalten

: Bearbeitet durch User
von Falk B. (falk)


Lesenswert?

@ Arno (Gast)

>> Wenn du Timer 0 in COMPA abschaltest, solltest du alle CSxy Bit auf 0
>> setzen, damit es auch bei anderen Prescalern ohne Änderung funktioniert,
>> so wie du es schon in stop_Timer0() gemacht hast.

>Halt, der Plan war doch, da nur den IRQ abzuschalten, oder?

Stimmt, das war der Plan! Ist wohl durchgerutscht 8-0
Denn so geht der Timer 0 falsch!

von Carl D. (jcw2)


Lesenswert?

Wenn kein sleep möglich ist, dann vielleicht das versuchen, was das 
sleep einfach sicherstellen sollte: weniger oft die Ints sperren.
Man kann in millis() auch ohne Sperre auskommen, in dem man
- zusätzlich zum Wert selber ein Bit/Byte anlegt,
- in das die ISR wenn sie am Wert gefummelt hat eine 1 reinschreibt.
 millis():
- setzt als erstes das Bit/Byte auf 0, was atomar ist,
- liest den Wert in eine temp. Variable und
- überprüft danach das "geändert" Flag
Sollte es gesetzt sein, einfach nochmal lesen. Das Flag muß dabei nicht 
mehr angesehen werden, falls man sich sicher ist, daß die diversen ISRen 
millis() nie länger als 1ms unterbrechen.
Damit wird man zuverlässig die Interruptsperren los und kann millis 
bedenkenlos in einer Warteschleife benutzen.

von Wilhelm M. (wimalopaan)


Lesenswert?

Joachim B. schrieb:
> #pragma once
> wieder was gelernt
>
> ich mag aber weiterhin
> #ifndef usart_h
> #define usart_h
>
> weil
> https://msdn.microsoft.com/de-de/library/4141z1cx.aspx
> Wir empfehlen das #include-Schutz-Idiom, wenn Code für Compiler portabel
> sein muss, die die #pragma once-Richtlinie nicht implementieren, um die
> Konsistenz mit vorhandenem Code aufrechtzuerhalten

Die Vorteile überwiegen m.E. allerdings. Und: welcher Compiler 
unterstützt heute kein #pragma once ?

von Falk B. (falk)


Lesenswert?

Hier die korrigierten ISRs mit der Abschaltung des Timer 0 Interrupts 
anstatt des Timers selber
1
ISR(TIMER1_COMPB_vect)    // dauert 6,2µs, wird aller >1ms aufgerufen
2
{   
3
    static uint8_t servo = 0;
4
    const uint8_t SERVO_CNT = 3;        // Anzahl Servos
5
    const uint8_t TIMING_WINDOW = 20;   // 1 Count = 1µs
6
    static uint16_t temp = 0;
7
    
8
    switch(servo) {     // ISR Zeit 4,75µs
9
        // Count   999 =  1ms
10
        // Count  1999 =  2ms
11
        // Count 19999 = 20ms
12
        case 0: Led2_OFF;   Led5_ON;    temp =  1499; break;
13
        case 1: Led5_OFF;   Led6_ON;    temp =  1499; break; 
14
        case 2: Led6_OFF;               temp = 13999; break; // virtuelle
15
        case 3:             Led2_ON;    temp =  1499; break;
16
    }
17
    
18
    OCR1B += temp;
19
    OCR1A = OCR1B-TIMING_WINDOW;
20
    // enable USART0 RX Complete Interrupt
21
    // enable USART0 Data Register Empty Interrupt
22
    UCSR0B |= ((1<<RXCIE0) | (1<<UDRIE0) | (1<<TXB80));
23
                                           
24
    TIMSK0 |= (1 << OCIE0A);        // enable Timer0 COMPA ISR
25
   
26
    servo++;
27
    if (servo > SERVO_CNT)  servo = 0;
28
                                            
29
    Led1_OFF;                   
30
}
31
32
33
ISR(TIMER1_COMPA_vect)          // dauert 1,8µs
34
{   
35
    TIMSK0 &= ~(1 << OCIE0A);   // disable Timer0 COMPA ISR
36
   
37
    // disable USART0 RX Complete Interrupt
38
    // disable USART0 Data Register Empty Interrupt
39
    UCSR0B &= ~((1<<RXCIE0) | (1<<UDRIE0) | (1<<TXB80));
40
                                  
41
    Led1_ON; // für Datalogger, debuggen
42
}

von Veit D. (devil-elec)


Angehängte Dateien:

Lesenswert?

Hallo,

oh ha, so muss man das also machen. Man sieht auch sofort die 
Handschrift eines richtigen Programmierers. Alles aufgeräumter. Letzte 
Code Korrektur ist auch vollzogen. Vielen vielen herzlichen Dank - Falk. 
Und alle anderen die ihre Idee eingebracht haben. Kann man nicht in 
Worte fassen was in den letzten Tagen alles mit dem Code passiert ist 
nur um den Jitter in den Griff zubekommen.

Falls jemand die ATtiny841 Definitionsdatei benötigt, hängt dran.

Jetzt muss ich natürlich fragen was der funktionale Unterschied ist 
zwischen
1
if ( UCSR0B & (1<<TXB80) ) {
2
        
3
      /* enable UDRE interrupt */
4
      ATOMIC_BLOCK (ATOMIC_RESTORESTATE) {  // wegen ISR(TIMER1_COMPA_vect)
5
         if (UCSR0B & (1<<TXB80)) {
6
            UART0_CONTROL |= _BV(UART0_UDRIE);  // enable UDRE interrupt
7
            return;
8
         }
9
      }
10
}

und dem
1
if ( UCSR0B & (1<<TXB80) ) {
2
        
3
      /* enable UDRE interrupt */
4
      ATOMIC_BLOCK (ATOMIC_RESTORESTATE) {  // wegen ISR(TIMER1_COMPA_vect)
5
          UART0_CONTROL |= _BV(UART0_UDRIE);  // enable UDRE interrupt
6
          return;
7
      }
8
}

für mich sieht das gleich aus.

Zum Abschluss noch ein Filmchen zur Jittermessung. Alles nur ohne millis 
Abfrage.
https://youtu.be/Y5rib4x1k60
Man beachte die Zeiteinstellung von 500ns/DIV.

Bewegt sich alles innerhalb 1µs, ganz ganz selten scheint es einen 
größeren Ausschlag zugeben. Der stört aber nicht, die Servos verhalten 
sich ruhig. Deswegen kann ich auf die Servoabschaltung verzichten, was 
ich am Anfang drin hatte. Kein zucken, kein brummen, keine 
Stromaufnahme. Der Code zur Pulserzeugung ist auch in der Hinsicht 
besser das nicht alle Servos zeitgleich Strom ziehen.

@Carl, den Timer 0 Compare müssen wir aber kurz sperren, auf alles 
andere kann verzichtet werden nach Umbau.

Wegen mehreren Zeitvergleichen habe ich auch eine Idee, brachte mich 
eure Flag Idee drauf, wenn ich zum Bsp. 3 Intervalle benötige, kann ich 
diese in die Timer 0 Comp ISR schreiben und entweder ein Flag setzen mit 
> Vergleich und nullen oder mit Modulo. Mit Modulo Vergleich benötige 
ich keine zusätzlichen Variablen, nur noch so viele Flags wie ich 
Intervalle benötige.

Zum Bsp.
1
ISR(TIMER0_COMPA_vect)
2
{  
3
  millis_count++;           // der ms Zähler
4
  
5
  if (millis_count % 20) {
6
    Flag 1 setzen ...     // für Funktion die aller 20ms aufgerufen werden soll
7
  }
8
  if (millis_count % 30) {
9
    Flag 2 setzen ...     // für Funktion die aller 30ms aufgerufen werden soll
10
  }
11
  if (millis_count % 40) {
12
    Flag 3 setzen ...     // für Funktion die aller 40ms aufgerufen werden soll
13
  }
14
}

Wäre das sinnvoll und Ressourcen schonend? In der Funktion frage ich 
dann das Flag ab ob sie ausgeführt werden soll oder nicht, wenn ja, 
setze ich das Flag zurück.

von Carl D. (jcw2)


Lesenswert?

Veit D. schrieb:
>
> Zum Bsp.
>
1
> ISR(TIMER0_COMPA_vect)
2
> {
3
>   millis_count++;           // der ms Zähler
4
> 
5
>   if (millis_count % 20) {
6
>     Flag 1 setzen ...     // für Funktion die aller 20ms aufgerufen 
7
> werden soll
8
>   }
9
>   if (millis_count % 30) {
10
>     Flag 2 setzen ...     // für Funktion die aller 30ms aufgerufen 
11
> werden soll
12
>   }
13
>   if (millis_count % 40) {
14
>     Flag 3 setzen ...     // für Funktion die aller 40ms aufgerufen 
15
> werden soll
16
>   }
17
> }
18
>
>
> Wäre das sinnvoll und Ressourcen schonend? In der Funktion frage ich
> dann das Flag ab ob sie ausgeführt werden soll oder nicht, wenn ja,
> setze ich das Flag zurück.

vor allem: verzichte auf 16-Bit Modulo in einer ISR! das dauert!

Nochmal ein Versuch:
Wenn du nicht exakt 10/20/30 ms, sondern eher n*Größenordnung 
Timer1-Zyklus (vielleicht ist der ja auch nicht in Stein gemeißelt, dann 
das millis-Zählen am Ende des Comp1B. Da ist der Jitter-kritische Puls 
durch.

von Nico W. (nico_w)


Lesenswert?

Dauert nicht nur. So ist sie auch falsch.
if (!(millis % 30)) ist wahrscheinlich das was gewünscht ist.

von Carl D. (jcw2)


Lesenswert?

Nico W. schrieb:
> Dauert nicht nur. So ist sie auch falsch.
> if (!(millis % 30)) ist wahrscheinlich das was gewünscht ist.

Vermutlich doch:
Im Abstand von 30ms ein Flag setzen, denn nur dann ist der Rest einer 
Division durch 30 nicht ungleich 0.

Nur, wenn das die einzige Verwendung von millis ist, warum dann nicht 
nur alle 10ms auf 1/2/3 zählen?

: Bearbeitet durch User
von Falk B. (falk)


Lesenswert?

@ Veit Devil (devil-elec)

>Falls jemand die ATtiny841 Definitionsdatei benötigt, hängt dran.

Hier sind die PCINTx jetzt drin

>Jetzt muss ich natürlich fragen was der funktionale Unterschied ist
>zwischen

Ist es aber nicht, weil das spezielle Timings von ISRs berücksichtigt 
wird.

>für mich sieht das gleich aus.

Da muss man den gesamten Codeabschnitt betrachten.

Außerdem sollte man sich das Timing von COMPA und COMPB ISR vor Augen 
halten.
1
      -------------A-B-------------A-B-------------A-B---
2
TXB80 --------------__--------------__--------------__---

In der kurzen Zeit zwischen COMPA und COMPB sind alle anderen ISRs 
gesperrt und auch das normale Hauptprogramm sollte KEINE ISR-Sperren 
durchführen, um den Jitter von COMPB zu minimieren. Ebenso dürfen in der 
Zeit vom Hauptprogramm keine ISRs freigegeben werden.
1
void uart0_putc(uint8_t data)
2
{
3
    uint8_t tmphead;
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
/*
15
Wenn hier TXB80 HIGH ist, befinden wir uns zeitlich vor der
16
COMPA-ISR. Eine kurze ISR-Sperre durch ATOMIC_BLOCK erzeugt keinen Jitter für COMPB.
17
*/        
18
    if ( UCSR0B & (1<<TXB80) ) {
19
        /* enable UDRE interrupt */
20
/*
21
Die COMPA-ISR kann aber JEDERZEIT aktiv werden, auch 1 CPU-Takt nach
22
dem erfolgreichen Vergleich! Also muss die endgültige Abfrage + Setzen von
23
UART0_UDRIE unter Interruptsperre erfolgen, damit eben nicht aus Versehen
24
COMPA schon aktiv wurde und UART0_UDRIE gelöscht ist!!! Wenn während der ISR-Sperre COMPA aktiv wird, wird in COMPA Jitter erzeugt, das ist aber unkritisch.
25
*/
26
        ATOMIC_BLOCK (ATOMIC_RESTORESTATE) {  // wegen ISR(TIMER1_COMPA_vect)
27
            if (UCSR0B & (1<<TXB80)) {
28
/*
29
Wenn TXB80 immer noch HIGH ist, können wir UART0_UDRIE sicher löschen und die Funktion verlassen, alles palletti!
30
*/
31
                UART0_CONTROL    |= _BV(UART0_UDRIE);  // enable UDRE interrupt
32
                return;
33
            }
34
        }
35
    }    
36
   
37
/*
38
Hier kommen wir hin, wenn
39
40
-TXB80 beim 1. Vergleich ohne ISR-Sperre LOW war, dann befinden wir uns zeitlich zwischen COMPA und COMPB ISR. Während dieser Zeit darf keinerlei anderer Interrupt freigegeben werden, wir müssen auf das Ende von COMPB warten
41
42
-TXB80 beim 2. Vergleich mit ISR-Sperre LOW war, weil COMPA in der kurzen Zwischenzeit aktiv wurde.
43
44
Der doppelte Vergleich, einmal ohne und einmal mit ISR-Sperre wird gemacht, um keinen zusätzlichen Jitter für COMPB zu erzeugen, welche eine ISR-Sperre erzeugt. Denn wenn TXB80 schon beim 1. Vergleich LOW ist, wissen wir, daß gerade die heiße Sperrphase der Interrupts aktiv ist und wir keine ISR-Sperre durchführen dürfen. Wenn das beim 2. Vergleich passiert ist es unkritisch, denn das ist der ANFANG der Sperrphase, da ist noch genug Zeit bis zum COMPB Interrupt und es entsteht kein Jitter, denn der zeitliche Abstand TIMING_WINDOW verhindert das.
45
*/   
46
   // warte auf das Ende der ISR-Sperre
47
   // reiner Lesezugriff ist atomar, daher kein atomic block nötig
48
   while (!(UCSR0B & (1<<TXB80)));
49
50
   ATOMIC_BLOCK (ATOMIC_RESTORESTATE) {  // wegen ISR(TIMER1_COMPA_vect)
51
      UART0_CONTROL    |= _BV(UART0_UDRIE);  // enable UDRE interrupt
52
   }
53
54
} // uart_putc


>Zum Abschluss noch ein Filmchen zur Jittermessung. Alles nur ohne millis
>Abfrage.
>Youtube-Video "OpenCam 20180105 112525"
>Man beachte die Zeiteinstellung von 500ns/DIV.

>Bewegt sich alles innerhalb 1µs, ganz ganz selten scheint es einen
>größeren Ausschlag zugeben.

Super! Die Messung wird noch ein wenig besser, denn du das Nachleuchten 
im Oszi einschaltest (display persistance, da gibt es irgendwo ein 
Menu).

>eure Flag Idee drauf, wenn ich zum Bsp. 3 Intervalle benötige, kann ich
>diese in die Timer 0 Comp ISR schreiben und entweder ein Flag setzen mit
>> Vergleich und nullen oder mit Modulo.

Modulo ist aber ungünstig, denn das braucht eine echte Division und die 
ist auf einem AVR relativ langsam. Außerdem wird die meist mit einem 
Funktionsaufruf gemacht, was in einer ISR zusätzlichen Aufwand fürs 
Registersichern kostet. Zählen und Vergleichen ist deutlich schneller 
und einfacher.

>Wäre das sinnvoll und Ressourcen schonend?

Nur ohne Modulo.

>In der Funktion frage ich
>dann das Flag ab ob sie ausgeführt werden soll oder nicht, wenn ja,
>setze ich das Flag zurück.

Genau.

von Frank (Gast)


Lesenswert?

Die Controller gibts fuer 1,60 beim Chinesen
https://learn.adafruit.com/16-channel-pwm-servo-driver/overview

von Nico W. (nico_w)


Lesenswert?

Carl D. schrieb:
> Nico W. schrieb:
>> Dauert nicht nur. So ist sie auch falsch.
>> if (!(millis % 30)) ist wahrscheinlich das was gewünscht ist.
>
> Vermutlich doch:
> Im Abstand von 30ms ein Flag setzen, denn nur dann ist der Rest einer
> Division durch 30 nicht ungleich 0.

Denke nicht.
1
28 % 30 == 28 -> !(28) == 0
2
29 % 30 == 29 -> !(29) == 0
3
30 % 30 == 0  ->  !(0) == 1
4
31 % 30 == 1  ->  !(1) == 0

Im Interrupt könnte man sowas basteln, wenn der pro ms einmal aufgerufen 
wird:
1
static uint8_t counter_1ms;
2
static uint8_t counter_10ms;
3
counter_1ms++;
4
if (counter_1ms >= 10) {
5
  counter_1ms -= 10;
6
  flag_1ms = 1;
7
  counter_10ms++;
8
  if (counter_10ms >= 10) {
9
    counter_10ms = 0;
10
    flag_10ms = 1;
11
  }
12
}

Und irgendwo außerhalb vom ISR
1
for (;flag_1ms;flag_1ms=0) {
2
  do_stuff_1ms();
3
  for (;flag_10ms;flag_10ms=0) {
4
    do_stuff_10ms();
5
  }
6
}

von Veit D. (devil-elec)


Lesenswert?

Hallo,

das IC kenne ich, möchte ich aber nicht.


TXB80. Danke für die Erklärung, jetzt verstehe ich das auch.

Der Module Vergleich sollte eigentlich so aussehen.   :-)
1
if (millis_count % 20 == 0) {
2
    Flag 1 setzen ...     
3
}

Aber ihr habt recht mit euren neuen Hinweisen. Das Timer 0 Comp 
Intervall ist nicht in Stein gemeißelt. Der Timer 0 kann ja auf das 
kleinste benötigte Intervall eingestellt werden. Und mit der Methode 
reicht ein Byte als Zähler. Größere Intervalle wie 255ms werden nicht 
benötigt. Guter Hinweis. Muss aber auch sagen, dass sind alles 
Optimierungen die ganz schön ans Eingemachte gehen. Hoffe ich versaue 
das durch spätere Programmerweiterungen nicht wieder. Wobei nicht mehr 
viel dazu kommt.
1
ISR(TIMER0_COMPA_vect)
2
{  
3
  static uint8_t millis_count = 0;
4
  static uint8_t last_1;
5
  static uint8_t last_2;
6
  static uint8_t last_3;
7
  
8
  millis_count++;           // der ms Zähler
9
  
10
  if (millis_count - last_1 > 20) {
11
    last_1 = millis_count;
12
  Flag 1 setzen ...     
13
  }
14
  if (millis_count - last_2 > 30) {
15
    last_2 = millis_count;
16
  Flag 2 setzen ...     
17
  }
18
  if (millis_count - last_3 > 40) {
19
    last_3 = millis_count;
20
  Flag 3 setzen ...     
21
  }
22
}

Oder wäre jeweils ein Vergleich auf 0 noch besser? Hatte mal wo gelesen 
das  dies noch 1-2 Takte sparen würde?

von Dieter F. (Gast)


Lesenswert?

Dieter F. schrieb:
> 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.

Nachdem Du nun Scheibchenweise weitergekommen bist und sicher die Bilder

Beitrag "Re: ISR Code schneller machen?"

noch im Hinterkopf hast - glaubst Du immer noch an die 
Modellbahn-Version?

Sportlich würde ich eine hardwarenahe Lösung vorziehen - möglichst ohne 
Jitter (wobei das bei 08/15-Modellbau-Servos keine Rolle spielen 
dürfte). Interrupts sind eher hinderlich - obwohl es da auch 
Möglichkeiten gibt, den Jitter (ohne den MC-Jitter) weitgehend zu 
eliminieren.

Die 250 kB-Kommunikation im Hobby-Bereich kann ich nach wie vor nicht 
nachvollziehen - genau wie die Festlegung auf den ATTiny841 mit 12 Pins 
incl. Quarz und Reset - wenn auch noch Pins für Sensoren etc. geplant 
sind.

Nur mal so - am Rande ... (und z. B. Open-DCC im Blickfeld) :-)

von Falk B. (falk)


Lesenswert?

@Frank (Gast)

>Die Controller gibts fuer 1,60 beim Chinesen
>https://learn.adafruit.com/16-channel-pwm-servo-dr...

Den Lerneffekt aber nicht.

von Dieter F. (Gast)


Lesenswert?

Falk B. schrieb:
> Den Lerneffekt aber nicht.

Mit komplettem Link aber schon :-)

https://learn.adafruit.com/16-channel-pwm-servo-driver/overview

von Falk B. (falk)


Lesenswert?

@Dieter F. (jim_quakenbush)

>noch im Hinterkopf hast - glaubst Du immer noch an die
>Modellbahn-Version?

Ist mir eigentlich schnuppe, MIR ging es nur um den Beweis der 
Jitterminimierung der Servosignale.

>Sportlich würde ich eine hardwarenahe Lösung vorziehen - möglichst ohne
>Jitter

Kann man machen, erfordert aber eine Hardwareänderung.

von Falk B. (falk)


Lesenswert?

@ Dieter F. (jim_quakenbush)

>> Den Lerneffekt aber nicht.

>Mit komplettem Link aber schon :-)

>https://learn.adafruit.com/16-channel-pwm-servo-dr...

Was lernt man da? Wie man 16 Sollwert per I2C an einen IC schickt? 
C'mon!

von Dieter F. (Gast)


Lesenswert?

Falk B. schrieb:
> Kann man machen, erfordert aber eine Hardwareänderung.

Yep - analog des zusätzlichen Quarzes ... im Cent-Bereich, wobei es im 
"Hobby-Bereich" auf 1 - 10 (oder wie auch immer) € nicht ankommen sollte 
:-)

von Dieter F. (Gast)


Lesenswert?

Falk B. schrieb:
> Was lernt man da? Wie man 16 Sollwert per I2C an einen IC schickt?
> C'mon!

K. A. - nur Dein Link war (und ist) nicht vollständig :-)

von Dieter F. (Gast)


Lesenswert?

Falk B. schrieb:
> Was lernt man da?

Exakt: Was man über die Arduino-Servo-Library lernen kann :-)

Nicht mein Fall und auch nicht meine Intension - aber gut abgelenkt :-)

von Dieter F. (Gast)


Lesenswert?

Falk B. schrieb:
> Ist mir eigentlich schnuppe, MIR ging es nur um den Beweis der
> Jitterminimierung der Servosignale.

Ist der Jitter weg?

von Veit D. (devil-elec)


Lesenswert?

Hallo,

Dieter, ich baue mir meine Steuerung genauso wie ich es möchte. Ich 
möchte eben nicht irgendwo eine riesige Zentrale mit hunderten 
Porterweiterungen stehen haben um dann von dort aus tausende Drähte 
unter der Anlage zuverlegen. Ich möchte nah vor Ort die Slaves setzen 
und kurze Wege zu dem was sie steuern oder abfragen sollen. Das ist 
modern. Warum 841er? Habe ich auch schon mehrfach erklärt. Auf der 
10x10cm Platine bekomme ich nicht mehr unter. Selbst wenn ich einen 
größeren IC nehme, fehlt mir der Platz für die zusätzlichen 
Klemmleisten. Komplett auf LED Anzeige möchte ich aber auch nicht 
verzichten. 10x10 deshalb, weil die mich bei elecrow praktisch nichts 
kosten im Vergleich zu anderen Größen. In Summe aller Kosten Nutzung 
Rechnungen ist das für mich optimal. Das macht auch kein größerer IC und 
dann weniger Platinen wieder gut. Die Platinenkosten explodieren 
förmlich und im dümmsten Fall sitzt dann irgendwo eine Platine die kaum 
genutzt wird. Nur wenige der möglichen Pins meine ich.
Ganz blöd bin ich nun auch nicht!

Die Frage "ob ich jetzt noch sicher bin ..." verstehe ich nun nicht 
wirklich. Gerade jetzt wo der Jitter weg ist bzw. so klein ist, dass er 
nicht stört. Hast du scheinbar überlesen. Das unterstelle ich dir mal im 
positiven Sinne. Denn ich unterstelle niemanden was negatives den ich 
nicht kenne.

Bei Vollbestückung kosten mich die Bauteile ca. 17,- Euro pro Platine + 
2,- Euro Platine. Die erste Bauteilbestellung bei Mouser war für 10 
Platinen ausgelegt. Für 30 Platinen ginge auf 15,- runter.

-----------------------------------------------------------------

Übrigens gehts jetzt sogar ohne Quarz. Der Code ist jetzt dermaßen 
optimiert, dass selbst der größere Jitter vom internen RC die Servos 
nicht animiert zu zucken. Wie geil ist das denn.   :-)

ATtiny841, Quarz, Jittermessung, mit nachleuchten
https://youtu.be/HxyHFqIxtho

ATtiny841, interner RC, Jittermessung, mit nachleuchten
https://youtu.be/tvNKzUWxUtY

: Bearbeitet durch User
von Dieter F. (Gast)


Lesenswert?

Veit D. schrieb:
> Ich
> möchte eben nicht irgendwo eine riesige Zentrale mit hunderten
> Porterweiterungen stehen haben um dann von dort aus tausende Drähte
> unter der Anlage zuverlegen.

Du weisst aber schon, dass DCC eine Bus-Lösung ist?

Veit D. schrieb:
> Warum 841er? Habe ich auch schon mehrfach erklärt. Auf der
> 10x10cm Platine bekomme ich nicht mehr unter.

Mir kommen die Tränen - der Chip verliert sich auf der Prototyp-Platine.

Veit D. schrieb:
> Selbst wenn ich einen
> größeren IC nehme, fehlt mir der Platz für die zusätzlichen
> Klemmleisten.

Rundum - bei aktuell max. 10 Pins incl. Reset - da fühle ich mich leicht 
veralbert :-) bei 10 * 10 cm

Veit D. schrieb:
> In Summe aller Kosten Nutzung
> Rechnungen ist das für mich optimal.

Im Hobby-Bereich - klar.

Veit D. schrieb:
> Das macht auch kein größerer IC und
> dann weniger Platinen wieder gut. Die Platinenkosten explodieren
> förmlich und im dümmsten Fall sitzt dann irgendwo eine Platine kaum
> genutzt wird.

dto.

Veit D. schrieb:
> Bei Vollbestückung kosten mich die Bauteile ca. 17,- Euro pro Platine +
> 2,- Euro Platine. Die erste Bauteilbestellung bei Mouser war für 10
> Platinen ausgelegt. Für 30 Platinen ginge auf 15,- runter.

Hobby - klar.

Veit D. schrieb:
> Übrigens gehts jetzt sogar ohne Quarz. Der Code ist jetzt dermaßen
> optimiert, dass selbst der größere Jitter vom internen RC die Servos
> nicht animiert zu zucken. Wie geil ist das denn.   :-)

Für jemanden der große Chargen plant - Supergeil :-)

von Carl D. (jcw2)


Lesenswert?

Nico W. schrieb:
> Carl D. schrieb:
>> Nico W. schrieb:
>>> Dauert nicht nur. So ist sie auch falsch.
>>> if (!(millis % 30)) ist wahrscheinlich das was gewünscht ist.
>>
>> Vermutlich doch:
>> Im Abstand von 30ms ein Flag setzen, denn nur dann ist der Rest einer
>> Division durch 30 nicht ungleich 0.
.
> Denke nicht.
>
1
> 28 % 30 == 28 -> !(28) == 0
2
> 29 % 30 == 29 -> !(29) == 0
3
> 30 % 30 == 0  ->  !(0) == 1
4
> 31 % 30 == 1  ->  !(1) == 0
5
>

Ja stimmt, irgendwo hatte ich das Ausrufezeichen gesehen, vermutlich 
weil ich das gar nicht anders erwartet hatte.

von Joachim B. (jar)


Lesenswert?

Veit D. schrieb:
> Warum 841er? Habe ich auch schon mehrfach erklärt. Auf der
> 10x10cm Platine bekomme ich nicht mehr unter

echt jetzt?

da würde ich doch locker auch den ATmega328p sehen als DIL oder als SMD 
auch den m1284p nach Bedarf.

Kosten bei Hobby? die CPU machts da ja wohl nicht

: Bearbeitet durch User
von Falk B. (falk)


Lesenswert?

@Veit Devil (devil-elec)

>nicht animiert zu zucken. Wie geil ist das denn.   :-)

TOP!

>ATtiny841, Quarz, Jittermessung, mit nachleuchten
>Youtube-Video "Oszi Restjittermessung mit nachleuchten"

Hier sieht man schön die diskreten Linien im Jitter, weil der Quarz sehr 
stabil ist.

>ATtiny841, interner RC, Jittermessung, mit nachleuchten
>Youtube-Video "Oszi Jitter Messung mit nachleuchten"

Beim RC-Oszillator verschwimmt alles. Trotzdem noch relativ gut.

von Dieter F. (Gast)


Lesenswert?

Veit D. schrieb:
> Die Frage "ob ich jetzt noch sicher bin ..." verstehe ich nun nicht
> wirklich.

Übrigens habe ich nicht Dich gefragt, sonden Falk :-)

von Dieter F. (Gast)


Lesenswert?

Falk B. schrieb:
> TOP!

Darf ich das als kostenloses Beratungs-Angebot für künftige Projekte 
verstehen?

von Falk B. (falk)


Lesenswert?

@Dieter F. (jim_quakenbush)

>Falk B. schrieb:
>> TOP!

>Darf ich das als kostenloses Beratungs-Angebot für künftige Projekte
>verstehen?

???

Meine Kommentare hier im Forum sind für alle kostenlos, wenn gleich 
sicher nicht umsonst ;-)

von Dieter F. (Gast)


Lesenswert?

Falk B. schrieb:
> Meine Kommentare hier im Forum sind für alle kostenlos, wenn gleich
> sicher nicht umsonst ;-)

d'accord :-) - manches Mal halt nur für Andere lukrativ ...

von Veit D. (devil-elec)


Angehängte Dateien:

Lesenswert?

Joachim B. schrieb:
> Veit D. schrieb:
>> Warum 841er? Habe ich auch schon mehrfach erklärt. Auf der
>> 10x10cm Platine bekomme ich nicht mehr unter
>
> echt jetzt?
>
> da würde ich doch locker auch den ATmega328p sehen als DIL oder als SMD
> auch den m1284p nach Bedarf.
>
> Kosten bei Hobby? die CPU machts da ja wohl nicht

Hallo,

bitte alles im Zusammenhang lesen und verstehen, sonst macht das alles 
keinen Sinn. Zuerst müsste mehr Platz für weitere Klemmleisten 
geschaffen werden. Hier sehe ich keine Chance. Erst wenn das möglich 
wäre, macht ein größer µC Sinn.

von Joachim B. (jar)


Lesenswert?

Veit D. schrieb:
> bitte alles im Zusammenhang lesen und verstehen,

hatte ich, deine liegenden R  verbrauchen viel Platz, stehend würde auch 
gehen, oder gar SIL Array oder wenns seriell sein muss als DIL Arrays.
Einige KerKos könnten auch in SMD sein.
Die Jumper mit 3 Reihen könnten auch DIP switch sein?

spart alles etwas Platz

von Veit D. (devil-elec)


Lesenswert?

Hallo,

das ändert jetzt was am Platz der Klemmleisten außen herum?

von Falk B. (falk)


Lesenswert?

@Veit Devil (devil-elec)

>das ändert jetzt was am Platz der Klemmleisten außen herum?

Nichts. Trotzdem herrscht auf deiner Platine gähnende Leere. Wenn man 
auf moderate SMD-Bauteile umschwenkt, ist mindestens ein ATmega88/328 im 
TQFP32 Gehäuse drin. Man muss ja nicht auf 0402er Kondensatoren gehen, 
0805 geht hier auch locker. Dann bleibt immer noch Platz für ein 
Mäusefußballfeld ;-)

Deine 3x1 Jumper kann man ins normale 2,54mm Raster ohne Lücke setzen, 
spart Platz und man kann einfach eine passende 3reihige Stiftleiste 
einlöten.

Und selbst wenn man bei THT Bauteilen bleiben will ist da Platz für 
einen ATmega328 im DIL28 Gehäuse. Das kriegt man auch verdrahtet, 
schließlich hast du 2 Lagen + VIAs, das war vor 30 Jahren selbst im 
Massenkonsumerbereich purer Luxus! ;-)

von Dieter F. (Gast)


Lesenswert?

Veit D. schrieb:
> das ändert jetzt was am Platz der Klemmleisten außen herum?

Wie viele Pins hast Du nochmal an Deinem ATTiny841?

von Carl D. (jcw2)


Lesenswert?

Da wäre auch Platz für 2x 8-Fach-Mux um bis zu 16 Servos per HW ohne 
Softwareverränkungen jitterfrei bekommt. Aber vielleicht lernt ja die 
Version 3 etwas aus der Software V2.2

von Dieter F. (Gast)


Lesenswert?

Carl D. schrieb:
> Da wäre auch Platz für 2x 8-Fach-Mux um bis zu 16 Servos per HW ohne
> Softwareverränkungen jitterfrei bekommt

Ja - da gibt es Beispiele.

Aber - unsportlich :-) - wobei ich das bei einem Pin mit 16 Servos und 
20 ms "Fenster" gerne sehen würde; (ein Pin bei max. 8 Servos mit max. 
2,5 ms/Servo wäre so meine Vorstellung) ...

Aber - zeig mal :-)

von Veit D. (devil-elec)


Lesenswert?

Hallo,

aus Eurer Sicht mag die Platine leer sein, aus meiner Sicht ist die 
schon gut gefüllt. Für Leiterbahnen muss ja auch Platz sein. Und solange 
ich außen herum nicht mehr Platz habe, macht es keinen Sinn innen mehr 
Platz zu schaffen. Das sollte einleuchten. Zudem THT Bauteile auch 
Vorteile beim routen haben. Ich werde nochmal darüber nachdenken, mal 
sehen was mir noch so einfällt, vielleicht verabschiede ich mich von den 
praktischen Steckklemmleisten, nur dann wäre mehr Platz. Es ist ja nicht 
so als wenn ich das mal soeben ohne Überlegungen hingerotzt hätte. Dafür 
sieht das viel zu gut aus.  :-)
Auf der anderen Seite hatte ich auch schon angeführt, macht es keinen 
echten Sinn mehr Pins pro Slave zu haben, sonst ziehe ich wieder endlose 
Drähte unter der Anlage von A nach B.


Zurück zur Softwarethematik.
Auch auf die Gefahr hin das dieses Thema ein heißes Eisen ist.

Was war falsch daran den Code als C++ kompilieren zulassen?
C++ wird laut meiner Meinung ständig weiterentwickelt. Version C++ 18 
steht in den Startlöchern. C ist mit Version C11 irgendwie stehen 
geblieben.

Zudem ich angefangen habe das Buch "C++ Die Programmiersprache" von 
Bjarne Stroustrup zu lesen. Hier stehen sehr informative Dinge drin. 
Irgendwann möchte auch Templates usw. anwenden.

von Falk B. (falk)


Lesenswert?

@ Veit Devil (devil-elec)

>Was war falsch daran den Code als C++ kompilieren zulassen?

Sagte ich bereits.

Beitrag "Re: ISR Code schneller machen?"

>C++ wird laut meiner Meinung ständig weiterentwickelt. Version C++ 18
>steht in den Startlöchern. C ist mit Version C11 irgendwie stehen
>geblieben.

Wenn du das "alte C" einigermaßen gut beherrscht, kannst du fast alle 
machen.

>Zudem ich angefangen habe das Buch "C++ Die Programmiersprache" von
>Bjarne Stroustrup zu lesen. Hier stehen sehr informative Dinge drin.
>Irgendwann möchte auch Templates usw. anwenden.

Auf kleinen Mikrocontrollern? Vergiss es! Du kannst dir bestenfalls in 
Arduino-Manier ein paar nette Klassen mit Vererbung und bissel 
Polymophie zusammenbastlen, das reicht locker, um sehr gute, 
leistungsfähige Programme zu entwickeln.

Die Gefahr für dich und andere Anfänger/Fortgeschritte liegt bei C++ 
schlicht im deutlich größeren Funktionsumfang sowie der deutlich 
größeren Komplexität. Da kann man sich ganz fix verheddern und 
beschäftigt sich dann nur noch mit dem Werkzeug C++ anstatt dem 
eigentlichen Problem.

Uund wie immer gilt. SOLIDE Grundlagen sind durch NICHTS zu ersetzen. 
Grundlagen lernt man aber eher mit einfachen Werkzeugen wie C und nicht 
mit C++ oder Java. Das kleine 1x1 der Mathematik lernt man auch nicht 
mit dem Smartphone, im GEGENTEIL!

von Wilhelm M. (wimalopaan)


Lesenswert?

Veit D. schrieb:

> Zurück zur Softwarethematik.
> Auch auf die Gefahr hin das dieses Thema ein heißes Eisen ist.

In der Tat ;-)

> Was war falsch daran den Code als C++ kompilieren zulassen?

Hatte ich bereits geschrieben: ggf. UB bei unions.

Ansonsten: m.E. nur Vorteile!

> C++ wird laut meiner Meinung ständig weiterentwickelt.

Ist wirklich so.

> Version C++ 18
> steht in den Startlöchern.

Nein. C++20. Der Release-Zyklus ist ab C++11 alle 3 Jahre.

von Dieter F. (Gast)


Lesenswert?

Wilhelm M. schrieb:
> Nein. C++20. Der Release-Zyklus ist ab C++11 alle 3 Jahre

C steht still - seit der Steinzeit?

Nein - merkwürdig ...

https://www.gnu.org/software/gcc/releases.html

von Wilhelm M. (wimalopaan)


Lesenswert?

Falk B. schrieb:

> Auf kleinen Mikrocontrollern? Vergiss es!

Falsch!
Richtig gemacht, d.h. eben nicht so wie Arduino, ist es definitiv 
zero-overhead.

> Du kannst dir bestenfalls in
> Arduino-Manier ein paar nette Klassen mit Vererbung und bissel
> Polymophie zusammenbastlen,

Wenn möglich sollte man dynamische Polymorphie vermeiden. Man kann 
(meistens) alles in statische Polymorphie umformen lassen, d.h mit 
Template-Meta-Programmierung. Auf die Art spart man sich V-Tables im Ram 
und zusätzliche Indirektionen.

> Die Gefahr für dich und andere Anfänger/Fortgeschritte liegt bei C++
> schlicht im deutlich größeren Funktionsumfang sowie der deutlich
> größeren Komplexität.

Was m.E. sehr wichtig ist, aber von den meisten Leuten übersehen wird, 
ist, dass es ohne Overhead möglich ist, sich ein "reiches" Typsystem zu 
schaffen, mit dem man schon zur Compile-Zeit viele Fehler ausschließen 
kann. Leider denken viele bei C++ zunächst an OOP im klassichen Sinn, 
also dyn. Polymorphie und Liskov. Das ist aber in der µC-Welt nur 
nachrangig. Viel wichtiger ist stat. Polymorphie (templates) und 
Typ-Algebren/-Systeme (Template-Meta-Programmierung). Zugegeben: das 
lernt man nicht in 21 Tagen und es weicht vom gängigen Programmierschema 
eben stark ab.

> Uund wie immer gilt. SOLIDE Grundlagen sind durch NICHTS zu ersetzen.
> Grundlagen lernt man aber eher mit einfachen Werkzeugen wie C und nicht
> mit C++ oder Java. Das kleine 1x1 der Mathematik lernt man auch nicht
> mit dem Smartphone, im GEGENTEIL!

Wer C++ wirklich lernen will, und das heißt dann neben der OOP auch 
funktionale und generische Programmierung, der sollte m.E. zunächst gar 
nicht C lernen ("stop teaching C") und er sollte nicht versuchen, C++ in 
Verbindung mit µC zu lernen. C++ Stärke ist die Abstraktionsmöglichkeit, 
da ist ein µC zunächst nur hinderlich. Das lernt man mit einer guten IDE 
am PC - und natürlich mit guten Büchern, die man allerdings auch lesen 
muss ;-)

von Dieter F. (Gast)


Lesenswert?

Wilhelm M. schrieb:
> Was m.E. sehr wichtig ist, aber von den meisten Leuten übersehen wird,
> ist, dass es ohne Overhead möglich ist, sich ein "reiches" Typsystem zu
> schaffen, mit dem man schon zur Compile-Zeit viele Fehler ausschließen
> kann. Leider denken viele bei C++ zunächst an OOP im klassichen Sinn,
> also dyn. Polymorphie und Liskov. Das ist aber in der µC-Welt nur
> nachrangig. Viel wichtiger ist stat. Polymorphie (templates) und
> Typ-Algebren/-Systeme (Template-Meta-Programmierung). Zugegeben: das
> lernt man nicht in 21 Tagen und es weicht vom gängigen Programmierschema
> eben stark ab.

Sei mir nicht böse - aber was will ich ich auf einem (Hardare-nahen) MC 
mit C++. Polymorphie etc. Diese sind nicht unbedingt angebracht auf 
einem 8-Bit-Prozessor - oder?

von Wilhelm M. (wimalopaan)


Lesenswert?

Dieter F. schrieb:
> Wilhelm M. schrieb:
>> Was m.E. sehr wichtig ist, aber von den meisten Leuten übersehen wird,
>> ist, dass es ohne Overhead möglich ist, sich ein "reiches" Typsystem zu
>> schaffen, mit dem man schon zur Compile-Zeit viele Fehler ausschließen
>> kann. Leider denken viele bei C++ zunächst an OOP im klassichen Sinn,
>> also dyn. Polymorphie und Liskov. Das ist aber in der µC-Welt nur
>> nachrangig. Viel wichtiger ist stat. Polymorphie (templates) und
>> Typ-Algebren/-Systeme (Template-Meta-Programmierung). Zugegeben: das
>> lernt man nicht in 21 Tagen und es weicht vom gängigen Programmierschema
>> eben stark ab.
>
> Sei mir nicht böse - aber was will ich ich auf einem (Hardare-nahen) MC
> mit C++. Polymorphie etc. Diese sind nicht unbedingt angebracht auf
> einem 8-Bit-Prozessor - oder?

Sorry, hast Du das obige gelesen ... und verstanden?

Welche Art von Polymorphie meinst Du(!) denn?

von Dieter F. (Gast)


Lesenswert?

Wilhelm M. schrieb:
> Sorry, hast Du das obige gelesen ... und verstanden?
>
> Welche Art von Polymorphie meinst Du(!) denn?

Erkläre es bitte mal :-) - bin ich wahrscheinlich zu unbedarft ...

von Veit D. (devil-elec)


Lesenswert?

Hallo,

>> Zurück zur Softwarethematik.
>> Auch auf die Gefahr hin das dieses Thema ein heißes Eisen ist.

> In der Tat ;-)

solange es sachlich bleibt wie bis jetzt sollten alle damit kein Problem 
haben.

Wenn in C keine Klassen mit Vererbung möglich ist, dann habe ich ein 
Problem. Denn ich habe eine Klasse mit Vererbung schon fertig. Das müßte 
ich demzufolge zurück in Funktionen "zerlegen"?

Wilhelm. Ich merke du bist C++ Programmierer. Ich lese auch daraus, dass 
C++ nur Sinn bei PC Anwendungen bringt. Da ich nur meine µC 
programmieren werde, macht da aus deiner Sicht C++ noch Sinn? Weil ich 
bin auch der Meinung das man C und C++ beim erlernen nicht mischen 
sollte. Momentan stecke ich dummerweise dazwischen ohne es richtig 
bemerkt zu haben und muss mich entscheiden um irgendwann programmieren 
zu können. Egal ob das dann C oder C++ wird.

: Bearbeitet durch User
von Wilhelm M. (wimalopaan)


Lesenswert?

Veit D. schrieb:
> Hallo,
>
>>> Zurück zur Softwarethematik.
>>> Auch auf die Gefahr hin das dieses Thema ein heißes Eisen ist.
>
>> In der Tat ;-)
>
> solange es sachlich bleibt wie bis jetzt sollten alle damit kein Problem
> haben.
>
> Wenn in C keine Klassen mit Vererbung möglich ist, dann habe ich ein
> Problem. Denn ich habe eine Klasse mit Vererbung schon fertig. Das müßte
> ich demzufolge zurück in Funktionen "zerlegen"?
>
> Wilhelm. Ich merke du bist C++ Programmierer. Ich lese auch daraus, dass
> C++ nur Sinn bei PC Anwendungen bringt.

Habe ich mich oben so undeutlich ausgedrückt. C++ macht m.E. auf µC 
absolut Sinn! Ich programmieren nur in C++. Aber eben in einer 
speziellen Art - hatte ich oben kurz angerissen. Wie gesagt, der 
Schlüssel zum Erfolg liegt in stat. Polymorphie, d.h. generischer 
Programmierung mit templates. Und eine besondere Variante sind sog. 
Meta-Funktionen, die die wiederum der Template-Meta-Programmierung 
bedient.

> Da ich nur meine µC
> programmieren werde, macht da aus deiner Sicht C++ noch Sinn?

Ja.

von Dieter F. (Gast)


Lesenswert?

Wilhelm M. schrieb:
> Habe ich mich oben so undeutlich ausgedrückt. C++ macht m.E. auf µC
> absolut Sinn! Ich programmieren nur in C++. Aber eben in einer
> speziellen Art - hatte ich oben kurz angerissen. Wie gesagt, der
> Schlüssel zum Erfolg liegt in stat. Polymorphie, d.h. generischer
> Programmierung mit templates. Und eine besondere Variante sind sog.
> Meta-Funktionen, die die wiederum der Template-Meta-Programmierung
> bedient.

Ja :-)

von Michael R. (Firma: Brainit GmbH) (fisa)


Lesenswert?

Veit D. schrieb:
> Egal ob das dann C oder C++ wird.

Zuerst C, und dann wende dich von den stark typisierten Sprachen ab 
<duckundwegrenn>

von Falk B. (falk)


Lesenswert?

@Wilhelm M. (wimalopaan)

>> Auf kleinen Mikrocontrollern? Vergiss es!

>Falsch!
>Richtig gemacht, d.h. eben nicht so wie Arduino, ist es definitiv
>zero-overhead.

Kann sein, weiß ich nicht, hab von C++ nicht wirklich Ahnung ;-)
Das ist aber gar nicht der Punkt.

>Wenn möglich sollte man dynamische Polymorphie vermeiden. Man kann
>(meistens) alles in statische Polymorphie umformen lassen, d.h mit
>Template-Meta-Programmierung. Auf die Art spart man sich V-Tables im Ram
>und zusätzliche Indirektionen.

Glaubst du ernsthaft, daß der OP auf DEM Level ist, um mit diesem Zeug 
was anfangen kann? Als Hobbybastler?

>> Die Gefahr für dich und andere Anfänger/Fortgeschritte liegt bei C++
>> schlicht im deutlich größeren Funktionsumfang sowie der deutlich
>> größeren Komplexität.

>Was m.E. sehr wichtig ist, aber von den meisten Leuten übersehen wird,
>ist, dass es ohne Overhead möglich ist, sich ein "reiches" Typsystem zu
>schaffen, mit dem man schon zur Compile-Zeit viele Fehler ausschließen
>kann.

Dito! Das berfordert in der Situation mehr als es hilft!

>Leider denken viele bei C++ zunächst an OOP im klassichen Sinn,
>also dyn. Polymorphie und Liskov. Das ist aber in der µC-Welt nur
>nachrangig. Viel wichtiger ist stat. Polymorphie (templates) und
>Typ-Algebren/-Systeme (Template-Meta-Programmierung). Zugegeben: das
>lernt man nicht in 21 Tagen und es weicht vom gängigen Programmierschema
>eben stark ab.

AHA!

>Wer C++ wirklich lernen will,

Will das der OP? Braucht das der OP? Nein!

>Verbindung mit µC zu lernen. C++ Stärke ist die Abstraktionsmöglichkeit,
>da ist ein µC zunächst nur hinderlich.

AHA!

> Das lernt man mit einer guten IDE
>am PC - und natürlich mit guten Büchern, die man allerdings auch lesen
>muss ;-)

Und durcharbeiten = denken!

C++ ist für die meisten uCs und Hobbybastler genauso daneben wie 
Assembler!  Assembler kann zu wenig, C++ viel zu viel!

von Falk B. (falk)


Lesenswert?

@Veit Devil (devil-elec)

>Wenn in C keine Klassen mit Vererbung möglich ist,

In C gab es noch nie Klassen und Vererbung, nur schnöde Funktionen.

> dann habe ich ein
>Problem. Denn ich habe eine Klasse mit Vererbung schon fertig.

Wirklich? Zeig mal.

> Das müßte ich demzufolge zurück in Funktionen "zerlegen"?

Kann sein.

>sollte. Momentan stecke ich dummerweise dazwischen ohne es richtig
>bemerkt zu haben und muss mich entscheiden um irgendwann programmieren
>zu können. Egal ob das dann C oder C++ wird.

In der Tat!

von Veit D. (devil-elec)


Angehängte Dateien:

Lesenswert?

Hallo,

so sieht das aus. Beim programmieren stehe ich sicherlich noch am 
Anfang, dass gebe ich gern zu. Den "normalen" Encoder (nicht den für 
Alps) brauche ich mehrfach pro µC. Der Gray Code basiert auf dem von 
Peter Dannegger.

: Bearbeitet durch User
von Wilhelm M. (wimalopaan)


Lesenswert?

Falk B. schrieb:

>>Wenn möglich sollte man dynamische Polymorphie vermeiden. Man kann
>>(meistens) alles in statische Polymorphie umformen lassen, d.h mit
>>Template-Meta-Programmierung. Auf die Art spart man sich V-Tables im Ram
>>und zusätzliche Indirektionen.
>
> Glaubst du ernsthaft, daß der OP auf DEM Level ist, um mit diesem Zeug
> was anfangen kann? Als Hobbybastler?

Nein, derzeit kann er nicht mal richtig C. Aber das ist nicht 
entscheidend: ich traue es zunächst mal jedem zu ;-)

>>Was m.E. sehr wichtig ist, aber von den meisten Leuten übersehen wird,
>>ist, dass es ohne Overhead möglich ist, sich ein "reiches" Typsystem zu
>>schaffen, mit dem man schon zur Compile-Zeit viele Fehler ausschließen
>>kann.
>
> Dito! Das berfordert in der Situation mehr als es hilft!

Und genau das ist m.E. ein Trugschluß: Programme, die nicht compilieren, 
weil sie fehlerhaft sind, sind besser als Programme, die compilieren und 
zur Laufzeit fehlerhaft sind.


>>Leider denken viele bei C++ zunächst an OOP im klassichen Sinn,
>>also dyn. Polymorphie und Liskov. Das ist aber in der µC-Welt nur
>>nachrangig. Viel wichtiger ist stat. Polymorphie (templates) und
>>Typ-Algebren/-Systeme (Template-Meta-Programmierung). Zugegeben: das
>>lernt man nicht in 21 Tagen und es weicht vom gängigen Programmierschema
>>eben stark ab.
>>Wer C++ wirklich lernen will,
>
> Will das der OP? Braucht das der OP? Nein!

Ob er es braucht, weiß ich nicht. Aber wenn er es will: warum nicht. 
Doch sollte er dabei völlig losgelöst von irgendwelchen µC 
Fragestellungen es zunächst in einer normalen Laufzeitumgebung (aka PC) 
erlernen.

>>Verbindung mit µC zu lernen. C++ Stärke ist die Abstraktionsmöglichkeit,
>>da ist ein µC zunächst nur hinderlich.
>
> AHA!

Die Betonung lag auf: zunächst! Hat man diese Möglichkeiten erst einmal 
begriffen, kann man sie sehr gewinnbringend auch auf dem µC einsetzen 
(s.o. reiches Typsystem).

>> Das lernt man mit einer guten IDE
>>am PC - und natürlich mit guten Büchern, die man allerdings auch lesen
>>muss ;-)
>
> Und durcharbeiten = denken!

Ganz genau: und die Menge der guten(!) Bücher ist sehr begrenzt.

> C++ ist für die meisten uCs und Hobbybastler genauso daneben wie
> Assembler!  Assembler kann zu wenig, C++ viel zu viel!

Ich denke, man sollte grob Assembler lesen können. Schreiben braucht man 
es m.E. nicht.

von Carl D. (jcw2)


Lesenswert?

Dieter F. schrieb:
> Carl D. schrieb:
>> Da wäre auch Platz für 2x 8-Fach-Mux um bis zu 16 Servos per HW ohne
>> Softwareverränkungen jitterfrei bekommt
>
> Ja - da gibt es Beispiele.
>
> Aber - unsportlich :-) - wobei ich das bei einem Pin mit 16 Servos und
> 20 ms "Fenster" gerne sehen würde; (ein Pin bei max. 8 Servos mit max.
> 2,5 ms/Servo wäre so meine Vorstellung) ...
>
> Aber - zeig mal :-)

Bekanntlich hat der Timer1 2 Compare-Kanäle, deshalb auch 2x 1:8 Mux.

von Wilhelm M. (wimalopaan)


Lesenswert?

Veit D. schrieb:
> Hallo,
>
> so sieht das aus. Beim programmieren stehe ich sicherlich noch am
> Anfang, dass gebe ich gern zu. Den "normalen" Encoder (nicht den für
> Alps) brauche ich mehrfach pro µC. Der Gray Code basiert auf dem von
> Peter Dannegger.

Was sind denn Deiner Ansicht nach Getter? Vllt sollte man sie besser 
Beobachter nennen (nein, ich meine nicht das Observer-Pattern). Hast 
schon mal die verschiedenen Bedeutungen von const gehört?

Möchtest Du Deine Klasse mit dyn. Polymorphie verwenden? Dann ist sie 
klassisch falsch.

Aber ich glaube, dieser Thread sollte nicht als Grundkurs C++ 
"missbraucht" werden. Vllt machst Du einen neuen auf?

von Falk B. (falk)


Lesenswert?

@Veit Devil (devil-elec)

>    GrayEncoder.h (1,73 KB, 0 Downloads) | Codeansicht
>    GrayEncoder.cpp (1,54 KB, 0 Downloads) | Codeansicht

Soso, ein "Klasse". ;-)

Das ist un gefähr so, wie wenn ich ne Tütensuppe koche und mich dann als 
"Koch" bezeichnen würde . . .

>so sieht das aus. Beim programmieren stehe ich sicherlich noch am
>Anfang, dass gebe ich gern zu.

Eben darum ist C++ gar nichts für dich.

> Den "normalen" Encoder (nicht den für
>Alps) brauche ich mehrfach pro µC. Der Gray Code basiert auf dem von
>Peter Dannegger.

Ja und? Wo braucht man da C++? Das kriegt man mit plain, old C SPIELEND 
hin! Sogar in Mehrfachausführung!

Beitrag "Re: Mehrere Drehencoder gleichzeitig abfragen"

von Falk B. (falk)


Lesenswert?

@Wilhelm M. (wimalopaan)

>Aber ich glaube, dieser Thread sollte nicht als Grundkurs C++
>"missbraucht" werden. Vllt machst Du einen neuen auf?

Weise Worte. Danke.

von Wilhelm M. (wimalopaan)


Lesenswert?

Falk B. schrieb:

> Eben darum ist C++ gar nichts für dich.

Warum so abfällig?

Ich bin nur der Meinung, er sollte C++ eben nicht versuchen in µC 
Anwendungen zu erlernen (s.o.).

>> Den "normalen" Encoder (nicht den für
>>Alps) brauche ich mehrfach pro µC. Der Gray Code basiert auf dem von
>>Peter Dannegger.
>
> Ja und? Wo braucht man da C++? Das kriegt man mit plain, old C SPIELEND
> hin! Sogar in Mehrfachausführung!

Ich denke, dass weiß er. Es geht ihm um den Lerneffekt ...

von Falk B. (falk)


Lesenswert?

@ Wilhelm M. (wimalopaan)

>> Eben darum ist C++ gar nichts für dich.

>Warum so abfällig?

Weil es so ist! Gibt man einem 3 Jährigen, der Spaß an Musik hat, ein 50 
Mann Orchester?

>Ich bin nur der Meinung, er sollte C++ eben nicht versuchen in µC
>Anwendungen zu erlernen (s.o.).

Er will aber nur uCs programmieren!

Kennst du keinen Rückwärtsgang?

>> Ja und? Wo braucht man da C++? Das kriegt man mit plain, old C SPIELEND
>> hin! Sogar in Mehrfachausführung!

>Ich denke, dass weiß er. Es geht ihm um den Lerneffekt ...

Das glaube ich nicht. Er ist verliebt in den "SchickMicki" von C++.
Ist ja auch viel cooooler und plain old C was für Doofe.
Würde ich mal so interpretieren.

von Carl D. (jcw2)


Lesenswert?

Wilhelm M. schrieb:
> Falk B. schrieb:
>
>> Eben darum ist C++ gar nichts für dich.
>
> Warum so abfällig?
>
> Ich bin nur der Meinung, er sollte C++ eben nicht versuchen in µC
> Anwendungen zu erlernen (s.o.).
.
>>> Den "normalen" Encoder (nicht den für
>>>Alps) brauche ich mehrfach pro µC. Der Gray Code basiert auf dem von
>>>Peter Dannegger.
>>
>> Ja und? Wo braucht man da C++? Das kriegt man mit plain, old C SPIELEND
>> hin! Sogar in Mehrfachausführung!
>
> Ich denke, dass weiß er. Es geht ihm um den Lerneffekt ...

Es spricht doch nichts dagegen einfache Klassen-Hierachien zu bauen. 
Blos weil das in C noch geht, soll man es in C++ nicht machen? Wie 
sonst, wenn nicht mit einfachen Beispielen soll man Neues lernen. Was 
bei "Lernen an Kompliziertem" rauskommt, kann man hier ja lesen: 
Ablehnung.

von Falk B. (falk)


Lesenswert?

@ Carl Drexler (jcw2)

>Es spricht doch nichts dagegen einfache Klassen-Hierachien zu bauen.
>Blos weil das in C noch geht, soll man es in C++ nicht machen?

Klassen in C? Hab ich was verpaßt?

> Wie
>sonst, wenn nicht mit einfachen Beispielen soll man Neues lernen.

Der OP sollte erstmal ein paar mehr solide Grundlagen lernen als sich 
mit "SchickiMicki" abzulenken.

> Was
>bei "Lernen an Kompliziertem" rauskommt, kann man hier ja lesen:
>Ablehnung.

Zu Recht. Lernen tut man schrittweise erstmal an einfachen Beispielen 
mit stiegendem Schwierigkeitsgrad. Man fängt in der 1.Klasse an und 
nicht beim Abitur.

von Veit D. (devil-elec)


Lesenswert?

Hallo,

Getter Methoden sind doch Standard um irgendwelche Werte abzuholen etc.

Nur warum das obige keine Klasse sein soll verstehe ich nicht. Was ist 
"plain"? Noch nie davon gehört. Habe mal gegoogelt. Gibts in C gar 
nicht.

: Bearbeitet durch User
von Falk B. (falk)


Lesenswert?

@Veit Devil (devil-elec)

>ist der Fall wo klar. Nur warum das obige keine Klasse sein soll
>verstehe ich nicht.

Das ist bestenfalls ein Grundgerüst einer Klasse. Aber für die Lösung 
deiner Aufgabe, mehreren Drehgeber abzufragen braucht man das 
Werkzeug "Klasse" nicht. Ist ungefähr so wie wenn man sagt, man braucht 
unbedingt ein Smartphone mit Navi, um von Dresden nach Berlin zu fahren 
8-0

> Was ist "plain"? Noch nie davon gehört. Habe mal
>gegoogelt. Gibts in C gar nicht.

Willst du mich verarschen? "plain" ist was es im Englischen immer war. 
"einfach"

plain, old C ist einfaches, altes C. ;-)

von Dieter F. (Gast)


Lesenswert?

C++ - Alarm!!

Ihr habt recht!

Ist O. K., wenn ihr jetzt geht ... kommt nur noch uninterressantes ... 
:-)

Beitrag #5267537 wurde von einem Moderator gelöscht.
Beitrag #5267542 wurde von einem Moderator gelöscht.
von Veit D. (devil-elec)


Lesenswert?

Hallo,

Falk, sorry, ich möchte niemanden verarschen, war nur dermaßen mit all 
den C vs. C++ beschäftigt, dass ich nicht mehr unterscheiden konnte.

Naja, irgendwo hat unser C Liebhaber auch recht. Letztenendes macht 
jeder Compiler daraus Assembler bzw. dann Maschinencode. Nur sind 
Hochsprachen einfacher zu erlernen und zu lesen. Man muss sich nicht um 
jedes Bit selbst kümmern. Meine persönliche Meinung. Zudem ich schon 
soweit in C stecke, dass ich mir bestimmte Dinge nicht vorstellen kann 
wie man die in Assembler schreiben soll mit dem begrenzten Befehlssatz. 
Dabei muss man wie er schon sagte komplett anders denken, was mir jetzt 
schwer fallen würde komplett umzudenken. Zudem dass dann auch um Faktor 
x mehr Codezeilen werden, schon deshalb leidet die Lesbarkeit.

von Falk B. (falk)


Lesenswert?

@Veit Devil (devil-elec)

>Naja, irgendwo hat unser C Liebhaber auch recht.

Hat er nicht.

> Letztenendes macht
>jeder Compiler daraus Assembler bzw. dann Maschinencode.

Darum geht es gar nicht.

> Nur sind
>Hochsprachen einfacher zu erlernen und zu lesen.

Darum schon eher! Der entscheidende Vorteil von Hochsprachen ist, daß 
sich der Compiler um viel Verwaltungskram kümmert, Speicheraufteilung, 
Variablenverwaltung, Typprüfung etc. Krümelkram, der gut automatisierbar 
ist. Durch die größere Abstraktion kann man auch viel komplexere 
Probleme viel leichter und flexibler angehen! C ist, wenn man es 
halbwegs gescheit nutzt, plattformunabhängig. Assembler nie im Leben.

> Man muss sich nicht um
>jedes Bit selbst kümmern. Meine persönliche Meinung.

Eben.

> Zudem ich schon
>soweit in C stecke, dass ich mir bestimmte Dinge nicht vorstellen kann
>wie man die in Assembler schreiben soll mit dem begrenzten Befehlssatz.

Man muss jeden Krümlkram von der Pike auf neu erfinden. Viel Spaß!

von Veit D. (devil-elec)


Lesenswert?

Hallo,

da hier sowieso die Fronten aufeinander prallen, macht eine weitere 
Diskussion zwischen Assembler und C keinen Sinn. Ich denke jeder soll in 
der Sprache programmieren die für ihn am leichtestens erlernbar ist. Man 
hat ja freie Wahl. Ich sehe es auch nicht für schlimm an, wenn man aus 
Speichermangel einen größeren µC nimmt. Warum manche Ausführungen so arg 
knapp ausgestattet sind weiß wohl nur der Hersteller.

Für mich nehme ich mit, ich muss mich alleine zwischen C oder C++ 
entscheiden.

Ich bedanke mich bei allen für die aufschlussreiche Unterhaltung zum 
Thema Programmiersprachen. Ich werde den Thread nochmal überfliegen, 
paar Themen hatte ich bewusst zurückgestellt. Falls die noch wichtig 
sind greife ich diese auf. Ansonsten war es das erstmal aus meiner 
Sicht.

Tschau
Veit

Beitrag #5267633 wurde von einem Moderator gelöscht.
Beitrag #5267647 wurde von einem Moderator gelöscht.
Beitrag #5267666 wurde von einem Moderator gelöscht.
Beitrag #5267679 wurde von einem Moderator gelöscht.
von Veit D. (devil-elec)


Lesenswert?

Hallo,

die letzten beiden gelöschten konnte ich nicht mehr lesen. Naja, ich 
werde es überleben. Falls die provokant gegen meine Person gerichtet 
waren, dann vielleicht besser so.

Die beiden gelöschten darüber darüber hatten wirklich keinen Grund zur 
Klage. War alles sachlich gewesen. Weiß auch nicht warum. Den letzten 
Beitrag von Dieter hätte er in dem Zuge dann mit löschen können.

Ansonsten gibts aus dem Thread wirklich viel zum mitnehmen worüber ich 
mir Gedanken machen kann und sollte. Man muss nicht immer einer Meinung 
sein, man muss nur zuhören, den anderen verstehen wollen, mitdenken, 
sachlich bleiben. Wenn das alle machen hat jeder etwas davon. Aus meiner 
Sicht ist alles i.O.

Tschau
Veit

Beitrag #5267767 wurde von einem Moderator gelöscht.
von Np R. (samweis)


Lesenswert?

Wilhelm M. schrieb:
> Ganz genau: und die Menge der guten(!) Bücher ist sehr begrenzt.
Ich möchte den (interessanten) Thread ja nicht hijacken, aber wenn sie 
so begrenzt ist, dann wäre doch Platz für ein oder zwei Beispiele, oder?
Oder im Wiki...

von Wilhelm M. (wimalopaan)


Lesenswert?

np r. schrieb:
> Wilhelm M. schrieb:
>> Ganz genau: und die Menge der guten(!) Bücher ist sehr begrenzt.
> Ich möchte den (interessanten) Thread ja nicht hijacken, aber wenn sie
> so begrenzt ist, dann wäre doch Platz für ein oder zwei Beispiele, oder?
> Oder im Wiki...

Dafür erscheint es mir am besten, Du / jemand stellt eine konkrete Frage 
;-)

Und auch nicht in diesem Thread, sondern mach dazu einen eigenen auf. 
Wenn ich den Thread dann entdecke und Zeit habe, werde ich gerne 
antworten und eigene Vorschläge machen. Auf der anderen Seite gibt es 
hier ja schon einige andere Threads, in denen auch ich schon ne ganze 
Menge inkl. Beispiele gepostet habe. Vielleicht einfach mal da anfangen 
...

von Np R. (samweis)


Lesenswert?

Wilhelm M. schrieb:
> sondern mach dazu einen eigenen auf.
Nein.
Warum nicht? Weil:
Wilhelm M. schrieb:
> Auf der anderen Seite gibt es hier ja schon einige andere Threads, in
> denen auch ich schon ne ganze Menge inkl. Beispiele gepostet habe.
jeder Thread in diesem Forum, in dem jemand nach "guten" Büchern oder 
Wegen fragt, um C++ "richtig" (tm) zu erlernen, die Gefahr birgt, in 
einen Flame-War C vs. C++ auszuarten. Und Java, Assembler oder Tofu ist 
sowieso viel besser.

Der Grund, dass ich überhaupt gefragt habe, ist dieser:
Du hast mehrfach "gute Bücher" als Notwendigkeit bezeichnet. Ohne 
irgendeines beim Namen zu nennen, ist diese "Hilfestellung" für den TO 
und alle Mitleser wertlos und nur eine "Selbstbeweihräucherung".


Edit: Wobei ich sagen muss, dass dieser Thread für dieses Forum 
überraschend friedlich und konstruktiv verlaufen ist. Kompliment!

: Bearbeitet durch User
von Carl D. (jcw2)


Lesenswert?

np r. schrieb:
>
> Edit: Wobei ich sagen muss, dass dieser Thread für dieses Forum
> überraschend friedlich und konstruktiv verlaufen ist. Kompliment!

Es macht ja auch keinen Sinn mit bestimmten Teilnehmern diskutieren zu 
wollen, in der Zeit beschäftige ich mich lieber mit dem 
Diskussionsobjekt.

von Wilhelm M. (wimalopaan)


Lesenswert?

np r. schrieb:
> Wilhelm M. schrieb:
>> sondern mach dazu einen eigenen auf.
> Nein.
> Warum nicht? Weil:

Ok, Deine Entscheidung.

> Wilhelm M. schrieb:
>> Auf der anderen Seite gibt es hier ja schon einige andere Threads, in
>> denen auch ich schon ne ganze Menge inkl. Beispiele gepostet habe.
> jeder Thread in diesem Forum, in dem jemand nach "guten" Büchern oder
> Wegen fragt, um C++ "richtig" (tm) zu erlernen, die Gefahr birgt, in
> einen Flame-War C vs. C++ auszuarten. Und Java, Assembler oder Tofu ist
> sowieso viel besser.

Das ist aber m.E. kein Grund, nicht trotzdem das Thema anzuschneiden. 
Ansonsten ist es ja ein Bankrotterklärung.

> Der Grund, dass ich überhaupt gefragt habe, ist dieser:
> Du hast mehrfach "gute Bücher" als Notwendigkeit bezeichnet. Ohne
> irgendeines beim Namen zu nennen, ist diese "Hilfestellung" für den TO
> und alle Mitleser wertlos und nur eine "Selbstbeweihräucherung".

Hier ist extra ein Thread zu dem Thema:

Beitrag "Informationen zu C vs C++ / aka Futter für die Diskussion"

Und hier wurde auch schon viele besprochen:

Beitrag "AVR GPIOR Bit Verwaltung C++"

Und mit ein klein bisschen Suchen findest Du hier ne ganze Menge.

Als Bücher füge ich hinzu (wenn man schon ein Grundverständnis der 
Sprache hat):

Meyers: Effective modern C++

Kormanyos: RealTime C++

Josuttis: C++ Standard Library

Vandervoorde: C++-Templates

Pohmann: C++17

Zu TMP kenne ich wirklich kein gutes Buch. Die Ideen kann man sich aber 
anhand diverser aktuelle Vorträge auf CppCon15/16/17 abholen.

: Bearbeitet durch User
von Np R. (samweis)


Lesenswert?

Carl D. schrieb:
> Es macht ja auch keinen Sinn mit bestimmten Teilnehmern diskutieren zu
> wollen

Ohne Teilnehmer zu diskutieren kann aber sehr mühsam werden. ;-)
Wenn man ausschließlich mit Sachargumenten und nicht mit ad hominem 
diskutiert, gerät das Diskussionsobjekt auch nicht aus dem Blick.
Dann ist es auch egal, ob der Diskussionspartner C liebt oder hasst, 
Praktiker oder Hochschullehrer oder gestern 12 geworden ist.
Oder ob Veit sich nun (wie oben schon einmal anklang) gnadenlos 
bereichert hat und die Steuerung samt Software in Großserie verkauft. 
;-)

Wilhelm M. schrieb:
> Als Bücher füge ich hinzu
Danke!
Jeder Beitrag besteht ja naturgemäß in unterschiedlichen Anteilen aus 
Meinung und Information.
Hiermit hast Du Deiner Meinung die Information hinzugefügt. ;-)

von Veit D. (devil-elec)


Lesenswert?

np r. schrieb:

> Oder ob Veit sich nun (wie oben schon einmal anklang) gnadenlos
> bereichert hat und die Steuerung samt Software in Großserie verkauft.

Solch falsche Unterstellung verbitte ich mir. Völlig egal wie diese 
gemeint sind.

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Falk B. schrieb:
> @Veit Devil (devil-elec)
>>  case UART:  // UART0 wird benutzt
>>        handle_Serial_0_to_Serial_0();
>>        break;  // minimal 7,7µs ... maximal 18,2µs
>
> Das break setze ich immer in die gleiche Einrückung wie das case, denn
> es ist praktisch mit einer schließenden Klammer identisch.
>

Das break ist schon ok und die Einrückung entspricht üblichen 
Coding-Rules.

Case / break ist auch keine "Klammer": Während "break" eher wie eine 
normale Anweisung agiert, ist "case" eher als Code-Label anzusehen.  Zum 
Beispiel würde die Einrückung bei bedingtem break sein:
1
  case XXX:
2
        if (cond) {
3
           break;
4
        }
5
        // code
6
        // fallthru
7
  case YYY:
und nicht etwa:
1
  case XXX:
2
        if (cond) {
3
  break;
4
        }
5
        // code
6
        // fallthru
7
  case YYY:

von Np R. (samweis)


Lesenswert?

Veit D. schrieb:
> Solch falsche Unterstellung verbitte ich mir.

Öhm - meinst Du jetzt mich?

Beitrag #5269248 wurde von einem Moderator gelöscht.
Beitrag #5269251 wurde von einem Moderator gelöscht.
Beitrag #5269255 wurde von einem Moderator gelöscht.
Beitrag #5269357 wurde von einem Moderator gelöscht.
Beitrag #5269381 wurde von einem Moderator gelöscht.
Beitrag #5279147 wurde vom Autor gelöscht.
von Veit D. (devil-elec)


Angehängte Dateien:

Lesenswert?

Hallo,

als letzte öffentliche Ergänzung zum Thema zeige ich noch die verzögerte
Servopulsabschaltung. Wurde doch wieder notwendig, weil die Servos bei
leichten Gegendruck anfangen zu brummen, wollen ja gegen regeln.

Timer 1 ist auf 5ms konfiguriert. Setzt aller 5ms ein flag auf true.


das alles in die "Hauptschleife" eingebaut sieht dann so aus.
1
void handle_Function_UART0_Pins()
2
{
3
  //static state_usart state_USART_MODE = SYNC;      // Startbedingung
4
  static state_usart state_USART_MODE = FINISH;       // mit externen 8MHz Quarz
5
   
6
  switch (state_USART_MODE) {
7
    
8
  case USART:  // UART0 wird benutzt
9
               handle_Serial_0_to_Serial_0();
10
               if (countErrorsUART0 > 50000) {  // erstmal ne wilde Hausnummer
11
                  state_USART_MODE = ERROR;
12
               }
13
               
14
               turn_on_off_servos();
15
               if (flag_servos_updated == true) {
16
                  calc_ServoPositions();
17
                  flag_servos_updated = false;
18
               }
19
               break;

und wenn die Servos nicht abschalten sollen, dann nimmt man den
Funktionsaufruf 'turn_on_off_servos()' einfach raus. Die neue
Servoposition kommt über die serielle rein in 'servo[x].pulscount_New'

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.