Forum: Compiler & IDEs Taschenrechner Kommunikation


von Flo S. (tuxianer)


Angehängte Dateien:

Lesenswert?

Hi,
ich meld mich nochmal. Also beim Praxistest bin ich auf verschiedene 
Probleme gestoßen. Wenn ich mit beiden Rechnern zugleich Sende/Empfange, 
hängt sich eine Seite auf. Auch der Timeout greift nicht mehr. 
Theoretisch müsste es doch funktionieren...das Würde ja bedeuten, dass 
sich ein UART komplett aufhängt. Ich kann mir das nicht so ganz 
vorstellen!

Im Anhang nochmal der Code


Viele Grüße Florentin

von Peter D. (peda)


Lesenswert?

Florentin S. wrote:
> Hi,
> ich meld mich nochmal. Also beim Praxistest bin ich auf verschiedene
> Probleme gestoßen.

Aha, dann weiß ja gleich jeder, wer Du bist und wovon Du redest.

Im Forum gibts ja nur 10 neue Beiträge pro Jahr. Da muß natürlich jeder 
Leser jeden kennen, der mal was geschrieben hat.


Peter

von Flo S. (tuxianer)


Lesenswert?


von Philipp B. (philipp_burch)


Lesenswert?

Wie wär's, wenn du das alles kurz zusammenfassen würdest (Was hast du, 
was geht nicht)? Einfach so aus Nächstenliebe wird sich hier wohl 
niemand drei Threads durchlesen ;)

von Flo S. (tuxianer)


Lesenswert?

Also das Programm ermöglicht die Kommunikation zwischen 2 Rechnern aus 
dem Programm heraus, was sonst nicht funktioniert. Mann kann mit dem 
Rechner Variablen an den µC schicken oder diese Abfragen. Das 
Funktioniert wunderbar.   Es greift auch der Timeout bei falscher 
Kommunikation. Die alles funktioniert allerdings nur, wenn man nicht mit 
2 Rechnern zugleich auf den µC zugreift. Theoretisch müsste das auch 
klappen, da ja jedes Byte nacheinander Verarbeitet wird. Und der µC auch 
einen Puffer hat. Wenn ich mit 2 Rechnern zugleich sende greift jedoch 
auch der Timeout nicht. Warum auch immer.

von mox (Gast)


Lesenswert?

vlt wird der buffer ja überfüllt, weil der controller die daten nicht so 
schnell verarbeiten kann, wie er von den beiden gefüttert wird?

von Flo S. (tuxianer)


Lesenswert?

Naja also die 2 Uarts müssten ja theoretisch komplett unabhängig 
voneinander arbeiten. Und jeder UART hat ja 2 Byte Puffer. Zeichen 
können nur zwischen Cli und Sei verloren gehen. Und wenn in der Zeit was 
kommt müssten si ja gepuffert werden. Und 9600k ist meiner Meinung nach 
nicht so schnell im Gegensatz zu ca. 7 MHz. Komisch ist ja auch, dass nu 
ein UART ausfällt und der Time out auch nicht anspringt.

von Stefan B. (stefan) Benutzerseite


Lesenswert?

Zwei Sachen:

Programmstrategie
Du hast (exemplarisch) diese Stelle im Code
1
    else if(State0 == SEND_HEADER) 
2
    {
3
      // ...
4
5
      //Header Senden
6
      for (i=0; i<(50-1); i++)
7
      {
8
        Transmit0(SendHeader[i]);
9
        Summe0 += SendHeader[i];
10
      }
11
12
      // ...
13
    }

Die zerhaut dir dein Timing. In der äussersten Schleife arbeitest du 
Zeichen für Zeichen ab und jetzt kommst du irgendwann in deinem 
Protokoll an diese Stelle (und vergleichbare) und schickst einzeln, 
hintereinander mit "Riesenwartezeiten" Zeichen an nur eine UART raus. 
Kommen hier auf der anderen UART Zeichen rein, gehen die hoffnungslos 
verloren.

Du könntest dem auf mehrere Arten begegnen.

Einmal indem du einen Sendepuffer einrichtest (bzw. bei dem eh knappen 
RAM einen vorhandenen Puffer dafür verwendest - hier SendHeader[]) und 
wenn du dann in dem "Sendpuffer-Leer-Interrupt" die Daten rausschickst. 
Dadurch erhält die andere UART Gelegenheit Daten zu empfangen ohne 
Zeichen zu verlieren.

Du könntest auch wie beim Empfangen nur ein Zeichen pro Durchlauf in dem 
if(State0 == SEND_HEADER) Fall rausschicken und den State0 erst 
wechseln, wenn alle Zeichen raus sind.

Von beiden Lösungen halte ich die erste für geschickter. Die Delays beim 
Senden auf UART-"A" könnte (sollte) man mit dem sowieso vorhandenen und 
laufenden Timer behandeln. Das gibt dir noch bessere Performance beim 
Empfangen auf UART-"B"

Timer
Könntest du in einfachen Worten beschreiben, wie deine Timer arbeiten 
sollen?

Du hast je im Moment für die beiden UART-Kanäle zwei sehr 
unterschiedliche Timer aktiv - Timer0 ist ein 8-Bit-Timer und Timer1 ist 
ein 16-Bit Timer.

Meiner Ansicht nach würde es genügen, einen durchlaufenden Timer zu 
haben und im Programm anhand von gemerkten Startzeitpunkten (einer pro 
UART) und vorgegebenen Alarmwerten (dito.) zu prüfen, ob seit dem 
letzten Empfang oder Senden ein Timeout auf dem betreffenden UART-Kanal 
aufgetreten ist.

von Flo S. (tuxianer)


Lesenswert?

Ok vielen Danke erstmal für die Ausführliche Antwort. Also ich hab dich 
bestimmt mit der Variable Timer1 durcheinander gebracht. Ich verwende 
Timer1 und Timer2.
Die von dir angesprochene Stelle scheint wirklich Problematisch zu 
seien. Ich werde versuchen das Problem schnellstmöglich zu lösen. 
Allerdings ist das ja eine Stelle, die nur ausgeführt wird, wenn eine 
Variable angefordert wird. Das Problem tritt ja aber auch schon beim 
gleichzeitigen Senden auf. Also kann es nicht an der Stelle liegen. Das 
ist ja das merkwürdige.

von Flo S. (tuxianer)


Lesenswert?

Zum Timer:
Also die Timer erzeugen nach 2 Sekunden einen Interrupt. Bei jedem 
Empfangenen Zeichen wird er zurück gesetzt. Läuft er über wird der State 
auf IDLE gesetzt.

von Stefan B. (stefan) Benutzerseite


Lesenswert?

Aus welcher Sicht "gleichzeitigen Senden"?

AVR als Sender
Wenn aus der Sicht des µC kommt die Stelle schon in Frage. Z.B. das 
Senden auf UART-A blockiert Empfangen auf UART-A, Senden auf UART-B und 
Empfangen auf UART-B. Auch alle Timeout-Checks werden in der Zeit nicht 
gemacht. Möglicherweise läuft auch in der relativ langen, exklusiven 
Sendezeit für den 50-Byte Block (250-300 ms!) ein Timer über und der 
anschliessende Timeout-Check versagt.

Casios als Sender
Betrachtet man Senden aus Sicht der Casios sind die Delays von 4,35ms 
bei jedem zu Protokollbestätigungszeichen vom µC aus kritisch zu 
betrachten. Wie in einem anderen Thread berechnet, könntenbei den 
verwendeten 9600 Baud mehr als vier Zeichen ankommen. angenommen UART-A 
macht Handshake und UART-B wartet auf 50 Zeichen für den Header. Es ist 
IMHO sehr wahrscheinlich, dass UART-B die 50 Zeichen nicht fehlerfrei 
bekommt. Das in Zusammengang mit der ungeklärten Timeout-Geschichte...

von Flo S. (tuxianer)


Lesenswert?

