Hallo liebes Microcontroller Forum, ich nutze einen ATMEGA88A-PU. UART, 8Data Bit; 1 Stop Bit; BAUD: 9600 Ich programmiere den Controller mit C im Atmel Studio. Dem Controller schicke ich über LabView 2 Byte. Zuerst das MSB dann das LSB. Die beiden Bytes füge ich auf dem Controller in eine 16 Bit Variable zusammen. Das klappt soweit ganz gut. Wenn ich nun aber in einer Dauerschleife permanent das MSB und das LSB an den Microcontroller schicke, springt der Wert der 16 Bit Variable auf dem Controller kurz auf einen anderen Wert und dann wieder zurück auf den Wert den ich permanent schicke. Konkrekt bedeuetet das: Wenn ich dauerhaft 1000 (dezimal) sende, springt die Variable kurzzeitig auf 59624. Das bedeutet, dass das MSB = 232 ist und das LSB auch = 232 obwohl ja MSB ungleich LSB sein sollte. Etwas ähnliches passiert wenn ich 100 (dezimal) sende. Dann empfänge ich mittendrin kurzzeitig 25700. Das entspricht MSB = 100(dezimal) und LSB = 100 (dezimal). Es ist also ein deutliches Muster zu erkennen. Leider habe ich keine Idee wie ich das beseitigen kann. Bin sehr unerfahren in Sachen Mikrocontroller Programmierung. Im Folgendem beschreibe ich wie ich den Empfang programmiert habe. Sicherlich keine elegante Lösung. Vielleicht liegt hier schon das Problem. Der Empfang auf dem Mikrocontroller läuft folgendermaßen ab: Ich empfange Daten per Interrupt. Jedes mal wenn ein Byte empfangen wird, also der Empfangsinterrupt ausgelöst wird, erhöhe ich eine Zählvariable um den Wert 1. Empfängt der Mikrocontroller nun den Befehl, dass er in die 16 Bit Variable schreiben (Befehl: 85) soll, wird der Zähler = 0 gesetzt. Das nächste Byte, das dann empfangen wird setzt den Zähler = 1. Das erkennt meine if-Abfrage und speichert diesen Wert dann als MSB ab. Beim nächsten Byte, das empfangen wurde ist der Zähler = 2. Auch das erkennt eine if-Abfrage und speichert das Byte in einer Variablen namens LSB. Das MSB und das LSB verknüpfe ich dann ODER und erhalte so den 16 Bit Wert. Anschließend setze ich den Zähler = 10, sodass die beiden if Bedingungen erst wieder erfüllt sind, sobald ich den Befehl schicke um in diese Variable zu schreiben. Viele Grüße & besten Dank
Generell sagen ein paar Zeilen Code mehr als tausend Worte. Es klingt aber ganz danach als würde mit deiner Zählung irgendetwas nicht stimmen, so dass das LSB versehentlich als MSB interpretiert wird.
Jay K. schrieb: > Es klingt aber ganz danach als würde mit deiner Zählung irgendetwas > nicht stimmen, so dass das LSB versehentlich als MSB interpretiert wird. Deswegen schicken clevere die Zahl in ASCII mit einem newline als Trennzeichen. MfG Klaus
Vermutlich benötigst du eine Synchronisation zwischen Sender und Empfänder für den Fall das irgendwann mal ein Byte verschütt geht. Das geht z.B. über eine Sendepause, wenn LabView in eine gröberen Zeitraster sendet als der UART zum Senden beider Bytes benötigt: <Befehl><MSB><LSB>...Pause...<Befehl><MSB><LSB>...Pause... Hier könntest du auf dem µC einen Timer mitlaufen lassen, der den Abstand zwischen zwei empfangenen Bytes misst. Oder du verpackst das in ein Protokoll, z.B. (binär) 0b00BB BBBB 0b01BB MMMM 0b10MM MMLL 0b11LL LLLL B = Bits des Befehls M = Bits des MSB L = Bits des LSB Die '0' und '1' ober wären dann Byte-Zähler, aus denen sich die Reihenfolge rekonstruieren lässt, bzw. verlorene Bytes erkannt werden können. Den Luxus muss man sich dann mit ein wenig Bitschieberei erkaufen.
Jo, Programm bitte. D.h. eigentlich funktioniert es, aber manchmal nicht?
Klaus schrieb: > Jay K. schrieb: >> Es klingt aber ganz danach als würde mit deiner Zählung irgendetwas >> nicht stimmen, so dass das LSB versehentlich als MSB interpretiert wird. > > Deswegen schicken clevere die Zahl in ASCII mit einem newline als > Trennzeichen. > > MfG Klaus Was zur Folge hat, dass du für eine uint16 in worst-case (0xffff) 5 ASCII Zeichen und damit 5 Bytes übertragen musst...und wenn eins verloren geht, wird es trotzdem nicht erkannt? Sinnvoller ist da ein Protokoll, wie von Sebastian vorgeschlagen. Ein ganz einfaches wäre ein Acknowledge vom Empfänger nach jedem Byte <Befehl1></ACK><MSB1></ACK><LSB1></ACK><Befehl2></ACK><MSB2></ACK><LSB2> </ACK>
Hans D. schrieb: > Wenn ich nun aber in einer Dauerschleife permanent das MSB und das LSB > an den Microcontroller schicke, springt der Wert Ev liegt Dein Problem gar nicht im Microcontroller, sondern im Sender, d.h. Du hast einen Buffer-Overflow. Der Vorteil eines ASCII-Protokolls liegt für mich in der einfachen Debug-Möglichkeit, jedes Terminalprogramm reicht zum Testen. Gruß, Stefan
Hans D. schrieb: > Empfängt der Mikrocontroller nun den Befehl, dass er in die 16 Bit > Variable schreiben (Befehl: 85) soll, wird der Zähler = 0 gesetzt. > Das nächste Byte, das dann empfangen wird setzt den Zähler = 1. Das > erkennt meine if-Abfrage und speichert diesen Wert dann als MSB ab. Beim > nächsten Byte, das empfangen wurde ist der Zähler = 2. Auch das erkennt > eine if-Abfrage und speichert das Byte in einer Variablen namens LSB. > Das MSB und das LSB verknüpfe ich dann ODER und erhalte so den 16 Bit > Wert. Anschließend setze ich den Zähler = 10, sodass die beiden if > Bedingungen erst wieder erfüllt sind, sobald ich den Befehl schicke um > in diese Variable zu schreiben. Darf dein Programm zwischen dem Empfangen vom MSB und LSB auf die Werte zugreifen? Für mich klingt das nämlich danach, dass der uC ab und an schneller als der UART ist und dann auf deine 16 bit Variable schon zugreift obwohl die noch gar nicht fertig gebaut ist. Du musst hier also einen atomaren Zugriff auf die 16 bit Variable zusammenstellen damit man nicht während des Neubaus schon mal auf die Variable zugreift.
Jay K. schrieb: > Was zur Folge hat, dass du für eine uint16 in worst-case (0xffff) 5 > ASCII Zeichen und damit 5 Bytes übertragen musst Was für eine Verschwendung! Stefan K. schrieb: > Der Vorteil eines ASCII-Protokolls liegt für mich in der einfachen > Debug-Möglichkeit, jedes Terminalprogramm reicht zum Testen. Warum sind wohl die meißten Protokolle im Internet ASCII? Clever, halt. MfG Klaus
Hans D. schrieb: > Im Folgendem > beschreibe ich wie ich den Empfang programmiert habe. das hilft nix, der Programmtext dagegen um so mehr
M. K. schrieb: > Für mich klingt das nämlich danach, dass der uC ab und an > schneller als der UART ist und dann auf deine 16 bit Variable schon > zugreift obwohl die noch gar nicht fertig gebaut ist. Oder er ist langsamer und der nächste Wert des MSB liegt an bevor der alte abgeholt wurde. Quelltext als Anhang, bitte.
Jim M. schrieb: > Oder er ist langsamer und der nächste Wert des MSB liegt an bevor der > alte abgeholt wurde. Die Übertragung eines Bytes dauert 1ms. So langsame Controller gibt es gar nicht.
Hans D. schrieb: > Dem Controller schicke ich über LabView 2 Byte. Zuerst das MSB dann das > LSB. Die beiden Bytes füge ich auf dem Controller in eine 16 Bit > Variable zusammen. Das klappt soweit ganz gut. Woher soll der Empfänger wissen, welche das erste und welches das zweite Byte ist. Jeweils ein Start- und ein Stopbit reichen nicht, um die Bytes beim Empfänger zu unterscheiden. Du könntest drei Byte übertragen. Das würde für 21 Bit Daten zzgl. 1 Bit Synchronisationsinformation pro gesendetem Byte reichen.
Hallo an alle, ich bin sehr erfreut, dass mir so viele Leute helfen wollen.:) Schonmal ein Dankeschön dafür. Den Quellcode findet ihr im Anhang. Viele Grüße
Du verwendest UARTInterruptZaehler sowohl im Interrupt als auch in der Hauptroutine und deine if() Abfragen sind sicherlich nicht atomar -> Es kann passieren dass du gerade einen deiner if() Blöcke abarbeitest, dann kommt ein Interrupt mit neuen Daten und "Command" wird überschrieben. Du könntest das ganze synchronisieren indem du nicht ständig die Hauptroutine laufen lässt, sondern in der ISR eine Art Semaphore setzt, das in der Hauptroutine gepollt und ausgewertet wird. In der ISR selbst setzt du nur das Semaphore und pufferst evtl noch "Command" weg, so dass die Hauptroutine mit einer Kopie von "Command" arbeitet
Mir ist grade klar geworden, dass es sinnvoll wäre nochmal zu beschreiben wie der Sender arbeitet: Er sendet: <Befehl 85><warten auf Quittierung vom Mikrocontroller uC sendet 1 (dezimal)><sendet MSB><warten auf Quittierung vom Mikrocontroller uC sendet 1 (dezimal)><sendet LSB><Wartet auf 2 Byte(uC antwortet mit MSB und LSB)> Anschließend beginnt der Zyklus von Vorne, also wieder: <Befehl 85><warten auf Quittierung vom Mikrocontroller uC sendet 1 (dezimal)><sendet MSB><warten auf Quittierung vom Mikrocontroller uC sendet 1 (dezimal)><sendet LSB><Wartet auf 2 Byte(uC antwortet mit MSB und LSB)> .... Das Ganze in einer Dauerschleife. Dabei treten dann die oben beschriebenen Probleme auf.
Hans D. schrieb: > Das Ganze in einer Dauerschleife. Dabei treten dann die oben > beschriebenen Probleme auf. Ja aber wie verhinderst du, dass der Mikrocontroller lesend auf die 16 bit Variable während grade eine neue 16 bit Variable an den Mikrocontroller übertragen wird? Bei einen atomaren Zugriff wird verhindert, dass während eines Lesezugriffs schreibend auf die Variable zugegriffen wird und umgekehrt.
Auf die 16 Bit Variable "Aufstromeit" greift der uC nur zu, wenn ich ihm den Befehl 5 (dezimal) sende. Verhindert das nicht, dass ich die Variable lese während ich Sie schreibe?
Hans D. schrieb: > Auf die 16 Bit Variable "Aufstromeit" greift der uC nur zu, wenn ich ihm > den Befehl 5 (dezimal) sende. Hm? Du greifst auch beim Zusammenbauen darauf zu. Und genau da kann was schief gehen, wenn du einen Interrupt bekommst und dann in "Command" das LSB geschrieben wird, obwohl du in deiner MSB if-Schleife bist
Hans D. schrieb: > Mir ist grade klar geworden, dass es sinnvoll wäre nochmal zu > beschreiben wie der Sender arbeitet: Dir sollte klar werden, daß es totaler Mist ist, was du machst. Wenn ein Datensatz, der aus mehreren Bytes besteht, übertragen werden soll, brauchst du ein Protokoll. Das sieht minimal so aus: Präambel Daten Postambel Wenn die Länge der Daten immer gleich ist, kann man auf die Postambel auch verzichten. Fehlererkennung bzw. -korrektur lassen wir mal aussen vor. Der Sinn der Präambel ist, dem Empfänger eindeutig zu signalisieren, daß jetzt ein Satz von Daten folgt. Der der Postambel ist es, ihm mitzuteilen, daß die Übertragung jetzt zuende ist. Die Daten, die dazwischen übertragen werden, dürfen weder für eine Präambel noch für eine Postambel gehalten werden. Alles, was dafür nötig ist, wurde schon vor Jahrzehnten erfunden und nennt sich ASCII. Also benutze das auch. Keiner hat hier Lust, deinen Murks zum Laufen zu bringen.
Thomas E. schrieb: > Wenn ein Datensatz, der aus mehreren Bytes besteht, übertragen werden > soll, brauchst du ein Protokoll. Das sieht minimal so aus: > > Präambel > Daten > Postambel Übernimmt in dem Fall nicht der Befehl die Funktion der Präambel? Wie man das mit ASCII macht, verstehe ich nicht. Finde nirgendwo eine Anleitung dafür..
Danke an Thomas Eckmann dass du einmal ein klares Wort gegen dieses Herumge-Eiere in diesem Thread geschrieben hast.
Hi, bei 2 Byte langt doch auch der 9Bit Modus der UART, MSB hat 9. bit gesetzt, LSB nicht. (wo das zusätzliche bit ist würde ich halt erst gucken wie die Darstellung im Atmega ist. Minimal, keine Absicherung, aber schon mal kein Vertauschen von MSB mit LSB. MfG myasuro
Okay....wenn das alles Murks ist, würde ich versuchen ein richtiges Übertragungsprotokoll, das mit ASCII Zeichen arbeitet, zu programmieren. Habe sowas leider noch nie gemacht. Könnt ihr mir mit Links weiterhelfen wo beschrieben steht wie man sowas macht? Vielen Dank
Hallo Hans, Hans D. schrieb: > Könnt ihr mir mit Links weiterhelfen > wo beschrieben steht wie man sowas macht? https://ccrma.stanford.edu/courses/250a-fall-2005/docs/avrlib/ dort nach STX/ETX Packet Protocol suchen. ms
Thomas E. schrieb: > Präambel > Daten > Postambel > > Wenn die Länge der Daten immer gleich ist, kann man auf die Postambel > auch verzichten. Oder wenn im Präambel die Länge der Daten mitkodiert ist, oder wenn in den Daten selber das Ende mitkodiert ist (wie zB bei MIDI). Hans D. schrieb: > Okay....wenn das alles Murks ist, würde ich versuchen ein > richtiges Übertragungsprotokoll, Das hast du eigentlich schon, nur ein wenig zu umständlich. Poste erstmal deine vollständige Code. Als C-Datei.
Hans D. schrieb: > Okay....wenn das alles Murks ist, würde ich versuchen ein richtiges > Übertragungsprotokoll, das mit ASCII Zeichen arbeitet, zu > programmieren. Nein, lass es sein. Das sind die Tipps von Leuten, die keine Ahnung haben, weil die so etwas noch nie implementiert haben. Mach es mit: <SOF><LSB><MSB><EOF> Das sind (und bleiben) immer 4 Byt. Bei ASCII sind es zwischen 1 und 5 Bytes ( + CR/LF) und das Ganze muss dann auch noch umgerechnet werden. <SOF> ist bei mir 0xAA, <EOF> ist 0xA9 und da gab es noch nie irgendwelche Probleme damit. Auf <SOF> warten. Weitere 3 Bytes einlesen. Letztes Byte == <EOF> ?
:
Bearbeitet durch User
1 | #define F_CPU 18432000
|
2 | #include <stdio.h> |
3 | #include <string.h> |
4 | #include <stdlib.h> |
5 | #include <stdio.h> |
6 | |
7 | |
8 | #include <avr/wdt.h> |
9 | #include <util/delay.h> |
10 | #include <avr/io.h> |
11 | #include <avr/interrupt.h> |
12 | |
13 | volatile uint8_t AufstromzeitHB = 0; |
14 | volatile uint8_t AufstromzeitLB = 0; |
15 | volatile uint16_t Aufstromzeit = 0; |
16 | int y = 0; |
17 | int x = 0; |
18 | uint8_t buesy = 0; |
19 | uint8_t adc_write_config[56]; |
20 | uint8_t adc_read_com[49]; |
21 | uint8_t register_status[49]; |
22 | uint8_t Zahlen[12]; |
23 | volatile uint8_t t = 0; |
24 | volatile int z =0; |
25 | uint8_t Byte1, Byte2, Byte3; |
26 | uint8_t datain_spi; |
27 | uint8_t START= 0b00000000; |
28 | volatile uint8_t Command = 0; |
29 | volatile uint8_t EmpfangeAufstromzeit = 1; |
30 | uint8_t AnzahlBytes = 0; |
31 | uint8_t UARTInterruptZaehler =10; |
32 | |
33 | void long_delay(uint16_t ms) { |
34 | for (; ms>0; ms--) _delay_ms(1); |
35 | }
|
36 | |
37 | |
38 | void USART_Init(uint16_t UBRR) |
39 | {
|
40 | UBRR0H = (uint8_t)(UBRR >> 8); //Schiebe 16-Bit um 8 Stellen nach rechts (1111111111111111 --> 0000000011111111) |
41 | UBRR0L = (uint8_t)UBRR; //Lese die ersten 8 Bit der Variable UBRR ein und schneide den Rest ab. |
42 | UCSR0A = ~(1<<U2X0); |
43 | UCSR0C = (1<<UCSZ01)|(1<<UCSZ00); //Frame: 1 stop Bit, 8 data bits |
44 | UCSR0B = (1<<RXEN0)|(1<<TXEN0)|(1<<RXCIE0); //Aktiviere Sender(TXD) und Empfänger(RXD) sowie Receive Complete Interrupt. |
45 | }
|
46 | void USART_Flush(void) |
47 | {
|
48 | uint8_t dummy; |
49 | while (UCSR0A & (1<<RXC0)) dummy = UDR0; |
50 | }
|
51 | void SPI_MasterInit(void) |
52 | {
|
53 | //SPCR |= (0<<DORD);
|
54 | SPCR |= (1<<MSTR); //Controller = Master |
55 | //SPCR &= ~(1<<CPOL); //CLK positive phase
|
56 | SPCR |= (0<<CPHA); //Sampling data @ falling edge |
57 | SPCR |= (0<<SPR1) | (0<<SPR0); |
58 | DDRB |= (1<<PB3); |
59 | //Set MOSI output
|
60 | DDRB &= ~(1<<PB4); //Set MISO input |
61 | DDRB |= (1<<PB5); //Set SCK output |
62 | DDRB |= (1<<PB2); //Set SS output |
63 | |
64 | SPCR |= (1<<SPE)|(0<<SPIE)|(0<<CPOL);// | (1<<SPIE); //SPI enable |
65 | }
|
66 | void IOports(void) |
67 | {
|
68 | DDRB &= ~(1<<DDB1); // Set /DRDY as Input |
69 | DDRD |= (1<<DDD2); // |
70 | PORTD |= (1<<PD2); // |
71 | DDRC |= (1<<DDC2) | (1<<DDC0); |
72 | PORTC |= (0<<PC2) | (1<<PC0); |
73 | }
|
74 | void USART_Senden(uint8_t Daten) |
75 | {
|
76 | while (!(UCSR0A & (1<<UDRE0))); // Warte auf leeren Sendebuffer, Sendebuffer leer, wenn UDREO = 1. |
77 | UDR0 = Daten; //Schreibe, die der Funktion übergebenen 8-Bit, in den Sendebuffer. |
78 | }
|
79 | void SPI_Senden(uint8_t Daten) |
80 | {
|
81 | //PORTB &= ~(1<<PB2); //SS = low (Slave= enabled)
|
82 | |
83 | SPDR = Daten; //Übertragung starten |
84 | while (!(SPSR & (1<<SPIF))); //Warte auf Ende der Übertragung |
85 | //return SPDR;
|
86 | |
87 | }
|
88 | |
89 | uint8_t SPI_Empfangen(void) |
90 | {
|
91 | while (!(SPSR & (1<<SPIF))); |
92 | return SPDR; |
93 | }
|
94 | |
95 | |
96 | |
97 | void reset_SPI_ADS(void) |
98 | {
|
99 | DDRD |= (1<<DDD2); |
100 | PORTD ^= (1<<PD2); |
101 | _delay_us(100); |
102 | PORTD ^= (1<<PD2); |
103 | }
|
104 | void stop_conversion(void) |
105 | {
|
106 | PORTC &= ~(1<<PC2); |
107 | }
|
108 | void start_conversion(void) |
109 | {
|
110 | PORTC |=(1<<PC2); |
111 | }
|
112 | |
113 | void reset_ADS1258(void) |
114 | {
|
115 | PORTC &= ~(1<<PC0); |
116 | _delay_us(100); |
117 | PORTC |= (1<<PC0); |
118 | _delay_ms(17); |
119 | }
|
120 | void ADCInit(void) |
121 | { //stop_conversion(); |
122 | PORTD &= ~(1<<PD2); // /CS of ADC auf GND |
123 | for(int i=0; i<=3; i++) |
124 | {
|
125 | SPI_Senden(adc_write_config[i]); |
126 | }
|
127 | PORTD |= (1<<PD2); |
128 | |
129 | }
|
130 | uint8_t USART_Empfangen(void) |
131 | {
|
132 | while(!(UCSR0A &(1<<RXC0))) //Während ungelesene Daten im Buffer sind, lies den Inhalt des Registers aus |
133 | ;
|
134 | return UDR0; |
135 | }
|
136 | |
137 | |
138 | |
139 | int main(void) |
140 | { adc_write_config[0]=0b01100000; //CONFIG0 |
141 | adc_write_config[1]=0b00000010; //Inhalt CONFIG0 |
142 | adc_write_config[2]=0b01100001; //select config1 |
143 | adc_write_config[3]=0b01110000; //write config1 max delay, low data rate |
144 | |
145 | /////alle PT100////////////////////////////////////////////
|
146 | adc_write_config[4]=0b01100100; //select MUXG0 |
147 | adc_write_config[5]=0b11110000; //write MUXG0 Lese nur PT100 aus |
148 | adc_write_config[6]=0b01100101; //select MUXSG1 |
149 | adc_write_config[7]=0b00000000; //write MUXSG1 |
150 | ///////////////////////////////////////////////////////////
|
151 | |
152 | ////////Lese nur DUT1//////////////////////////////////////
|
153 | adc_write_config[8]=0b01100100; //select MUXG0 |
154 | adc_write_config[9]=0b00000000; |
155 | adc_write_config[10]=0b01100101; //select MUXSG1 |
156 | adc_write_config[11]=0b00000000; //write MUXSG1 |
157 | adc_write_config[12]=0b01100011; //select MUXDIF |
158 | adc_write_config[13]=0b01000000; //write MUXDIF |
159 | ///////////////////////////////////////////////////////////
|
160 | |
161 | //////Lese nur DUT2///////////////////////////////////////
|
162 | adc_write_config[14]=0b01100100; //select MUXG0 |
163 | adc_write_config[15]=0b00000000; |
164 | adc_write_config[16]=0b01100101; //select MUXSG1 |
165 | adc_write_config[17]=0b00000000; //write MUXSG1 |
166 | adc_write_config[18]=0b01100011; //select MUXDIF |
167 | adc_write_config[19]=0b10000000; //write MUXDIF |
168 | //////////////////////////////////////////////////////////
|
169 | |
170 | //////Lese nur DUT3///////////////////////////////////////
|
171 | adc_write_config[20]=0b01100100; //select MUXG0 |
172 | adc_write_config[21]=0b00000000; |
173 | adc_write_config[22]=0b01100101; //select MUXSG1 |
174 | adc_write_config[23]=0b00000000; //write MUXSG1 |
175 | adc_write_config[24]=0b01100011; //select MUXDIF |
176 | adc_write_config[25]=0b00000001; //write MUXDIF |
177 | //////////////////////////////////////////////////////////
|
178 | |
179 | ////Lese nur DUT4/////////////////////////////////////////
|
180 | adc_write_config[26]=0b01100100; //select MUXG0 |
181 | adc_write_config[27]=0b00000000; |
182 | adc_write_config[28]=0b01100101; //select MUXSG1 |
183 | adc_write_config[29]=0b00000000; //write MUXSG1 |
184 | adc_write_config[30]=0b01100011; //select MUXDIF |
185 | adc_write_config[31]=0b00000010; //write MUXDIF |
186 | //////////////////////////////////////////////////////////
|
187 | |
188 | /////PT100 DUT1///////////////////////////////////////////
|
189 | adc_write_config[32]=0b01100100; //select MUXG0 |
190 | adc_write_config[33]=0b00010000; |
191 | adc_write_config[34]=0b01100101; //select MUXSG1 |
192 | adc_write_config[35]=0b00000000; //write MUXSG1 |
193 | adc_write_config[36]=0b01100011; //select MUXDIF |
194 | adc_write_config[37]=0b00000000; //write MUXDIF |
195 | //////////////////////////////////////////////////////////
|
196 | |
197 | /////PT100 DUT2///////////////////////////////////////////
|
198 | adc_write_config[38]=0b01100100; //select MUXG0 |
199 | adc_write_config[39]=0b00100000; |
200 | adc_write_config[40]=0b01100101; //select MUXSG1 |
201 | adc_write_config[41]=0b00000000; //write MUXSG1 |
202 | adc_write_config[42]=0b01100011; //select MUXDIF |
203 | adc_write_config[43]=0b00000000; //write MUXDIF |
204 | //////////////////////////////////////////////////////////
|
205 | |
206 | /////PT100 DUT3///////////////////////////////////////////
|
207 | adc_write_config[44]=0b01100100; //select MUXG0 |
208 | adc_write_config[45]=0b01000000; |
209 | adc_write_config[46]=0b01100101; //select MUXSG1 |
210 | adc_write_config[47]=0b00000000; //write MUXSG1 |
211 | adc_write_config[48]=0b01100011; //select MUXDIF |
212 | adc_write_config[49]=0b00000000; //write MUXDIF |
213 | //////////////////////////////////////////////////////////
|
214 | |
215 | /////PT100 DUT4///////////////////////////////////////////
|
216 | adc_write_config[50]=0b01100100; //select MUXG0 |
217 | adc_write_config[51]=0b10000000; |
218 | adc_write_config[52]=0b01100101; //select MUXSG1 |
219 | adc_write_config[53]=0b00000000; //write MUXSG1 |
220 | adc_write_config[54]=0b01100011; //select MUXDIF |
221 | adc_write_config[55]=0b00000000; //write MUXDIF |
222 | //////////////////////////////////////////////////////////
|
223 | |
224 | |
225 | adc_read_com[0]=0b00110000; //Channel Data Read Command |
226 | adc_read_com[1]=0b00000000; |
227 | adc_read_com[2]=0b00000000; |
228 | adc_read_com[3]=0b00000000; |
229 | adc_read_com[4]=0b00000000; //LSB CH1 |
230 | |
231 | |
232 | |
233 | Zahlen[0]=0b00000000; |
234 | Zahlen[1]=0b00000000; |
235 | Zahlen[2]=0b00000011; |
236 | Zahlen[3]=0b00000100; |
237 | Zahlen[4]=0b00000101; |
238 | Zahlen[5]=0b00000110; |
239 | Zahlen[6]=0b00000111; |
240 | Zahlen[7]=0b00001000; |
241 | Zahlen[8]=0b00001001; |
242 | Zahlen[9]=0b00001010; |
243 | Zahlen[10]=0b00001011; |
244 | Zahlen[11]=0b00001100; |
245 | |
246 | |
247 | //sei(); //Interrupts aktivieren (SREG; I =1);
|
248 | USART_Init(119); //39 //U2Xn = 0 (normal speed in Asynchronus Mode) // f=18.432Mhz // Baudrate = 2400 //39=28800 Baudrate |
249 | sei(); |
250 | SPI_MasterInit(); |
251 | IOports(); |
252 | DDRC |= (1<<DDC5); //LED Ausgang |
253 | _delay_ms(100); |
254 | ADCInit(); |
255 | //DDRC |= (1<<PC5); /LED
|
256 | |
257 | |
258 | |
259 | |
260 | |
261 | while(1) |
262 | { /////////////////////////////////////////////////////////////////////////////////////////// |
263 | if (Command == 0b00000001 && EmpfangeAufstromzeit == 1) |
264 | { cli(); |
265 | buesy = 1; |
266 | PORTD &= ~(1<<PD2); // ChipSelect low |
267 | for (int i = 8;i<=13;i++) |
268 | {
|
269 | SPI_Senden(adc_write_config[i]); |
270 | }
|
271 | |
272 | PORTD |= (1<<PD2); // ChipSelect high |
273 | PORTC |=(1<<PC2); // START pin of ADS high level |
274 | PORTC &=~(1<<PC2); // START pin low again |
275 | |
276 | AnzahlBytes = 5; |
277 | Command = 0; |
278 | buesy = 0; |
279 | sei(); |
280 | USART_Senden(1); |
281 | }
|
282 | /////////////////////////////////////////////////////////////////////////////////////////////////
|
283 | if (Command == 0b00000010 && EmpfangeAufstromzeit == 1) |
284 | { cli(); |
285 | buesy = 1; |
286 | PORTD &= ~(1<<PD2); // ChipSelect low |
287 | for (int i = 14;i<=19;i++) |
288 | {
|
289 | SPI_Senden(adc_write_config[i]); |
290 | }
|
291 | |
292 | PORTD |= (1<<PD2); // ChipSelect high |
293 | PORTC |=(1<<PC2); // START pin of ADS high level |
294 | PORTC &=~(1<<PC2); // START pin low again |
295 | |
296 | AnzahlBytes = 5; |
297 | Command = 0; |
298 | buesy = 0; |
299 | sei(); |
300 | USART_Senden(1); |
301 | }
|
302 | /////////////////////////////////////////////////////////////////////////////////////////////////////
|
303 | if (Command == 0b00000011 && EmpfangeAufstromzeit == 1) |
304 | { cli(); |
305 | buesy = 1; |
306 | PORTD &= ~(1<<PD2); // ChipSelect low |
307 | for (int i = 20;i<=25;i++) |
308 | {
|
309 | SPI_Senden(adc_write_config[i]); |
310 | }
|
311 | |
312 | PORTD |= (1<<PD2); // ChipSelect high |
313 | PORTC |=(1<<PC2); // START pin of ADS high level |
314 | PORTC &=~(1<<PC2); // START pin low again |
315 | |
316 | AnzahlBytes = 5; |
317 | Command = 0; |
318 | buesy = 0; |
319 | sei(); |
320 | USART_Senden(1); |
321 | }
|
322 | /////////////////////////////////////////////////////////////////////////////////////////////////////
|
323 | if (Command == 0b00000100 && EmpfangeAufstromzeit == 1) |
324 | { cli(); |
325 | buesy = 1; |
326 | PORTD &= ~(1<<PD2); // ChipSelect low |
327 | for (int i = 26;i<=31;i++) |
328 | {
|
329 | SPI_Senden(adc_write_config[i]); |
330 | }
|
331 | |
332 | PORTD |= (1<<PD2); // ChipSelect high |
333 | PORTC |=(1<<PC2); // START pin of ADS high level |
334 | PORTC &=~(1<<PC2); // START pin low again |
335 | |
336 | AnzahlBytes = 5; |
337 | Command = 0; |
338 | buesy = 0; |
339 | sei(); |
340 | USART_Senden(1); |
341 | }
|
342 | /////////////////////////////////////////////////////////////////////////////////////////////////////
|
343 | if (Command == 0b10000000 && EmpfangeAufstromzeit == 1) |
344 | { cli(); |
345 | buesy = 1; |
346 | PORTD &= ~(1<<PD2); // ChipSelect low |
347 | for (int i = 32;i<=37;i++) |
348 | {
|
349 | SPI_Senden(adc_write_config[i]); |
350 | }
|
351 | |
352 | PORTD |= (1<<PD2); // ChipSelect high |
353 | PORTC |=(1<<PC2); // START pin of ADS high level |
354 | PORTC &=~(1<<PC2); // START pin low again |
355 | |
356 | AnzahlBytes = 5; |
357 | Command = 0; |
358 | buesy = 0; |
359 | sei(); |
360 | USART_Senden(1); |
361 | }
|
362 | /////////////////////////////////////////////////////////////////////////////////////////////////////
|
363 | if (Command == 0b01000000 && EmpfangeAufstromzeit == 1) |
364 | { cli(); |
365 | buesy = 1; |
366 | PORTD &= ~(1<<PD2); // ChipSelect low |
367 | for (int i = 38;i<=43;i++) |
368 | {
|
369 | SPI_Senden(adc_write_config[i]); |
370 | }
|
371 | |
372 | PORTD |= (1<<PD2); // ChipSelect high |
373 | PORTC |=(1<<PC2); // START pin of ADS high level |
374 | PORTC &=~(1<<PC2); // START pin low again |
375 | |
376 | AnzahlBytes = 5; |
377 | Command = 0; |
378 | buesy = 0; |
379 | sei(); |
380 | USART_Senden(1); |
381 | }
|
382 | ////////////////////////////////////////////////////////////////////////////////////////////////////
|
383 | /////////////////////////////////////////////////////////////////////////////////////////////////////
|
384 | if (Command == 0b11000000 && EmpfangeAufstromzeit == 1) |
385 | { cli(); |
386 | buesy = 1; |
387 | PORTD &= ~(1<<PD2); // ChipSelect low |
388 | for (int i = 44;i<=49;i++) |
389 | {
|
390 | SPI_Senden(adc_write_config[i]); |
391 | }
|
392 | |
393 | PORTD |= (1<<PD2); // ChipSelect high |
394 | PORTC |=(1<<PC2); // START pin of ADS high level |
395 | PORTC &=~(1<<PC2); // START pin low again |
396 | |
397 | AnzahlBytes = 5; |
398 | Command = 0; |
399 | buesy = 0; |
400 | sei(); |
401 | USART_Senden(1); |
402 | }
|
403 | ////////////////////////////////////////////////////////////////////////////////////////////////////
|
404 | /////////////////////////////////////////////////////////////////////////////////////////////////////
|
405 | if (Command == 0b00100000 && EmpfangeAufstromzeit == 1) |
406 | { cli(); |
407 | buesy = 1; |
408 | PORTD &= ~(1<<PD2); // ChipSelect low |
409 | for (int i = 50;i<=55;i++) |
410 | {
|
411 | SPI_Senden(adc_write_config[i]); |
412 | }
|
413 | |
414 | PORTD |= (1<<PD2); // ChipSelect high |
415 | PORTC |=(1<<PC2); // START pin of ADS high level |
416 | PORTC &=~(1<<PC2); // START pin low again |
417 | |
418 | AnzahlBytes = 5; |
419 | Command = 0; |
420 | buesy = 0; |
421 | sei(); |
422 | USART_Senden(1); |
423 | }
|
424 | ////////////////////////////////////////////////////////////////////////////////////////////////////
|
425 | ////////////////////////////////////////////////////////////////////////////////////////////////////
|
426 | if (Command == 0b01010101 && EmpfangeAufstromzeit == 1) |
427 | {
|
428 | EmpfangeAufstromzeit =0; |
429 | UARTInterruptZaehler = 0; |
430 | |
431 | USART_Senden(1); |
432 | Command = 0; |
433 | x=0; |
434 | y=0; |
435 | }
|
436 | |
437 | if (UARTInterruptZaehler == 1) |
438 | {
|
439 | |
440 | AufstromzeitHB = Command; |
441 | |
442 | Aufstromzeit = (AufstromzeitHB << 8); |
443 | while(y<1) |
444 | {
|
445 | USART_Senden(1); |
446 | y = y+1; |
447 | }
|
448 | |
449 | |
450 | }
|
451 | if (UARTInterruptZaehler == 2) |
452 | {
|
453 | AufstromzeitLB = Command; |
454 | Aufstromzeit = Aufstromzeit | AufstromzeitLB; |
455 | //Aufstromzeit = ( AufstromzeitHB << 8 ) | AufstromzeitLB;
|
456 | |
457 | while(x<1) |
458 | {
|
459 | USART_Senden(AufstromzeitHB); |
460 | USART_Senden(AufstromzeitLB); |
461 | x=x+1; |
462 | }
|
463 | }
|
464 | if (UARTInterruptZaehler == 3) |
465 | {
|
466 | EmpfangeAufstromzeit = 1; |
467 | }
|
468 | |
469 | ///////////////////////////////////////////////////////////////////////////////////////////////////
|
470 | |
471 | ///////////////////////////////////////////////////////////////////////////////////////////
|
472 | if (Command == 0b00000101 && EmpfangeAufstromzeit == 1) |
473 | { cli(); |
474 | buesy=1; |
475 | PORTC |=(1<<PC5); |
476 | long_delay(Aufstromzeit); |
477 | PORTC &= ~(1<<PC5); |
478 | |
479 | Command = 170; |
480 | sei(); |
481 | buesy = 0; |
482 | }
|
483 | |
484 | if (AnzahlBytes == 5 && Command == 0b10101010 && EmpfangeAufstromzeit == 1) |
485 | { cli(); |
486 | Command=0; |
487 | buesy=1; |
488 | t=0; |
489 | while(t==0) |
490 | {
|
491 | PORTC |=(1<<PC2); // START pin of ADS high level |
492 | PORTC &=~(1<<PC2); // START pin low again |
493 | PORTD &= ~(1<<PD2); //CS low |
494 | |
495 | for (int i=0; i<=4; i++) |
496 | {
|
497 | SPI_Senden(adc_read_com[i]); |
498 | register_status[i] = SPI_Empfangen(); |
499 | |
500 | }
|
501 | |
502 | |
503 | |
504 | for (int i=1;i<=4;i++) |
505 | {
|
506 | USART_Senden(register_status[i]); |
507 | }
|
508 | t=t+1; |
509 | }
|
510 | //Command=0;
|
511 | buesy=0; |
512 | sei(); |
513 | }
|
514 | //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
515 | if (Command == 6 && EmpfangeAufstromzeit == 1 ) |
516 | { Command = 0; |
517 | z=0; |
518 | cli(); |
519 | buesy=1; |
520 | while (z==0) |
521 | {
|
522 | USART_Senden(AufstromzeitHB); |
523 | USART_Senden(AufstromzeitLB); |
524 | z=z+1; |
525 | }
|
526 | buesy = 0; |
527 | sei(); |
528 | }
|
529 | //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
530 | }
|
531 | |
532 | |
533 | }
|
534 | |
535 | ISR(USART_RX_vect) |
536 | {
|
537 | UARTInterruptZaehler = UARTInterruptZaehler +1; |
538 | if (UARTInterruptZaehler==11) |
539 | {
|
540 | UARTInterruptZaehler = 10; |
541 | }
|
542 | |
543 | if (buesy == 0) |
544 | {
|
545 | Command = USART_Empfangen(); |
546 | }
|
547 | |
548 | }
|
Hans D. schrieb: > Übernimmt in dem Fall nicht der Befehl die Funktion der Präambel? > Wie man das mit ASCII macht, verstehe ich nicht. Finde nirgendwo eine > Anleitung dafür.. Jein. Was sendest du? Die Zahl 85? Die Zahl 85 gehört zum Textsatz des ASCII. Diese Codes werden auch nur dafür benutzt. Guck in deine ASCII-Tabelle, dann siehst du, wie das aufgebaut ist. Solltest du keine haben, nimm diese hier: http://www.ascii-code.com/ Es gibt Steuercodes und Textcodes. Steuercodes sind die von 0 bis 31. So, und jetzt ist Schluß mit dezimal. Da mußt du durch. Steuercodes gehen von 0 - 0x1F. Darstellbare Zeichen von 0x20 - 0x7F Erweiterte darstellbare Zeichen von 0x80 - 0xFF Für die Präambel benutzt man Zeichen aus dem Steuercode, für die Daten aus dem Textcode. Warum macht man das so? Damit Steuercodes nicht mit Daten verwechselt werden können. Das ist das A und O der Übertragung. Die Übertragung beginnt also mit einem Steuerzeichen. Escape, 0x1B, wird dafür gerne genommen. Mit ein paar Ausnahmen, kann man aber auch jedes andere dafür nehmen. Bleiben wir mal bei Esc. Darauf folgen die Daten. Wir übertragen den Wert 27. Nun hat die Dezimalzahl 27 die unangenehme Eigenschaft aus den gleichen Bits zu bestehen, wie unser Escape. Deswegen können wir das nicht so übertragen. Die Zahl in Hex umzuwandeln, 0x1B, nützt uns natürlich auch nichts. Deswegen wird die Zahl in Text umgewandelt. Und zwar Nibble für Nibble. Spätestens jetzt ist definitiv Schluß mit Dezimalzahlen. Das Byte mit dem Inhalt 27 = 0x1B wird in zwei Zeichen umgewandelt: Aus dem Highnibble 1 wird '1' und aus dem Lownibble B wird 'B'. Hä? 1 ist die Zahl mit dem Wert 1. Also 1 Meter, 1 Apfel oder was weiß ich. '1' ist in C die Schreibweise für das Zeichen 1, welches, wenn es gedruckt wird, vereinbarungsgemäß der Zahl 1 entspricht. B und 'B' entsprechend. Jetzt guckst du in die Tabelle und siehst '1' hat den Code 0x31 und 'B' den Code 0x42. Diese beiden Codes werden jetzt statt der Zahl 27 übertragen. Darauf folgt in gleicher Weise die Hexzahl B. Die Umwandlung ist ganz einfach: Man addiert einfach zu dem Nibble die Zahl 0x30. Wenn diese Zahl dann größer als 0x39 ist, müssen nochmal 7 hinzuaddiert werden. Um die Hexumwandlung brauchst du dich nicht zu kümmern. Die Zahl steht sowieso so im Register. Dezimal ist nur für uns wenn wir es lesen wollen. Der gesamte Datensatz sieht jetzt also so aus: 0x1B = Escape 0x31 = '1' 0x42 = 'B' ... ... 0x0A = '\n'(Line Feed) Der Empfänger macht zunächst nichts anderes als auf das Escape zu warten. Hat er dieses empfangen, setzt er den Empfangszähler, -counter, -pointer, nenne es, wie du willst, auf 0 und schreibt die Bytes , die darauf folgen in einen Puffer. Der Puffer muß so groß, daß alles, was an Daten kommt, da reinpasst. Wenn er das 0x0A empfangen hat, setzt er ein Flag, das im Hauptprogramm abgefragt wird und dieses bearbeitet die Daten. Das mit dem Flag setzten, ist eine einfache Möglichkeit von vielen, das zu handhaben. Soweit erstmal klar?
Marc V. schrieb: > Nein, lass es sein. Das sind die Tipps von Leuten, die keine > Ahnung haben, weil die so etwas noch nie implementiert haben. Sprücheklopfer.
Thomas E. schrieb: > Sprücheklopfer. Nein, nur jemand der so etwas tatsächlich geschrieben und zum laufen gebracht hat. Warum sollte er 2 Bytes umrechnen, 6 Zeichen senden und empfangen, diese dann wieder zurückrechnen, wenn es mit nur 4 Bytes genausogut, sogar besser geht, weil kürzer und ohne Umrechnen ? P.S. Und, um dich zu zitieren: Thomas E. schrieb: > Denn die gebetsmühlenartig > vorgetragene So-kurz-wie-möglich-Regel ist zwar nicht kompletter Unsinn, > sollte aber einem, bezogen auf das gesamte Programm, "So kurz wie nötig" > weichen.
:
Bearbeitet durch User
Marc V. schrieb: > Nein, nur jemand der so etwas tatsächlich geschrieben und zum laufen > gebracht hat. Wow. Respekt!
Mein Gott, entscheidend ist doch dass ein Protokoll verwendet wird, ihr streitet jetzt darum welches es denn sein soll .... Bin gespann wer Recht hat/bekommt ;-)
Thomas E. schrieb: > Der Empfänger macht zunächst nichts anderes als auf das Escape zu > warten. > Hat er dieses empfangen, setzt er den Empfangszähler, -counter, > -pointer, nenne es, wie du willst, auf 0 und schreibt die Bytes , die > darauf folgen in einen Puffer. Der Puffer muß so groß, daß alles, was an > Daten kommt, da reinpasst. Wenn er das 0x0A empfangen hat, setzt er ein > Flag, das im Hauptprogramm abgefragt wird und dieses bearbeitet die > Daten. Das mit dem Flag setzten, ist eine einfache Möglichkeit von > vielen, das zu handhaben. Geht das in die richtige Richtung?
1 | int main(void) |
2 | {
|
3 | while(UARTInterruptZaehler<=4) |
4 | {
|
5 | if (UARTInterruptZaehler = 1) |
6 | {
|
7 | Empfangsbuffer[0]=Command; |
8 | }
|
9 | if (UARTInterruptZaehler = 2) |
10 | {
|
11 | Empfangsbuffer[1]=Command; |
12 | }
|
13 | if (UARTInterruptZaehler = 3) |
14 | {
|
15 | Empfangsbuffer[2] =Command |
16 | }
|
17 | }
|
18 | }
|
19 | |
20 | |
21 | |
22 | |
23 | ISR(USART_RX_vect) |
24 | { UARTInterruptZaehler=UARTInterruptZaehler + 1; |
25 | Command = USART_Empfangen(); |
26 | if (Command == 0x1B) |
27 | {
|
28 | UARTInterruptZaehler = 0; |
29 | }
|
30 | if (Command == 0x0A) |
31 | {
|
32 | dataready=1; |
33 | }
|
34 | }
|
Hallo Hans, das einfachste und mit nur zwei Byte geht so. uC im MPCM mode betreiben. LabView sendet das erste Byte mit MARK Parity. uC empfängt das Byte und schaltet das MPCM ab. LabView sendet das zweite Byte mit SPACE Parity. uC empfängt das Byte und schaltet das MPCM wieder an. Das Mark Parity ist der eindeutige Start. ms
ms schrieb: > Das Mark Parity ist der eindeutige Start. Wird Zeit dass wir hier endlich die uint9_t / int9_t und uint18_t / int18_t Datentypen salonfähig machen, wa?
Marc V. schrieb: > Mach es mit: <SOF><LSB><MSB><EOF> > Das sind (und bleiben) immer 4 Byt. > > <SOF> ist bei mir 0xAA, <EOF> ist 0xA9 und da gab es noch nie > irgendwelche Probleme damit. Und was passiert, wenn die binären Nutzdaten zufällig 0xAA oder 0xA9 sind?? Offensichtlich hast du selbst nicht die geringste Ahnung von einem zuverlässig funktionierenden Protokoll. Aber angeben wie der Gröpaz. Georg
Georg schrieb: > Und was passiert, wenn die binären Nutzdaten zufällig 0xAA oder 0xA9 > sind?? Nichts, werden richtig gedeutet, da vor den Daten schon ein 0xAA empfangen wurde und nach den Daten ein 0xA9 gekommen ist. Dein Protokoll zur Kommunikation mit dem Gehirn funktioniert wohl nicht, oder Gehirn nicht vorhanden ? > Offensichtlich hast du selbst nicht die geringste Ahnung von einem > zuverlässig funktionierenden Protokoll. Aber angeben wie der Gröpaz. Nein, aber das scheint bei dir der Fall zu sein. Auf jeden Fall hast du mit Sicherheit keine Ahnung.
Arduinoquäler schrieb: > Wird Zeit dass wir hier endlich die uint9_t / int9_t und > uint18_t / int18_t Datentypen salonfähig machen, wa? man muss sich nur zu helfen wissen. ms
Hans D. schrieb: > ISR(USART_RX_vect) > { UARTInterruptZaehler=UARTInterruptZaehler + 1; > Command = USART_Empfangen(); > if (Command == 0x1B) > { > UARTInterruptZaehler = 0; > } > if (Command == 0x0A) > { > dataready=1; > } > } Im Prinzip. Aber für den Zähler interessiert sich eigentlich niemand.
1 | volatile unsigned char Rbuf[4]; |
2 | volatile unsigned char Ready = 0; |
3 | |
4 | ISR(USART_RX_vect) |
5 | {
|
6 | static unsigned char ind = 0; |
7 | unsigned char udr = UDR; |
8 | |
9 | switch(udr) |
10 | {
|
11 | case 0x1B: ind = 0; break; |
12 | case 0x0A: Ready = 1; break; |
13 | default:
|
14 | RBuf[ind++] = udr; |
15 | ind %= 4; |
16 | }
|
17 | }
|
:
Bearbeitet durch User
Marc V. schrieb: > Nichts, werden richtig gedeutet, da vor den Daten schon ein 0xAA > empfangen wurde und nach den Daten ein 0xA9 gekommen ist. Dazu ist jeder sachliche Kommentar reine Zeitverschwendung. Dummheit siegt, wenn sie nur laut genug vorgetragen wird. Georg
Marc V. schrieb: > Nichts, werden richtig gedeutet, da vor den Daten schon ein 0xAA > empfangen wurde und nach den Daten ein 0xA9 gekommen ist. Das setzt aber voraus, dass beim Empfang bisher kein Byte verschluckt wurde. Damit führst Du dann aber auch den sicheren Empfang eines Frames ad absurdum und Du könntest damit auf Start- und Ende-Zeichen komplett verzichten, weils denselben Effekt hat - nämlich keinen. Wenn die Start- und Ende-Zeichen auch in den Daten vorkommen, muss man sie entweder "escapen" durch ein Vorschaltzeichen, oder man muss sicherstellen, dass die beiden Zeichen in den eigentlichen Daten nicht vorkommen. Sonst kann man im Falle einer Fehlübertragung (z.B. verschlucktes Byte beim Empfangen) nicht mehr sicher am Anfang eines neuen Frames aufsetzen, um sich wieder zu synchronisieren. Wenn Du versuchst, auf das nächste 0xAA aufzusetzen, könnte es sein, dass Du mitten in den Daten aufsetzt. Und das ist ein Fehler. Ganz schlimm ist es, wenn dann im Datenstrom tatsächlich noch ein 0xA9 steckt. Wo ist Dein Frame dann zuende? Ich kann Dir auch gern ein Beispiel konstruieren, wo es sicher fehschlägt. Aber ich nehme an, Du verstehst es auch so: Eine Sicherung einer Datenübertragung darf nicht eine korrekte Übertragung voraussetzen, denn sonst ist sie unnütz. Aber genau das machst Du.
:
Bearbeitet durch Moderator
Thomas E. schrieb: > ber für den Zähler interessiert sich eigentlich niemand. > > volatile unsigned char Rbuf[4]; > volatile unsigned char Ready = 0; > > ISR(USART_RX_vect) > { > static unsigned char ind = 0; > unsigned char udr = UDR; > > switch(udr) > { > case 0x1B: ind = 0; break; > case 0x0A: Ready = 1; break; > default: > RBuf[ind++] = udr; > ind %= 4; > } > } Gut...deinen Code verstehe ich so: Jedesmal, wenn ein Byte empfangen wird, wird die Variable "ind" = 0 gesetzt und der Inhalte des Empfangsregisters in die Variable udr geschrieben. Die Variable ind wird dann bei jedem Interrupt um 1 erhöht. Irgendwann wird dann 0x1B empfangen und ind = 0 gesetzt. Das Byte das danach empfangen wird, wird dann in RBuf[1] gespeichert, das danach in RBuf[2], dann RBuf[3] und zum Schluss RBuf[4]. Wird 0x0A empfangen, wird Ready gleich 1 gesetzt. Ready = 1 müsste dann in der while(1)-Schleife erkannt werden und der Empfangsbuffer ausgelesen werden?
Hans D. schrieb: > Jedesmal, wenn ein Byte empfangen wird, wird die Variable "ind" = 0 > gesetzt Nein, nicht jedesmal. Das ist eine statische Variable. Die verhält sich wie eine globale Variable, nur daß man sie außerhalb der ISR nicht sieht. Den Rest hast du richtig verstanden. Du solltest die Lücken in deinen C-Kenntnissen schließen.
:
Bearbeitet durch User
Frank M. schrieb: > Ich kann Dir auch gern ein Beispiel konstruieren, wo es sicher > fehschlägt. Aber ich nehme an, Du verstehst es auch so: Eine Sicherung > einer Datenübertragung darf nicht eine korrekte Übertragung > voraussetzen, denn sonst ist sie unnütz. Aber genau das machst Du. Das mag ich. Alle auf einen, dass die anderen Beispiele gar keine Fehlerprüfung haben, macht ja nichts. Erinnert mich an ein bestimmtes Tier aus Afrika, fängt mit H und endet mit e. Kann mich nicht mehr genau auf den Namen erinnern, aber egal, vielleicht kommt einer von Euch auf den Namen... Okay. Pseudocode auf die schnelle, so aus dem Kopf:
1 | const c_SOF = 0xAA |
2 | const c_EOF = 0xA9 |
3 | const sofOK = 0 |
4 | const eofOK = 1 |
5 | |
6 | RxPtr =0 |
7 | RxFlag = 0 |
8 | |
9 | main() |
10 | while(RxFlag.eofOK <> 1) |
11 | |
12 | |
13 | ISR: |
14 | RxByt = UDDR |
15 | if RxFlag.eofOK == 1 then return |
16 | |
17 | if RxFlag.sofOK == 0 |
18 | if RxByt == c_SOF then |
19 | RxPtr = 0 |
20 | RxFlag = (1<<sofOK) |
21 | endif
|
22 | |
23 | else
|
24 | RxPtr++ |
25 | if RxPtr >= 3 then |
26 | If RxByt <> c_EOF then |
27 | RxFlag = 0 // Habe auch irgendwo eine Routine welche die |
28 | RxPtr = 0 // schon empfangenen Bytes auf SOF prüft |
29 | else
|
30 | RxFlag |= (1<<c_EOF) |
31 | endif
|
32 | endif
|
33 | endif
|
34 | RxBuff[RxPtr] = RxByt |
Hier kann zwar ein ganzes Frame verlorengehen, aber aus dem Takt kann diese Rotine niemals kommen . Und mit der oben erwähnten Routine nichteinmal das. Und jetzt bitte ich um dein konstruiertes Beispiel.
Thomas E. schrieb: > Nein, nicht jedesmal. Das ist eine statische Variable. Die verhält sich > wie eine globale Variable, nur daß man sie außerhalb der ISR nicht > sieht. > Den Rest hast du richtig verstanden. > > Du solltest die Lücken in deinen C-Kenntnissen schließen. Mit den Lücken stimme ich dir zu. Bei den Dingen, die ich bisher so programmiert habe, bin ich mit meinen "Kenntnissen" zum Ziel gekommen. Hier ist wohl eine Grenze erreicht. Die empfangenen Daten würde ich dann so wieder decodieren?
1 | while(1) |
2 | { if (Ready = 1) |
3 | { Zeichen1 = Rbuf[1]; |
4 | Zeichen1 = Zeichen1 + 0x30; |
5 | if(Zeichen1 > 0x39) |
6 | {
|
7 | Zeichen1 = Zeichen1 + 7; |
8 | }
|
9 | Zeichen2 = Rbuf[2]; |
10 | Zeichen2 = Zeichen2 + 0x30; |
11 | if(Zeichen2 > 0x39) |
12 | {
|
13 | Zeichen2 = Zeichen2 + 7; |
14 | }
|
15 | Zeichen3 = Rbuf[3]; |
16 | Zeichen3 = Zeichen3 + 0x30; |
17 | if(Zeichen3 > 0x39) |
18 | {
|
19 | Zeichen3 = Zeichen3 + 7; |
20 | }
|
21 | Zeichen4 = Rbuf[4]; |
22 | Zeichen4 = Zeichen4 + 0x30; |
23 | if(Zeichen4 > 0x39) |
24 | {
|
25 | Zeichen4 = Zeichen4 + 7; |
26 | }
|
27 | |
28 | Befehl = (Zeichen1<<24)|(Zeichen2<<16)|(Zeichen3<<8)|(Zeichen4); |
29 | }
|
Klaus schrieb: > Warum sind wohl die meißten Protokolle im Internet ASCII? Diese Behauptung halte ich gelinde gesagt für Blödsinn.
Hans D. schrieb: > Zeichen1 = Zeichen1 + 0x30; > if(Zeichen1 > 0x39) Nein. So macht das im Prinzip der Sender. Du empfängst für die Zahl 1 das Zeichen '1'. Also 0x31. Daraus mußt du 0x01 machen. unsigned char AsciiToByte(char nH, char nL) { if(nH & 0x40) nH += 9; if(nL & 0x40) nL += 9; return (nH << 4) + (nL & 0x0F); } Hans D. schrieb: > Befehl = (Zeichen1<<24)|(Zeichen2<<16)|(Zeichen3<<8)|(Zeichen4); Damit hast du ein lückenhaftes Long gebastelt.
1 | unsigned int Value = AsciiToByte(RBuf[3], RBuf[2]) << 8; |
2 | Value += AsciiToByte(RBuf[1], RBuf[0]); |
Die Byte-Reihenfolge ist jetzt: RBuf[0] = Lowbyte Lownibble RBuf[1] = Lowbyte Highnibble RBuf[2] = Highbyte Lownibble RBuf[3] = Highbyte Highnibble Diese Reihenfolge ist grundsätzlich beliebig. Du solltest aber in jedem deiner Programme dieselbe Reihenfolge verwenden. Sonst steigst du spätestens beim übernächsten Programm nicht mehr durch. Denn diese sehr einfache Empfangsroutine...
1 | volatile unsigned char Rbuf[4]; |
2 | volatile unsigned char Ready = 0; |
3 | |
4 | ISR(USART_RX_vect) |
5 | {
|
6 | static unsigned char ind = 0; |
7 | unsigned char udr = UDR; |
8 | |
9 | switch(udr) |
10 | {
|
11 | case 0x1B: ind = 0; break; |
12 | case 0x0A: Ready = 1; break; |
13 | default:
|
14 | RBuf[ind++] = udr; |
15 | ind %= 4; |
16 | }
|
17 | }
|
... lässt sich auch sehr einfach universell einsetzen:
1 | #define MAX_DATA 13
|
2 | volatile unsigned char Rbuf[MAX_DATA]; |
3 | volatile unsigned char Ready = 0; |
4 | |
5 | ISR(USART_RX_vect) |
6 | {
|
7 | static unsigned char ind = 0; |
8 | unsigned char udr = UDR; |
9 | |
10 | switch(udr) |
11 | {
|
12 | case 0x1B: ind = 0; break; |
13 | case 0x0A: Ready = 1; break; |
14 | default:
|
15 | RBuf[ind++] = udr; |
16 | ind %= MAX_DATA; |
17 | }
|
18 | }
|
Damit kannst du Daten bis 13 Bytes Länge empfangen. Sind die Daten kürzer, wird nach z.B. 4 Bytes durch Ready das Ende signalisiert. vn nn schrieb: > Diese Behauptung halte ich gelinde gesagt für Blödsinn. Dem kann man nicht widersprechen. ASCII ist ein Code und kein Protokoll. Dieser Code macht aber Protokolle sehr einfach.
:
Bearbeitet durch User
Thomas E. schrieb: > Dieser Code macht aber Protokolle sehr einfach. Aha. Wenn die Computer lesen könnten. Ansonsten nimmt man CRC und hat ein Byte overhead anstatt doppelter Lange.
Thomas E. schrieb: > vn nn schrieb: >> Diese Behauptung halte ich gelinde gesagt für Blödsinn. > > Dem kann man nicht widersprechen. ASCII ist ein Code und kein Protokoll. > Dieser Code macht aber Protokolle sehr einfach. Mein Kommentar bezog sich eher auf die Behauptung, dass die meisten Protokolle im Internet auf ASCII aufbauen würden, dass er es auch noch falsch ausgedrückt hat, hab ich überlesen.
Marc V. schrieb: > Hier kann zwar ein ganzes Frame verlorengehen, aber aus dem Takt > kann diese Rotine niemals kommen . Hier das versprochene Beispiel:
1 | Sender: |
2 | |
3 | Sendet: <1234> <A9AA> <AAA9> <AAA9> <AAA9> |
4 | |
5 | Frames: |
6 | |
7 | Frame Daten |
8 | ============ |
9 | 1: AA 12 34 A9 |
10 | 2: AA A9 AA xx : xx = A9, welches beim Empfang verschluckt wird. |
11 | 3: AA AA A9 A9 |
12 | 4: AA AA A9 A9 |
13 | 5: AA AA A9 A9 |
14 | 6: .... |
15 | |
16 | Empfänger sieht dann: |
17 | |
18 | Frame 1 1 1 1 2 2 2 3 3 3 3 4 4 4 4 5 5 5 ... |
19 | Data AA 12 34 A9 AA A9 AA AA AA A9 A9 AA AA A9 A9 AA AA A9 ... |
20 | RxPtr 0 1 2 3 ACK 0 1 2 NACK 0 1 2 NACK 0 1 2 NACK 0 1 ... |
ACK: Frame als gültig erkannt NACK: Frame wird verworfen Lediglich 1 Byte wird verschluckt, nämlich das, welches ich oben mit xx gekennzeichnet habe. Danach ist Deine Empfängerroutine aus dem Takt und kann keinen einzigen der nachfolgenden Frames mehr erkennen.
:
Bearbeitet durch Moderator
vn nn schrieb: >> Warum sind wohl die meißten Protokolle im Internet ASCII? > > Diese Behauptung halte ich gelinde gesagt für Blödsinn. Die wichtigsten Dienste im Internet sind meiner Meinung nach Web (HTTP), Mail (IMAP, SMTP, POP3), Chat (XMPP = Jabber = Facebook-Chat = Google-Chat, IRC) und Telefonie (SIP). Das sind, wie die meisten offenen Standards, alles ASCII-Protokolle. Verbreitete Binärprotokolle sind z.B. Skype, H.323 und RTP.
Frank M. schrieb: > Lediglich 1 Byte wird verschluckt, nämlich das, welches ich oben mit xx > gekennzeichnet habe. Danach ist Deine Empfängerroutine aus dem Takt und > kann keinen einzigen der nachfolgenden Frames mehr erkennen. Ja. Nur ist das weitere senden von Daten zwecklos, da immer wieder die selben Daten kommen. Natürlich kann das bei ASCII nicht vorkommen, oder ? Auf deinem Prinzip basierend, kann ich dasselbe für jedes Protokoll konstruieren. Wie schon gesagt, man nimmt binär und CRC, wenn Zeichen dauernd "verschluckt" werden (wo passiert das ?). Oder man baut die Verbindung richtig auf, den mit richtiger Verbindung gibt es auch kein "Verschlucken" von Zeichen.
S. R. schrieb: > Verbreitete Binärprotokolle sind z.B. Skype, H.323 und RTP. Wobei H323 auch noch gegen SIP verloren hat. MfG Klaus
S. R. schrieb: > Die wichtigsten Dienste im Internet sind meiner Meinung nach Web (HTTP), > Mail (IMAP, SMTP, POP3), Chat (XMPP = Jabber = Facebook-Chat = > Google-Chat, IRC) und Telefonie (SIP). Das sind, wie die meisten offenen > Standards, alles ASCII-Protokolle. Tja, nur werden diese ASCII nicht RAW übertragen, sondern eingebettet.
Marc V. schrieb: > Nur ist das weitere senden von Daten zwecklos, da immer wieder die > selben Daten kommen. Ich kann auch Beispiele mit variierenden Daten aufführen, wo Du mehr als einen Frame pro Übertragungsfehler wegwerfen musst. Das heisst, die Frameerkennungsrate sinkt auch so beträchtlich im Vergleich zu einem Protokoll, welches als Framebegrenzungen spezielle Sonderzeichen verwendet, die in den Daten selbst nicht vorkommen. > Natürlich kann das bei ASCII nicht vorkommen, oder ? Nein, da gibt es ein oder zwei Ende-Zeichen, in der Regel. NL oder CRNL. Diese kommen in den Daten nicht vor. Beispiel: 1234 <NL> A9AA <NL> AAA9 <NL> AAA9 <NL> AAA9 <NL> Bei einem Bytefehler geht max. ein Frame verloren. Noch besser wäre das: <STX> 1234 <ETX> <STX> A9AA <ETX> <STX> AAA9 <ETX> <STX> AAA9 <ETX> <STX> AAA9 <ETX> Aber das ist etwas unleserlicher in einem Datenmitschnitt. > Auf deinem Prinzip basierend, kann ich dasselbe für jedes > Protokoll konstruieren. Konstruiere es bitte für ASCII <NL> oder <STX>...<ETX>
:
Bearbeitet durch Moderator
Frank M. schrieb: > Zeig es mir :-) Nicht nötig, du weisst auch, dass so etwas ohne Probleme geht. Aber ich kann mir sinnvollere Sachen vorstellen, als mit Gewalt unrealistische Beispiele zu konstruieren. Wie man das Ganze Bulletproof macht, ist schon seit Jahrzehnten bekannt, hier ging es um ein Beispiel (von mir) wo Zeichen verschluckt werden und ein Beispiel (von anderen) wo Zeichen nicht verschluckt werden. Die Frage für den Deutschen: - Wie viele Leute sind in dem Bus ? - 1,2,3, ... 49. - Genau !!! Und die Frage für den Juden: - Und jetzt sagen Sie uns die Namen und Vornamen von diesen Leuten.
Hallo, es gibt leute die hier was lernen wollen. Ob man jetzt ASCII oder Binär verwendet kommt auf die Situation drauf an. Ich bin auch kein freund von ASCII aber hier in diesem fall scheint es passend zu sein. @Moderator ein Moderator sollte das Ziel (Fragestellung) nicht aus den Augen verlieren. Er lenkt das gespräch gezielt auf eine Lösung hin und nicht zu einer Diskussion. Punkt. ms
Soweit habe ich es jetzt verstanden: Es soll zum Beispiel 5000(dezimal) übertragen werden. 1. Schritt: 5000 --> 0x1388 2.Jede einzelne Ziffer als Zeichen betrachten und in der ASCII Tabelle die entsprechende Hexadezimalzahl raussuchen. 3. Folgenden Frame an den uC senden um 5000 zu übertragen: 0x1B 0x38 0x38 0x33 0x31 0x0A Dank der Hilfe von Thomas Eckmann (vielen Dank, das hätte ich allein auf keinen Fall geschafft), bin ich jetzt soweit, dass ich mit HTerm Frames mit dem gegebenen Aufbau an den uC senden kann und diese wieder decodiert werden. Wie gehts jetzt am besten weiter? Wie übertrage ich das jetzt am besten auf mein altes Programm sodass ich möglichst wenig ändern muss. Jetzt müsste ich doch 85(dez.) zunächst so codiert an den uC senden und im Anschluss daran meinen 16 Bit Wert, den ich in eine bestimmte Variable schreiben möchte oder? Vielen Dank an alle und ein schönes Wochenende:)
:
Bearbeitet durch User
MIDI war gar kein so schlechtes Beispiel. Man könnte das oberste Bit als Sync-Bit nehmen, und pro byte einfach nur 7 Datenbit übertragen. Dann käme man mit 3 Byte pro Übertragung aus. Sinngemäß:
1 | void uart_tx_byte(uint8_t x); |
2 | uint8_t uart_rx_byte(); |
3 | |
4 | void send_16_bit(uint16_t data) |
5 | {
|
6 | uart_tx_byte(0x80 | (data >> 9)); |
7 | uart_tx_byte(0x7F & (data >> 2)); |
8 | uart_tx_byte(0x7F & (data << 5)); |
9 | }
|
10 | |
11 | uint16_t receive_16_bit() |
12 | {
|
13 | uint16_t data; |
14 | uint8_t b=0; |
15 | |
16 | while (! (b & 0x80)) |
17 | b = uart_rx_byte(); |
18 | |
19 | data = (uint16_t)(b) << 9; |
20 | |
21 | b = uart_rx_byte(); |
22 | data |= (uint16_t)(b) << 2; |
23 | |
24 | b = uart_rx_byte(); |
25 | data |= (uint16_t)(b) >> 5; |
26 | |
27 | return data; |
28 | }
|
Hans D. schrieb: > #define F_CPU 18432000 > ... Was verstehst du unter C-Datei? Ist die Forumsfunktion zum Senden von Dateianhängen schon wieder kaputt?
Hans D. schrieb: > Wie gehts jetzt am besten weiter? Du dekodierst deine Daten im Hauptprogramm. Das Befehlsbyte liegt vor den Daten. Dieses kommt in eine Variable(unsigned char) und die eigentlichen Daten in eine andere Variable(unsigned int). Und dann wertest du das aus:
1 | if(Befehlsbyte == 85) |
2 | {
|
3 | // tu was
|
4 | }
|
oder
1 | switch(Befehlsbyte) |
2 | {
|
3 | case 0: |
4 | //tu dies
|
5 | break; |
6 | case 1: |
7 | //tu das
|
8 | break; |
9 | case 85: |
10 | //tu jenes
|
11 | break; |
12 | }
|
Alles im Hauptprogramm oder in Funktionen, die aus dem Hauptprogramm aufgerufen werden.
Marc V. schrieb: >> Die wichtigsten Dienste im Internet sind meiner Meinung nach Web (HTTP), >> Mail (IMAP, SMTP, POP3), Chat (XMPP = Jabber = Facebook-Chat = >> Google-Chat, IRC) und Telefonie (SIP). Das sind, wie die meisten offenen >> Standards, alles ASCII-Protokolle. > > Tja, nur werden diese ASCII nicht RAW übertragen, sondern eingebettet. Weil das Internet Protocol ein Binärprotokoll ist, ist ein ASCII-Protokoll im Internet logisch unmöglich? Unfug schreibst du ja öfter, aber ich wüsste manchmal gerne, warum. Hans D. schrieb: > Es soll zum Beispiel 5000(dezimal) übertragen werden. Ja. Das geht am einfachsten, indem du die Dezimalzahl ziffernweise als Text überträgst und z.B. mit einem Endezeichen abschließt. Dein Frame besteht also aus den Bytes { '5', '0', '0', '0', '\n' } oder, in hexadezimaler Darstellung { 0x35, 0x30, 0x30, 0x30, 0x0A }. (Beide Darstellungen sind exakt gleich.) > 1. Schritt: 5000 --> 0x1388 Du willst eine Dezimalzahl übertragen. Warum wandelst du sie dann in Hex um? Ist doch nur Arbeit! > 2.Jede einzelne Ziffer als Zeichen betrachten und in der ASCII Tabelle > die entsprechende Hexadezimalzahl raussuchen. Wenn du eine Zahl in eine Zeichenkette umgewandelt hast, dann ist jedes Zeichen darin (abzüglich dem Nullbyte am Ende) eine Ziffer. Jede Ziffer liegt bereits nach Tabelle passend codiert vor. > 3. Folgenden Frame an den uC senden um 5000 zu übertragen: > 0x1B -- ESC > 0x38 -- '8' > 0x38 -- '8' > 0x33 -- '3' > 0x31 -- '1' > 0x0A -- '\n' Erstens überträgst du damit die Hexadezimaldarstellung, ohne sie zu markieren (üblicherweise markiert man sowas mit einem "0x"-Präfix). Zweitens überträgst du die Zahl rückwärts und machst es damit dem empfandenden System schwerer, sie zu dekodieren. Thomas hat vollkommen Recht und Marc schreibt mal wieder halbgaren Unfug. Wenn deine Kommunikation immer aus "Befehl Parameter Parameter" besteht, dann kannst du dein Protokoll auch so definieren: - ASCII-Zahl ( Leerzeichen ASCII-Zahl )wiederholen Zeilenende Also "85 17 32 535\n" bedeutet dann "Befehl 85 mit den Parametern 17, 32 und 535". Dein Empfangscode schreibt also so lange Bytes in einen Puffer, bis ein "\n" kommt und übergibt die gesamte Zeile auf einmal an eine Auswertefunktion. Die zerlegt die Zeile dann in die Stücke.
S. R. schrieb: > Ja. Das geht am einfachsten, indem du die Dezimalzahl ziffernweise als > Text überträgst und z.B. mit einem Endezeichen abschließt. Nope, am einfachsten geht das indem man die zwei Bytes überträgt und das ist dann auch noch schneller. Sender macht: LowByte = 5000 & 0xff; HighByte = (5000 >> 8) & 0xff; Empfänger macht: LowByte = EmpfangsByte; HighByte = EmpfangsByte << 8; Value = HighByte | LowByte; Und mit einem geeigneten Protokoll weiß man was wann gesendet wird bzw. angekommen ist.
S. R. schrieb: > Weil das Internet Protocol ein Binärprotokoll ist, ist ein > ASCII-Protokoll im Internet logisch unmöglich? Unfug schreibst du ja > öfter, aber ich wüsste manchmal gerne, warum. Einen ASCII-Protokoll gibt es nicht. Das, wovon du redest sind Nutzdaten, es ist absolut uninteressant ob da ASCII-Zeichen oder binäre Daten übertragen werden. Schreib nicht wieder so einen Unfug bitte, du machst dich nur immer wieder lächerlich mit deinem Halbwissen. S. R. schrieb: > Thomas hat vollkommen Recht und Marc schreibt mal wieder halbgaren > Unfug. Erstens ist das nicht wahr und zweitens ist das, was ich vorgeschlagen habe weder die einzig richtige Methode, noch habe ich geschrieben, dass der Thomas Unrecht hat und seine Methode falsch ist. Was willst du also beweisen (ausser deinem Unwissen und notorischem rumhaken an mir weil ich klüger und wahrscheinlich auch schöner bin) ?
ms schrieb: > es gibt leute die hier was lernen wollen. > Ob man jetzt ASCII oder Binär > verwendet kommt auf die Situation drauf an. Das sehe ich genauso. > Ich bin auch kein freund von > ASCII aber hier in diesem fall scheint es passend zu sein. Auch hier stimme ich Dir zu. Der mögliche Wertebereich liegt bei max. 5 Ziffern, ist zudem portabel und ist unabhängig von Endianess des Senders oder Empfängers. > @Moderator > ein Moderator sollte das Ziel (Fragestellung) nicht aus den Augen > verlieren. > Er lenkt das gespräch gezielt auf eine Lösung hin und nicht zu einer > Diskussion. Punkt. Das allerdings sehe ich anders. Auch die Anregung zu einer Diskussion kann zu einer Führung der Teilnehmer auf den richtigen Weg führen und hat sogar noch einen gewissen Vorteil: die Teilnehmer können sich die Lösung selbst erarbeiten. Aber so oberlehrerhaft möchte ich das ganze gar nicht aufhängen: auch ich bin in der Regel ein ganz normaler Forumsteilnehmer, der sich hier gerne an solchen Diskussionen beteiligen möchte. Aus meiner Erfahrung mit IR-Protokollen kann ich jedoch beurteilen, dass eine Rahmung des Frames mit einer Präambel, die selbst nicht in den Nutzdaten vorkommen kann, zu einer erheblichen Verbesserung der Kommunikationssicherheit führt. Genau das wollte ich mit meinen Beiträgen als Erwiderung auf Marcs 0xAA-0xA9-Lösung zum Ausdruck bringen. Ich sehe daher keinen Grund zur Aufregung.
:
Bearbeitet durch Moderator
Marc V. schrieb: > Nicht nötig, du weisst auch, dass so etwas ohne Probleme geht. Sorry, Marc. An dieser Stelle kann ich Dir nicht folgen. Ich habe es versucht, aber ich kann beim besten Willen bei dem einfachen zeilenweisen Protokoll, 12345 <NL> 23456 <NL> 34567 <NL> ... wo ein simples Newline-Zeichen, welches selbst in den Nutzdaten nicht vorkommen kann, lediglich einen einzigen Frame durch Wegnehmen, Hinzufügen oder Kippen eines Bits im Datenstrom kaputtmachen - mehr nicht. Ich bekomme es nicht hin, hier mehr als einen Frame zu zerstören. > Aber ich kann mir sinnvollere Sachen vorstellen, als mit Gewalt > unrealistische Beispiele zu konstruieren. Bei dem Beispiel ging es um das Vorkommen der Präambel/Postambel in den Nutzdaten, was durchaus bei Deinem 0xAA-0xA9-Protokoll vorkommen kann. Genau das habe ich durchexerziert. Dabei kann man leicht zeigen, dass durch genau einen Übertragungsfehler, ein, zwei oder mehr Frames zerstört werden. Zugegebenermaßen war mein Beispiel der Worst-Case, der passieren kann. Aber es gibt noch jede Menge andere mögliche Kombinationen, die mehr als einen Frame dabei kaputtmachen. Allein die theoretische Möglichkeit sollte einen davon abbringen, solch eine Lösung zu wählen. Murphy existiert! Ich beschäftige mich schon seit vielen Jahren mit Kommunikationsprotokollen, zudem habe ich mit der Entwicklung von IRMP und der Dekodierung von mittlerweile 50 IR-Protokollen jede Menge Erfahrung sammeln können. Das Fazit daraus: Ohne eine ausgezeichnete Präambel wird es verdammt schwierig, die Daten sicher sicher zu extrahieren. Da habe ich schon immer die Entwickler des Denon-Protokolls verflucht. Alle anderen mir bekannten Protokolle arbeiten da glücklicherweise anders. Okay, eine ausgezeichnete Präambel ist eher wichtig bei Übertragungswegen, bei denen es bei Übertragungspausen zu vermehrten Rauschen kommen kann. Das ist eher bei Funk und bei IR der Fall. Bei UART-Übertragung kann man darauf verzichten, daher ist dort nicht unbedingt eine Form von <STX>Data<ETX> notwendig. Hier reicht daher ein Trenner per Postambel, zum Beispiel das einfache Newline. Wichtig ist aber auch hier, dass dieses Zeichen nicht in den Nutzdaten vorkommt.
:
Bearbeitet durch Moderator
Frank M. schrieb: > Kommunikationssicherheit führt. Genau das wollte ich mit meinen > Beiträgen als Erwiderung auf Marcs 0xAA-0xA9-Lösung zum Ausdruck > bringen. Es ist keine Lösung, natürlich. Es war nur ein Vorschlag, nur wusste ich damals noch nicht, dass die Verbindung bei binärer Übertragung so schlecht ist, bei ASCII-Zeichen aber einwandfrei funktioniert, sonst hätte ich gleich CRC oder Checksum vorgeschlagen...
Marc V. schrieb: > Es ist keine Lösung, natürlich. Es war nur ein Vorschlag, nur wusste > ich damals noch nicht, dass die Verbindung bei binärer Übertragung > so schlecht ist, bei ASCII-Zeichen aber einwandfrei funktioniert, > sonst hätte ich gleich CRC oder Checksum vorgeschlagen... Eine ausgezeichnete Präambel/Postambel schützt natürlich noch lange nicht vor gefälschten Daten. Sie hilft nur bei der Eingrenzung der Frames. Wer hier auch noch eine gewisse Datensicherheit innerhalb der Frames möchte, kommt natürlich nicht um die zusätzliche Übertragung von redundanten Daten herum. Die damit verbundene Erhöhung der Datensicherheit ist mathematisch berechenbar. Ein Paritätsbit ist dabei die geringstmögliche Erhöhung (fifty-fifty, also 1/2), eine CRC8 erhöht die Sicherheit immerhin schon auf 255/256. Und so weiter...
:
Bearbeitet durch Moderator
Frank M. schrieb: > Sorry, Marc. An dieser Stelle kann ich Dir nicht folgen. Ich habe es > versucht, aber ich kann beim besten Willen bei dem einfachen > zeilenweisen Protokoll, > > 12345 <NL> > 23456 <NL> > 34567 <NL> Normalerweise ja. Aber diese von mir konstruierte Verbindung ist ein bisschen komisch - es wird genau der <NL> verschluckt und das auch nur wenn ASCII-Zeichen übertragen werden - bei binären Werten funktioniert die Übertragung ausgezeichnet :) > Ich beschäftige mich schon seit vielen Jahren mit > Kommunikationsprotokollen Ich auch, deswegen schrieb ich, dass ich da noch eine Routine habe, welche bei auftretendem Fehler die schon empfangenen Bytes nach SOF abklappert und dann von dort wieder anfängt. Und es ist sehr, sehr selten, dass mit UART/RS485/CAN ein Byte verschluckt wird - falsch empfangen ja, dann aber mit Frame error und der CRC oder Checksum stimmt dann auch nicht mehr. Bei IR vielleicht möglich, da habe ich wenig bis Null Erfahrung.
:
Bearbeitet durch User
Marc V. schrieb: > bei binären Werten funktioniert die Übertragung ausgezeichnet :) Bei binären Werten ist es natürlich auch um das Newline schlecht bestellt. Bei reiner ASCII-Übertragung (d.h. der Übertragung von druckbaren 7-Bit-Zeichen) klappt natürlich auch die Übertragung mit <AA>...<A9>, da der Wertebereich der Daten hier nur maximal von 0x20 bis 0x7E geht. Bei binären Daten, welche zudem auch noch eine variable Länge haben, kann man sich natürlich auch einen Frame-Trenner aussuchen, der dann in den Daten selbst "escaped" wird. Beispiel: Wir benutzen als Frame-Trenner das Newline <NL>, oder auch Linefeed <LF> (0x0A) genannt. Kommt es in den Daten selbst vor (binäre Übertragung), dann wird ein <ESC> (0x1B) vorangestellt. Kommt das <ESC> in den Daten vor, wird es verdoppelt. Aus dem Datenstrom <ESC> wird also <ESC><ESC>, aus <LF> wird <ESC><LF>. Alle anderen Zeichen werden 1:1 ausgegeben und am Ende des Frames mit <LF> terminiert. Man muss jedoch auf der Empfängerseite darauf achten, dass die Datengröße eines Frames sich im Worst-Case verdoppeln kann. Diese Methode benutzten in der Vergangenheit schon Modem-Protokolle wie Kermit, X-Modem und wie sie alle hießen, um Binärdaten zu übertragen.
:
Bearbeitet durch Moderator
Die oben genannte Methode, Frametrenner selbst zu "escapen", kann man natürlich auch in C übersetzen:
1 | #define ESC 0x1B
|
2 | #define LF 0x0A
|
3 | |
4 | void put16 (uint16_t w) |
5 | {
|
6 | uint8_t h = w >> 8; |
7 | uint8_t l = w & 0xFF; |
8 | |
9 | if (h == ESC || h == LF) |
10 | {
|
11 | putc (ESC); |
12 | }
|
13 | |
14 | putc (h); |
15 | |
16 | if (l == ESC || l == LF) |
17 | {
|
18 | putc (ESC); |
19 | }
|
20 | |
21 | putc (l); |
22 | |
23 | putc (LF); |
24 | }
|
(Der Code ist ungetestet hier reingetippt, ohne Gewähr auf Tippfehler) Hier wird das obere Byte zuerst übertragen, dann das untere Byte. Das kann man natürlich auch umdrehen. Die Framelänge wird durch das "escapen" variabel, d.h. sie kann zwischen 3 und 5 Bytes betragen. Die Empfängerroutine überlasse ich dabei dem geneigten Leser :-)
Frank M. schrieb: > Aus dem Datenstrom <ESC> wird also <ESC><ESC>, aus <LF> wird <ESC><LF>. > Alle anderen Zeichen werden 1:1 ausgegeben und am Ende des Frames mit > <LF> terminiert. Ohne jetzt endlos auf verschiedenen Vorteilen und Nachteilen rumreiten zu wollen, aber ich mache es seit langem so: <SOF><RcvAdr><SndAdr><Len><Cmnd><Data>...<CRC><EOF> Habe die entsprechenden Bibliotheken geschrieben, brauche nicht mehr darüber nachzudenken, wird einfach eingebunden. Funktioniert ohne irgendwelche Probleme. Fehler werden erkannt und (fast alle) selbständig in der Routine behandelt - nochmaliges senden, automatische Bestätigung und ähnlich. Aber ob es sich lohnt, so etwas für 2 Bytes zu schreiben, ist eine ganz andere Frage. Eine Pause zwischen 2 Blocks würde es auch ohne weiteres tun, da könnte man in der Timerroutine ganz einfach den Flag für ein neues Packet setzen...
:
Bearbeitet durch User
Marc V. schrieb: > Ohne jetzt endlos auf verschiedenen Vorteilen und Nachteilen > rumreiten zu wollen, aber ich mache es seit langem so: Ohne Frage, wenn man die obige put16()-Funktion um beliebige Datenmengen plus zusätzlicher CRC erweitern will, wird man irgendwann auf dieselbe oder zumindest ähnliche Form kommen, die Du mit > <SOF><RcvAdr><SndAdr><Len><Cmnd><Data>...<CRC><EOF> skizziert hast. Dabei kann <Data> auch eine "escaped" Form sein, was dann im Ganzen gesehen die höchstmögliche Frame- und Datensicherheit bringen kann. Bei lediglich 16-Bit-Daten ist das jedoch wie Kanonen auf Spatzen. > Eine Pause zwischen 2 Blocks würde es auch ohne weiteres tun, da > könnte man in der Timerroutine ganz einfach den Flag für ein neues > Packet setzen... Ich bin kein Freund davon. Stecken zwischen Sender und Empfänger irgendwelche Repeater, Medienwandler (z.B. UART-IP-UART-Brücke) oder ähnliches, ist das Zeitverhalten schlecht vorhersehbar. Da müsste man die Pausen schon arg lang machen, um sich sicher zu sein. Das drückt aber die Datenrate.
:
Bearbeitet durch Moderator
Frank M. schrieb: > Ich bin kein Freund davon. Stecken zwischen Sender und Empfänger > irgendwelche Repeater, Medienwandler (z.B. UART-IP-UART-Brücke) oder Ich auch, aber der TO hat dieses Problem ganz eindeutig nicht ;) Bei ihm würde eine Pause von etwa 3-4 Bytes vollkommen reichen.
Marc V. schrieb: > Einen ASCII-Protokoll gibt es nicht. Stimmt, denn es wäre "ein ASCII-Protokoll", weil Substantiv Neutrum. > Das, wovon du redest sind Nutzdaten, es ist absolut uninteressant ob > da ASCII-Zeichen oder binäre Daten übertragen werden. Nö. Bei HTTP sende ich einen Header, gefolgt von den eigentlichen Nutzdaten. Der Header hingegen besteht laut Standard ausschließlich aus ASCII-Zeichen (inklusive CR und LF). Die Nutzdaten dürfen beliebige binäre Daten sein *und sind selbstverständlich nicht Teil des Protokolls*, denn das Protokoll sagt über deren Struktur nichts aus. (Sie sind aber Teil der Kommunikation.) Da das Protokoll ausschließlich ASCII-Zeichen verwendet, spricht man von einem ASCII-basiertem Protokoll. Und die haben Vor- und Nachteile. Du darfst das in deiner Welt natürlich jederzeit so umdefinieren, wie du möchtest, aber dann musst du damit leben, dass andere dir vorwerfen, Unfug zu schreiben. Ich wäre dann damit raus, da der Marc ja laut eigener Aussage der klügste und schönste Unfugerzähler von allen ist. Merkt man ja auch daran, dass seine Aussagen grundsätzlich und vor allem hier auf vollständige Zustimmung stoßen.
Frank M. schrieb: > Bei binären Werten ist es natürlich auch um das Newline schlecht > bestellt. Deine vollkommen erfolglosen Versuche, Marc klarzumachen, dass eindeutige Trennzeichen in den Daten nicht vorkommen dürfen, weil sie dann eben nicht mehr eindeutig sind, beweist nur eines: Auch beim Programmieren hat das postfaktische Zeitalter begonnen. Georg
S. R. schrieb: >> Einen ASCII-Protokoll gibt es nicht. > > Stimmt, denn es wäre "ein ASCII-Protokoll", weil Substantiv Neutrum. Tja, ich bin kein Deutscher, das ist nicht meine Muttersprache, ich benutze es in der letzten Zeit praktisch nur hier, im Forum. Im übrigen verweise ich auf Mark Twain und seine Bemerkungen dazu. >> Das, wovon du redest sind Nutzdaten, es ist absolut uninteressant ob >> da ASCII-Zeichen oder binäre Daten übertragen werden. > > Nö. Bei HTTP sende ich einen Header, gefolgt von den eigentlichen > Nutzdaten. Der Header hingegen besteht laut Standard ausschließlich > aus ASCII-Zeichen (inklusive CR und LF). Nö - Nein - No - Nada - Njet. Der Header, inclusive ev. Nutzdaten muss erst beim Empfänger ankommen. Dazu wird das Ganze in einem (oder mehreren) Paketen verschickt. Wenn dieser Paket beim Empfänger angekommen ist, sind sowohl der Header als auch ev. Nutzdaten 100% fehlerfrei. Das wird durch den benutzten Protokoll garantiert. Insofern ist es absolut uninteressant, ob da ASCII oder binary übertragen wird. Ist hoffe, dir ist das Ganze jetzt ein bisschen klarer geworden, obwohl, viel Hoffnung habe ich nicht...
Georg schrieb: > Deine vollkommen erfolglosen Versuche, Marc klarzumachen, dass > eindeutige Trennzeichen in den Daten nicht vorkommen dürfen, weil sie Du hast doch schon gezeigt, dass du ein Moron bist, musst du das immer wieder beweisen ? Und warum erklärst du es hier, warum schreibst du nicht die Firmen, die sich mit Protokollen beschäftigen und davon leben ? Vielleicht sehen diese ihren Fehler ein, gehen zu ASCII rüber und du wirst reich und berühmt?
:
Bearbeitet durch User
Marc V. schrieb: > Das, wovon du redest sind Nutzdaten, es ist absolut uninteressant ob > da ASCII-Zeichen oder binäre Daten übertragen werden. Das kommt wohl ganz schwer auf das Protokoll drauf an. Wenn das spezielle Steuerzeichen für die Synchronisation verwendet, z.B. für Start einen Datenblocks, kann es durchaus passieren, dass diese Steuerzeichen nicht anderweitig im Datenstrom auftauchen dürfen, bzw. mittels zusätzlicher Escape Sequenz markiert werden müssen. Es sei denn, man baut zusätzlichen Protokolloverhead ein, um sich dagegen zu schützen. Bei reinen ASCII-Daten kannst du Codes für Daten und Steuerung trennen, bei Binärdatenübertragung nicht. Früher (tm) hat man für eine saubere Synchronisation bei Übertragung von 16-Bit Binärdaten einfach 3 Byte übertragen, in jedem Byte ein Bit für die Synchronisation (S/s) reserviert und dann sogar noch Platz für eine Datensatzkennung/Adresse (a) mit 6 Bit, also z.B.
1 | 1 2 3 |
2 | Saaaaaad sddddddd sddddddd |
Das Synchronisationsbit wird beim ersten Byte auf 1 (S) gesetzt und beim zweiten und dritten auf 0 (s). Heutzutage ist das bei nahezu unbegenzter Datenrate wohl zu einfach und Protokollökonomie nicht sonderlich gefragt. Mit weniger Overhead kann man bei diesen Simpelprotokoll aus, wenn man die Daten zu größeren Blöcken zusammenfasst und auf die Adressbits verzichtet, also bei einem Nutzdatenblock mit 7 oder 14 Bytes.
Ist schon lustig zu lesen, bin mal gespannt wie lange es noch dauert bis einer von euch erkennt, dass ihr beide recht habt und gezielt aneinander vorbei redet.
Die Diskussion über Protokolle ist das Eine. Aber die Ursache für die vermutlich gedroppten Bytes ist das andere. Einmal: wenn die serielle Übertragung mit Parity konfiguriert ist, wie ist das bei UART auf der Empfängerseite, das Byte wird einfach verworfen? Oder bekommt man das Byte trotzdem? Ansonsten die Zuverlässigkeit der Verbindung: Welche Baudrate, wie lange ist das Kabel und ist es geschirmt? Ich finde es etwas seltsam dass so häufig etwas verschluckt wird. Ich verwende mit uCs jede Menge serielle Übertragung und wenn man Baudrate etc passen sehe ich quasi nie falsche Bytes. Damit meine ich jetzt nicht, dass es das Protokoll nicht braucht. Aber ich meine da wurde ein Schritt übersprungen, die Verbindung zuverlässig genug zu machen. Denn wenn am Ende nur 80% durchkommen ist das ja auch Mist.
M. K. schrieb: > Ist schon lustig zu lesen, bin mal gespannt wie lange es noch dauert bis > einer von euch erkennt, dass ihr beide recht habt Kaum. Wenn das Ganze so rausgesendet würde, hätte er recht und ich nicht. Da dies aber nicht der Fall ist...
Conny G. schrieb: > Einmal: wenn die serielle Übertragung mit Parity konfiguriert ist, wie > ist das bei UART auf der Empfängerseite, das Byte wird einfach > verworfen? > Oder bekommt man das Byte trotzdem? Man bekommt es, nur wird der Flag PE gesetzt (für Parity Error). > Denn wenn am Ende nur 80% durchkommen ist das ja auch Mist. Selbstverständlich. Und die ganze Diskussion über Trennzeichen und ähnliches auch.
Conny G. schrieb: > Einmal: wenn die serielle Übertragung mit Parity konfiguriert ist, wie > ist das bei UART auf der Empfängerseite, das Byte wird einfach > verworfen? Kommt drauf an. Bei den AVRs kann - wie von Marc beschrieben - das Byte trotzdem ausgeliefert werden. Bei Paritätsfehlern wird dann ein Flag gesetzt, welches man vor dem Lesen aus UDR checken muss. Bei anderen Systemen wie zum Beispiel Unix/Linux werden die Daten mit Parity-Error einfach verworfen. > Oder bekommt man das Byte trotzdem? Du bekommst es auf jeden Fall, wenn Du auf der Empfängerseite den UART ohne Parity, aber mit Anzahl Datenbits + 1, einstellst. Beispiel: Sender 7E1 Empfänger: 8N1 Dann kannst Du selbst entscheiden, ob Du das Paritätsbit auswerten willst oder nicht. Aber Du musst es auf jeden Fall aus den Daten rausmaskieren. Ich benutze das an ATmegas mit vorgeschaltetem Multiplexer. Dort können dann Geräte wie z.B. Barcode-Leser sowohl mit 8N1 als auch mit 7E1 angeschlossen werden. Auf Empfängerseite (ATmega) ist immer 8N1 eingestellt und ich entscheide selbst über das Paritätsbit. > Ich finde es etwas seltsam dass so häufig etwas verschluckt wird. Das passiert meist dann, wenn man keinen oder einen zu kleinen Ringbuffer auf der Empfängerseite hat. Läuft der Ringbuffer voll, weil das Programm gerade mit etwas anderem beschäftigt ist, gehen Daten verloren. Dagegen gibt es zwei Mittel - eventuell auch in Kombination: 1. Ringbuffer vergrößern 2. Mit dem Sender in einen Dialog treten, d.h. nach einer bestimmten Anzahl von Frames mit einer Quittung antworten, auf die der Sender warten muss. Dadurch kann man den Sender dann ausbremsen. > Ich verwende mit uCs jede Menge serielle Übertragung und wenn man > Baudrate etc passen sehe ich quasi nie falsche Bytes. Das kommt immer auf die Anwendung an.
:
Bearbeitet durch Moderator
Moin, ich wollte nur Bescheid sagen, dass ich mich Montag wieder zurückmelde sobald ich den Controller weiter programmiere. Bis dahin ein schönes Wochenende Viele Grüße
Frank M. schrieb: >> Oder bekommt man das Byte trotzdem? > > Du bekommst es auf jeden Fall, wenn Du auf der Empfängerseite den UART > ohne Parity, aber mit Anzahl Datenbits + 1, einstellst. Man bekommt es immer, egal wie der UART auf der Empfängerseite eingestellt ist. Es werden nicht die Flanken oder ähnliches gezählt, es werden ganz einfach Samples zu bestimmten Zeiten genommen und am Ende wird nach Majority Prinzip entschieden, was da genau empfangen worden ist. D.h. konkret beim AVR werden 16 Samples pro bit genommen. Samples 8,9 und 10 werden dannn ausgewertet. Beim Stoppbit sind es nur 10 Samples aber es sind wiederum die Samples 8,9 und 10 die ausgewertet werden. > Sender 7E1 > Empfänger: 7N1 Byte wird empfangen, Parity wird als Stoppbit empfangen. > Empfänger: 8N1 Byte wird empfangen, Parity wird als bit7 empfangen, Stoppbit OK. > Empfänger: 8E1 Byte wird empfangen, Parity wird als bit7 empfangen, Stoppbit wird als Parity empfangen. Das gesendete Byte wird manchmal falsch, manchmal richtig gedeutet, aber es wird immer empfangen und bleibt in UDR.
:
Bearbeitet durch User
Marc V. schrieb: >> Stimmt, denn es wäre "ein ASCII-Protokoll", weil Substantiv Neutrum. > Tja, ich bin kein Deutscher, das ist nicht meine Muttersprache, Dann sei dir verziehen und fass es als Kompliment auf. Mein Schwedisch ist bei Weitem nicht so gut. >>> Das, wovon du redest sind Nutzdaten, es ist absolut uninteressant ob >>> da ASCII-Zeichen oder binäre Daten übertragen werden. >> Nö. Bei HTTP sende ich einen Header, gefolgt von den eigentlichen >> Nutzdaten. Der Header hingegen besteht laut Standard ausschließlich >> aus ASCII-Zeichen (inklusive CR und LF). > Nö - Nein - No - Nada - Njet. Wenn im Protokoll festgeschrieben steht, dass die Daten ASCII zu sein haben, dann darf ich da keine beliebigen Binärdaten reinkippen. Außerdem ist die Interpretation der Daten ziemlich genau vorgegeben. Beispiel: "Content-Length: 1024\r\n" ist so spezifiziert, dass die Zeichenfolge "1024" als (beliebig lange!) ASCII(!)-Repräsentation einer Dezimalzahl(!) aufgefasst wird. Ich darf da kein Zeichen reinschreiben, welches nicht zwischen 0x30 und 0x39 liegt! Diese Eigenschaft (also menschenlesbare Inhalte zu übertragen) zeichnet ASCII-Protokolle aus. > Der Header, inclusive ev. Nutzdaten muss erst beim Empfänger ankommen. Nein. Bevor der Empfänger den Header nicht analysiert hat, weiß er noch nichtmal, wieviele Nutzdaten überhaupt ankommen werden. Wenn ich eine 1 GB große Datei aus dem Internet herunterlade, dann ist für den Browser nur der HTTP-Header relevant, denn was in der Datei drin ist, hat ihn nicht zu interessieren. Er will aber wissen, wie sie heißt und wie groß sie ist - ohne sie vollständig herunterzuladen. > Insofern ist es absolut uninteressant, ob da ASCII oder binary > übertragen wird. Nein. Nicht alle Transportkanäle erlauben die Übertragung von rohen Binärdaten. Da ASCII aber nur eine Teilmenge aller Binärwerte ist, muss der Kanal es aber auch nicht sein. Eine serielle Schnittstelle mit dem Format 7N1 wird den Bytewert 0xFF nur als 0x7F übertragen. Aus genau diesem Grund sind Internet-Protokolle wie SMTP eben keine Binärprotokolle (sondern ASCII-Protokolle!), denn sie verlangen nicht, dass der Transportkanal "8 bit clean" sein muss. Und aus genau diesem Grund verlangt SMTP auch, dass die Nutzdaten keine Binärdaten sind. Also werden E-Mails vor dem Versand so codiert (Base64, quoted-printable, ...), damit sie nur noch ASCII-Zeichen enthalten. Sie dürfen keine Binärdaten sein, denn sonst können sie nicht korrekt mittels SMTP übertragen werden! > Ist hoffe, dir ist das Ganze jetzt ein bisschen klarer geworden, > obwohl, viel Hoffnung habe ich nicht... Für dich sind ASCII-Wert normale Binärwerte. Das ist auch richtig. Aber nicht jeder Binärwert ist auch ein ASCII-Wert und daraus ergeben sich Konsequenzen, die dazu führen, dass - jedes ASCII-Protokoll ein Binärprotokoll ist, aber - nicht jedes Binärprotokoll ein ASCII-Protokoll ist. Und darum wird zwischen ASCII-Protokollen und Binärprotokollen unterschieden.
S. R. schrieb: >> Der Header, inclusive ev. Nutzdaten muss erst beim Empfänger ankommen. > > Nein. Bevor der Empfänger den Header nicht analysiert hat, weiß er noch > nichtmal, wieviele Nutzdaten überhaupt ankommen werden. Nachdem sich die Gemüter ein bisschen beruhigt haben, versuche ich zu erklären, was (meinerseits) gemeint ist: Du schickst ein Paket nach Schweden - was drin steht ist vorerst uninteressant, aber die Adresse muss draufstehen (und zwar in ASCII). Die Post hat die Aufgabe, dieses Paket unbeschädigt und ohne dessen Inhalt zu verändern, nach Schweden zu befördern. Das passiert auch, dieses Paket ist intakt nach Schweden angekommen, der erste Teil des Headers (die Adresse auf dem Paket) hat ihren Zweck erfüllt. Jetzt wird dein Paket aufgemacht und der Inhaltsverzeichnis (der zweite Teil des Headers) wird gelesen. Nachdem du das gemacht hast, weisst du was sich in dem Paket befindet (das wären dann Nutzdaten). Aber: Ob sich nun in deinem Paket Briefe oder Bücher auf Deutsch (ASCII) oder auf Chinesisch (Binär) befinden, spielt gar keine Rolle. Warum: Weil die Daten (oder Paket) dank der Post (Internet) fehlerfrei angekommen sind. Die Post ihrerseits hat verschiedene Systeme der Nachrichten- sowie Brief- und Paketübermittlung entwickelt. Du als Nutzer weisst gar nicht wie das alles funktioniert, brauchst du auch nicht, aber deswegen kann man nicht sagen:
1 | "Ich habe ein Paket ohne Beschädigung (Fehler) nach Schweden getragen" |
- nein, du hast nur dieses Paket bei der Post abgegeben. Somit sind Fehler bei der Übertragung (Zustellung) ausgeschlossen (bzw. nicht ausgeschlossen aber du hast keinen Einfluss auf diese) deswegen hat es auch keinen Sinn, in den Briefen nur jede zweite Zeile zu beschreiben oder nur auf Deutsch und nicht auf Chinesisch zu schreiben, um die Übertragung (Zustellung) sicherer zu machen. Ich hoffe, ich habe mich einigermassen verständlich ausgedrückt...
:
Bearbeitet durch User
Marc V. schrieb: > Kaum. > Wenn das Ganze so rausgesendet würde, hätte er recht und ich nicht. > Da dies aber nicht der Fall ist... Ich denke schon, dass ihr aneinander vorbei geredet habt und in den letzten Posts wird das IMO deutlich. Der eine redet von Protokollen, die die Datenübertragung betreffen, der andere redet von Protokollen, die die Dateninterpretation betreffen. Du hast das jetzt mit dem Postpaketbeispiel IMO schön verdeutlicht. Deshalb finde ich, dass ihr beide recht habt, ihr spracht nur nicht vom Gleichen ;)
Hallo Hans, vielleicht etwas altmodisch aber für deine Anwendung ideal. Die serielle schnittstelle besteht nicht nur aus TX und RX du kannst auch noch das RST Signal verwenden. Mein Vorschlag: LabView aktiviert das RST vor dem senden nach einer kuzen pause sendet es deine [85][INT_LO/HI] [INT_LO/HI] und optinal [CS] danach eine kuze Pause und deaktiviert das RST. Dein uC erkennt den Pegelwechsel vom RST und beginnt alle Variabel zu initalisieren und aktiviert den RX Interrupt. Nach einem erneuten Pegel wechesel des RST wird der RX Interrupt deaktiviert.Jetzt kann man die Prüfsumme abgleichen ist alles OK dan Flag setzen für das Hauptprogramm. Jetzt dürfte es egal sein ob du 0-65535 als 1-5 byte ASCII oder nur mit 2 Byte überträgst. ms
Ja, du hast dich verständlich ausgedrückt. Das ändert aber nichts an der Tatsache, dass man sowohl das Format der Nutzdaten als auch das Format sämtlicher sonstiger Kommunikation auf ASCII einschränken kann. Warum sollte man das tun? Weil du in deiner Analogie den Zollbeamten vergessen hast, der das Paket aufmacht und alles wegwirft, was er nicht lesen kann. Protokolle, die auf ASCII aufbauen, sind oft einfacher zu - verstehen und zu debuggen (Terminalprogramm reicht zum Zuschauen) - erweitern (Länge des Headers nicht vorgegeben *) - aktualisieren (weil z.B. Zahlenbereiche nicht implizit begrenzt sind) - übertragen (geringere Anforderungen an den Transportweg). * Beispiel: Weil HTTP die Datenlängen im Header als ASCII-Zahl überträgt, kann es implizit mit beliebig großen Dateien umgehen. Binärprotokolle können das nicht. Das sind Gründe, ASCII-Protokolle von Binärprotokollen zu unterscheiden.
Man könnte sagen: ASCII-Übertragung ist ein Protokoll mit Feldern variabler Länge, das mit Trennzeichen/Trenntags arbeitet. So wie XML und Verwandte. Und ist damit robuster und flexibler als feste Länge mit/ohne Binär.
S. R. schrieb: > Warum sollte man das tun? Weil du in deiner Analogie den Zollbeamten > vergessen hast, der das Paket aufmacht und alles wegwirft, was er nicht > lesen kann. LOL. Frank hat eine Verbindung konstruiert, bei der nur ASCII fehlerfrei durchkommt, bei binär werden Zeichen an genau bestimmbaren Stellen verschluckt, du hast diesen Zollbeamten... Also: Zollbeamter wird gefeuert oder er muss einen Sprachkurs erfolgreich beenden. Punkt. Schande, sowas.
:
Bearbeitet durch User
Dann nimm eine serielle Verbindung mit 7N1 als Beispiel. Da geht ASCII fehlerfrei durch, aber nicht jeder Binärwert. Nachtrag: PS/2 wäre auch ein binäres Protokoll, wo selektiv Bytes verschluckt werden können. Viele Controller filtern nämlich sämtlichen Traffic, der nicht wie "kommt von einer Tastatur oder eine Maus" aussieht - das ist für (Multi-)Touchpads tatsächlich ein Problem. Geht das nicht in deinen Kopf rein, dass sowas ein Problem sein kann (nicht muss)?
:
Bearbeitet durch User
S. R. schrieb: > .... Kommunikation auf > ASCII einschränken kann. > > Warum sollte man das tun? Nun, man muß sowas nicht wirklich tun, wenn die ganze Konfiguration überschaubar genug ist. Und in dem eingangs geschilderten Falle ist sie überschaubar. Was der TO nicht wirklich verstanden hat ist, daß er hier zwei unabhängige Instanzen (PC und µC) vor sich hat, die eine Art Synchronisation benötigen, um sinnvoll miteinander kommunizieren zu können. Ob man das über bestimmte ASCII-Zeichen macht oder mit anderen Binärwerten, ist vom Prinzip her egal. Wichtig sind nur zwei Dinge: - Synchronisation, also Erkennung beim Empfänger, an welcher Stelle einer Datenübertragung man ist, wenn man ein Zeichen empfangen hat - Fehlererkennung, also ob eine Übertragung OK ist oder nicht. Oft ist ebenso wichtig, daß man mit leichtem Gepäck daherkommt, also ohne übertrieben aufwendigen Umfang an zusätzlichem Zeugs. Nochwas: Wenn man bei beiden Teilnehmern sowas wie 8N1 einstellt und back to back sendet, dann ist die Gefahr groß, daß man damit nach einiger Zeit einen Übertragungsfehler verursacht, weil die Bit-Takte beider Teilnehmer dezent differieren können. Ist ja häufig, daß man im µC eine gewünschte Baudrate nicht ganz exakt hinbekommt und einen Restfehler von einigen Prozent hat. Dagegen hilft eigentlich immer, mit 8N2 zu senden oder mal eine Pause einzulegen. W.S.
W.S. schrieb: > Wenn man bei beiden Teilnehmern sowas wie 8N1 einstellt und back to back > sendet, dann ist die Gefahr groß, daß man damit nach einiger Zeit einen > Übertragungsfehler verursacht, weil die Bit-Takte beider Teilnehmer > dezent differieren können In Zahlen: Ist der Sender langsamer als der Empfänger, so trifft das nächste Startbit später ein als erwartet, das ist garkein Problem, ist ja asynchron. Im umgekehrten Fall kommt das Starbit zu früh; es gibt zwar unzählige Implementationen von UARTs, aber i.A. kann man davon ausgehen, dass der Empfänger zufrieden ist, wenn er das Stopbit (in der Mitte der Bit-Zeit) verifiziert hat, also darf das nächste Startbit um maximal eine halbe Bitzeit zu früh kommen, das ergibt also 9,5 statt 10 Bitzeiten und damit 5% zulässige Abweichung. Das ist bei Quarz auf beiden Seiten kein Problem, andrerseits erklärt das auch, warum eben Quarztakt für UARTs empfohlen wird. Allerdings wird das in diesem Forum immer wieder vehement bestritten. Georg
W.S. schrieb: >> .... Kommunikation auf ASCII einschränken kann. >> Warum sollte man das tun? > Nun, man muß sowas nicht wirklich tun, wenn die ganze Konfiguration > überschaubar genug ist. Marc behauptete, es gäbe keine ASCII-Protokolle, und dem widerspreche ich vehement. Wenn die Bandbreite kein Problem darstellt, bevorzuge ich ASCII-Protokolle allein aus dem Grund, dass ich einen Teilnehmer entwickeln und im Terminal testen kann, bevor die Gegenstelle bereit ist. So habe ich zu jeder Zeit nur eine Baustelle. > Wenn man bei beiden Teilnehmern sowas wie 8N1 einstellt und back to back > sendet, dann ist die Gefahr groß, daß man damit nach einiger Zeit einen > Übertragungsfehler verursacht, weil die Bit-Takte beider Teilnehmer > dezent differieren können. Ja, ist eine allgemeine Grundregel für Kommunikation: - ist der Empfänger zu schnell, nutzt du die Bandbreite nicht optimal, - ist der Sender zu schnell, gibt es irgendwann Datenverlust. Georg schrieb: > Allerdings wird das in diesem Forum immer wieder vehement bestritten. Hängt vom Anwendungsfall ab. Eine serielle Debugschnittstelle, die nur im Labor bei Raumtemperatur benutzt wird, braucht keinen Quarztakt. Aber die wird auch nicht dauerhaft senden...
S. R. schrieb: > Ja, ist eine allgemeine Grundregel für Kommunikation: > ... > - ist der Sender zu schnell, gibt es irgendwann Datenverlust. Prinzipiell gibt es doch das Stop-Bit als Puffer. Je nach dem, wo der Empfänger das Stop-Bit abtastet, könnte er doch sofort danach wieder auf die Flanke vom nächsten Start-Bit warten und damit deutlich schneller sein, als der Sender, auch wenn die Symbolrate des Senders etwas nach oben abweicht.
Ist der Sender dauerhaft schneller als der Empfänger, wird irgendwann jeder dazwischenliegende Puffer voll sein. Egal welcher Größe. Außerdem redest du davon, dass der Empfänger schneller ist, nicht der Sender.
:
Bearbeitet durch User
S. R. schrieb: > Außerdem redest du davon, dass der Empfänger schneller ist, nicht der > Sender. Nein, ich rede davon, dass der Empfänger mit einer etwas geringeren Symbolrate abtastet als der Sender die Zeichen sendet und damit langsamer bezogen auf die Bitrate ist. Einen gewissen Taktratenfehler kann er trotzdem ausgleichen, wenn er das Stopbit nicht am Ende abtastet, sondern vorher schon wieder in Idle geht und auf die nächste Start-Flanke wartet, während der Sender das Stop-Bit in voller Länge senden muss.
Verstehe. Ob moderne UARTs das machen, weiß ich allerdings nicht - ich vermute "eher nein".
Ich hielt das OSI Schichtenmodell eigentlich für zu theoretisch. Aber wenn ich lese, wie hier die Übertragung von Bits, die Sicherung der Übertragung und die Nutzdaten durcheinandergeworfen werden, sollte so mancher sich doch damit beschäftigen. http://www.elektronik-kompendium.de/sites/kom/0301201.htm MfG Klaus
Klaus schrieb: > Ich hielt das OSI Schichtenmodell eigentlich für zu theoretisch. Aber > wenn ich lese, wie hier die Übertragung von Bits, die Sicherung der > Übertragung und die Nutzdaten durcheinandergeworfen werden, sollte so > mancher sich doch damit beschäftigen. LOL. Stimme ich voll zu, nur wollte ich nicht wieder derjenige sein, der darauf rumreitet.
Ihr redet tatsächlich an einander vorbei. Denn ihr habt beide recht, da ihr verschiedene OSI Schichten meint...
M. Keller schrieb: > Ihr redet tatsächlich an einander vorbei. Denn ihr habt beide recht, da > ihr verschiedene OSI Schichten meint... Kann sein, nichtsdestotrotz hat der HTTP mit Transport, sicherer Übertragung und Datenintegrität genausoviel zu tun, wie die Würstchenbude an der Ecke, nämlich genau nichts. Schritt 1. Client bereitet eine HTTP-Anfrage vor. Schritt 2. Die Anfrage wird fur den Versand vorbereitet, also in einen Paket verpackt. Die Headerdaten werden dafür benutzt. Schritt 3. Das Paket wird über Internet verschickt. Ab jetzt ist der ganze HTTP-Kram nur noch Paketinhalt, also Nutzdaten. Schritt 4. Paket ist beim Empfänger angekommen, wird ausgepackt, die Headerdaten werden wieder benutzt. Schritt 5. Ab jetzt wird HTTP wieder verwendet. Es geht um Schritt 3 und 4, also um sicheren Transport der Daten. Was vorher und nachher mit diesen Daten gemacht wird und wie das Ganze dann interpretiert wird, ist eine ganz andere Baustelle und hat mit dem Transport nichts zu tun. Insofern ist es absolut uninteressant, was da als Nutzdaten transportiert wird - ASCII, Binär, HTTP, Deutsch, Chinesisch... P.S. Auch ein Zollbeamter hat mit Transport herzlich wenig zu tun...
:
Bearbeitet durch User
Marc V. schrieb: > Schritt 3. Das Paket wird über Internet verschickt. Ab jetzt ist der > ganze > HTTP-Kram nur noch Paketinhalt, also Nutzdaten Ja klar. Ab hier ist die nächste Schicht verantwortlich, nämlich TCP. Es ging w.a. darum, dass die niedrigen Schichten (transport, sicherungsschicht...) ebenfalls ASCII basiert sein können, wodurch diese Schichten einfacher zu debuggen sind. Z.b. modbus, das gibt es als ASCII oder RTU (binär)
Marc V. schrieb: > Kann sein, nichtsdestotrotz hat der HTTP mit Transport, sicherer > Übertragung und Datenintegrität genausoviel zu tun, wie die > Würstchenbude an der Ecke, nämlich genau nichts. Richtig. Und genausowenig hat es etwas mit Paketen zu tun. Marc V. schrieb: > Schritt 2. > Die Anfrage wird fur den Versand vorbereitet, also in einen Paket > verpackt. Die Headerdaten werden dafür benutzt. Bei HTTP hab ich noch nie Pakete gesehen. Da schreibt man einfach in einen Socket, solange man was zu sagen hat. Und sowohl die HTML-Header als auch der HTML-Body sind reines 7-Bit ASCII, selbst simple Umlaute muß man escapen. Ist man fertig, schließt man den Socket wieder. Will man Pakete sehen, muß eine Schicht tiefer schauen, da sieht man aber HTTP nicht. In den Paketen könnte auch SMTP oder FTP oder auch Telnet sein. Das Protokol auf dieser Ebene heißt aber auch TCP und nicht HTTP. MfG Klaus
Lassen wir das. Marc geht davon aus, dass jeder Übertragungskanal fähig ist, binäre (Nutz-)Daten vollständig zu übertragen und folgert daraus korrekt, dass jede Datenübertragung im Endeffekt binär ist. Jede Beschränkung (z.B. auf ASCII) ist daher Verschwendung und Unsinn. Dieses Weltbild ist schlüssig. Da aber die Annahme in dieser Allgemeinheit falsch ist, sind auch die Schlussfolgerungen nicht gültig. Gegenbeispiele habe ich genannt. Ob binäre Nutzdaten nun durch einen ungeeigneten Kommunikationskanal, einen übereifrigen Zollbeamten (technisch: "Firewall mit DPI") oder einen Programmfehler dahingerafft werden, ist unerheblich. Gewöhnliches ASCII hat hier eine höhere Überlebenschance (nebst anderen Vor- und Nachteilen).
S. R. schrieb: > Lassen wir das. > > Marc geht davon aus, dass jeder Übertragungskanal fähig ist, binäre > (Nutz-)Daten vollständig zu übertragen und folgert daraus korrekt, dass > jede Datenübertragung im Endeffekt binär ist. Jede Beschränkung (z.B. > auf ASCII) ist daher Verschwendung und Unsinn. > > Dieses Weltbild ist schlüssig. > > Da aber die Annahme in dieser Allgemeinheit falsch ist, sind auch die > Schlussfolgerungen nicht gültig. Gegenbeispiele habe ich genannt. > > Ob binäre Nutzdaten nun durch einen ungeeigneten Kommunikationskanal, > einen übereifrigen Zollbeamten (technisch: "Firewall mit DPI") oder > einen Programmfehler dahingerafft werden, ist unerheblich. Gewöhnliches > ASCII hat hier eine höhere Überlebenschance (nebst anderen Vor- und > Nachteilen). Stimmt nicht ganz. Wenn Daten kaputt gehen, dann geht auch ASCII kaputt. Aber das Prinzip ASCII + Control Characters vermischt einfach Nutzdaten und Übertragungssteuerung in einem Byte, also zwei verschiedene Schichten des ISO-Modells :-)
S. R. schrieb: > jede Datenübertragung im Endeffekt binär ist. Jede Beschränkung (z.B. > auf ASCII) ist daher Verschwendung und Unsinn. Nein, selbstverständlich nicht. Bei 100% fehlerfreier Übertragung ist ASCII eine (ganz akzeptable) Möglichkeit, Datenpakete zuverlässig zu unterscheiden, genau wie Präambel oder <SOF>. Bei GPS (NMEA) startet eine Nachricht mit "$" und endet mit CR/LF. Checksum ist Bestandteil der Nachricht. Funktioniert fehlerfrei, da diese Zeichen sonst nirgendwo in der Nachricht vorkommen. Aber es gibt auch ein binär Protokoll für GPS, welches viel schneller ist, aber nicht HR ist. Natürlich macht es für mich keinen Sinn, GPS mit ASCII zu benutzen, da die Umwandlung nach binär doppelte Arbeit darstellt. Aber zur Kontrolle (Debug) ist es natürlich besser. > Da aber die Annahme in dieser Allgemeinheit falsch ist, sind auch die > Schlussfolgerungen nicht gültig. Gegenbeispiele habe ich genannt. Nein, missverstanden, bin in dieser Beziehung niemals ausschlieslich geworden, ASCII hat auch Vorteile, unbestritten.
S. R. schrieb: > Verstehe. Ob moderne UARTs das machen, weiß ich allerdings nicht - ich > vermute "eher nein". Ich hab's beim Schreiben des STM32-Bootlader-Programms neulich mal wieder erlebt: FTDI-Chip auf PC-Seite, Bootlader per uart1 auf STM Seite. Kann jeder nachvollziehen. Mit 8N2 am PC war die Sache dann erledigt. W.S.
Moin Thomas, ich bin jetzt soweit, dass ich mit LabView einen Wert an den uC senden möchte. Ich habe im Control Panel, ein uint16 Eingabefeld. Wenn ich da eine 77 (dezimal) eintrage, muss ja zum uC der String 4D gesendet werden. Kannst du mir erklären, wie ich einen uint16 so in LabView umrechne, dass das richtige an den uC geschickt wird? Danke & viele Grüße Thomas E. schrieb: > Du dekodierst deine Daten im Hauptprogramm. Das Befehlsbyte liegt vor > den Daten. Dieses kommt in eine Variable(unsigned char) und die > eigentlichen Daten in eine andere Variable(unsigned int). Und dann > wertest du das aus: > > if(Befehlsbyte == 85) > { > // tu was > } > > oderswitch(Befehlsbyte) > { > case 0: > //tu dies > break; > case 1: > //tu das > break; > case 85: > //tu jenes > break; > } > > Alles im Hauptprogramm oder in Funktionen, die aus dem Hauptprogramm > aufgerufen werden.
:
Bearbeitet durch User
Hans D. schrieb: > Kannst du mir erklären, wie ich einen uint16 so in LabView umrechne, > dass das richtige an den uC geschickt wird? Das ist wohl das finale Ergebnis des gesamten Threads. W.S.
Hans D. schrieb: > Ich habe im Control Panel, ein uint16 Eingabefeld. Wenn ich da > eine 77 (dezimal) eintrage, muss ja zum uC der String 4D gesendet > werden. Nein. Ich fürchte, du hast den Unterschied zwischen Dezimalzahl, Hexadezimalzahl und String nicht verstanden.
Conny G. schrieb: > Stimmt nicht ganz. Wenn Daten kaputt gehen, dann geht auch ASCII kaputt. Dann nimm nicht ASCII, sondern irgendwelche Codeworte mit einem Hammingabstand größer zwei. Schon damit kannst du Einzelfehler im Codewort korrigieren und die effektive Sicherheit der Übertragung erheblich steigern.
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.