Hallo,
ich will eine Variablenfolge die z.B so aussieht 99|33|01|01 über die
serielle Schnittstelle zum Mikrocontroller schicken und im Mikro dann
das Empfangene auf Gleichheit vergleichen.
Im Moment mache ich das so:
1 | char ReceiveSerial[40];
| 2 | char Lr99110101[] = "99|11|01|01";
| 3 |
| 4 | // Zeichen Empfangen wenn was anliegt
| 5 | if ((UCSR0A & (1<<RXC0)))
| 6 | {
| 7 |
| 8 | uart_gets(ReceiveSerial, sizeof( ReceiveSerial));
| 9 |
| 10 | USART_Transmit(); // Kontrolle zum Terminalprogramm
| 11 |
| 12 |
| 13 | if (!strcmp(ReceiveSerial,Lr99110101))
| 14 | {
| 15 |
| 16 | stopbit = false;
| 17 | }
| 18 |
| 19 | }
|
Das ist das uart_gets() 1 | /*String empfangen*/
| 2 | void uart_gets( char* Buffer, uint8_t MaxLen )
| 3 | {
| 4 | uint8_t NextChar;
| 5 | uint8_t StringLen = 0;
| 6 |
| 7 | NextChar = uart_getc(); // Warte auf und empfange das nächste Zeichen
| 8 |
| 9 | // Sammle solange Zeichen, bis:
| 10 | // * entweder das String Ende Zeichen '\0'kam
| 11 | // * oder das aufnehmende Array voll ist
| 12 | while( NextChar != '\r' && StringLen < MaxLen - 1 ) {
| 13 | *Buffer++ = NextChar;
| 14 | StringLen++;
| 15 | NextChar = uart_getc();
| 16 | }
| 17 | // Noch ein '\0' anhängen um einen Standard
| 18 | // C-String daraus zu machen
| 19 | *Buffer = '\0';
| 20 | }
|
Funktionieren tut das, doch merke ich das die uart_gets() unglaublich
viel Zeit beansprucht, da andere Funktionen ins stocken geraten.
Wie könnte ich denn auch eleganter dieses char Array auf Gleichheit
abfragen ohne eine Funktion zu benutzen die das mit eine Stringfunktion
macht?
Vielen Dank für die Tips....
1.) UART Receive Interrupt verwenden
ODER
2.) while-schleife abbrechen, wenn z.B. 10ms kein Zeichen gekommen ist.
Wenn allerdings ein Zeichen kommt, dann alle einlesen, bis '\r'. Sonst
wartest du bis auf den St.Nimmerleinstag auf deinen String
Hallo Markus,
Deine Empfangsfunktion bringt wahrscheinlich deshalb den Rest ins
Stocken, weil es je nach Baudrate, Sendegeschwindigkeit/-frequenz der
Gegenseite und Länge der Nachricht ein paar Millisekunden oder länger
dauern kann, bis die Nachricht gelesen wurde.
Die Abhilfe geht in die Richtung, die Daniel geschrieben hat. Falls das
nicht möglich ist, müßtest Du während des Empfangens hin und wieder eine
Funktion aufrufen, die die anderen gewünschten Funktionen ausführt, so
eine Art Miniatur-Scheduler.
Zum Stringvergleich:
In diesem Fall ist der in Ordnung, anders geht es auch nicht schneller.
Wenn Du mit mehreren anderen Strings vergleichen willst, dann solltest
Du Dir einen Zustandsautomaten schreiben, der mit jedem Zeichen
weitergesetzt wird oder Dich mal mit String-Matching-Algorithmen
beschäftigen
(http://en.wikipedia.org/wiki/Category:Algorithms_on_strings,
http://de.wikipedia.org/wiki/String-Matching-Algorithmus).
Danke schonmal.
Kann mir vielleicht einer ein Tipp geben und zwar will ich diese
Variable:
1 | char Codetest2[] = {'29','34','41','52'};
|
per RS232 ohne eine String Funktion übertragen. Also einfach nur per
Zahl. Wie ist da der beste Weg? Ich habe folgende Funktionen zur
Verfügung (s. Unten), gerne nehme ich dann auch noch Beispiele entgegen.
DAs Problem welches ich einfach habe ist das ein char nur 1 Zeichen sein
darf, also bei der erste Ziffer mit 29 würde mit der usart_write_char
Funktion nur eine 9 übertragen.
Wie stelle ich das am besten an das nachher "29344152" auf meinem
Terminal steht?
Vielen Dank.
1 | void usart_write_char(char c)
| 2 | {
| 3 | #if CMD_TELNET
| 4 | if(usart_status.usart_disable)
| 5 | {
| 6 | if(rx_buffer_pointer_in == (rx_buffer_pointer_out - 1))
| 7 | {
| 8 | //Datenverlust
| 9 | telnetd_send_data ();
| 10 | while(telnetd_status.ack_wait)
| 11 | {
| 12 | eth_get_data();
| 13 | }
| 14 | }
| 15 |
| 16 | *rx_buffer_pointer_in++ = c;
| 17 |
| 18 | if (rx_buffer_pointer_in == &usart_rx_buffer[BUFFER_SIZE-1])
| 19 | {
| 20 | rx_buffer_pointer_in = &usart_rx_buffer[0];
| 21 | }
| 22 | }
| 23 | return;
| 24 | #else
| 25 | #if !USE_CAM
| 26 | if(!usart_status.usart_disable)
| 27 | {
| 28 | //Warten solange bis Zeichen gesendet wurde
| 29 | while(!(USR & (1<<UDRE)));
| 30 | //Ausgabe des Zeichens
| 31 | UDR = c;
| 32 | }
| 33 | return;
| 34 | #endif //USE_CAM
| 35 | #endif
| 36 | }
| 37 |
| 38 | //------------------------------------------------------------------------------
| 39 | void usart_write_P (const char *Buffer,...)
| 40 | {
| 41 | va_list ap;
| 42 | va_start (ap, Buffer);
| 43 |
| 44 | int format_flag;
| 45 | char str_buffer[10];
| 46 | char str_null_buffer[10];
| 47 | char move = 0;
| 48 | char Base = 0;
| 49 | int tmp = 0;
| 50 | char by;
| 51 | char *ptr;
| 52 |
| 53 | //Ausgabe der Zeichen
| 54 | for(;;)
| 55 | {
| 56 | by = pgm_read_byte(Buffer++);
| 57 | if(by==0) break; // end of format string
| 58 |
| 59 | if (by == '%')
| 60 | {
| 61 | by = pgm_read_byte(Buffer++);
| 62 | if (isdigit(by)>0)
| 63 | {
| 64 |
| 65 | str_null_buffer[0] = by;
| 66 | str_null_buffer[1] = '\0';
| 67 | move = atoi(str_null_buffer);
| 68 | by = pgm_read_byte(Buffer++);
| 69 | }
| 70 |
| 71 | switch (by)
| 72 | {
| 73 | case 's':
| 74 | ptr = va_arg(ap,char *);
| 75 | while(*ptr) { usart_write_char(*ptr++); }
| 76 | break;
| 77 | case 'b':
| 78 | Base = 2;
| 79 | goto ConversionLoop;
| 80 | case 'c':
| 81 | //Int to char
| 82 | format_flag = va_arg(ap,int);
| 83 | usart_write_char (format_flag++);
| 84 | break;
| 85 | case 'i':
| 86 | Base = 10;
| 87 | goto ConversionLoop;
| 88 | case 'o':
| 89 | Base = 8;
| 90 | goto ConversionLoop;
| 91 | case 'x':
| 92 | Base = 16;
| 93 | //****************************
| 94 | ConversionLoop:
| 95 | //****************************
| 96 | itoa(va_arg(ap,int),str_buffer,Base);
| 97 | int b=0;
| 98 | while (str_buffer[b++] != 0){};
| 99 | b--;
| 100 | if (b<move)
| 101 | {
| 102 | move -=b;
| 103 | for (tmp = 0;tmp<move;tmp++)
| 104 | {
| 105 | str_null_buffer[tmp] = '0';
| 106 | }
| 107 | //tmp ++;
| 108 | str_null_buffer[tmp] = '\0';
| 109 | strcat(str_null_buffer,str_buffer);
| 110 | strcpy(str_buffer,str_null_buffer);
| 111 | }
| 112 | usart_write_str (str_buffer);
| 113 | move =0;
| 114 | break;
| 115 | }
| 116 |
| 117 | }
| 118 | else
| 119 | {
| 120 | usart_write_char ( by );
| 121 | }
| 122 | }
| 123 | va_end(ap);
| 124 | }
| 125 |
| 126 | //----------------------------------------------------------------------------
| 127 | //Ausgabe eines Strings
| 128 | void usart_write_str(char *str)
| 129 |
| 130 | {
| 131 | while (*str)
| 132 | {
| 133 | usart_write_char(*str++);
| 134 | }
| 135 | }
| 136 |
| 137 | void USART_Transmit( unsigned char data )
| 138 | {
| 139 | /* Wait for empty transmit buffer */
| 140 | while ( !( UCSR0A & (1<<UDRE0)) )
| 141 | ;
| 142 | /* Put data into buffer, sends the data */
| 143 | UDR0 = data;
| 144 | }
| 145 |
| 146 | /* puts ist unabhaengig vom Controllertyp */
| 147 | void uart_puts (char *s)
| 148 | {
| 149 | while (*s)
| 150 | { /* so lange *s != '\0' also ungleich dem "String-Endezeichen" */
| 151 | USART_Transmit(*s);
| 152 | s++;
| 153 | }
| 154 | }
|
Matthias H. schrieb:
> Hallo Markus,
>
> Deine Empfangsfunktion bringt wahrscheinlich deshalb den Rest ins
> Stocken, weil es je nach Baudrate, Sendegeschwindigkeit/-frequenz der
> Gegenseite und Länge der Nachricht ein paar Millisekunden oder länger
> dauern kann, bis die Nachricht gelesen wurde.
> Die Abhilfe geht in die Richtung, die Daniel geschrieben hat. Falls das
> nicht möglich ist, müßtest Du während des Empfangens hin und wieder eine
> Funktion aufrufen, die die anderen gewünschten Funktionen ausführt, so
> eine Art Miniatur-Scheduler.
>
Ich merke es auch beim Empfangen, also da passiert auch irgendetwas
zeitkritisches...
Hallo Markus,
> Ich merke es auch beim Empfangen, also da passiert auch irgendetwas
> zeitkritisches...
Das kannst Du ausrechnen. Nehmen wir an, Du benutzt eine Baudrate von
9600 und die Einstellungen 8N1 (8 Bits, keine Parität, 1 Stoppbit). Dann
werden pro Zeichen 9 Bits plus Startbit übertragen, also max. 960
Zeichen pro Sekunde. Deine Nachricht ist anscheinend 12 Zeichen lang.
Also dauert die Übertragung mindestens 1/80 Sekunde, also 12,5 ms. Falls
Dein Sender langsamer sendet, mit Pausen zwischen den Zeichen, oder
Deine Empfangsfunktion aus irgendeinem Grund noch viel Rechenleistung
verbrät, dann dauert es entsprechend länger.
Markus P. schrieb:
> Danke schonmal.
>
> Kann mir vielleicht einer ein Tipp geben und zwar will ich diese
> Variable:
>
> 1 | > char Codetest2[] = {'29','34','41','52'};
| 2 | >
|
>
>
> per RS232 ohne eine String Funktion übertragen. Also einfach nur per
> Zahl. Wie ist da der beste Weg? Ich habe folgende Funktionen zur
> Verfügung (s. Unten), gerne nehme ich dann auch noch Beispiele entgegen.
> DAs Problem welches ich einfach habe ist das ein char nur 1 Zeichen sein
> darf, also bei der erste Ziffer mit 29 würde mit der usart_write_char
> Funktion nur eine 9 übertragen.
> Wie stelle ich das am besten an das nachher "29344152" auf meinem
> Terminal steht?
Indem du damit anfängst erst mal das Array richtig zu definieren und zu
initialisieren.
1 | uint8_t Codetest2[] = { 29, 34, 41, 52 };
|
Du willst Zahlen speichern. Also ist schon mal char kein geeigneter
Datentyp dafür. Gewöhn dir an, char ausschliesslich für echte Zeichen
(also 'A', 'B', 'C'...) bzw. in Arrayform für Strings ("Hallo") zu
benutzen.
und dann schreibst du dir eine Funktion, die eine Zahl ausgeben kann. 1 | void usart_write_uint8( uint8_t nr )
| 2 | {
| 3 | char buffer[6];
| 4 |
| 5 | itoa( nr, buffer, 10 );
| 6 | usart_write_str( buffer );
| 7 | }
|
mit dieser Hilfsfunktion schreibst du dir eine weitere Funktion, die ein
Array von Zahlen ausgeben kann
1 | void usart_write_uint8Array( uint8_t size, uint8_t numbers )
| 2 | {
| 3 | uint8_t i;
| 4 |
| 5 | for( i = 0; i < size; i++ )
| 6 | usart_write_uint8( numbers[i] );
| 7 | }
|
und die benutzt du dann um dein ursprüngliches Array auszugeben
1 | #define ARRAY_SIZE(x) (sizeof(x)/sizeof(*x))
| 2 |
| 3 | int main()
| 4 | {
| 5 | .....
| 6 |
| 7 |
| 8 | usart_write_uint8Array( ARRAY_SIZE( Codetest2 ), Codetest2 );
| 9 | }
|
Das Geheimnis beim Programmieren besteht nicht darin, sich für jeden
Pfurz immer wieder möglichst komplizierte neue Funktionen einfallen zu
lassen. Das Geheimnis besteht darin, sich mit Basisfunktionen und darauf
aufbauenden Hilfsfunktionen letztendlich bis zu der Ebene
hochzuarbeiten, mit der man sein Problem lösen kann.
> char Codetest2[] = {'29','34','41','52'};
Das funktioniert so natürlich nicht, falls es überhaupt durch den
Compiler geht.
Du hast im wesentlichen folgende Möglichkeiten, falls Du nicht was
völlig eigenes basteln möchtest:
- Du überträgst zeichenweise (+ Nullbyte o. ä. als Endemarkierung). Da
braucht man dann nichts umzucodieren.
1 | char Codetest2[] = "29344152";
|
- Du überträgst zeichenweise binär. Dann mußt Du irgendein Zeichen außer
der 0 als Endezeichen festlegen. Ist aber ineffizient.
- Du überträgst in BCD-Codierung
(http://de.wikipedia.org/wiki/BCD-Code) binär. Dabei werden 2 Ziffern in
ein Byte gepackt, was effizienter ist als zeichenweiser Versand.
BCD geht ungefähr so (mit ungültiger Ziffer als Endemarkierung): 1 | char Codetest2[] = {0x29, 0x34, 0x41, 0x52, 0xf0};
|
- Du überträgst binär. Dann mußt Du festlegen, welchen Datentyp Du
verwenden möchtest, z. B. 4-Byte-Integer mit Vorzeichen, und mußt auf
die Byte Order achten, also in welcher Reihenfolge der Prozessor die
einzelnen Datenbytes, die die Variable bilden, im Speicher anordnet. Es
ist Network Byte Order = "Big Endian" empfehlenswert. Siehe Funktionen
htonl()/ntohl() usw. der Standardbibliothek, soweit vorhanden.
- Wenn Du kompliziertere Datenstrukturen übertragen möchtest, so wäre es
eine Überlegung wert, sich mal Google Protocol Buffers oder andere
Standardverfahren anzusehen (wobei Protocol Buffers wahrscheinlich zu
groß für Deinen Mikrocontroller ist...).
Vielen Dank Karl-Heinz du hast mir sehr weiter geholfen!!!!
Mein Problem wird nur auch noch das sein was der Matthias mir schon sehr
gut vorgerechnet hat. Ich betreibe auf den gleichen yc auch noch einen
Phasenanschnitt und da muss ich alle paar ms einen Triac ansteuern, da
kann ich mir keinen Zeitverlust gönnen. Jetzt weiß ich garnicht wie ich
das machen kann ohne den Phasenanschnitt zu beeinflussen ? Hättest du
(ihr) da auch vielleicht eine Idee ?
Vielen super dank schonmal ...
Regelmässige Dinge tun?
ganz klar: ein Fall für einen Timer.
Hallo Markus,
Markus P. schrieb:
> Mein Problem wird nur auch noch das sein was der Matthias mir schon sehr
> gut vorgerechnet hat. Ich betreibe auf den gleichen yc auch noch einen
> Phasenanschnitt und da muss ich alle paar ms einen Triac ansteuern, da
> kann ich mir keinen Zeitverlust gönnen. Jetzt weiß ich garnicht wie ich
> das machen kann ohne den Phasenanschnitt zu beeinflussen ? Hättest du
> (ihr) da auch vielleicht eine Idee ?
Du mußt Dir überlegen, in welchem Zeitraster Du den Triac bedienen mußt,
damit er funktioniert, wie Du willst und wieviel Zeit Dir zum Lesen der
Nachricht übrig bleibt.
Falls die Funktion uart_getc() blockierend liest, mußt Du eine andere
Möglichkeit finden.
Daniel V. hat ja dazu schon Anregungen geschrieben.
Interruptbetrieb heißt, daß der Schnittstellenbaustein einen
Hardware-Interrupt auslöst, sobald ein Zeichen ankommt und dann eine
Funktion aufgerufen wird, die das Zeichen z. B. in einen Buffer (am
besten eine globale Variable) liest. Die Interruptfunktion sollte so
wenig wie möglich machen, das heißt, das Prüfen, ob eine Nachricht
vollständig gelesen wurde und die Verarbeitung sollten außerhalb des
Interrupthandlers in der Hauptschleife gemacht werden. Es könnte noch
weitere Nebenbedingungen geben, z. B. daß Funktionen der
Standardbibliothek nicht aufgerufen werden dürfen oder so. Ob das von
Deinem Mikrocontroller unterstützt wird und wie es geht, sollte in der
Dokumentation stehen. Dazu sind noch ein paar Kleinigkeiten zu beachten,
z. B. sollten die Interrupts abgeschaltet werden, während der Puffer in
der Hauptschleife geleert wird, sonst gibt es undefiniertes Verhalten
und Fehler.
Beim Lesen mit Timeout brauchst Du eine nichtblockierende Lesefunktion
für die Schnittstelle und liest so lange, bis ein Timer zuende ist, z.
B. 10 ms, oder die Nachricht komplett ist.
Hab es jetzt mit einem Timer gelöst.
Markus P. schrieb:
> 1 | // Sammle solange Zeichen, bis:
| 2 | > // * entweder das String Ende Zeichen '\0'kam
| 3 | > // * oder das aufnehmende Array voll ist
| 4 | > while( NextChar != '\r' && StringLen < MaxLen - 1 )
|
Hey, dein Kommentar und dein Code passen nicht zusammen.
Das ist kein oder_ sondern ein _und.
Julian Schild schrieb:
> Markus P. schrieb:
>> 1 | // Sammle solange Zeichen, bis:
| 2 | >> // * entweder das String Ende Zeichen '\0'kam
| 3 | >> // * oder das aufnehmende Array voll ist
| 4 | >> while( NextChar != '\r' && StringLen < MaxLen - 1 )
|
>
> Hey, dein Kommentar und dein Code passen nicht zusammen.
> Das ist kein oder_ sondern ein _und.
Aber es ist eine while-Abfrage. Er soll solange drin bleiben, wie BEIDE
(also UND) Bedingungen erfüllt sind. Ist die eine ODER die andere
Bedingung nicht mehr erfüllt, dann wird die Schleife beendet.
Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
|