Also geht darum, wenn ich mit 2 Casios zugleich sende. Also müsste ich 
die Wartezeiten durch einen Timer realisieren. Also kann ich ja für 
alles mit Zeit(Timeout, warten) etc. einen Timer verwenden, indem ich 
immer messe, wie viel zeit schon vergangen ist. Also Überlauf bei ner 
Sekunde und dann wie ne Uhr die zeit berechnen. Aber ob man das Auf ms 
genau hin bekommt?Das Warten kann ich ja so realisieren, dass es im loop 
der Main Schleife ggf. prüft ob die Warte Zeit schon verstrichen ist. So 
können auch weiterhin Zeichen verarbeitet werden.
Bleibt halt nur die Frage, warum das Timeout nicht funktioniert.

von Stefan B. (stefan) Benutzerseite


Lesenswert?

> Bleibt halt nur die Frage, warum das Timeout nicht funktioniert.

Ich denke das ist ein Copy&Paste-Fehler. Bei den beiden Timern des 
Atmega162 hast du diese Stelle drin, Kommentare von mir.
1
  // Je ein 8-Bit Timer pro UART
2
  TCCR0 |= (1<<CS00)|(1<<CS02); // Vorteiler 1024
3
  TCCR2 |= (1<<CS00)|(1<<CS02); // Vorteiler 128 !!!

Das bedeutet, auf UART0 hast du bei 7,3728 MHz den gewünschten ca. 2s 
Timeout. Aber auf UART1 beträgt der Timeout mehr als 16s.

Um bei Timer2 ebenfalls einen Vorteiler von 1024 einzustellen, müsste 
die Zeile so lauten:
1
  // Je ein 8-Bit Timer pro UART
2
  TCCR0 |= (1<<CS00)|(1<<CS02);           // Vorteiler 1024
3
  TCCR2 |= (1<<CS22)|(1<<CS21)|(1<<CS20); // Vorteiler 1024

von Flo S. (tuxianer)


Lesenswert?

Ok das wäre eine Sache. Nur ich hab noch mal probiert, eine Matrix etc. 
zu senden, wo der µC ja nix mit Anfangen kann und auch da hängt er sich 
auf.

von Stefan B. (stefan) Benutzerseite


Lesenswert?

Damit hast du eine dritte Baustelle aufgemacht.

1/ Aufhänger bei zwei Casios an einem AVR

2/ Timeout

3/ Aufhänger bei "illegalem" Input vom Casio zum AVR

Zu 3
Kannst du ausmachen, worin der Unterschied zwischen Variable und Matris 
besteht?

Und zu welchen Abweichungen bei der state machine führt das, d.h. in 
welchem state fährt sich AVR mit der Matrix fest?

Zu 3 und 2
Tritt das bei beiden UARTs auf oder kommt der AVR bei einer UART durch 
Timeout wieder raus?

von Flo S. (tuxianer)


Angehängte Dateien:

Lesenswert?

Hallo,
ich hab mich nochmal rangesetzt und konnte den "illegalen Input" 
verhindern. Der Timeout geht nicht. Ich möchte jetzt aber gleich die 
Pausen etc. alles mit Timer lösen. Wie mache ich das am besten. Ich habe 
mir das so gedacht, dass es sich die Startzeit merkt und dann bei Zeit 
Differenz xx weiter macht etc. Nur wie realisiere ich das. Ich müsste es 
ja wie eine Uhr aufbauen. Denn sonnst habe ich ja das Problem, das wenn 
ich eine Aktion kurz vor Timer Überlauf starte die Differenz nicht 
korrekt errechnen kann. Mit einer Uhr würde ich es aber nicht so genau 
hin bekommen. Also wie kann ich so einen globalen Timer realisieren?

Viele Grüße Florentin

von Flo S. (tuxianer)


Lesenswert?

Hat niemand ne Idee, wie ich den Globalen Timer realisieren kann?

von Stefan B. (stefan) Benutzerseite


Lesenswert?

Florentin S. wrote:
> Denn sonst habe ich ja das Problem, das wenn
> ich eine Aktion kurz vor Timer Überlauf starte die Differenz nicht
> korrekt errechnen kann.

Wieso?

Zeitpunkt1 = Timerstand1 + Timerumfang * Überlaufstand1;
//           208           256           0  => 208 Ticks

Zeitpunkt2 = Timerstand2 + Timerumfang * Überlaufstand2;
//           23            256           10 => 2583 Ticks

Vergangenezeit = Zeitpunkt2 - Zeitpunkt1;
//                                          => 2375 Ticks

Vergangenezeit = Timerstand2 + Timerumfang * Überlaufstand2 - 
Timerstand1 - Timerumfang * Überlaufstand1:

Vergangenezeit = (Timerstand2 - Timerstand1) + Timerumfang * 
(Überlaufstand2 - Überlaufstand1);
// (23 - 208) + 256 * (10 - 0)              => 2375 Ticks

und so weiter...

Anpassungen:

Will man immer positive Werte für (Timerstand2 - Timerstand1) und 
timerstand2 ist kleiner timerstand1, dann kann man künstlich einen 
Überlauf wegrechnen.

Vergangenezeit = ((Timerstand2+256) - Timerstand1) + Timerumfang * 
((Überlaufstand2-1) - Überlaufstand1);

(Timerstand2+256) - Timerstand1) entspricht aber gerade einem UND mit 
0xFF, wenn man unsigned char Werte ohne Addition von 256 subtrahiert
// 23=0x17 - 208=0xD0 => 0xFFFFF47 => & 0xFF => 0x47=71
// 71 * 256 ((10-1))-0)                      => 2375 Ticks

Wenn man nicht Multiplizieren möchte, kann man

+ Timerumfang * (Überlaufstand2 - Überlaufstand1) als mehrfache Addition 
implementieren.

Wenn man keine grossen Variablen möchte, kann man

für die Zeiten immer zwei getrennte Variablen führen (timerstand, 
überlaufstand) quasi wie man es mit der Uhrzeit macht: Stunden, Minuten, 
Sekunden statt alles in Sekunden seit der Landung in der Normandie 
umzurechnen.

Bsp: vergangenezeit:
unsigned char Timerstand1 = 208;
unsigned char Timerstand2 = 23;
unsigned char Überlaufstand1 = 0;
unsigned char Überlaufstand2 = 10;

if (timerstand1 > timerstand2)          // 208 > 23
  Überlaufstand1++;                     // 0+1=1
timerstand = Timerstand2 - Timerstand1; // 23_8Bit - 208_8Bit = 71_8Bit
überlaufstand = Überlaufstand2 - Überlaufstand1; // 10-1 = 9

// 2375 Ticks entspricht timerstand=71 überlaufstand=9

Löse dich vielleicht für ein paar Tage von dem Projekt Taschenrechner 
Kommunikation und implementiere einfach eine Uhr, die über RS232 alle 
10s die Laufzeit seit Programmstart ausgibt.

von Flo S. (tuxianer)


Lesenswert?

Also ich verstehe das jetzt so, ich lasse den Timer immer laufen und 
Zähle die Überlaufe, die der Timer schon gemacht hat. Oder die 
Überläufe, die seit dem ersten Zeitpunkt vergangen sind. So richtig 
verstehe ich das noch nicht. Es wäre ja dann aber trotzdem Möglich. das 
überlaufstand zu groß für die Variable ist.

von Stefan B. (stefan) Benutzerseite


Lesenswert?

Dann mach die Variable doch grösser, also z.B. 16-Bit statt 256 Bit.

Oder richte einen Überlauf für den Überlauf ein... Wie ist es denn bei 
der Uhr? Sekunden und der erste "Überlauf" sind die Minuten. Und wenn 
die überlaufen? Tja dann kommen als "Überlauf für den Überlauf" die 
Stunden und so weiter.

von Flo S. (tuxianer)


Lesenswert?

