Protokoll für einfache Kommunikation rs485

#7991608
Lesenswert?

hallo,

ich will meinen Deye-Solar-Wechselrichter steuern. Dazu habe ich einen nano-AVR, der Lichteinfall, Batteriespannung, und Stellung des Lasttrennschalters misst und über zwei Relais den Lasttrennschalter und den Akku steuert. Funktioniert alles ohne größere Probleme. Lediglich die i2c-rtc und die Lcd-Anzeige sind manchmal - wohl aufgrund von Störimpulsen - weg. Deswegen will ich lcd und rtc auf einen zweiten Nano auslagern. uart-RS485-Kommunikation funktioniert auch - fast - fehlerfrei.

1
#if sender0_empfaenger1==0//Sender
2
if(sec%7==3)
3
  {
4
//0110006C0001020078 AF1E //crc-teststring
5
    u8 len=9;
6
    u16 crc=0;
7
    txBuffer[0]=0x01;
8
    txBuffer[1]=0x10;
9
    txBuffer[2]=0x00;
10
    txBuffer[3]=0x6c;
11
    txBuffer[4]=0x00;
12
    txBuffer[5]=0x01;
13
    txBuffer[6]=0x02;
14
    txBuffer[7]=0x00;
15
    txBuffer[8]=0x78;
16

17
  crc= function_crc( txBuffer,  len);
18
  txBuffer[len]  = crc & 255 ;
19
  txBuffer[len+1]= crc>>8  ;
20

21
  serial_tx(txBuffer,len+2);
22
  }
23
  
24
#else//empfänger  
25
  if(sec%7==3  
26
  && uart_str_rx[9]>0
27
  )
28
  {
29
    for (u8 i=0;i<15;i++)
30
    {
31
      lcd_goto(5+i,1);
32
      lcd_int4(uart_str_rx[i]);
33
    }
34
    
35
    u16  crc= function_crc( uart_str_rx,  9);
36
    uart_str_rx_ct=0;
37
    
38
    if(((crc>>8)==uart_str_rx[10]) && ((crc&255)==uart_str_rx[9]))
39
    {    
40
      lcd_goto(5+9,5);
41
      lcd_int4(crc&255);
42
      lcd_int4(crc>>8);
43
    }    
44
    memset(uart_str_rx, 0, 13);
45
  }
46
#endif

Jetzt geht es um das Protokoll. Wie macht man das am besten - wiederholt Senden bis vom Empfänger eine Bestätigung kommt oder z.B. 5mal Senden und der Empfänger steigt aus, wenn er korrekt empfangen hat?

Das ist sicher ein Problem, über das sich schon viele kluge Köpfe Gedanken gemacht haben. Gibt es vielleicht eine einfache Standard-Musterlösung?

: Bearbeitet durch User
#7991622
Lesenswert?

Nimm Modbus-RTU. Das ist erprobt, dafür gibt es zig Testwerkzeuge und fertige Libraries auf so gut wie allen Plattformen.

Modbus-RTU bietet Fehlererkennung. Einerseits durch die Hardwareschicht der UART mit Parity- und Framingauswertung und andererseits durch 16-Bit-CRC in jedem Telegramm. Ein Gerät ("Slave") kann ein fehlerhaftes Paket monieren (mit Modbus-Exceptions, der Gegenpart ("Master") kann wiederum bei fehlerhaften Paketen den zuletzt gesendeten Kram wiederholen.

Sofern nicht die Hardware besonders schrottig ist oder aber die Leitungen besonders ungünstig verlegt sind (z.B. um einen Wechselrichter gewickelt oder komplett auf Twisted-Pair verzichtet), treten im Normalbetrieb aber keine Fehler auf; Modbus-Verbindungen können jahre- und jahrzehntelang stabil arbeiten.

Ja, das Protokoll ist primitiv, da es genau zwei Datentypen kennt: Bit und 16-Bit-Wort. Aber es ist möglich und wird zigtausendfach praktiziert, auch größere Datentypen durch Zusammenfassen mehrerer 16-Bit-Worte zu erhalten.

Das ist dann ein zusätzlicher Protokollayer, den man nur gut dokumentieren sollte. Die üblichen Modbus-Testprogramme (wie z.B. Modbus-Poll) kommen auch damit zurecht.

#7991631
Lesenswert?

Karl K. schrieb:

Funktioniert alles ohne größere Probleme. Lediglich die i2c-rtc und die Lcd-Anzeige sind manchmal - wohl aufgrund von Störimpulsen - weg.

Hier die Ursache suchen statt das System zusätzlich zu komplizieren wäre der bessere Weg. Wie ist die Masseverbindung der unterschiedlichen Komponenten. Das ist eine häufige Störquelle.

Protokoll Modbus wie schon gesagt wurde.

#7991648
Lesenswert?

Karl K. schrieb:

"wohl aufgrund von Störimpulsen" hört sich nach blindem Raten an.

Guck, was wirklich das Problem ist und beseitige es.

Meinst du, dass es einfacher wird, wenn zusätzlich noch die beiden Controller miteinander kommunizieren sollen?

Wie macht man das am besten - wiederholt Senden bis vom Empfänger eine Bestätigung kommt oder z.B. 5mal Senden und der Empfänger steigt aus, wenn er korrekt empfangen hat?

Fällt die Übertragung teilweise aus oder werden Daten fehlerhaft empfangen? Wie lang sind deine Leitungen? Was gibt es für Störer in der Nähe?

Ohne die Art der Fehler zu kennen, kann man vieles probieren. Man kann die Zeichenkodierung ändern, also z.B. den Hammingabstand zwischen den Symbolen vergrößern, so dass Bitfehler erkannt und z.T. auch korrigiert werden können oder man kann eine Prüfsumme hinzufügen und bei Prüfsummenfehler die Daten neu anfordern - aber es kommt eben auf die Art der Fehler an.

Gegen Programmierfehler hilft das nichts.

: Bearbeitet durch User
#7991748
Lesenswert?

Nick schrieb:

Abschlusswiderstand richtig

das ist eigentlich seit Jahren bewährte Technik. Der Zusammenhang "Umschalten der Stromquelle" ist auch relativ klar. Da entsteht manchmal ein Störimpuls, da das Hausnetz während des Umschaltens unterbrochen wird. Die Versorgung zum Nano wird natürlich nicht unterbrochen.

Nick schrieb:

Bilder! :-)

ich bitte um Nachsicht für die noch provisorische Verkabelung der Steuerung - erstmal muss es richtig funktionieren.

Udo S. schrieb:

Hier die Ursache suchen statt das System zusätzlich zu komplizieren wäre der bessere Weg.

Ich finde die Idee von zwei Nanos eigentlich nicht schlecht. Die können sich dann gegenseitig überwachen.

#7991783
Lesenswert?

Karl K. schrieb:

Bilder! :-)

ich bitte um Nachsicht für die noch provisorische Verkabelung der Steuerung - erstmal muss es richtig funktionieren.

Schon OK. Ich hab nach scope-Bildern auf dem I2C gefragt ... oh ... gedacht gefragt zu haben. :-)

Wie lange ist denn die I2C-Leitung? Denn eigentlich ist das nur für kurze Strecken geplant gewesen. Es gibt zwar Isolatoren dafür, aber damit hab ich Null Erfahrungen.

#7991990
Lesenswert?

Karl K. schrieb:

Lediglich die i2c-rtc und die Lcd-Anzeige sind manchmal - wohl aufgrund von Störimpulsen - weg.

Karl K. schrieb:

ich bitte um Nachsicht für die noch provisorische Verkabelung der Steuerung - erstmal muss es richtig funktionieren.

Wie soll es mit provisorischer Verkabelung störungsfrei funktionieren ?

Ich sehe Antennen, zum Aussenden von Störungen und Empfangen von Störungen und Masse wird kreuz und quer geführt.

Das ist keine elektronische Schaltung sondern ein Breakout Aufbau mit der Hoffnung daß es irgendwie glatt geht. Bloss muss die Hoffnung ja nicht immer erfüllt werden.

#7992122
Lesenswert?

Rainer W. schrieb:

Also liegt es gar nicht am Mikrocontroller?

ja - genau das vermute ich. Ich hatte das ganze per Blechgehäuse abgeschirmt - hat aber nichts geholfen.

Rainer W. schrieb:

Backup+Batterie

ist angeschlossen - Sinn ist ja, dass der uc nach Neustart das korrekte Datum findet.

Rainer W. schrieb:

Wie sieht dein Schaltplan aus?

Ich verwende fertige module, die über Steckleisten mit dem nano verbunden werden. Einen Schaltplan gibt es nicht.

Michael B. schrieb:

Breakout Aufbau

