Hallo liebe Community, ich habe eine Frage an euch: ich implementiere gerade einen MDB-Sniffer. Ich darf zitieren: "MDB is more or less a simple serial protocol. It works at 9600, 9, N, 1. That means 9600 Baud, 9 data bits, no parity bit, and one stopbit." [1]. Also es handelt sich einfach um ein UART Protokoll. Nun, nachdem es sich um ein Bussystem handelt, werden die Slaves vom Master (Vending Machine Controller, VMC) genannt gepollt. Wie hier sehr gut zu sehen ist: http://blog.bouni.de/blog/2012/07/11/the-mdb-protocol-part-3/ sendet der VMC einen Datenblock der max. 36 Byte lang sein kann / die Adressierung folgt über das erste Byte des Datenblocks (siehe auch den ersten Link). Ich möchte die Kommunikation zw. dem VMC und den Slaves mit sniffen und komme nun schon langsam zu der Frage: Ich möchte diese Bytes Event based (ISR) auslesen. Nachdem die Länge des Datenblocks variabel sein kann, kann ich nicht mit Hilfe eines UART Ringbuffers mit einer fixen Länge arbeiten (so hätte ich nämlich mit der SW überprüfen können, wenn voll dann gib mir den Block quasi :) ) .. also ich möchte komplette Blöcke erkennen. Der VMC setzt beim ersten Byte ein Modebit, der letzte Byte ist die CHK. Dh hier kann ich leider nicht mit Hilfe des Modebits das Ende des Datenblocks effizient erkennen (und bis zum nächsten Datenblock kann ich ebenfalls nicht warten, da ich in bestimmten Fällen sofort antworten muss - ich gebe mich quasi als Slave aus). Nun um das Ende des Blocks zu erkennen fallen mir zwei Optionen ein: * lt. spec haben wir einen interbyte timeout von 1ms (Timeouts zwischen der jeweiligen Bytes des Blocks) .. ich kann in meiner Schleife also die Logik implementieren "ist das erste byte da (modebit!), warte ~2 ms (1ms timeout + 11 Bits bei 9600 Baud ~= 1,14ms), ist uart rx available, lese rx buffer .. wenn nicht datenblock beendet, vmc in "receiving state". Die 2ms sind jez schnell daher gesagt. Ein höherer Wert wäre eventuell besser... * ich könnt ebenfalls jedes byte rauslesen und ab dem zweiten byte eine "isThisTheLastByte()" Funktion schreiben die mein Array quasi interiert und die checksum berechnet und dann mitm letzten byte des arrays vergleicht. sprich das ist die checksume, und juhu Datenblock beendet. Erste Frage ist, welche Lösung ist die bessere / saubere Variante? Bzw noch wichtigere Frage, kann ich dieses Problem nicht mit Hilfe meiner ISR viel leichter lösen? :-x Dass die HW erkennt, ok Sendepause -> hier mein Buffer :) Ich hoffe das ist irgendwie verständlich für euch :) Freue mich auf eure Antowrten :) Danke und Liebe Grüße aus Wien [1] http://blog.bouni.de/blog/2012/05/06/the-mdb-protocol-part-1/
Wichtiger Nachsatz: nachdem sich nicht viele Entwickler an Regeln/Specs usw halten, sehe ich das mit dem Interbyte Timeout ein bisschen problematischer :) Add: und klar ist die zweite Variante rechenaufwendiger, jedoch bestehen mehr 85% aller Blöcke aus zwei Bytes da der VMC die Devices einfach pollt und ACK zurückbekommt - sprich, es passiert gerade nichts :) Add: ich arbeite mit einem ATxmega32
:
Bearbeitet durch User
Martin Z. schrieb: > haben wir einen interbyte timeout von 1ms (Timeouts zwischen > der jeweiligen Bytes des Blocks) 1. Im RxD-Interrupt wird das empfangene Byte in den Buffer geschrieben und ein Timer mit 2 ms gestartet bzw. neu gestartet. 2. Im Time-Interrupt (d.h. es sind 2 ms seit dem letzten Empfang vergangen) wird ein Flag gesetzt, dass die VMC-Message komplett ist, und diese in den Message-Buffer kopiert. Der Timer wird gestoppt, falls er sonst seinen Zyklus wiederholt. CNC-Prüfung und Auswertung erfolgt dann im Hauptprogramm aufgrund des Flags. Georg
Hi, danke für die Antwort. Denke auch dass es die beste Variante ist. Somit habe ich doppelte Überprüfung. Wie du im Anhang sehen kannst, verzichtet der Controller auf das Interbyte Timeout :) und die Byte-Übertragungsdauer ist ~1.1ms. Werde den Timer auf 1.25ms stellen. Bei einer max. Datenblocklänge von 36 Byte habe ich dann eine Abweichung von ~3.6ms .. um dies zu umgehen, werde ich mitm USART buffer arbeiten. Was sagst du? :) LG
:
Bearbeitet durch User
Martin Z. schrieb: > Bei einer max. Datenblocklänge von > 36 Byte habe ich dann eine Abweichung von ~3.6ms Nein, wieso? So wie ich geschrieben habe, wird der Timer bei jedem empfangenen Byte neu mit x ms gestartet, da addiert sich nichts auf. Martin Z. schrieb: > Werde den Timer auf 1.25ms stellen Warum? Die Pause zwischen den kompletten messages ist doch deutlich grösser, oder nicht? Georg
Natürlich hast recht. Jez habe ich verstanden - denke ich - wie du es vorschlägst. Hier ein Pseudo-Code:
1 | bool timer_running = 0; |
2 | bool timer_flag = 0; |
3 | |
4 | UART_Rx_ISR() { |
5 | if (timer_running) { |
6 | Timer_Stop(); |
7 | if (timer_flag) { |
8 | timer_flag = 0; |
9 | } |
10 | } |
11 | USART_RXComplete(); // Stores received data in RX software buffer |
12 | |
13 | Timer_Set_Counter(0); |
14 | Timer_Start(); |
15 | timer_running = 1; |
16 | } |
17 | |
18 | TC_ISR() { |
19 | timer_flag = 1; |
20 | } |
21 | |
22 | /*******************************/ |
23 | /** some where in a function **/ |
24 | /*******************************/ |
25 | |
26 | mdb_sniffer() { |
27 | while(!timer_flag) { |
28 | if(USART_RXBufferData_Available()) { |
29 | rec_byte = USART_RXBuffer_GetByte() |
30 | } |
31 | } |
32 | if (recv_len > 0) { |
33 | // VMC Data block complete |
34 | // do something |
35 | // recv_len = 0; |
36 | } |
37 | } |
Habe ich das richtig verstanden? :) Timer-Wert setze ich auf 2,5ms oder
3ms.
> Warum? Die Pause zwischen den kompletten messages ist doch deutlich
grösser, oder nicht?
Es gehts ja nicht um die gesamten Blöcke sondern um die einzelnen Bytes.
Greetz
:
Bearbeitet durch User
Martin Z. schrieb: > Jez habe ich verstanden - denke ich Nein, nicht wirklich. Prinzip A: Zeichen werden per RxD-Interrupt empfangen. Immer. Prinzip B: Eine empfangene message ist vollständig, wenn eine längere Pause eintritt. Dann läuft ein Timer ab und in der Timer ISR wird ein Flag gesetzt, dass die message jetzt fertig empfangen ist und bearbeitet werden kann. Prinzip C: Dazu wird der Timer bei jedem empfangenen Zeichen neu gestartet mit 2 ms, das geschieht im RxD-Interrupt, zusätzlich zum Abspeichern des Zeichens. Folglich läuft der Timer nie ab, solange zeichen an Zeichen gesendet wird, erst wenn nichts mehr kommt (B). Prinzip D: Damit danach nichts weiter passiert, wenn ein Zeichen einer neuen message empfangen wird, wird der Timer in der Timer ISR nach B ganz abgeschaltet. Prinzip E: Der ganze Zyklus startet neu, wenn das erste Zeichen einer message empfangen wird. Die Implementierung ist furchtbar einfach: RxD Interrupt: speichere Zeichen. Starte 2 ms-Timer fertig Time-Interrupt: Stop Timer Setze Flag für die Verarbeitung optional: kopiere die message in einen weiteren Buffer für die Verarbeitung und lösche den Empfangsbuffer fertig Verarbeitung: Setze Flag zurück tu was nötig ist. Martin Z. schrieb: > Es gehts ja nicht um die gesamten Blöcke sondern um die einzelnen Bytes. Ganz ganz falsch. Was willst du denn mit einem einzelnen Byte? Georg
Sei nicht streng, wir reden eh vom selben :-P hab deine Logik eh verstanden, nur ein wenig verkompliziert mit meinen flags (sollte ein pseudo-code sein ;) )
1 | ISR(USART_SLV_RXC_vect) { |
2 | USART_NineBits_RXComplete(&USART_SLV); |
3 | TCC0_RESTART(); |
4 | } |
5 | |
6 | ISR(TCC0_OVF_vect) { |
7 | TCC0_STOP(); |
8 | TCC0_flag = 1; |
9 | } |
10 | |
11 | void mdb_slave_poll() { |
12 | if (TCC0_flag) { |
13 | uint16_t recv_byte; |
14 | while (mdb_receive_9bit(&USART_SLV, &recv_byte)) { |
15 | uint8_t modebit = recv_byte >> 8; |
16 | uint8_t payload = recv_byte && 0xFF; |
17 | |
18 | if (modebit) { |
19 | mdb_slave.slv_addr = payload & MDB_BYTE_FORMAT_ADDR_MASK; |
20 | } |
21 | |
22 | if (mdb_slave.slv_rx_len < MDB_DATA_MAX_BLOCKSIZE) { |
23 | mdb_slave.slv_rx_buf[mdb_slave.slv_rx_len++] = payload; |
24 | } else { |
25 | LOGGER_INFO("VMC data block exceeds the allowed maximum size\n"); |
26 | } |
27 | } |
28 | /* VMC data block complete */ |
29 | if (mdb_slave.slv_rx_len > 0) { |
30 | if (mdb_validate_chk(mdb_slave.slv_rx_buf, &mdb_slave.slv_rx_len)) { |
31 | /* TODO: report data and so on*/ |
32 | } |
33 | /* reset the payload length counter */ |
34 | mdb_slave.slv_rx_len = 0; |
35 | } |
36 | TCC0_flag = 0; |
37 | } |
38 | } |
> Ganz ganz falsch. Was willst du denn mit einem einzelnen Byte?
Auch hier reden wir vom selben :) Natürlich will ich den gesamten Block
herausfischen. Um dies zu tun, brauche ich ja eben diese pseudo ~2ms um
Sendepause - sprich "das ist kein Byte mehr gekommen" - zu erkennen :)
Also wie gesagt, ich denke ich habs :) Danke dir
:
Bearbeitet durch User
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
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.