Ok also programmiere ich einfach eine Uhr mit ms-s-m-h dann merke ich 
mir die Zeit und errechne daraus die Differenz. Oder hasst du das anders 
gemeint?

Nu wenn ich damit rechnen will komme ich ja auch auf ne recht große 
Zahl, wenn ich da die Differenz in ms ausrechnen will.

Zeit 1:
30 Min, 10s, 50 ms

Zeit 2:
30 Min, 10s, 55 ms

Zeit=
((Min * 60)*100)+(Sek*100)+ms

Zeit2-Zeit1=181055-181050=5ms

Oder löst das C selbstständig und ich kann mit so Großen Werten rechnen?

von Flo S. (tuxianer)


Lesenswert?

noch was ich denke Ich werde das mit CTC und nem 16 Bit Timer lösen. Hab 
ich zwar noch nie gemacht, aber es gibt immer ein erstes Mal.

Ich habe 7372800 Hz

73728/7372800=0.01s
also eine ms.

Da die Auswertung des Überlaufs wie ich verstanden habe erst im nächsten 
Takt folgt muss ich den CTC auf 73727 setzten oder? Nur leider passt das 
nicht in den Timer. Kann ich ihn da bis 36862 zählen lasen und alle 2 
Resets eine ms erhöhen? Oder lieber Prescaler 8 also9 9216-1 ins CTC? 
Also ne möglichkeit auf µs genau zu machen habe ich noch nicht gefunden. 
Ich komme nie auf einen ganzzahligen Wert.

von Flo S. (tuxianer)


Lesenswert?

so ich habe den Code jetzt wie folgt ergänzt:
1
//Timer1 Compare Match Interrupt
2
ISR(TIMER1_COMPA_vect)
3
{
4
  //Milli Sekunden um 1 erhöhen
5
  ms++;
6
  //sind 100 ms um?
7
  if (ms==100) {
8
    //ms zurücksetzen und Sekunden erhöhen
9
    ms=0;
10
    s++;
11
    //sind 60 Sekunden um?
12
    if (s==60) {
13
      //Sekunden zurück setzen und Minuten erhöhen
14
      s=0;
15
      min++;
16
    }    
17
  }
18
}
19
20
21
  //CTC Modus & Vorteiler 8
22
  TCCR1B |= (1<<CS11)|(1<<WGM12);
23
  //Interrupt bei Compare Match
24
  TIMSK = (1<<OCIE1A);
25
  //Compare Wert setzen
26
  OCR1A = 9216-1;

von Gerhard (Gast)


Lesenswert?

Seit wann hat eine Sekunde 100 Millisekunden?

von Flo S. (tuxianer)


Lesenswert?

sind natürlich 1000 sorry für den tipp Fehler.
Ich habe jetzt auch eine Funktion geschrieben, welche die aktuelle Zeit 
speichert. Sie arbeitet mit Zeigern, was ich noch nie zuvor gemacht 
habe. Der Compiler spuckt auch eine Warnung aus:
"assignment makes integer from pointer without a cast"

hier der Code:
1
void Time (int *ms, uint8_t *s, uint8_t *min) {
2
*ms=ms;
3
*s=s;
4
*min=min;
5
}

und hier der Funktionsaufruf:
1
Time(&Timeout11_ms, &Timeout11_s, &Timeout11_min);

kann das sein, dass der Fehler daher kommt, weil die Speicheradresse zu 
groß für uint8_t ist?

von Stefan B. (stefan) Benutzerseite


Lesenswert?

Nee du hast zweimal (mal drei) den gleichen Variablennamen.

Beispiel:

ms ist deine globale Variable für die Millisekunden

und (!!!)

ms ist ein Pointer als dein lokales Funktionsargument in Time.

Innerhalb der Funktion Time fällt wegen Namensgleichheit die globale 
Variable weg, der Code sieht nur die zuletzt definierte lokale Variable 
mit gleichem Namen. Die Zuweisung ist dann Unfug und die Warnung kommt.

Schreib Time() so

void Time (int *pms, uint8_t *ps, uint8_t *pmin)
{
  *pms = ms;
  *ps = s;
  *pmin = min;
}

von Flo S. (tuxianer)


Angehängte Dateien:

Lesenswert?

Vielen dank ich habs vorhin auch durch probieren raus gefunden. Ich 
dachte ne Pointer Variable ist was anderes als ne normale. Egal. Auf 
jeden Fall funktioniert irgend etwas nicht so, wie es soll...auch der 
Timeout springt nicht an. Es ist vermutlich irgendwas an der 
Initialisierung falsch. Ich habe nochmal den kompletten Code angehangen:

von Stefan B. (stefan) Benutzerseite


Lesenswert?

Ich kann (und will) diesen Code nicht testen, sorry. Um an den 
eigentlich zu debuggenden Code für den Timer zu kommen, müsste ich mich 
durch die beiden UARTs durchbeissen. Das macht mir keinen Spass.

Du hättest es einfacher, wenn du den fraglichen Timercode in ein kleines 
Extraprojekt von übersichtlichen 30-50 Zeilen packst, dieses lauffähig 
machst und dann mit dem gewonnenen Wissen dein Hauptprojekt verbesserst.

von Flo S. (tuxianer)


Lesenswert?

ich habe jetzt mal die Waits probiert zu ersetzen. So geht es natürlich 
nicht:
1
      //Header Init0
2
      if(State0 == IDLE && c == 0x15) {
3
        //Zeit holen
4
        Time(&wait1_ms, &wait1_s, &wait1_min);
5
        if((((min*60)*1000)+(s*1000)+ms) - (((wait1_min*60)*1000)+(wait1_s*1000)+wait1_ms) > 5) {
6
          //Handshake
7
          Transmit0(0x13);
8
          //Status erhöhen
9
          State0 = REC_HEADER;
10
        }
11
      }

der Teil wird ja nur ausgeführt, wenn ein Byte empfangen wurde. Ich muss 
also eine Art warte Code machen, der das Byte dann sendet aber so lange 
noch andere bytes bearbeiten kann. Ich habe mir das so gedacht, dass ich 
noch eine While schleife um die Byteverarbeitung mache, welche immer 
solange ausgeführt wird, bis die warte Zeit um ist. Aber wie bekomme ich 
dass dann hin, das an der richtigen Stelle weiter gemacht wird?

von Flo S. (tuxianer)


Lesenswert?

oder komme ich doch besser wenn ich noch States ala Send_Ack rein mache 
und das ganze weiter aufdriesle?

von Flo S. (tuxianer)


Angehängte Dateien:

Lesenswert?

!Ok so klappt es...Nur wird der Code immer un übersichtlicher.

von Stefan B. (stefan) Benutzerseite


Lesenswert?

Aufdrieseln ist gut. Exemplarisch hast du diese Stelle im Code:
1
      //Request
2
      else if(State1 == REQUEST) {
3
      
4
        //Nur die 0x06 aufnhemen!
5
      
6
        State1 = SEND_HEADER;
7
        //WARTESCHLEIFE!!!!!!!
8
        _delay_ms(4.35);
9
      
10
      }

Jetzt kann die Statemachine so erweitert werden, dass es einen 
zusätzlichen Wartezustand gibt.
1
#if 0
2
// werden von Timer-Interrupt befüllt
3
// sollten volatile sein!
4
volatile int     ms;   // Millisekunden 
5
volatile uint8_t s;    // Sekunden
6
volatile uint8_t min;  // Minuten
7
//
8
// Die ganze Rechnung mit Minuten/Sekunden... sparen wir uns für 
9
// die kleinen Delays
10
// 
11
// Dafür brauchen wir einen einmal gestarteten Zähler für die 
12
// Millisekunden seit RESET (Timerstart). 
13
// uint32_t reicht für 49,7 Tage bis ein Überlauf kommt. 
14
// (Wenn nicht strategisch im Programm zurückgesetzt wird z.B. 
15
// wenn beide Statemachines im IDLE Modus sind)
16
//
17
volatile uint32_t total_ms; // wird wie ms einfach im IRQ hochgezählt
18
uint32_t startzeit1;
19
#endif
20
21
      //Request