Ja, genau. Ich bin ja am Entwickeln - also brauche ich einen Breakout-Aufbau.

Rainer W. schrieb:

Meinst du, dass es einfacher wird, wenn zusätzlich noch die beiden Controller miteinander kommunizieren sollen?

Es eröffnet Möglichkeiten. Ich habe dann ein Netzwerk und kann den uc auch vom PC aus steuern. Z.B. Grenzwerte für das Schalten des ucs vom PC aus ändern ohne den uc neu zu programmieren.

Angehängte Dateien:
#7992132
Lesenswert?

Ein Protokoll könnte Deine Fähigkeiten arg fordern. Der wesentlichen Punkte:

Wenn Nutzdatenbytes den Wertebereich 0..255 haben, dann sind Start- und Endekennung schwierig.

Du brauchst zumindest ein exklusives StartByte, wenn Du nicht mit komplizierten Timeouts den Anfang eines Telegrams erkennst. (Zählen der Bytes ist noch komplizierter)

Exklusiv heißt: kein Nutzdatenbyte (incl. möglicher Checksumme) hat diesen Wert (diese Werte). Dazu gibt es mehrere Möglichkeiten:

  • Du teilst jedes Nutzdatenbyte in 2 Bytes (z.B. 2 Hex-Ascii-Werte)
  • Du teilst 7 Nutzdatenbytes auf die unteren 7 Bits von 8 Bytes
  • "Bytestuffing" (nutze ich sehr häufig, mehr bei Bedarf)
  • Strings statt Binärwerte (ist die Standardlösung, da unabhängig von Alignment, Endianess, etc.)

Für den Anfang würde ich "Hex" nehmen. 0x1a wird z.B. als '1' und 'a' übertragen , also deren ASCII-Werte.

Dann kannst Du einen beliebigen Wert als Startwert nehmen, z.B. 'S'. Mit 13 oder so als Endekennung kannst Du Telegramme im Terminalprogramm direkt mitlesen (ein wertvoller Vorteil!).

Dein Telegramm im OP sähe dann so aus:

S0110006c0001020078

: Bearbeitet durch User
#7992141
Lesenswert?

Ich persönlich würde es über Timeouts machen. Ist auch kein Hexenwerk. Es gibt aber auch andere einfache Wege.

Bruno V. schrieb:

Du brauchst zumindest ein exklusives StartByte

Nicht zwingend. Zumindest nicht in der Art dass man einen eingeschränkten Zeichenbereich hätte. Eine einfache Möglichkeit wäre den 9Bit Modus zu verwenden. Ist das oberste Bit gesetzt, ist es das erste Byte im Frame. Die restlichen 8 Bit innerhalb eines Zeichens sind dann normale Nutzdaten. Im ersten Zeichen könnte man z.B. neben der Start Kennennung noch die Anzahl der nachfolgenden Bytes unterbringen. Ein Frame könnte also maximal 256 Bytes lang sein. Danach das CMD, Nutzdaten, CRC wobei dabei das 9. Bit nie gesetzt ist.

Man kann also sehr leicht auf den Anfang eines Frames synchronisieren (9.Bit) und den Frame anhand Längenangabe,CMD und CRC validieren.

#7992162
Lesenswert?

Bruno V. schrieb:

Ein Protokoll könnte Deine Fähigkeiten arg fordern.

Aus gutem Grund habe ich Modbus/RTU vorgeschlagen. Das hat zwar einen gewissen Overhead, ist aber etabliert, in Form von zig Libraries verfügbar und es gibt jede Menge Testprogramme dafür, so daß beide Seiten der Kommunikation problemlos alleinstehend getestet werden können.

Sowas hilft Fehlerquellen zu reduzieren und ist alleine deswegen schon jeder Selbstbaulösung überlegen.

Und ... man lernt dazu und hat was in der Tasche, das man später auch woanders verwenden kann; Modbus ist so selten nicht.

Wenn nur genau zwei Geräte miteinander kommunizieren sollen, kann man Modbus auch ohne RS485 betreiben, einfach durch Verbindung zweier serieller Schnittstellen. Bei kurzen Verbindungen und gemeinsamer Masse kann man auch auch Pegelwandler verzichten (sofern beide Seiten die gleichen Pegel nutzen).

#7992167
Lesenswert?

N. M. schrieb:

Ich persönlich würde es über Timeouts machen. Ist auch kein Hexenwerk. Es gibt aber auch andere einfache Wege.

Das über timing zu machen geht im Mikrocontroller-Umfeld gut. Sobald aber ein Betriebssystem an der Kommunikation beteiligt ist, geht das leicht ins Gehöse. Die können kein genaues Timing.

Da hab ich schlechte Erfahrungen mit RT-Linux gemacht. Ich hatte eine ModBus-Kommunikation über RS485 und Zweidraht. Die Umschaltung von Senden zu Empfangen am PC war einfach zu lahm. Konsequenz: Lieber 4-Draht, dann muss man nicht umschalten.

Zweiter Reinfall: Mein Kasterl zur Verkehrserfassung hat über 2-Draht-RS485 mit einem Master kommuniziert (war ähnlich ModBus). Vom Kunden gab es immer wieder mal Klagen, dass Pakete verloren gehen. Das war aber nie nachvollziehbar. Bis ich dann ein Protokoll der Kommunikation bekommen hab und -nach langen Grübeln und Anschauen- mir aufgefallen ist, dass es nur bei besonders langen Paketen war. Die hatten einfach stur nach x ms den Bus umgeschaltet statt den Datenverkehr zu beobachten und sich an das 3.5 Zeichen timeout zu halten. Deren Betriebssystem war BusyBox. Ihre "Lösung" war dann, einfach genauso stur umzuschalten, aber halt später.

Also Vorsicht mit timing-Geschichten wenn "echte Betriebssysteme" beteiligt sind. Ansonsten bekommt man das hin, 1 ms Auflösung geht problemlos.

#7992173
Lesenswert?

Bruno V. schrieb:

Wenn Nutzdatenbytes den Wertebereich 0..255 haben, dann sind Start- und Endekennung schwierig.

Du brauchst zumindest ein exklusives StartByte, wenn Du nicht mit komplizierten Timeouts den Anfang eines Telegrams erkennst. (Zählen der Bytes ist noch komplizierter)

Das stimmt so nicht. Dazu mal TCP anschauen, oder ModBus. Das Synchronisieren ist halt bissl aufwendig.

Startbyte z.B. 0xAA Dann Längenangabe des Paketes Daten ... CRC

Wenn man irgendwo im Datenstrom aufsetzt wartet man das Startbyte ab, liest die vermeintliche Länge, wartet den vermeintlichen CRC ab (Timeout beachten) und dann muss der CRC passen. Falls nicht, ein Zeichen nach dem -jetzt bekannterweise falschen- Startbyte wieder neu probieren.

#7992174
Lesenswert?

Karl K. schrieb:

Einen Schaltplan gibt es nicht.

Schlechter Plan. Die Module nehmen dir das Nachdenken über Stromversorgung, Aufbau I2C-Bus und die Verbindung der Module untereinander nicht ab. Für die Dokumentation deines Aufbaus brauchst du den Schaltplan sowieso.

Es eröffnet Möglichkeiten. Ich habe dann ein Netzwerk und kann den uc auch vom PC aus steuern. Z.B. Grenzwerte für das Schalten des ucs vom PC aus ändern ohne den uc neu zu programmieren.

Und warum brauchst du dafür ein Netzwerk? Bisher hört es sich nicht so an, als ob deine GPIOs knapp sind.

: Bearbeitet durch User
#7992208
Lesenswert?

Bruno V. schrieb:

Ein Protokoll könnte Deine Fähigkeiten arg fordern

Deswegen frage ich ja hier. Fleury-bib leicht modifiziert:

1
ISR (UART0_RECEIVE_INTERRUPT)  
2
{
3
if(uart_str_rx_ct>uart_string_len+1)uart_str_rx_ct=0;
4
uart_str_rx[uart_str_rx_ct++] = UDR0;
5
return;
6
...
7
  if(sec%10==3)
8
  {
9
    u16 crc=0;
10

11
    uart_string_len=14;
12
    txBuffer[0]=0x01;//adr-sender
13
    txBuffer[1]=0x02;//adr-empfänger
14
    txBuffer[2]=uart_string_len;//len-payload
15
    txBuffer[3]=0;//cmd
16

17
    txBuffer[4]=hou;
18
    txBuffer[5]=min;
19
    txBuffer[6]=sec;
20
    txBuffer[7]=tag1_nacht0;
21
    txBuffer[8]=status_wr;
22
    
23
    txBuffer[9]=akku_volt>>8;
24
    txBuffer[10]=akku_volt;
25

26
    txBuffer[11]=pv_in>>8;
27
    txBuffer[12]=pv_in;
28

29
    txBuffer[13]=LTswitch;
30

31
  crc= function_crc( txBuffer,  uart_string_len);
32
  txBuffer[uart_string_len]  = crc & 255 ;
33
  txBuffer[uart_string_len+1]= crc>>8  ;
34

35
  serial_tx(txBuffer,uart_string_len+2);
36
  }
37
  
38
#else//empfänger  
39
  if(sec%7==3  
40
//  && uart_str_rx[9]>0
41
  )
42
  {
43
    for (u8 i=0;i<20;i++)
44
    {
45
      lcd_goto(5+i,1);
46
      lcd_int4(uart_str_rx[i]);
47
    }
48

49
    u8  uart_string_lenx = uart_str_rx[2];
50
      lcd_int4(uart_string_lenx);
51
    
52
    u16  crc= function_crc( uart_str_rx,  uart_string_lenx);
53
    uart_str_rx_ct=0;
54
    
55
    if(((crc>>8)==uart_str_rx[uart_string_lenx+1]) && ((crc&255)==uart_str_rx[uart_string_lenx]))
56
    {    
57
      akku_volt=(uart_str_rx[9]<<8)+uart_str_rx[10];
58
      
59
      pv_in=(uart_str_rx[11]<<8)+uart_str_rx[12];
60
        
61
      u8 LTswitchx=uart_str_rx[13];
62
      
63
      status_wr=uart_str_rx[8];
64
      
65
      tag1_nacht0=uart_str_rx[7];
66
    }    
67
    memset(uart_str_rx, 0, 20);
68
  }
69
#endif

Das funktioniert relativ problemlos. Jeder Teilnehmer im Netz bekommt ein bestimmtes Zeitfenster zum Senden zugewiesen. Das verhindert Kollisionen.

Nächstes Problem ist das handshake.

Angehängte Dateien:
#7992209
Lesenswert?

Karl K. schrieb:

Nick schrieb:

Wie lange ist denn die I2C-Leitung?

maximal 5cm auf der Streifenrasterplatine zum DS3231-Modul. Der gespeicherte Wert ist nach der Störung komplett weg - die rtc startet mit 1.1.2000 neu. Blöd weil ich eine Sonnenaufgangsfunktion hatte - geht aber mit der Messung des Lichteinfalls ohnehin besser.

Hättest du gleich schreiben können. Vergiss jetzt dein Protokoll. Das ist nicht dein Problem. Du hast mit der RTC generell ein Problem. Das gilt es zu lösen. Ich setzte voraus das die Knopfzelle der RTC i.O. ist. Hast du es nachgemessen? Ich habe das Gefühl, du schreibst keine Daten/Änderungen in die RTC, ansonsten wären die nämlich nicht weg. Ich vermute du hälst die aktuellen Daten nur im RAM vom µC und denkst die wären in der RTC. Schreibe ein einfaches Programm was das Datum + Zeit stellt und lies die Daten zurück. Bereite das Programm vor damit es nur ausliest. Wenn das klappt, mach das RTC Modul Spannungslos, die Knopfzelle bleibt drauf. Kurz warten, anstecken und nur auslesen. Was passiert?

#7992228
Lesenswert?

Karl K. schrieb:

Deswegen frage ich ja hier. Fleury-bib leicht modifiziert:

Dann lies dir doch bitte auch die Vorschläge hier durch. Und berücksichtige die dann auch.

Das was Du gezeigt hast ist "realativ" problematisch. Wenn alles klappt, dann geht es. Wenn ein Fehler auftritt, dann nicht mehr.

Karl K. schrieb:

Nächstes Problem ist das handshake.

ModBus! Polling der Gegenseite durch den Master. Antwort innerhalb einer vorgegebenen Zeit oder Anfrage wiederholen.

Karl K. schrieb:

Fleury-bib

Zu "Bib" fällt mir nur das Michelin-Männchen ein. https://de.wikipedia.org/wiki/Bibendum

#7992414
Lesenswert?

Wastl schrieb:

CRC Funktionen und LCD Ausgaben in der ISR.

1
return;
2
...

return heißt, dass die irc an dieser Stelle beendet ist.Kürzer geht wirklich nicht.

... heißt, dass dies ein anderer Programmteil ist.

Veit D. schrieb:

Ich habe das Gefühl, du schreibst keine Daten/Änderungen in die RTC, ansonsten wären die nämlich nicht weg.

No.

1
if(gbi(PIN(D),2)==1)
2
{
3
lcd_clear(BLACK);
4
}
5
else
6
{
7
lcd_clear(RED);
8
kk_ds1307_setTime(14,36,0);//Sommerzeit 
9
kk_ds1307_setDate(31,12,25);
10
}
11
kk_ds1307_getAll_to_global_struct_zt();

Die RTC wird nur beim Start und jeweils um 0Uhr ausgelesen und die Programmzeit aktualisiert. Ich habe bewährte Routinen zum Schreiben und Lesen der RTC. Ich werde das aber so umstellen, dass die RtC über RS485 vom PC aus aktualisiert wird. Es ist lästig, das Programm ändern zu müssen um die RTC einstellen zu können.

: Bearbeitet durch User
#7992417
Lesenswert?

Nick schrieb:

Ich hatte eine ModBus-Kommunikation über RS485 und Zweidraht. Die Umschaltung von Senden zu Empfangen am PC war einfach zu lahm. Konsequenz: Lieber 4-Draht, dann muss man nicht umschalten.

Das ist nur an einem Ende des Busses eine Lösung, alle anderen müssen trotzdem umschalten.

Das eigentliche Problem lässt sich aber durch Wahl der richtigen UARTs elegant lösen, die nämlich beherrschen die Sender-/Empfänger-Umschaltung in Hardware. Und dazu gehören auch viele USB-Seriell-Bridges, wie z.B. alles von FTDI.

Ansonsten hättest Du unter Deinem Linux nur den Devicetreiber anpassen müssen, dann hätte das auch Dein PC hinbekommen können. Die PC-Standard-UART löst zwar keinen Interrupt aus, wenn sie mit Senden des letzten Bits fertig ist, aber es gibt ein Flag in einem Statusregister, das man abfragen kann, notfalls in einem Timerinterrupt, der mit dem Übertragen des letzten zu sendenden Zeichens an die UART aufgezogen wird. Damit schafft das auch ein wirklich alter PC, sofern man nicht mit unnötig hohen Baudraten arbeitet.

#7992430
Lesenswert?

Karl K. schrieb:

Veit D. schrieb:

Ich habe das Gefühl, du schreibst keine Daten/Änderungen in die RTC, ansonsten wären die nämlich nicht weg.

Die RTC wird nur beim Start und jeweils um 0Uhr ausgelesen und die Programmzeit aktualisiert. Ich habe bewährte Routinen zum Schreiben und Lesen der RTC. Ich werde das aber so umstellen, dass die RtC über RS485 vom PC aus aktualisiert wird.

Das ist nicht die Antwort auf das Problem. Was passiert wenn du die RTC für paar Sekunden nur von der Knopfzelle versorgen lässt und danach wieder ausliest? Nur die RTC und der µC sollen dabei im Spiel sein. Ich glaube ja immer noch das die RTC sich die Zeit nicht merkt bzw. nicht merken kann. Durch was soll eine RTC ihre Einstellung verlieren. Nur wenn keine Puffer/Versorgungsspannung anliegt.

Es ist lästig, das Programm ändern zu müssen um die RTC einstellen zu können.

Wer hindert dich das zu ändern? Ist doch dein Programm.

1
> kk_ds1307_setTime(14,36,0);//Sommerzeit 
2
> kk_ds1307_setDate(31,12,25);

Und haste danach ausgelesen zum Gegenprüfen? RS485 hilft dir dabei jedenfalls nicht das Problem zu lösen.

Ich wundere mich das alle anderen hier immer nur vom Protokoll schreiben, aber das eigentliche Problem links liegen lassen. Verstehe ich nicht.

: Bearbeitet durch User
#7992448
Lesenswert?

N. M. schrieb:

Nicht zwingend. Zumindest nicht in der Art dass man einen eingeschränkten Zeichenbereich hätte.

Naja, wenn ich 8 Bit Daten in 9 Bit übertrage, dann ist das Startbyte in genau der von mir beschriebenen Weise exklusiv.

Nick schrieb:

Du brauchst zumindest ein exklusives StartByte, wenn Du nicht mit komplizierten Timeouts den Anfang eines Telegrams erkennst. (Zählen der Bytes ist noch komplizierter)

Das stimmt so nicht. Dazu mal TCP anschauen, oder ModBus. Das Synchronisieren ist halt bissl aufwendig.

Du beschreibst genau das, was ich beschrieben habe. Timeouts oder StartByte. Auf was bezog sich das "stimmt so nicht"? TCP hingegen ist eine Schicht höher und setzt komplette Telegramme voraus.

#7992449
Lesenswert?

Harald K. schrieb:

Senden zu Empfangen am PC war einfach zu lahm. Konsequenz: Lieber 4-Draht, dann muss man nicht umschalten.

Das ist nur an einem Ende des Busses eine Lösung, alle anderen müssen trotzdem umschalten.

Das ist die komplette Lösung. Denn das Umschalten am PC war zu lahm. Daher mein Hinweis auf "Betriebssysteme". Wer das am µC nicht timing-gerecht hinbekommt, stellt hier eine Frage nach "Protokollen für einfach Komm..."

#7992467
Lesenswert?

Karl K. schrieb:

CRC Funktionen und LCD Ausgaben in der ISR. return; ...

return heißt, dass die irc an dieser Stelle beendet ist.Kürzer geht wirklich nicht.

Da steht aber noch ein "if" davor.

Und dann kommt in der ISR noch ein "#else//empfänger"

Allerdings könnte es sein, dass der code durch den snip (...) unnötig verstümmelt wurde.

Lustig ist auch, dass dennoch weitergelesen wird wenn der string schon komplett empfangen wurde. Also: String komplett lesen, uart_str_rx_ct inkrementieren. Wenn der String komplett gelesen ist in der ISR dekodieren und dann vergessen uart_str_rx_ct wieder auf Null setzen.

Wo das "#if" zum "#else" hingekommen ist weiß ich nicht. Und in dem Zweig wird lustig das LCD behandelt. Gegen den CRC hab ich persönlich und ernsthaft nix, das ist von der Zeit her sehr überschaubar. Aber man sollte das eigentlich nicht in der ISR machen. Da sind wir uns einig.

Hier code zu kommentieren ist müssig. Für sowas würde ich mich schämen: "uart_string_len=14;"

"const uart_string_len=14;" ist richtig. K&R-Fans machen ein "#define" draus. Der Compiler will möglichst viele Chancen haben zu optimieren. Und das geht leichter mit Konstanten statt mit Variablen, auch wenn sie nie geändert werden.

#7992469
Lesenswert?

Karl K. schrieb:

Ich finde die Idee von zwei Nanos eigentlich nicht schlecht. Die können sich dann gegenseitig überwachen.

ich finde die Idee sowas mit Nanos usw zu machen generell schlecht

Irgendwann willst / musst kurz was ändern, dann geht die Sucherei, kompilieren usw wieder los

Hierzu gibt es heutzutage wesentlich einfachere Möglichkeiten

#7992482
Lesenswert?

Karl K. schrieb:

Veit D. schrieb:

Und haste danach ausgelesen zum Gegenprüfen?

Selbstverständlich - der uc startet immer mit RTC-Zeit. So wie er soll. Die RTC läuft. Strom weg - uc startet mit richtiger RTC-Zeit neu. Da liegt nicht das Problem.

Okay, hättest du gleich schreiben sollen wie du getestet hast. Welche "Störung" soll dann in der RTC die Daten löschen? Wenn die Knopfzelle die Versorgung übernimmt, kann da nichts passieren. Was erhoffst du dir demzufolge vom RS485 Bus? Der ist nachgelagert.

: Bearbeitet durch User
#7992516
Lesenswert?

Veit D. schrieb:

kann das nicht über die Datenleitung kommen. Wie soll das gehen?

Hab ich nicht behauptet und auch nicht gesagt.

Es gibt Probleme mit dem Protokoll. Mir fällt z.B. das fehlende Null-setzen von uart_str_rx_ct auf und das fehlende Syncbyte. So wie das jetzt ist, ist es schwieriger zu synchronisieren. Auch wenn er das dann schon merkt, dass er danaben ist. Dann sind die Daten aber futsch, denn das Wiederaufsetzen fehlt. Zumindest in dem gezeigten code. Wenn das Protokoll sicher läuft, kann er sich um die RTC kömmern. Er kann natürlich erst die RTC machen und dann das Protokoll.

Bis jetzt hat der TO keine scope-Bilder vom i2c-Bus gezeigt (Abschlusswiderstand), also ist es ihm wohl vorerst mal nicht so wichtig.

Oder er sitzt vorm PC und lässt den Kopf rauchen. Oder schaufelt Schnee. Für mich ist es erst wieder sinnvoll mich hier zu beteiligen, wenn ein geänderter code vorliegt und Messungen. Ohne Angaben und zielführende Rückmeldungen kann lange orakeln ...

#7992532
Lesenswert?

Hallo,

wie soll denn ein fehlerhaftes Protokoll die RTC resetten? Geht doch gar nicht. Also warum bastelt man hier an einem zusätzlichen Protokoll, wenn man das Problem auch mit der vorhandenen 5cm I2C Verbindung auf den Grund gehen kann. Diese verbleibt ohnehin vorhanden. Der TO fragt nach einem Protokoll, hat aber ein Problem mit einer sich resettenden RTC. Hat nichts miteinander zu tun. Man muss bei der RTC anfangen.

#7992627
Lesenswert?

Veit D. schrieb:

wie soll denn ein fehlerhaftes Protokoll die RTC resetten?

so:

1
    if(((crc>>8)==uart_str_rx[uart_string_lenx+1]) && ((crc&255)==uart_str_rx[uart_string_lenx]))
2
    {    
3
    u8 cmd =uart_str_rx[3];
4
    switch (cmd)
5
      {
6
        case (7)://command set rtc
7
        hou=uart_str_rx[4];
8
        min=uart_str_rx[5];
9
        sec=uart_str_rx[6];
10
        DAY=uart_str_rx[7];
11
        MON=uart_str_rx[8];
12
        YEA=uart_str_rx[9];
13

14
        kk_ds1307_setTime(hou,min,sec);//Sommerzeit 
15
        kk_ds1307_setDate(DAY,MON,YEA);
16

17
        break;
18
...

(noch nicht getestet)

#7992632
Lesenswert?

Nick schrieb:

Mir fällt z.B. das fehlende Null-setzen von uart_str_rx_ct auf und das fehlende Syncbyte.

uart_str_rx_ct wird nach jedem Auslesen von uart_str_rx auf 0 gesetzt - also in 10sec Zeitintervallen. Egal ob die crcv stimmt oder nicht.

das Syncbyte fehlt nicht sondern ist das 9.Byte. Das müsste im Fleury-code drin sein. Da bin ich mir aber nicht sicher. Ich hatte anfangs ein Problem mit dem Timing: uart_init(18230);//baudrate 9600=>18226-18233 ok Eigentlich müsste es ja 19200 wegen clock_prescale_set(clock_div_2);//8mhz sein. Möglicherweise ist das Timing jetzt so genau, dass es auch ohne 9.bit zur Synchronisierung funktioniert.

Bei Hterm ist das Stopbit mit drin und es wird korrekt angezeigt???

Das wäre aber natürlich ein Fehler der beseitigt werden müsste. Danke für den Hinweis.

#7992636
Lesenswert?

Karl K. schrieb:

uart_str_rx_ct wird nach jedem Auslesen von uart_str_rx auf 0 gesetzt - also in 10sec Zeitintervallen. Egal ob die crcv stimmt oder nicht.

Für mich ist das Gewurstel. Code muss klar strukturiert sein, und Abhängigkeiten vom Benutzer so gut wie möglich abgeschirmt werden.

Also: Eine ISR ist eine ISR, sonst nichts. Die ISR empfängt Zeichen, sonst nichts. Die schiebt sie dann in einen Ringpuffer. Der Ringpuffer sperrt die ISR wenn er head & tail des Ringpuffers verändert.

Der Task der die empfangenen Zeichen aufs Protokoll untersucht und das Timing kontrolliert frägt den Ringpuffer ab, und nur den Ringpuffer. Die ISR hat also keine Ahnung was das Protokoll ist, was ein CRC ist. Die kannst Du dann beliebig oft in anderen Projekten hernehmen, ohne mit #defines irgendwelche Seltsamkeiten zu beeinflussen. Genauso der Ringpuffer. Der hat keine Ahnung was CRC ist. Kannst Du dann auch immer wieder verwenden. Spezifisch wird es erst in dem Task der das Protokoll behandelt.

So musst Du nirgendwo nach X Sekunden irgend einen Zähler, der in deiner ISR essentiell ist, verändern. Vor Allem ist das Interupt-sicher. Denn der Interupt kann irgendwann auftreten und du hast keine Ahnung wann das genau ist und ob du nicht in dem Moment was machst was von der ISR verändert wird.

Module müssen strikt gekapselt sein. Alles Andere wird zwangsweise Gebastle und Gefrickle. Ich vermute, dass das "uart_str_rx_ct" eine globale Variable ist. Das ist jenseitig unterirdisch.

Sorry für die harten Worte, aber das gehört zum Handwerkszeug. Ah, und das 9. Bit würde ich persönlich nie verwenden. Das ist für mich auch wieder so was hingebasteltes. Auch wenn es die Krücke in Hardware gibt.

#7992786
Lesenswert?

Nick schrieb:

Eine ISR ist eine ISR, sonst nichts. Die ISR empfängt Zeichen, sonst nichts. Die schiebt sie dann in einen Ringpuffer.

Ja - verstehe ich. Bei meinem Code ist Platz für genau einen Datensatz. ein Ringpuffer kann mehrere Datensätze aufnehmen - muss dann aber wegen der Trennung der Datensätze verwaltet werden.

Der Ringpuffer sperrt die ISR wenn er head & tail des Ringpuffers verändert.

verstehe ich nicht.

Die ISR hat also keine Ahnung was das Protokoll ist, was ein CRC ist.

ja - das ist in meinem Code auch so. Die byte-complete-ISR schiebt die bytes stur in das RX-array und fängt - wenn das array voll ist - wieder von vorne an. Nur wenn im rx-array ein vollständiger crc-korrekter Datensatz steht, wird er weiterverarbeitet.

Ich muss zugestehen, dass ich die Empfangsroutine im Fleury-code nicht verstanden habe und es deswegen selbst gemacht habe. Kann durchaus sein, dass es mit dem Ringpuffer deutlich besser geht. Die Kommunikation zwischen den beiden ucs läuft aber seit dem Wochende völlig problemlos, Wechselrichter und Batteriespeicher werden an- und abgeschaltet und auch die Steuerung des Lasttrennschalters funktioniert so dass andere Baustellen erst mal drängender sind.

#7992804
Lesenswert?

Karl K. schrieb:

Ja - verstehe ich. Bei meinem Code ist Platz für genau einen Datensatz. ein Ringpuffer kann mehrere Datensätze aufnehmen - muss dann aber wegen der Trennung der Datensätze verwaltet werden.

Du musst jeden Puffer "verwalten". Ein Ringpuffer kann natürlich "beliebig" viel aufnehmen. Das machts nur einfacher. Einmal den Ringpuffer programmieren (incl. Schutz vor Überlauf) und ab dann glücklich sein. Irgendwelche Interpretationen haben in der ISR nichts verloren. Die müssen immer so schnell wie möglich beendet werden.

Karl K. schrieb:

Der Ringpuffer sperrt die ISR wenn er head & tail des Ringpuffers verändert.

verstehe ich nicht.

Du weisst nicht, wann der nächste INT auftritt. Wenn er genau in dem Moment auftritt wo du aus dem Puffer liest -und damit head & tail veränderst- sind die Daten möglicherweise nicht mehr konsisten. Zwei Prozesse wollen "gleichzeitig" etwas ändern.

Karl K. schrieb:

Nur wenn im rx-array ein vollständiger crc-korrekter Datensatz steht, wird er weiterverarbeitet.

Also kümmert sie sich um den CRC. Das soll nicht seine Aufgabe sein. Ein Zeichen empfangen hat nichts mit CRC zu tun. Ein Briefkasten empfängt Post und sortiert nicht nach Werbung, Steuerbescheide oder Liebesbriefe.

Karl K. schrieb:

Ich muss zugestehen, dass ich die Empfangsroutine im Fleury-code nicht verstanden habe und es deswegen selbst gemacht habe.

Das ist nicht schlimm, dass du ihn nicht verstehst. Und ich kenn ihn nicht.

Karl K. schrieb:

Die Kommunikation zwischen den beiden ucs läuft aber seit dem Wochende völlig problemlos,

Glaub ich nicht. :-) Was passiert, wenn ein Zeichen eine falsche Parity hat? Wann schlägt dann das Timeout zu? Oder will die ISR dann das nächste Paket wo hin schreiben? Trenn die Sachen in jeweils einfache, überschaubare Aufgaben und vermisch Funktionalitäten nicht.

#7992940
Lesenswert?

Karl K. schrieb:

Veit D. schrieb:

wie soll denn ein fehlerhaftes Protokoll die RTC resetten?

so:

(noch nicht getestet)

Hallo,

demnach schickst du falsche Daten. Ist alles nicht testbar. Die Fleury USART Lib muss man nicht bis ins Detail verstehen, man muss sie nur anwenden wissen. Dazu muss sie nicht verändert werden. Mach dir einen Plan und fang am Besten von vorn an. Du hast dich da irgendwie verrannt. Alles weitere dazu hat Nick schon geschrieben.

: Bearbeitet durch User
#7992997
Lesenswert?

Nick schrieb:

Also kümmert sie sich um den CRC. Das soll nicht seine Aufgabe sein. Ein Zeichen empfangen hat nichts mit CRC zu tun.

Das verstehst du falsch. Die ISR sieht genau so aus:

1
ISR (UART0_RECEIVE_INTERRUPT)  
2
{
3
if(uart_str_rx_ct>uart_string_len+1)uart_str_rx_ct=0;
4
uart_str_rx[uart_str_rx_ct++] = UDR0;
5
return;
6
}

Da passiert nichts außer dass UDRO in den rx-Speicher geschrieben wird. Das Hauptprogramm prüft, ob ein korrekter Datensatz drinsteht und verarbeitet ihn dann weiter.

#7993064
Lesenswert?

1
ISR (UART0_RECEIVE_INTERRUPT)  
2
{
3
if(uart_str_rx_ct>uart_string_len+1)uart_str_rx_ct=0;
4
uart_str_rx[uart_str_rx_ct++] = UDR0;
5
return;
6
}

C und anderer Code wird durch Einsatz von Leerzeihen und Zeilenumbrüchen deutlich lesbarer.

Funktionen, die keinen Rückgabewert haben, muss man nicht mit einem "return"-Statement beenden, sie hören auch auf, wenn sie aufhören.

1
ISR (UART0_RECEIVE_INTERRUPT)  
2
{
3
  if (uart_str_rx_ct > uart_string_len + 1)
4
    uart_str_rx_ct = 0;
5

6
  uart_str_rx[uart_str_rx_ct++] = UDR0;
7
}
#7993089
Lesenswert?

Aaaalso ... Ich möchte beim TO um Entschuldigung bitten. Ich hab den code nicht aufmerksam gelesen, sondern hab mich im Wesentlichen an der Formatierung orientiert (Berufskrankheit). Dadurch fallen ein paar meiner Vorwürfe weg. Die ISR ist kurz, lediglich würde ich wirklich und eindringlich zum Ringpuffer raten. Indem man mehr Abstraktionsebenen einführt wird der code lesbarer. Z.B. beim Ringpuffer must du keinen code für die ISR lesen und kannst auch nicht darin rumfummeln. Also von der Struktur her bleib ich bei meinen Aussagen.

Wie mein Vorposter schon schrieb, besser formatieren. So wie er es gemacht hat seh ich alles sofort auf einen Blick, auch wenn es nicht mein Stil ist.

Und wenns geht keine snipps, da fehlt sonst der Kontext. Ob hier der gesammte Quelltext besser gewesen wäre, ist zweifelhaft. Dann halt bitte die komplette Funktion.

Für eine strukturierte Fortsetzung!

#7993356
Lesenswert?

Veit D. schrieb:

Du hast dich da irgendwie verrannt.

Dieser Post hat mich einen halben Tag Arbeit gekostet. Viele kleinere Probleme. Hauptsächlich beim Timing. Ging dann nur mit Hilfe des Oszilloskops - aber jetzt läuft alles. RTC per PC-c#-Programm gestellt. uc startet mit von der rtc gelesener Zeiteinstellung (Foto).

Nick schrieb:

Ich möchte beim TO um Entschuldigung bitten.

Ich bin dir dankbar, dass du dich intensiv mit dem von mir geschilderten Problem befasst hast. Fehler von mir war, dass ich den Code nicht ordentlich genug präsentiert habe. Ich werde mir damit in Zukunft noch mehr Mühe geben.

Der Unterschied zwischen deinen Anregungen und meinem Code ist wohl, dass bei dir - ebenso wie bei Fleury - die Fehlerbehandlung in der irc erfolgt, bei mir im Hauptprogramm:

1
#if defined( ATMEGA_USART1 )
2
(Original-Fleury)
3
ISR(UART1_RECEIVE_INTERRUPT)
4
/*************************************************************************
5
Function: UART1 Receive Complete interrupt
6
Purpose:  called when the UART1 has received a character
7
**************************************************************************/
8
{
9
    unsigned char tmphead;
10
    unsigned char data;
11
    unsigned char usr;
12
    unsigned char lastRxError;
13
 
14
 
15
    /* read UART status register and UART data register */ 
16
    usr  = UART1_STATUS;
17
    data = UART1_DATA;
18
    
19
    /* get FEn (Frame Error) DORn (Data OverRun) UPEn (USART Parity Error) bits */
20
    lastRxError = usr & (_BV(FE1)|_BV(DOR1)|_BV(UPE1) );
21
            
22
    /* calculate buffer index */ 
23
    tmphead = ( UART1_RxHead + 1) & UART_RX_BUFFER_MASK;
24
    
25
    if ( tmphead == UART1_RxTail ) {
26
        /* error: receive buffer overflow */
27
        lastRxError = UART_BUFFER_OVERFLOW >> 8;
28
    }else{
29
        /* store new index */
30
        UART1_RxHead = tmphead;
31
        /* store received data in buffer */
32
        UART1_RxBuf[tmphead] = data;
33
    }
34
    UART1_LastRxError |= lastRxError;   
35
}

Wegen der manchmal auftretenden Störungen werde ich den uc am Wechselrichter auf die absolut notwendigen Funktionen reduzieren: Messen von Licht und WR-Spannung, schalten der zwei Relais, Berechnen der notwendigen Schaltvorgänge. Anzeige und Zeitbestimmung, Änderung von Schaltgrenzen sowie Feinsteuerung erfolgen mit dem zweiten uc bzw. vom PC aus.

#7993480
Lesenswert?

Karl K. schrieb:

Ich werde mir damit in Zukunft noch mehr Mühe geben.

Danke! Erfolgreich!

Karl K. schrieb:

ebenso wie bei Fleury

Also der code zeigt mir, dass die mit tail und head arbeiten. Das bedeutet für mich, dass die einen Ringpuffer haben (ohne den header-file zu kennen). Schau doch mal im header von Fleury, ob es da Funktionen gibt um einzelne Zeichen reinzuschubsen. Ich vermute, dass Du das nicht wie vorgesehen verwendest.

#7993483
Lesenswert?

Rainer W. schrieb:

Du musst den Fehler, d.h. die Stör-URSACHE finden und beseitigen.

Er muss zwei Sachen machen. Die Software robuster machen, so dass sie Fehler erkennt und abfängt (nochmal eine Anfrage machen). Und natürlich auch die Elektronik störungsresistent machen. RS485 ist eigentlich extrem störsicher, so man ordentliche Treiber (welche?) verwendet.

#7993566
Lesenswert?

Nick schrieb:

Das bedeutet für mich, dass die einen Ringpuffer haben

ja - hat er. 32Byte #define UART_RX_BUFFER_SIZE 32

Wenn man den Anfang des Datensatzes markiert (wie?) sind das nicht einmal zwei Datensätze (bei mir zur Zeit maximal 16bytes je Datensatz). Was bringt das außer Verwaltungsaufwand?

Einen Stapelspeicher, der mehrere Datensätze mit fester Anfangsadresse aufnimmt, könnte ich mir ja noch als vorteilhaft vorstellen. Bei meiner konkreten Anwendung mit nur ganz geringem Datenaustausch aber eher oversized.

Nick schrieb:

ich vermute, dass Du das nicht wie vorgesehen verwendest.

ja - weil ich den Sinn der Fehlerabfrage in der irc nicht verstehe.

Rainer W. schrieb:

Wer garantiert dir denn, dass dieser µC nicht gestört wird?

Niemand. Bislang ist nur die RTC und das TFT ausgefallen. Der uc hat weitergearbeitet - aber mit falscher Zeit. Bislang hat es also keinen Ausfall des uc gegeben.

Du musst den Fehler, d.h. die Stör-URSACHE finden und beseitigen.

RTC weg, TFT weg, zeitabhängige Berechnung des Sonnenstands durch Messung des Lichteinfalls ersetzt. Damit ist zwar nicht die Stör-Ursache aber die Störung beseitigt. Das, was jetzt noch übrig ist, ist hoffentlich störsicher. Deswegen wird das jetzt getestet.

#7993660
Lesenswert?

Karl K. schrieb:

ja - hat er. 32Byte #define UART_RX_BUFFER_SIZE 32

Kann man ja hochsetzen. Platz für 2 Datensätze wäre wohl genug. Ich gehe davon aus, dass Du nicht die Unmengen verschicks. Waren doch irgendwie alle 30 Sekunden.

Karl K. schrieb:

Wenn man den Anfang des Datensatzes markiert (wie?) sind das nicht einmal zwei Datensätze (bei mir zur Zeit maximal 16bytes je Datensatz).

Läuft das nicht über das 9. Bit? Wie gesagt, ich halte eher nichts davon, aber ist halt so.

Was bringt das außer Verwaltungsaufwand?

Die Trennung der Schickten. Ich hätte darauf früher verweisen sollen: https://de.wikipedia.org/wiki/OSI-Modell

Karl K. schrieb:

Einen Stapelspeicher, der mehrere Datensätze mit fester Anfangsadresse aufnimmt, könnte ich mir ja noch als vorteilhaft vorstellen.

Hmm!? Stapelspeicher? Wenn Du tatsächlich einen Stack meinst: Den braucht man, wenn Datensätze nochmal übertragen werden müssen (wg. Fehler) und man nicht mehr so recht weiß welches. Dann braucht man aber auch eine ID für die Pakete. Runtergenommen werden die Pakete vom Stapel, wenn eine positive Quittung dafür vorliegt. Bei striktem PingPong muss man sich nur das zuletzt gesendete Paket merken um es nochmal senden zu können. Also ein Stapel mit der Tiefe 1.

Karl K. schrieb:

ja - weil ich den Sinn der Fehlerabfrage in der irc nicht verstehe.

Die gehört da nicht rein. Schieb einfach Zeichen für Zeichen in das Fleury-Dingens. Und zeig mal den header davon. So kann ich nur orakeln. Bei "Fleury" liefert mir Google nur Bilder aus Frankreich.

#7993699
Lesenswert?

Karl K. schrieb:

Wenn man den Anfang des Datensatzes markiert (wie?) sind das nicht einmal zwei Datensätze (bei mir zur Zeit maximal 16bytes je Datensatz). Was bringt das außer Verwaltungsaufwand?

Deine "Datensätze" interessieren den Ringpuffer nicht. Die Logik, um Deine "Datensätze" zusammenzustellen, gehört nicht in die ISR, sondern in Deine Hauptschleife. Die sieht zyklisch nach, ob Zeichen im Ringpuffer angekommen sind, holt die da raus und packt sie in einen nur für Deine Hauptschleife interessanten Puffer für einen Datensatz.

Sofern Deine Hauptschleife schnell genug ist, kann die Größe des Ringpuffers auch deutlich kleiner sein als ein Datensatz; die Aufgabe der ISR und des Ringpuffers ist es nur, auf der UART eintrudelnde Zeichen zu empfangen und zwischenzuspeichern, ohne daß welche verloren gehen, weil die nachgelagerte Funktionalität in der Hauptscheife zu langsam ist.

So wird in zig Millionen(!) Anwendungen mit UARTs, Ringpuffern und ISRs gearbeitet.

Siehe https://www.mikrocontroller.net/articles/FIFO für die Grundlagen des Ringpuffers, und natürlich http://www.peterfleury.[epizy].com/avr-software.html

(Die eckigen Klammern sind dem eindeutig kaputten Spam-Filter der Forensoftware geschuldet)

: Bearbeitet durch User
#7993707
Lesenswert?

Harald K. schrieb:

ofern Deine Hauptschleife schnell genug ist, kann die Größe des Ringpuffers auch deutlich kleiner sein als ein Datensatz;

Das stimmt so nicht ganz. Man muss mindestens einen kompletten Datensatz - 1 Byte aufheben, damit man sich neu synchronisieren kann. Es ist hilfreich, wenn der Ringpuffer eine "peek"-Funktion hat. Damit kann man nachschauen welches Zeichen auf einer angegebenen Position ist, ohne es "tatsächlich" zu lesen und somit aus dem Ringpuffer zu nehmen. Das geht auch ohne peek, dann verlagert man aber die Pufferung auf eine höhere Ebene. Gefällt mir nicht so gut, weils dann im Fehlerfall eine unnötige Kopiererei und Schieberei gibt.

Ansonsten bin ich Deiner Meinung.

Den Puffer zwei oder paar Datensätze lang zu machen ist nur eine defensive Maßnahme die nur etwas RAM kostet. Der Ringpuffer wird dadurch nicht langsamer. Das ist ja das Schöne daran.

#7993807
Lesenswert?

Harald K. schrieb:

Die Logik, um Deine "Datensätze" zusammenzustellen, gehört nicht in die ISR, sondern in Deine Hauptschleife.

Danke für die hilfreichen Erklärungen. Fifo-Link habe ich mir angeschaut - interessant, wobei auf die Schnelle sicher nicht umzusetzen.

Verständnisfrage: Es stehen mehrere Datensätze unterschiedlicher Länge im Fifo. Wie erkennt das Hauptprogramm wo ein Datensatz anfängt und wie lang er ist. Man bräuchte für die Trennung ja ein byte, das nicht in den Datensätzen vorkommen darf?

#7993812
Lesenswert?

Karl K. schrieb:

Danke für die hilfreichen Erklärungen. Fifo-Link habe ich mir angeschaut

  • interessant, wobei auf die Schnelle sicher nicht umzusetzen.

Wenn es einen Tag länger dauert, wäre doch auch nicht so schlimm.

Man bräuchte für die Trennung ja ein byte, das nicht in den Datensätzen vorkommen darf?

Ja, das wäre eine Möglichkeit. Oder die Datensätze müssen eine Längeninformation enthalten.

: Bearbeitet durch User
#7993911
Lesenswert?

Nick schrieb:

Das stimmt so nicht ganz. Man muss mindestens einen kompletten Datensatz

  • 1 Byte aufheben, damit man sich neu synchronisieren kann.

Nö, die Hauptschleife muss nur das Empfangsfifo schneller leeren als es beschrieben wird. Und dann funktioniert auch die Datensatztrennung mit Timeout.

Die Synchronisation, wie auch immer sie erfolgt, ist ausschließlich Angelegenheit der Hauptschleife.

Wie ich schon schrieb, das ist seit Jahrzehnten etablierte Vorgehensweise in verdammt vielen Anwendungen.

Und bevor man anfängt, irgendwelche Spezialprotokolle zu verwenden, sollte man sich einfach mal ein etabliertes, weit verbreitetes und /einfaches/ Protokoll ansehen. Wie ich auch schon schrieb, bietet sich da Modbus/RTU an. Sofern nur zwei Geräte miteinander kommunizieren, kann man den etwas kniffligeren Teil der Sender/Empfänger-Umschaltung unter den Tisch fallen lassen, die sonst durch die Ansteuerung von RS485-Treibern nötig wird.

Und ich wiederhole mich schon wieder: Für Modbus/RTU gibt es ausreichend viel Testsoftware, so daß man beide Seiten der Kommunikation mit einem definiert "sauber" arbeitenden Gegenstück testen kann.

#7993922
Lesenswert?

Harald K. schrieb:

Nö, die Hauptschleife muss nur das Empfangsfifo schneller leeren als es beschrieben wird. Und dann funktioniert auch die Datensatztrennung mit Timeout.

Dann kannst Du ja gleich den Ringpuffer weglassen. So ganz hast Du das mit den Schichten nicht verstanden.

  1. Schicht: Wenn Du im Ringpuffer einen synchronisierten Datensatz hast, kannst Du das zweite Byte lesen (per peek) und dann den Ringpuffer fragen ob da auch so viele bytes sind. Wenn nicht, sag dem Taskmanager (oder wie immer du das nennen willst) Nöö, hab grad nix zu tun, kannst mich x ms schlafen legen. Wenn ja, dann lies genau so viele Bytes wie sich aus der Länge ergibt. Datensatz komplett aus dem Ringpuffer raus, und erstes Byte sollte wieder das Startbyte sein.

Das setzt aber voraus, dass man seinen code in Tasks aufteil, also nix mit "im Hauptprogramm".

Das "Hauptprogramm" sieht unglaublich banal und so aus (ohne einen Taskmanager):

1
void main(void) {
2
  init();
3
  for (;;) {
4
    Task1();
5
    Task2();
6
    ...
7
  }
8
}
#7993938
Lesenswert?

Harald K. schrieb:

Den Kelch geb' ich an Dich zurück. Danke.

Das ist aber ein rostiger Blumenkübel.

Du sagst, man muss nur schnell genug aus dem Ringpuffer lesen, dann kann er kleiner als ein Datensatz werden. Gut, Du musst dann letztendlich Byteweise vom Ringpuffer in einen Satzpuffer kopieren. Klingt wirklich elegant. Nennt sich das dann "Linearpuffer für genau einen Datensatz"? Und wenn Du noch schneller liest, dann degeneriert der Ringpuffer zum Bytepuffer. Lass ihn gleich aus und lass dich direkt von der ISR bedienen, den "Linearpuffer" kannst Du ja noch weiterverwenden. Und nicht vergessen: Immer feste pollen! Sonst läuft der zu kleine Ringpuffer über der per se, da ohne Polling, minimale Recourcen verbrauchen würde. Ist aber besser so!

Merkste?

So, ich werde hier keine Zeit mehr verschwenden. Viel Erfolg noch!

#7993982
Lesenswert?

Nick schrieb:

Du sagst, man muss nur schnell genug aus dem Ringpuffer lesen, dann kann er kleiner als ein Datensatz werden. Gut, Du musst dann letztendlich Byteweise vom Ringpuffer in einen Satzpuffer kopieren. Klingt wirklich elegant.

Ja, natürlich. Und so funktioniert das schon seit zig Jahrzehnten. So funktionieren unzählige UART-Treiber in unzählichen Anwendungen.

Könnte es sein, daß Du mit UARTs bislang eher nur theoretisch oder in einem sehr eng umschriebenen Anwendungsbereich zu tun hattest?

#7994006
Lesenswert?

Nick schrieb:

Wenn Du im Ringpuffer einen synchronisierten Datensatz hast,

Deine Ringpuffer machen eine Protokollauswertung. Kann man machen. Sollte man nicht.

Überlicherweise dienen Ringpuffer dazu, z.B. asynchrone Uarts mit der auslenden Task zu synchronisieren:

  • RX: Reinschreiben im Interrupt, rauslesen in einer Task
  • TX: Reinschreiben im Hauptprogramm, rauslesen im (TX-) Interrupt

Die Interruptroutine ist damit sehr kurz: Byte auslesen und einfügen. Keine Benachrichtigung, keine Protokollauswertung. Nada, niente.

Warum hält man die Interruptroutine (und die Schnittstelle) kurz und simpel? Damit viele unabhängige Interrupts niemals(!!!) 1ms warten müssen: die typische Zeit für den ms-Ticker und den RX-Fifo bei 115k

Auf verarbeitet man dann byte für byte und tut in aller Ruhe, was zu tun ist.

Der Code wird damit auf allen 3 Ebenen (Task, Interrupt, Fifo) kleiner, portabel er, zuverlässiger, einfacher.

Wie bei den Drehgebern: Die meisten hier sind zu Beginn auch durch die naheliegenden Ansätze gegangen.

D.h. nicht, dass man das Protokoll niemals im Interrupt dekodieren soll. Es heißt nur, dass es gute Gründe geben muss für so ein Gefrickel.

: Bearbeitet durch User
#7996361
Lesenswert?

Harald K. schrieb:

Hättest Du Modbus gewählt, wäre das nicht nötig.

Was ist Modbus? Meine Datensätze sind ja genau wie Modbus aufgebaut. Ist das dann nicht Modbus?

Was wäre mit Modbus nicht nötig? Ich will Einstellungen des ucs verändern ohne den uc neu zu programmieren. Wie soll Modbus das machen? Hat Modbus fertige Befehle um in das eprom des m328 zu schreiben? wenn ja, kann ich mir die Arbeit wirklich sparen - aber wo finde ich sie?

Beitrag #7996363 wurde vom Autor gelöscht.
#7996504
Lesenswert?

Karl K. schrieb:

Hat Modbus fertige Befehle um in das eprom des m328 zu schreiben?

Nein. Modbus ist ein Protokoll, das Speicherplätze adressiert. Also entweder abfragt oder setzt. Wie dein Controller damit umgeht ist dir überlassen. Manche lesen nur Messwerte darüber aus ("Input Registers"), andere senden auch Konfigurationsdaten, Grenzwerte o. dergl. ("Holding Registers").

Ohne jetzt deinen Quellcode betrachtet zu haben: Man würde einen bestimmten Adressbereich dem EEPROM zuweisen, und wenn die Modbus-Registeradresse in diesem Adressbereich liegt, wird das EEPROM entweder beschrieben oder ausgelesen. Wobei Modbus 16-Bit-Register adressiert.

Moderator (Firma: Titel) Persönliche Seite #7996520
Lesenswert?

Karl K. schrieb:

Was ist Modbus?

https://www.google.com/search?q=Was+ist+Modbus

Dieses Stichwort ist erstmals 16 Minuten nach deinem Eröffnungspost und danach wiederholt nochmal gefallen. Hattest du in diesen 10 Tagen nicht ein wenig Zeit, um dich mal in das Thema einzulesen?

Hat Modbus fertige Befehle um in das eprom des m328 zu schreiben?

Gegenfrage: Modbus wurde 1979 spezifiziert. Gab es damals schon einen Mega328?

aber wo finde ich sie?

Du implementierst/programmierst diesen kleinen Zwischenschritt von der Kommunikation zum EEPROM selber.

BTW: der Mega328 hat kein EPROM.

Karl K. schrieb:

Die Kommunikation läuft jetzt weitgehend fehlerfrei

Karl K. schrieb:

funktioniert jetzt zufriedenstellend

Netter Ansatz. Leider ist das das Motto vieler Programmierer: "Ziemlich gut ist gut genug!"

: Bearbeitet durch Moderator
#7996582
Lesenswert?

Lothar M. schrieb:

Dieses Stichwort ist erstmals 16 Minuten nach deinem Eröffnungspost und danach wiederholt nochmal gefallen. Hattest du in diesen 10 Tagen nicht ein wenig Zeit, um dich mal in das Thema einzulesen?

Die Frage bezog sich auf den Vorpost. Der deye-Wechselrichter hat Modbus. Das nützt aber nicht viel, weil sich der Akku damit nicht ausschalten lässt und der Standbye-Verbrauch bei >70Watt liegt - den Akku also nachts sinnlos leersaugt. Ich weiß also was Modbus ist.

Lothar M. schrieb:

BTW: der Mega328 hat kein EPROM.

Vielleicht doch? Datasheet: High endurance non-volatile memory segments ● 32K bytes of in-system self-programmable flash program memory ● 1Kbytes EEPROM ● 2Kbytes internal SRAM ● Write/erase cycles: 10,000 flash/100,000 EEPROM

Wenn er keins hätte - auf dem Rtc-Modul ist auch eins, das könnte ebenfalls genutzt werden.

Lothar M. schrieb:

Netter Ansatz. Leider ist das das Motto vieler Programmierer: "Ziemlich gut ist gut genug!"

Das System läuft. Alternative bessere Lösungen für das deye-WR-Problem gibt es meines Wissens nach nicht. Gut genug wäre also immer noch besser als ohne mein Programm.

#7996807
Lesenswert?

Karl K. schrieb:

Der deye-Wechselrichter hat Modbus. Das nützt aber nicht viel, weil sich der Akku damit nicht ausschalten lässt und der Standbye-Verbrauch bei >70Watt liegt - den Akku also nachts sinnlos leersaugt. Ich weiß also was Modbus ist.

Du stellst einen Zusammenhang her, der nicht existiert. Was Modbus ist, weißt Du offensichtlich /nicht/.

Aber da Du Dir nicht helfen lassen willst - bitte, viel Erfolg mit Deiner Frickellösung.

#7996838
Lesenswert?

Harald K. schrieb:

Was Modbus ist, weißt Du offensichtlich nicht.

Modbus ist ein Protokoll, das Speicherplätze adressiert. Also entweder abfragt oder setzt. Wie dein Controller damit umgeht ist dir überlassen.<

So sehe ich das auch. Ein Protokoll - mehr nicht.

Modbus-Frame-Aufbau: [Slave-Adresse][Funktionscode][Daten][CRC-16]< Genau so hab ich das. Also Modbus. Oder doch nicht Modbus?

Ich glaube dir, dass du mehr Ahnung vom Programmieren hast wie ich. Aber deine Aussagen sind unverständlich. Was meinst du damit, dass Modbus die Lösung der von mir geschilderten Aufgabenstellung sein soll? Es geht doch zuersteinmal um das hardwäremäßige Senden und Empfangen einzelner Bytes. Ob diese Bytes dann mittels Modbus oder mittels irgendeines anderen Protokolls zusammengesetzt sind ist doch völlig Banane.

#7998773
Lesenswert?

Harald K. schrieb:

Ich geb's auf.

ich nicht.

Ich habe das Terminal-Programm -Beitrag "Re: C# Daten über Serial Port empfangen und in Char speichern"

um crc-Berechnung und Programmtasten für feste Befehle ergänzt, so dass jetzt ein Netzwerk zwischen PC, ucs und Wechselrichter besteht. Das ganze funktioniert.

Es gibt ja schon jede Menge Terminal- und Modbus-Projekte. Ich werde meins sicher fortsetzen weil es halt meins ist. Es würde mich aber interessieren, ob es Vergleichbares fertig gegeben hätte.

Angehängte Dateien:
#7998780
Lesenswert?

Karl K. schrieb:

Hat Modbus fertige Befehle um in das eprom des m328 zu schreiben?

Lothar M. schrieb:

der Mega328 hat kein EPROM.

Karl K. schrieb:

Vielleicht doch? 32K bytes of in-system self-programmable flash program memory ● 1Kbytes EEPROM

EEPROM ist nicht EPROM, und Flash Speicher ist auch nicht EPROM. EPROM werden mit starkem UV Licht gelöscht. Erst danach kann man sie sinnvoll beschreiben.

Karl K. schrieb:

Es geht doch zuersteinmal um das hardwäremäßige Senden und Empfangen einzelner Bytes. Ob diese Bytes dann mittels Modbus oder mittels irgendeines anderen Protokolls zusammengesetzt sind ist doch völlig Banane.

Ich dachte, es geht "zuersteinmal" darum die korrekte und zuverlässige Übertragung zu sichern. Du willst deine Geräte sicher nicht nach jeder kleinen Störung (z.B wenn der Nachbar seine Leuchtstoffröhre schaltet) manuell resetten müssen. Du brauchst ein Kommunikationsprotokoll, dass Störungen erkennt, die fehlerhaften Daten verwirft, die Kommunikationspartner wieder synchronisiert und dann ggf. die fehlenden Teile wiederholt.

: Bearbeitet durch User
#7998811
Lesenswert?

Nemopuk schrieb:

Du brauchst ein Kommunikationsprotokoll, dass Störungen erkennt, die fehlerhaften Daten verwirft, die Kommunikationspartner wieder synchronisiert und dann ggf. die fehlenden Teile wiederholt.

hab ich doch. Über einen timer wird erkannt wann keine Bytes mehr kommen und der Datensatz übertragen ist. Dann setzt der Empfänger den rx-Zeiger für neuen Empfang zurück und prüft, ob die crc stimmt. Stimmt die crc, gibt es ein handshake. Der Sender wartet auf das handshake. Kommt das handshake nicht, wird erneut gesendet.

Nemopuk schrieb:

EEPROM ist nicht EPROM, und Flash Speicher

eine falsa demonstratio - ich gehe mal davon aus, dass der Mod genau gewusst hat, was gemeint ist. RO - read only - des EEPROMs passt auf den Speicher des M328 eh nicht so richtig - auch wenn es im Datenblatt steht.

#7998902
Lesenswert?

Nemopuk schrieb:

War lehrreich.

Das ist auch was anderes, als etwas "eigenes" zu stricken. Denn Deine Implementierung konntest Du sicherlich mit entsprechenden Gegenstellen testen, d.h. mit Terminalprogrammen à la Teraterm oder, sofern Du das zu entsprechenden Zeiten gemacht hat, mit irgendwelchen Mailboxen am anderen Ende der Telephonleitung ...

#7998907
Lesenswert?

Harald K. schrieb:

Denn Deine Implementierung konntest Du sicherlich mit entsprechenden Gegenstellen testen, d.h. mit Terminalprogrammen à la Teraterm ode

Ja, aber umgekehrt. Es war teil meines Terminalprogramm für Mailboxen (ein nicht weniger lehrreiches Hobbyprojekt).

Beruflich habe ich leider nicht die Zeit, mich so intensiv mit Grundlagen zu befassen. Da muss alles schnell (=billig) fertig werden. Ich hasse das. Aber man wird gut bezahlt.

: Bearbeitet durch User
#7998912
Lesenswert?

Nemopuk schrieb:

Ich habe mal das Z-Modem Protokoll (zwar nicht erfunden aber) selbst implementiert.

Wenn es für eine spezielle Plattform ist, ist das doch Standard. Ich habe auch schon einen Modbus-Parser für den STM32 implementiert. Ein Kommilitone hat - ohne den Ursprung zu kennen - den Bresenham-Algorithmus "erfunden". Dass es schon jemandem vor ihm gab, nach dem der Algorithmus benannt wurde, war für ihn OK.

Antwort schreiben

Bitte melde dich an, um einen Beitrag zu schreiben.

oder

Mit Google-Account einloggen

Die Registrierung ist kostenlos und dauert nur eine Minute.

Jetzt registrieren