22
      else if(State1 == REQUEST) {
23
        State1 = REQUEST_435_WARTEN;
24
        startzeit1 = totalms;
25
      }
26
      //Request
27
      else if(State1 == REQUEST_435_WARTEN) {
28
        // 4.35 leider unterhalb der Timer-Auflösung
29
        if ((totalms - startzeit1) >= 4) 
30
        {  
31
          // ggf. Verfeinern oder weglassen und 5 Millisekunden warten
32
          // was robuster beim Transfer µC <> Casio ist
33
          if ((totalms - startzeit1) == 4) 
34
            _delay_ms(0.35);
35
          State1 = SEND_HEADER;
36
        }
37
      }

In den Transmit1/Transmit0 Routinen, die ja eigene _delay_ms() haben, 
lässt sich das Warten so nicht behandeln. Dort muss eine andere Lösung 
her und zwar muss das Senden parallel zum Hauptprogramm laufen.

D.h. im Interrupt und es muss einen Puffer gehen (bzw. können die 
vorhandenen Puffer benutzt werden) und es muss eine Abfrage geben, ob 
der Puffer komplett gesendet wurde.

Grundsätzlich hat man die Wahl, welchen Interrupt man für das Senden 
benutzt. Man könnte auch den TX-Puffer-leer-Interrupt nehmen oder den 
TX-Zeichen-erfolgreich-verschickt-Interrupt.

Ich schlage aber vor, den vorhandenen Timer dafür zu benutzen. Das 
Senden erfolgt von dir (bzw. dem Casio Protokoll) vorgeheben 
grundsätzlich nur alle 4,35 ms. Das ist langsam und kann in jedem 4./5. 
Timer-Interrupt (der mit 1 ms Auflösung läuft) gut gemanagt werden. 
Nähme man einen der UART Interrupts, würden die sehr oft ohne eine 
Aktion zu machen aufgerufen, weil ja zuerst die Zeit abgewartet werden 
muss.

Im Hauptprogramm ändert sich der Teil in etwa (exemplarisch) so:
1
    //wenn status SEND_HEADER
2
    else if(State1 == SEND_HEADER) 
3
    {
4
      SendHeader[12-1] = VarName1; 
5
      // Umkopieren (bzw. mit Pointer arbeiten s.u.)
6
      for (i=0; i<(50-1); i++) 
7
        interruptsendepuffer1[i] = SendHeader[i]);
8
9
      // 50 Zeichen müssen raus
10
      // die folgende Zeile muss ein atomarer Zugriff sein, 
11
      // d.h. uint8_t interruptsendeanzahl1
12
      // oder Interrupts müssen gesperrt werden!
13
      interruptsendeanzahl1 = 50;
14
15
      // warten, aber der anderen Statemachine Gelegenheit 
16
      // zum Reagieren geben!
17
      State1 == SEND_HEADER_WARTEN;
18
    }
19
    else if(State1 == SEND_HEADER_WARTEN) 
20
    {
21
      // Das ist das neue Warten
22
      // nur weiterschalten, wenn Sendepuffer leer
23
      if (interruptsendeanzahl1 == 0)
24
        State1 == SEND_HEADER_CHECKSUMME;
25
    }
26
    else if(State1 == SEND_HEADER_CHECKSUMME) 
27
    {
28
      // Warten fertig, jetzt Checksumme anhängen
29
      uint8_t Summe1 = 0;        
30
      uint8_t Checksumme1;   
31
32
      for (i=0; i<(50-1); i++) 
33
        Summe1 += SendHeader[i];
34
     
35
      Checksumme1 = 0x3a;
36
      Checksumme1 -= Summe1;
37
38
      // wie oben
39
      interruptsendepuffer1[0] = Checksumme1;
40
      interruptsendeanzahl1 = 1;
41
      State1 == SEND_HEADER_CHECKSUMME_WARTEN;
42
    }
43
    else if(State1 == SEND_HEADER_CHECKSUMME_WARTEN) 
44
    {
45
      // Nur weiterschalten, wenn Sendepuffer leer
46
      if (interruptsendeanzahl1 == 0)
47
        State1 == ACK1;
48
    }

Das Senden im Timer-Interrupt sähe dann in etwa so aus:
1
//Timer1 Compare Match Interrupt
2
ISR(TIMER1_COMPA_vect)
3
{
4
  //Millisekunden um 1 erhoehen
5
  totalms++;
6
  ms++;
7
8
  //sind 1000 ms um?
9
  if (ms==1000) {
10
    //ms zurueck setzen und Sekunden erhoehen
11
    ms=0;
12
    s++;
13
    //sind 60 Sekunden um?
14
    if (s==60) {
15
      //Sekunden zurueck setzen und Minuten erhoehen
16
      s=0;
17
      min++;
18
    }    
19
  }
20
21
  // Transmit0 Ersatz
22
23
  // Kann grundsätzlich gesendet werden?
24
  // Wenn nicht sind wir schon fertig, 
25
  // und hoffen auf die nächste Millisekunde
26
  if ((UCSR0A & (1<<UDRE0))
27
  {
28
    // Ja wir dürfen. Jetzt prüfen, ob seit letztem Senden min. 
29
    // 4.35 ms vergangen sind
30
    // globale Variable uint32_t startms0 muss angelegt sein, Startwert 0!
31
    if (totalms - startms0 > 4)
32
    {
33
      // wir dürfen. Zeitpunkt merken für das nächste Zeichen
34
      startms0 = totalms;
35
36
      // Zeichen da?
37
      if (interruptsendeanzahl0)
38
      {
39
        // globale Variable int interruptsendezähler0 = 0 anlegen!
40
        UDR0 = interruptsendepuffer0[interruptsendezähler0++];
41
        interruptsendeanzahl0--;
42
      }
43
      else
44
        interruptsendezähler0 = 0;
45
    }
46
  }
47
48
  // Entsprechend der Transmit1 Ersatz
49
}

Wenn man Platz sparen muss, kann man wenn man vorsichtig ist mit 
Pointern arbeiten. Statt dem interruptsendepuffer1[] wird z.B. ein 
Pointer auf SendHeader[] gerichtet.

Ich hoffe du bekommst die Idee hinter dem Ganzen mit, auch wenn ich den 
Code nicht komplett ausformuliert habe.

von Stefan B. (stefan) Benutzerseite


Lesenswert?

Florentin S. wrote:
> !Ok so klappt es...Nur wird der Code immer un übersichtlicher.

Deswegen wurden Editoren erfunden, die den Code einklappen können. Wenn 
jemand mit etwas Gefühl für Ästhetik den Quellcode massiert, sieht das 
gleich besser aus. Das Aussehen sollte dich im Moment nicht stören. Das 
ist im Moment Work in Progress, kein Schönheitswettbewerb ;-)

von Flo S. (tuxianer)


Lesenswert?

also so wie du das im ersten Code hasst, so habe ich das ja hetzt auch 
implementiert. Ich musste es aber wie gesagt außerhalb der 
empfangsroutine anbringen, da diese ja nur ausgeführt wird, wenn ein 
Zeichen ankommt. Also wenn ich für die ms uint32_t verwende kann ich 
quasi Minuten und Sekunden gleich weglassen, wenn das so lange reicht. 
Das würde dann das Zeit holen auch noch mal vereinfachen. Das senden 
habe ich auch grob verstanden. Es schreibt die zu Sendenden Bytes immer 
in einen Puffer und beim Interrupt wird immer ein gesendet.

von Flo S. (tuxianer)


Lesenswert?

ich hab grad festgestellt, das man beim raussenden µC--GTR kein Wait
braucht. Nun wollte ich die ganzen Hadshakes beim empfangen 
zusammenfassen:
1
//Header Init0
2
      if(State0 == IDLE && c == 0x15) {
3
        //Zeit holen
4
        Wait1=ms;
5
        //Handshake
6
        Handshakepuffer0=0x13;
7
        Handshake0=1;
8
        if (Handshake0 == 0) {
9
          //Status erhöhen
10
          State0 = REC_HEADER;
11
        }
12
        
13
        
14
      }
1
//Timer1 Compare Match Interrupt
2
ISR(TIMER1_COMPA_vect)
3
{
4
  //Milli Sekunden um 1 erhöhen
5
  ms++;
6
  
7
  if (Handshake0 == 1) {
8
    
9
    if ((UCSR0A & (1<<UDRE0))) {
10
    
11
      if(ms-Wait1 > 5) {    
12
        Handshake0=0;
13
        UDR0 = Handshakepuffer0;
14
      }
15
    
16
    }
17
    
18
19
  }
20
}

was natürlich nicht geht, da der Bereich ja wieder nur ausgeführt wird, 
wenn ein Zeichen ankommt. Also nur einmal.

von Flo S. (tuxianer)


Angehängte Dateien:

Lesenswert?

Also das Senden seitens des µCs funktioniert leider nicht.
interruptsendeanzahl1 = 50; das müsste ja eigentlich 49 seinen. Das
klappt aber auch nicht so.
1
    //wenn status SEND_HEADER
2
    else if(State0 == SEND_HEADER) 
3
    {
4
      SendHeader[12-1] = VarName0; //Varriablennamen einfügen
5
      
6
      //Header Senden
7
      for (i=0; i<(50-1); i++)
8
      {
9
        interruptsendepuffer0[i] = SendHeader[i];
10
      }
11
      cli();
12
      interruptsendeanzahl0 = 50;
13
      sei();
14
      State0 = SEND_HEADER_WARTEN;
15
16
17
    }
18
    
19
    else if (State0 == SEND_HEADER_WARTEN) {
20
      if(interruptsendeanzahl0 == 0) {
21
        State0 = SEND_HEADER_CHECKSUMME;
22
      }
23
    }
24
    
25
    else if (State0 == SEND_HEADER_CHECKSUMME) {
26
      uint8_t Summe0;     
27
      uint8_t Checksumme0;   
28
      Summe0 = 0;
29
      
30
      for (i=0; i<(50-1); i++) 
31
      {
32
        Summe0 += SendHeader[i];
33
      }
34
      
35
      //Checksumme berrechnen und Senden
36
      Checksumme0 = 0x3a;
37
      Checksumme0 -= Summe0;
38
      
39
      interruptsendepuffer0[0] = Checksumme0;
40
      cli();
41
      interruptsendeanzahl0 = 1;
42
      sei()
43
      State0 = SEND_HEADER_CHECKSUMME_WARTEN;
44
    
45
    
46
    }
47
    
48
    else if (State0 == SEND_HEADER_CHECKSUMME_WARTEN) {
49
      if(interruptsendeanzahl0 == 0) {
50
        //Status auf ACK1 setzen
51
        State0 = ACK1;
52
      }      
53
54
    
55
    }

1
//Timer1 Compare Match Interrupt
2
ISR(TIMER1_COMPA_vect)
3
{
4
  //Milli Sekunden um 1 erhöhen
5
  ms++;
6
  
7
  if ((UCSR0A & (1<<UDRE0))) {
8
9
  
10
    if (interruptsendeanzahl0) {
11
    
12
      UDR0 = interruptsendepuffer0[interruptsendezahler0++];
13
      interruptsendeanzahl0--;
14
    
15
    } else {
16
      interruptsendezahler0 = 0;
17
    }
18
  
19
  }
20
}

Das Programm hängt sich auch nicht auf sondern sendet nur irgendwas 
falsch.

von Stefan B. (stefan) Benutzerseite


Angehängte Dateien:

Lesenswert?

Ich habe jetzt deine Source von 23.01.2008 23:28 genommen und einiges
geändert. Wenn du das nachvollziehen willst, aufpassen, dass du ein
Backup deiner Ur-Version machst!

Die Änderungen sind

- Das Senden geschieht jetzt in einem Sendeinterrupt der UARTs
- Jedes Senden geschieht im Interrupt. Die Transmit0/Transmit1 füttern
nur
  die Interruptroutine.
- Paar Variablen wurden eingespart und der Code wurde gestrafft
  ("Beautifiziert")
- viele Kommentare warum und wieso was gemacht wird

ADD:

Bei ungültigen Zeichen die einzelnen Variablennamen und Puffernamen in 
der Source mal checken. Bei dem vielen Copy&Paste zwischen UART0 und 
UART1 Teilen, flutscht gerne mal ein Schreifehler bei den 9/1 Anhängseln 
durch. An einer Stelle ist mir ein solcher Fehler aufgefallen (<== 
markiert), einen habe ich selbst fabriziert und dies ist die zweite 
Version des Uploads, eine musste ich löschen nachdem ich einen Fehler 
gesehen habe.

von Stefan B. (stefan) Benutzerseite


Lesenswert?

> Das Programm hängt sich auch nicht auf sondern sendet nur irgendwas
> falsch.

Wenn du keinen Fehler im Protokoll an sich meinst (Hänger beim 
Handshake, Hänger in der stete machine, Timeouts und 
Prozokollresets...), sondern falsche Datensätze, dann habe ich dazu 
inzwischen auch eine Theorie.

Ein Casio kann ja auf dem AVR Daten ablegen und Daten abholen, die der 
andere Casio abgelegt hat. Die Protokolle dafür sind eingerichtet (und 
hoffentlich fehlerfrei).

Aber was passiert, wenn ein Casio Daten abholen will, aber der andere 
Casio noch keine Daten abgelegt hat? Im Moment bekommt dann der Anfrager 
illegale Daten. Es fehlt nämlich eine Kennzeichnung der Daten 
gültig/ungültig.

Grundsätzlich haben beide Varianten das Problem. Es tritt im Moment wohl 
eher auf als vorher. Das kann den Grund haben, dass vorher der AVR lange 
mit einem Casio beschäftigt war und der andere Casio nur selten eine 
Chance hatte, um seine Anfrage nach Daten abzugeben.

Jetzt mit dem fast gleichzeitigen Reagieren auf beide Casios und einem 
hoffentlich funktionierenden Timeout (!) kann es eher vorkommen, dass 
einer was abfragt und der andere noch nichts gültiges geliefert hat.

Um die Kennzeichnung gültig/ungültig im Programm zu vermerken, könnte 
man sechs einzelne Bits in einer 8-Bit Variablen pro Casio verwenden. 
Das Setzen würde im SAVE Teil erfolgen und das Prüfen im SEND_DATA Teil.

Ist es richtig, dass es pro Casio sechs Datensätze (Variablennamen 
A,B,C,D,E,F) gibt, die jeweils 16 Bytes lang sind? Auf 6*16 komme ich 
wegen der 100 Byte Länge des Datenpuffers in dem die Datensätze 
hintereinander abgelegt werden.

von Flo S. (tuxianer)


Angehängte Dateien:

Lesenswert?

Hallo,
also an deiner Version ist leider etwas falsch. Selbst beim senden an 
den µC kommt ein Error.
Zu deiner Theorie ja das Problem hatte ich auch einmal. Ich lade aber am 
Anfang gleich Standardwerte in den µc:
1
  //Werte laden
2
  for (Temp=0; Temp <96; Temp+=16){
3
  
4
    
5
    for (i=0; i<16; i++) {
6
  
7
    Daten0[Temp+i]=Default[i];
8
    Daten1[Temp+i]=Default[i];
9
  
10
    }
11
  
12
  }

also daran liegt es nicht. Ich habe nochmal den Code angehangen, der 
Funktioniert. Synchrones Senden an den µC klappt wunderbar das 
gleichzeitige Empfangen nur, wenn man das Empfangen am Rechner zur 
gleichen Zeit startet. Versetzt klappt es leider noch nicht. Ja und du 
hasst recht ich verwende im Moment   nur die Variablen A-F, weil zum 
Testen reicht das erstmal und ich hab noch genug Ram zum arbeiten.

von Stefan B. (stefan) Benutzerseite


Lesenswert?

Ich kann es halt nur schreiben und kompilieren. Schon Simulieren wäre 
eine Pein, weil ich dazu zwei UARTS mit einem rel. umfangreichen 
Protokoll füttern müsste.

Und über Debuganzeigen (wenigstens ein paar LED oder so), um wenigstens 
den Status im Protokoll festzustellen, hatten wir uns schon mal glaub 
ich erfolglos unterhalten.

von Flo S. (tuxianer)


Lesenswert?

Na statt LED kann ich wie gesagt auch einen Pin auf High schalten und 
testen. Ich muss mir mal LED's mit Vorwiderstand besorgen, die würden 
noch drauf passen. Ich könnte nochmal schritt für Schritt probieren, 
deine Änderungen zu implementieren. Ich probier jetzt erstmal die 
Interrupt Senden Funktion einzubauen.

von Flo S. (tuxianer)


Lesenswert?

Ich hab jetzt mal probiert ganz simpel das Transmit mit dem 
Sendeinterrupt zu verbinden. Selbst da gibt es schon Probleme:
1
// UART0 UDR EMPTY Interrupt => jetzt Senden wenn was da ist
2
ISR(USART0_UDRE_vect)
3
{
4
  if (irq_send_cnt0) {
5
  
6
    UDR0=Sendbuffer0;
7
    irq_send_cnt0=0;
8
  
9
  }
10
11
}
12
13
14
void Transmit0(int Senden) {
15
  //Sende Rutine UART0
16
  while(irq_send_cnt0);
17
  Sendbuffer0=Senden;
18
  irq_send_cnt0=1;
19
20
}

Ich denke bis das nicht funktioniert brauch ich gar nicht weiter zu 
machen.

von Stefan B. (stefan) Benutzerseite


Lesenswert?

Mit deinem Testcode für IRQ&Transmit0 und einem Transmit0('#') ale 
einziger Code in der while(1)-Schleife bekomme ich in der Simulation 
ein Programm, das brav ein Zeichen nach dem anderen über UART0 sendet.

Aber du hast Recht, in dem Code von gestern ist ein Bug in dem 
Interrupthandler drin.

Hier die entwanzte Version für IRQ&Transmit0, entsprechendes gilt für 
IRQ&Transmit1.

Die Namen habe ich mit ..._num... und ..._cnt... etwas sinnvoller 
gewählt als vorher (Search&Replace im Editor ...cnt durch ...num, dann 
...i durch ...cnt).
1
ISR(USART0_UDRE_vect)
2
{
3
  if (irq_send_num0) 
4
  {
5
    UDR0 = irq_send_buf0[irq_send_cnt0++];
6
    irq_send_num0--;      // Simulation: hier Breakpoint #1
7
  } 
8
9
  //
10
  // nicht else!
11
  // irq_send_cnt0 sofort zurücksetzen, nicht erst warten,
12
  // bis der nächste Interrupt kommt. Das geht nämlich 
13
  // schief, wenn eine nicht IRQ-Routine irq_send_num0 un-
14
  // gleich 0 setzt
15
  //
16
  if (!irq_send_num0)
17
    irq_send_cnt0 = 0;
18
}
19
20
void Transmit0(int Senden) 
21
{
22
  // ggf. warten bis irq_send_buf[] leer ist
23
  // optimierbar mit Anhängen an Puffer usw.
24
  while(irq_send_num0);
25
26
  irq_send_buf0[0] = Senden;
27
  irq_send_num0 = 1;      // Simulation: hier Breakpoint #2
28
}

Die Wanze hatte sich in dem else Fall im Interrupthandler versteckt.

von Flo S. (tuxianer)


Lesenswert?

Sehr merkwürdig. Aber irgendwas ist komisch ich habe jetzt nochmal 
deinen Code genommen. Und da kommt der Com-Error unmittelbar nach dem 
absenden...bei der minimal Variante dauert es eine Weile, bis der Error 
erscheint. Es ist trotzdem merkwürdig, das es nicht funktioniert. Es 
wurde ja nur das Transmit durch einen Interrupt ersetzt.
Interessant ist, dass sich der komplette µC aufhängt. Auch der Timeout 
greift nicht. Auch wenn ich die Timeout Überprüfung in den 
Timerinterrupt nehme greift er nicht. Es scheint also in nem Interrupt 
festzuhängen.

von Stefan B. (stefan) Benutzerseite


Lesenswert?

Das ist ein sehr guter Hinweis.

Die Däumchendreher sind tatsächlich die neuen 
Sendepuffer-leer-Interrupts. Die sollen so wie ich im Datenblatt sehe 
(Transmitter Flags and Interrupts) nur angeschaltet sein, wenn 
tatsächlich Daten geschrieben werden sollen/müssen.

ISR(USART0_UDRE_vect)
{
  if (irq_send_num0)
  {
    UDR0 = irq_send_buf0[irq_send_cnt0++];
    irq_send_num0--;      // Simulation: hier Breakpoint #1
  }

  //
  // nicht else!
  // irq_send_cnt0 sofort zurücksetzen, nicht erst warten,
  // bis der nächste Interrupt kommt. Das geht nämlich
  // schief, wenn eine nicht IRQ-Routine irq_send_num0 un-
  // gleich 0 setzt
  //
  if (!irq_send_num0)
  {
    irq_send_cnt0 = 0;
    // fertig diesen IRQ abschalten
    USART0_UDRE_ABSCHALTEN();
  }
}

void Transmit0(int Senden)
{
  // ggf. warten bis irq_send_buf[] leer ist
  // optimierbar mit Anhängen an Puffer usw.
  while(irq_send_num0);

  irq_send_buf0[0] = Senden;
  irq_send_num0 = 1;      // Simulation: hier Breakpoint #2
  USART0_UDRE_ANSCHALTEN();
}

Entsprechend diese Sequenz an den verschiedenen Stellen in main()

  irq_send_num0 = ...;
  USART0_UDRE_ANSCHALTEN();

auch benutzen, wenn 50/16/... Bytes gesendet werden.

Die neuen Funktionen als Makros:

#define USART0_UDRE_ANSCHALTEN()  ( UCSR0B |=  (1<<UDRIE0) )
#define USART0_UDRE_ABSCHALTEN()  ( UCSR0B &= ~(1<<UDRIE0) )

In der Initialisierung in main() ist das Einschalten des Interrupts 
herauszunehmen.

  UCSR0B |= (1<<RXCIE0)|(1<<RXEN0)|(1<<TXEN0) /* |(1<<UDRIE0) */;

Das ganze für USART1 entsprechend auch.

von Flo S. (tuxianer)


Angehängte Dateien:

Lesenswert?

Ok das scheint es gewesen zu sein. Ich hab jetzt mal die Interrupts rein 
genommen und die Transmits geändert. Nur verstehe ich nicht, wieso ich 
das in die main schreiben soll. Das geschieht doch schon automatisch 
beim Senden das aktivieren/deaktivieren. Auf jeden Fall geht es jetzt. 
Jetzt werde ich mich mal an das Senden mittels Buffer machen.

von Flo S. (tuxianer)


Lesenswert?

so also das Senden klappt irgendwie noch nicht. Ich denke das Interrupt 
wird nicht im richtigen Moment angeschalten:
1
    //wenn status SEND_HEADER
2
    else if(State0 == SEND_HEADER) 
3
    {
4
      SendHeader[12-1] = VarName0; //Varriablennamen einfügen
5
      
6
      //Header Senden
7
      for (i=0; i<(50-1); i++)
8
      {
9
        irq_send_buf0[i] = SendHeader[i];
10
      }
11
      
12
      irq_send_num0=49;
13
      State0 = SEND_HEADER_WARTEN;
14
    }  
15
    
16
    else if(State0 == SEND_HEADER_WARTEN) {
17
    
18
      if(irq_send_num0 == 0) {
19
        State0=SEND_HEADER_CHECKSUMME;
20
      } else {
21
        USART0_UDRE_ANSCHALTEN();
22
      }
23
    
24
    
25
    }
26
    
27
    else if(State0 == SEND_HEADER_CHECKSUMME) {
28
    
29
      uint8_t Summe0;     
30
      uint8_t Checksumme0;   
31
32
      Summe0 = 0;
33
    
34
      for (i=0; i<(50-1); i++) {
35
        Summe0 += SendHeader[i];
36
      }
37
      //Checksumme berrechnen und Senden
38
      Checksumme0 = 0x3a;
39
      Checksumme0 -= Summe0;
40
      Transmit0(Checksumme0);
41
    
42
    }
43
    
44
    
45
    else if(State0 == SEND_HEADER_CHECKSUMME_WARTEN) {
46
      if(irq_send_num0 == 0) {
47
        State0=ACK1;
48
      }
49
    
50
    }

von Flo S. (tuxianer)


Lesenswert?

Ich habs jetzt auch mal so probiert:
1
    //wenn status SEND_HEADER
2
    else if(State0 == SEND_HEADER) 
3
    {  
4
      if(Send_Header_Counter0 == 0) {
5
      
6
        Summe0 = 0;
7
        SendHeader[12-1] = VarName0; //Varriablennamen einfügen
8
      
9
      }else if(Send_Header_Counter0 <= 48) {
10
        Transmit0(SendHeader[Send_Header_Counter0]);
11
        Summe0 += SendHeader[Send_Header_Counter0];
12
      }else if(Send_Header_Counter0 == 49) {
13
        //Checksumme berrechnen und Senden
14
        Checksumme0 = 0x3a;
15
        Checksumme0 -= Summe0;
16
        Transmit0(Checksumme0);
17
        Send_Header_Counter0 = 0;
18
        //Status auf ACK1 setzen
19
        State0 = ACK1;
20
      }
21
      Send_Header_Counter0++;
22
23
    }

aber das scheint auch nicht zu klappen.

von Stefan B. (stefan) Benutzerseite


Lesenswert?

Dort (und an den anderen ähnlichen Stellen)

      irq_send_num0=49;
      State0 = SEND_HEADER_WARTEN;

muss auch der Sendepuffer-leer-Interrupt wieder eingeschaltet werden, 
also
ändern in:

      irq_send_num0 = 49;
      USART0_UDRE_ANSCHALTEN();
      State0 = SEND_HEADER_WARTEN;

Das muss so gemacht werden, weil jetzt nicht mehr alle Sendeaufgaben 
über die einzelzeichenorientierte Transmitx() gehen, sondern im 
Hauptprogramm der IRQ-Sendepuffer direkt befüllt wird.

Grundsätzlich sollte es an deiner Position auch funktionieren, die state 
machine kommt aber erst eine Runde später dort vorbei und man 
"vertrödelt" etwas Zeit.

BTW. die 49 oben macht mich stutzig. In der Version vom 20080124 hatte 
ich bereits eine Änderung drin bei der die Checksumme in den Header 
geschrieben wurde und direkt 50 Bytes statt getrennt 49 dann 1 Bytes 
gesendet wurden. Das spart u.a. ein paar Variablen und ein paar States 
(SEND_HEADER_CHECKSUMME, SEND_HEADER_CHECKSUMME_WARTEN).

Das Weiterschalten auf SEND_HEADER_CHECKSUMME_WARTEN in 
SEND_HEADER_CHECKSUMME fehlt bei dir übrigens und das halte ich für die 
Problemursache. Die state maschine fährt sich in SEND_HEADER_CHECKSUMME 
fest.

Ich habe im Moment zu wenig Zeit, um alle Änderungen hier einzubauen und 
die komplette Source anzuhängen. Vielleicht geht es heute abend oder 
morgen.

von Flo S. (tuxianer)


Angehängte Dateien:

Lesenswert?

Danke...war ein blöder Fehler jetzt klappt es. Dann habe ich noch was 
entdeckt, was die gleichzeitige Kommunikation stören könnte/wird. Der 
Variablen Name wird direkt in den Send_Header geschrieben. Wenn beide zu 
gleich auf Empfang sind kommt da Brühe raus, sofern sie eine andere 
Variable anfordern. Deswegen finde ich es auch besser, wenn die 
Checksumme angegangen wird. Man könnte natürlich jedem UART einen 
eigenen Send_Header zuordnen, was ja auch wieder Platz kostet.
Selber schreiben brauchst du es nicht. Ich finde man lernt beim selber 
schreiben mehr, was ja mein Ziel ist. Über Tipps bin ich natürlich immer 
dankbar.

von Flo S. (tuxianer)


Lesenswert?

ich habs jetzt nochmal zusammengefasst und den SendHeader unabhängig vom 
UART gemacht.
1
    //wenn status SEND_HEADER
2
    else if(State0 == SEND_HEADER) 
3
    {
4
      uint8_t Summe0;     
5
      uint8_t Checksumme0;   
6
7
      Summe0 = 0;
8
      
9
      //Header Senden
10
      for (i=0; i<(50-1); i++)
11
      {  
12
        if(i!=11){
13
          irq_send_buf0[i] = SendHeader[i];
14
          Summe0 += SendHeader[i];
15
        }
16
        else if(i==11) {
17
          irq_send_buf0[i]=VarName0;
18
          Summe0 += VarName0;
19
        }
20
      }
21
      
22
      //Checksumme berrechnen und Senden
23
      Checksumme0 = 0x3a;
24
      Checksumme0 -= Summe0;
25
      irq_send_buf0[49]=Checksumme0;
26
      irq_send_num0=50;
27
      USART0_UDRE_ANSCHALTEN();
28
      State0 = SEND_HEADER_WARTEN;
29
    }  
30
    
31
    else if(State0 == SEND_HEADER_WARTEN) {
32
    
33
      if(irq_send_num0 == 0) {
34
        State0=ACK1;
35
      } else {
36
        USART0_UDRE_ANSCHALTEN();
37
      }
38
    
39
    
40
    }

von Flo S. (tuxianer)


Angehängte Dateien:

Lesenswert?

So ich habs jetzt mal komplett geändert...das ganze funktioniert 
wunderbar auch syncron. Es hat nur einen Hacken. Receive(A) 
funktioniert. Receive(B) Klappt nicht. Irgendwas hab ich geändert und 
nun gehts nicht mehr...

von Flo S. (tuxianer)


Angehängte Dateien:

Lesenswert?

bei dem geht es noch:

ich glaub es muss hier liegen:
1
      //Header Senden
2
      for (i=0; i<(50-1); i++)
3
      {  
4
        if(i!=11){
5
          irq_send_buf0[i] = SendHeader[i];
6
          Summe0 += SendHeader[i];
7
        }
8
        else if(i==11) {
9
          irq_send_buf0[i]=VarName0;
10
          Summe0 += VarName0;
11
        }
12
      }

denn es ging vorhins beim programmieren auch mal nicht wo ich an der 
stelle gearbeitet habe.

von Stefan B. (stefan) Benutzerseite


Angehängte Dateien:

Lesenswert?

Sendheader nicht zu verändern ist eine gute Idee. Das hilft, wenn man 
für später daran denkt, die konstanten (read-only) Arrays ins ROM zu 
schaffen. Dort sind knapp 80% noch frei, während im RAM schon fast 70% 
belegt sind.

Neuschreiben meinte ich auch nicht. Ich dachte daran, wieder eine 
konsolidierte Version zu schaffen, in der aller Änderungen seit der 
Umstellung auf das interruptbasierte Senden sicher mit IRQ 
An-/Abschalten implementiert sind.

Das habe ich jetzt gemacht. Deine Sendheader-Geschichte habe ich auch 
eingebaut. Und einen Fehlercheck bzgl. dem Variablenname (nur A-F 
erlaubt, bei anderen Namen wird A eingesetzt).

von Flo S. (tuxianer)


Lesenswert?

Also dein Version funktioniert dür UART1 beim 0 kommt sowohl beim Senden 
als auch beim Empfangen nen Error. Was hasst du gegenüber meiner Version 
jetzt noch verändert? Und weist du wo der Fehler herkam, der das mit 
Reveive(B) etc. verursacht hat?

von Stefan B. (stefan) Benutzerseite


Lesenswert?

Zu dem Unterschied deiner letzten Version bzgl. Variablenname kann ich 
nichts sagen.

Ich hatte meine letzte Version genommen, deine Sendpuffer-Idee 
nachprogrammiert und die IRQ-Geschichte fast richtig gemacht.

Fast richtg...

Durch Copy & Paste & Umfummeln von 0 nach 1 schleichen sich Fehler ein. 
Man muss alle Stellen peinlich nachkontrollieren, damit man nach ^C und 
^V nicht die 0/1 Änderung verpasst.

U.a. habe ich jetzt einen 0/1 Vertauscherfehler gefunden in

ISR(USART1_UDRE_vect) => Richtige USART abschalten!!!

Der Vertauscher führt dazu, dass ein fertiges Senden bei UART1 das 
Senden bei UART0 abschaltet. Die Sendeblockade (Error) auf UART0 ist 
also erklärbar und wahrscheinlich einfach behebbar.

von Flo S. (tuxianer)


Lesenswert?

das scheint leider nicht das einzige gewesen zu seien.

von Flo S. (tuxianer)


Lesenswert?

der Error ist hier:
if(FlagACKTests0 == AKTIV)

wo genau weis ich nicht. Ich habe einfach nach und nach alles vom UART 1 
kopiert wieder auf 0 geändert und mittels vertify nach Differenzen 
gesucht!

von Stefan B. (stefan) Benutzerseite


Lesenswert?

Änderungen

1/ Debugcode
Ob es tatsächlich in an der von dir vermuteten Stelle hakt, kann man mit 
einer Debugausgabe auf einen der beiden freien Pins (PA0 PA1) an PortA 
herausfinden. Ich habe den Code dafür in dem Anhang: PA0 entspricht dem 
Zustand von FlagACKtests0 und PA1 dem von FlagACKtests1. Hast du 
vielleicht noch mehr freie Pins?

2/ RAM Einsparung
Jetzt werden 116 Bytes weniger benutzt. EndHeader0 und EndHeader1 wurden 
eingespart und Default[]. Daten0[] und Daten1[] werden bereits zur 
Compilezeit initialisiert statt erst zur Laufzeit)

3/ Timeout-Abfrage
Die Timeout-Abfrage wurde aus der Hauptschleife in den Timer-Interrupt 
verlagert. Das holt ein hängendes Programm aus dem Sumpf auch wenn die 
Hauptschleife nicht mehr durchlaufen wird. Ist das Programmverhalten 
eigentlich vom Timeout-Wert (derzeit 2000) abhängig? Ich habe den Wert 
mal als Define eingesetzt so dass man es rel. einfach mit einem 
wesentlich höheren Wert probieren kann.

4/ IDLE Zustand
Der IDLE Zustand wird jetzt in der Hauptschleife behandelt und 
initialisiert alle lebenswichtigen Variablen.

5/ Änderung im Status REC_FOOTER_ACK
Wenn die Statemachine Status REC_FOOTER_ACK ist und keine Variable 
gespeichert werden soll (Header?[5] != 'V'), welchen Status soll die 
Statemachine dann einnehmen? Im Moment verharrt sie dann im Status 
REC_FOOTER_ACK. Dieser Hänger wäre dann auch in der Nähe von der 
FlagACKTests Abfrage, so wie du es beobachtet hast. Die 
Sicherheitsabfrage (Header?[5] == 'V' im Status REC_FOOTER_ACK) ist eine 
rel. kurzfristige Änderung von dir, jedenfalls wurde die noch nicht mit 
lauffähigem Emfangen/Senden Code getestet. Man könnte meiner Meinung 
nach im else-Fall in den Zustand IDLE gehen oder die Sicherheitsabfrage 
entfernen und immer in den Zustand SAVE (so wie jetzt im Code) gehen, 
denn das Programm kommt aus REC_DATA und hatte eine Sicherheitsabfrage 
bereits erfolgreich überstanden (in REC_HEADER_ACK).

Fragen

1/ Sehe ich die verschiedenen Zustände und den Protokollablauf im 
Sourcecode so richtig?

Empfangen
IDLE               (bis 0x15 empfangen)
HEADER_INIT_ACK    (5ms warten, 0x13 senden)
REC_HEADER         (50 Zeichen empfangen)
REC_HEADER_ACK     (5ms warten, 0x06 senden)
 => REQUEST        (Header?[1] == 'R' && Header?[5] == 'V')
 oder
 => REC_DATA       (Header?[1] == 'V' && Header?[5] == 'V')
 oder
 => IDLE           (Header?[1] != 'V' && Header?[1] != 'R' || Header?[5] 
!= 'V')

Empfangen Untervariante 1
REC_DATA           (16 Zeichen empfangen)
REC_DATA_ACK       (5ms warten, 0x06 senden)
REC_FOOTER         (50 Zeichen empfangen)
REC_FOOTER_ACK     (5ms warten, 0x06 senden)
 => SAVE           (Header?[5] == 'V')
 oder
 => ???            (Header?[5] != 'V' Fall siehe oben)

Empfangen Untervariante 2
SAVE               (empfangene Daten speichern)
IDLE

Senden
REQUEST            (beliebiges Zeichen empfangen)
REQUEST_WAIT       (5ms warten)
SEND_HEADER        (50 Bytes senden)
SEND_HEADER_WARTEN (5ms warten)
ACK1               (beliebiges Zeichen empfangen)
ACK1_WAIT          (5ms warten)
SEND_DATA          (16 Bytes senden)
SEND_DATA_WARTEN   (5ms warten)
ACK2               (beliebiges Zeichen empfangen)
ACK2_WAIT          (5ms warten)
SEND_ENDHEADER     (50 Bytes senden)
SEND_IDLE_WARTEN   (5ms warten)
IDLE

2/ Ändern sich die Hänger wenn die Timeoutzeit geändert wird?

3/ Was zeigen die Debugpins an, wenn es hakt?

von Stefan B. (stefan) Benutzerseite


Angehängte Dateien:

Lesenswert?

Geänderter Quellcode

von Flo S. (tuxianer)


Lesenswert?

ich hatte mich vielleicht blöd ausgedrückt, aber der Code hat ja 
funktioniert, wo ich die besagte Stelle von UART1 kopiert habe und sie 
dann wieder in UART0 geändert habe. Da war sicher nur ein Tippfehler. 
Ich werde trotzdem mal deine Änderungen einbauen.

von Flo S. (tuxianer)


Lesenswert?

Zu den Fragen:
also so weit ich das Überblicken konnte ist der Ablauf korrekt. Das 
Timeout habe ich jetzt mal auf 2 Sekunden gewählt, da ja ab dem Senden 
der Status nicht zurückgesetzt wird. Deswegen die 2 Sekunden. Zu der 
Überprüfung in REC_HADER_ACK: Also Header[1] gibt an, ob vom Rechner 
gesendet wird(V) oder ob der Rechner eine Variable anfordert(R). 
Header[5] steht für den Typ. V ist Variable. Aber so weit ich das sehe 
sind doch alle falschen Inputs abgesichert. Die Abfrage hatte ih 
eigentlich getestet und sie Funktioniert. Ich hatte den Timeout 
abgeschalten und mal eine Matrix gesendet/Empfangen und kann danach 
problemlos weiter kommunizieren. Ohne dies Änderung blieb es hängen.

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.