Hallo, ich möchte gerne serielle Daten mit Hilfe meines Atmega 2560 kontrollieren. Dabei sendet mein Gerät verschiedene Strings, die durch jeweilige Zeichen am Anfang der Zeichenketten gekennzeichnet sind- 0x04 und 0x08. Ich möchte nur die Strings, die mit dem Terminator 0x08 beginnen untersuchen. Diese sind 66 Zeichen lang. Ich habe mich nun mal daran gemacht, die vom Gerät gesendeten Daten einfach an meinen PC bzw. Hyperterminal weiterzusenden. Allerdings happert’s hierbei leider schon. Mit dem unten angefügten Programm, kann ich nach der Sendeaufforderung der seriellen Daten, zwar den entsprechenden Terminator, also 0x08, detektieren, allerdings erhalte ich nur jeden zweiten String in seiner vollen Länge, sprich die 66 Zeichen (0x08 + 64-Zeichen + 0x0D). Dazwischen erhalte ich nur zwei bis drei Zeichen des seriellen Protokolls. Mir scheint es, dass die Daten einfach zu schnell „reinkommen“ und das Programm keine Zeit hat, diese zu verarbeiten. Es kann durchaus sein, das 120 Strings mit dem Terminator 0x08 nacheinander folgen. Ich habe noch nicht allzu viel Erfahrung und würde mich freuen, wenn mir jemand weiterhelfen könnte. VG, Louis #include <avr/io.h> #include <stdio.h> #include <string.h> #define FOSC 16000000 #define BAUD 9600 #define UBRR_VAL (FOSC/16/BAUD-1) void USART_INIT (unsigned int UBRR) { UBRR0H = (unsigned char)(UBRR>>8); UBRR0L = (unsigned char)(UBRR); UCSR0B =(1 << RXEN0) | (1 << TXEN0); UCSR0C =(1<< UCSZ01) | (1 << UCSZ00); } uint8_t GET_CHAR(void){ while (!(UCSR0A & (1<<RXC0))); return UDR0; } uint8_t GET_STRING(char *BUFFER, int MAX_LENGTH){ char NEXT_CHAR; int STRING_LENGTH = 0; NEXT_CHAR = GET_CHAR(); while( (NEXT_CHAR != 0x0D) && (STRING_LENGTH < MAX_LENGTH - 1)){ *BUFFER++ = NEXT_CHAR; STRING_LENGTH++; NEXT_CHAR = GET_CHAR(); } *BUFFER = '\0'; } void SEND_CHAR(unsigned char data){ while (!(UCSR0A & (1 << UDRE0))); UDR0 = data; } void SEND_STRING(char *STRING){ while(*STRING){ SEND_CHAR(*STRING); *STRING++; } SEND_CHAR( 0x0D ); } void SEND_DATA(void){ SEND_CHAR( 0xAB ); SEND_STRING("4555BCV"); } void main(void) { char TEMP[66]; char LOG_TEMP[66]; USART_INIT(UBRR_VAL); SEND_DATA(); while(1){ GET_STRING(TEMP,66); if(TEMP[0] == 0x08){ SEND_STRING(TEMP); } } }
> Mir scheint es, dass die Daten einfach zu schnell „reinkommen“ und das > Programm keine Zeit hat, diese zu verarbeiten. Es kann durchaus sein, ^^................................^^ jepp > das 120 Strings mit dem Terminator 0x08 nacheinander folgen. ^^...............^^ Rechne mal kurz nach wie lange es dauert um einen String der länge n zu übertragen.. und dann rechne nach wie lange es dauern ∗dürfte∗, damit der Anfang der nächsten (unmittelbar anschließend eingehenden) Übertragung ∗nicht∗ verloren geht. Genau, da liegt ein Faktor von (etwas mehr als) n dazwischen. Deswegen funktioniert dasda > GET_STRING(TEMP,66); > if(TEMP[0] == 0x08){ > SEND_STRING(TEMP); so gar nicht (richtig). HTH
Den Code bitte vernünftig formatieren und in [ c] [ /c]-Tags einfassen. Üblicherweise werden Funktionen und Variablen nicht komplett groß geschrieben, das wird nur mit Präprozessormakros usw. gemacht.
Hi, danke für die schnelle Antwort! Das mit der Formatierung werde ich mir merken. Hmmmm, wie kann ich das dann am besten umgehen, bzw. welchen Ansatz soll ich hierbei am besten verwenden? VG, Louis
> Hmmmm, wie kann ich das dann am besten umgehen, bzw. welchen Ansatz soll > ich hierbei am besten verwenden? Interruptgesteuert, ggf. zwei (echt unterschiedliche) Puffer. Dann schreibt der Empfänger(-Interrupt) in den einen Puffer, wenn der voll ist (und als aufhebenswert erachtet wurde), dann werden die Puffer getauscht (Achtung Nebenläufigkeit!), der Empfänger(-Interrupt) schreibt in den 'neuen' Puffer, der Sender(-Interrupt) überträgt zeitgleich den 'alten'. Mit etwas Geschickt kann mans auch in einen einzigen Puffer reinquetschen, der nur zur Sicherheit einen kleinen Überhang mitbring. Vom Prinzip ein Ringpuffer der von Empfänger(-Interrupt) vollgeschrieben und vom Sender(-Interrupt) geleert wird. HTH
Hallo zusammen, ich hab nun mal den Rat von "g457" befolgt und mich an ein interrupt-gesteuertes Empfangen und Senden der Daten gemacht. Allerdings erhalte ich nur "Nonsense" übers Hyperterminal. Immer nur ein Zeichen wird permanent gesendet. Ich hab mal die Tutorials verwendet und versucht den Fehler aufzuspüren. Ohne Erfolg...Ich würde mich freuen, wenn mir jemand auf die Sprünge helfen könnte. VG, Louis #include <avr/io.h> #include <stdio.h> #include <string.h> #include <avr/interrupt.h> #define FOSC 16000000 #define BAUD 9600 #define UBRR_VAL (FOSC/16/BAUD-1) #define UART_BUFFER_SIZE 117 volatile uint8_t UART_RX_FLAG = 0; // String komplett volatile uint8_t UART_TX_FLAG = 1; // String komplett volatile uint8_t UART_TX_COUNTER = 0; volatile uint8_t UART_RX_COUNTER = 0; volatile char UART_RX_BUFFER[UART_BUFFER_SIZE]; volatile char UART_TX_BUFFER[UART_BUFFER_SIZE]; void USART_INIT (unsigned int UBRR) { UBRR0H = (unsigned char)(UBRR>>8); UBRR0L = (unsigned char)(UBRR); UCSR0B =(1 << RXEN0) | (1 << TXEN0) | (1 << RXCIE0) | (1 << TXCIE0); UCSR0C =(1<< UCSZ01) | (1 << UCSZ00); } ISR(USART0_RX_vect){ unsigned char DATA; DATA = UDR0; if(UART_RX_FLAG == 0) { if( DATA == 0x0D ){ UART_RX_BUFFER[UART_RX_COUNTER] = 0; UART_RX_FLAG = 1; UART_RX_COUNTER = 0; } else if( (DATA != 0x0D) && (UART_RX_COUNTER < (UART_BUFFER_SIZE - 1)) ){ UART_RX_BUFFER[UART_RX_COUNTER] = DATA; UART_RX_COUNTER++; } } } ISR(USART0_UDRE_vect){ unsigned char DATA; char *UART_TX_POINTER = UART_TX_BUFFER; DATA = *UART_TX_POINTER++; if (DATA == 0x0D){ UCSR0B &=~ (1<<UDRIE0); UART_TX_POINTER = UART_TX_BUFFER; UART_TX_FLAG = 1; } else UDR0 = DATA; } void SEND_STRING(char *DATEN){ if(UART_TX_FLAG == 1){ strcpy (UART_TX_BUFFER, DATEN); UART_TX_FLAG = 0; UCSR0B |= (1<<UDRIE0); } } void GET_STRING(char *DATEN){ if(UART_RX_FLAG == 1){ strcpy (DATEN, UART_RX_BUFFER); UART_RX_FLAG = 0; } } void main(void) { char STRING_BUFFER[117]; uint8_t BUFFER_FULL = 0; USART_INIT(UBRR_VAL); sei(); while(1){ if((UART_RX_FLAG == 1) && (BUFFER_FULL == 0)) { GET_STRING(STRING_BUFFER); BUFFER_FULL = 1; cli(); } if((UART_TX_FLAG == 1) && (BUFFER_FULL == 1)) { SEND_STRING(STRING_BUFFER); BUFFER_FULL = 0; sei(); } } }
cli() und sei() müssen in der Hauptschleife entfernt werden...
Hallo Bernd, danke für die schnelle Antwort. Ich habs soeben probiert, leider ohne Erfolg! VG, Louis
Der Code scheint mir aber ansonsten vernünftig zu sein...
UART_TX_POINTER wird bei jedem Interrupt wieder mit UART_TX_BUFFER initialisiert, deshalb wird immer das selbe Zeichen gesendet. Da fehlt ein "static".
1 | UCSR0B = ... (1 << TXCIE0); |
Wenn der erste String komplett gesendet wurde und nicht sofort neue Daten zum versenden anstehen, wird dein Programm resetten. PS: Und BITTE benutze endlich die C-Code-Tags hier im Forum.
Hallo Stefan, vielen Danke für die Antwort. Ich habs mal abgeändert, aber es will immer noch nicht. Zum Haare raufen!!! Liegt das vielleicht an meinen Countern, hab ich da was falsch gemacht???
..ich bin mal so frei, das kann ja keiner lesen..
Louis schrieb: > Ich habs mal abgeändert, aber es will immer noch nicht. Zum Haare > raufen!!! Und wie der Code jetzt mit den Änderungen aussieht, sollen wir raten, oder wie? Und soll "will immer noch nicht" bedeuten, dass das Fehlerbild gleich geblieben ist, oder ist es jetzt was Anderes?
Oh sorry! Also ich empfange immer noch nicht die gewünschten Strings. Es werden immer nur einzelne Zeichen gesendet (0x00). Der Counter muss ja auch static sein. Das habe ich auch mal abgeändert. Beim durchschauen sind mir jetzt keine Fehler mehr aufgefallen, wobei das mit meiner noch etwas dürftigen Erfahrung im Programmieren nichts heißen soll. Nochmals Danke für die bisherige Hilfe! Grüße, Louis
Servus, funktioniert denn deine Schaltung richtig und ist die Baudrate richtig eingestellt ?
Wenn du im Receive-Interrupt das 0x0D nicht mit in den Buffer schreibst, wie soll der Send-Interrupt das dann als Ende-Kennung nutzen können? ;-)
Hallo zusammen, zunächst einmal vielen Danke für die Antworten. Klasse das Anfängern hier weiter geholfen wird. Ich habs ausprobiert und es funktioniert! Allerdings schlag ich mich jetzt die letzten Tage mit einer weiteren Sache herum. Folgendes: ich würde gerne die im Puffer abgelegten Strings, die mit 0x08 beginnen, auf bestimmte Stellen untersuchen. Beim Auftreten eines unerwünschten Zustandes soll eine LED aufleuchten. Wenn 9 bestimmte Zustände detektiert wurden, soll ein Befehl ausgesendet werden. Irgendwie hege ich den Verdacht, dass das mit meinem Übergeben in den Hilfspuffer zusammenhängt. Allerdings klappt rein gar nichts. Und wenn ich mit SEND_COMMAND versuche einen Befehl zu versenden, wird immer nur der Befehl "34ABC9\r" über das Hyperterminal gesendet. Ich wäre wirklich für jeden Tipp dankbar! VG, Louis
void SEND_COMMAND( char *STRING )
{
while( *STRING )
{
SEND_CHAR( *STRING );
> *STRING++;
STRING++;
}
SEND_CHAR( 0x0D );
}
Louis schrieb: > Allerdings klappt rein gar nichts. Wo rufst du eigentlich deine UART_INIT Funktion auf? > Und wenn ich mit SEND_COMMAND Bitte: Hör diesen Unsinn auf, alle Variablen und Funktionsnamen in Großbuchstaben zu schreiben. Ausschliesslich Grossbuchstaben ist in C eine übliche Konvention, die ausschliesslich Makros vorbehalten ist. Bei denen ist es auch sinnvoll, weil man an der verwendenden Stelle unter Umständen wissen muss, das etwas 'nur' ein Makro und keine richtige Funktion ist. Daher kann es lebenswichtig sein, im Text zu erkennen, dass etwas ein Makro ist. Diese Konventionen ist eine der wenigen, an die sich tatsächlich > 98% aller C Programmierer weltweit halten. Du hast keine Veranlassung hier dein eigenes Süppchen zu kochen, also lass es und benutze dieselbe Konvention wie alle anderen auch.
Arrggghhhh!!!! Verflucht, sorry!!!! Mit klappt leider auch nicht...Das mit der Großschreibung werde ich mir merken. VG,
>Und wenn ich mit SEND_COMMAND >versuche einen Befehl zu versenden, wird immer nur der Befehl "34ABC9\r" >über das Hyperterminal gesendet. Und genau der Teil in deinem Programm ist auskommentiert. Ich würde sagen du brennst die falsche HEX-Datei.
Hallo Holger, das war meinerseits etwas schlecht formuliert. Ich meine, wenn ich den Befehl auskommentiere, dann wird permanent "34ABC9\r" gesendet...
>Hallo Holger, >das war meinerseits etwas schlecht formuliert. Ich meine, wenn ich den >Befehl auskommentiere, dann wird permanent "34ABC9\r" gesendet... Dann legt dein AVR ne Bauchlandung hin (Reset). Das könnte daran liegen das du ein SEND_CHAR und eine Interruptroutine verwendest. Warum eigentlich doppelt? Du aktivierst TXCIE0 aber dafür gibt es keine Interruptroutine. USART0_UDRE_vect ist für TXCIE0 nicht zuständig.
Sorry, wie du natürlich schon bemerkt hast, hab ich noch nicht allzu viel Erfahrung in Sachen uC-Programmierung. Stimmt, im Grunde lade ich ich ja nur neue Daten in das Senderegister. Wie bereits oben erwähnt wollte ich das ja alles mittels Polling bewerkstelligen. Allerdings habe ich selbst gemerkt, und wie in den Tutorials gelesen, dass das den Programmablauf doch recht massiv blockieren kann. Nun gut, dann werde ich mich mal an die Arbeit machen. Dir vielen Dank!
Hallo, Du kannst auch nur mit Hilfe einer Interrupt Routine arbeiten, also RXCIE, und mit Hilfe von Send_Char deine Befehle versenden. Wichtig ist halt immer, das du im Hauptprogramm ein Flag setzt. Dann kannst du bspw. deine Uart-Buffer in einen Hilfs-Buffer entleeren, und dein Programm kann somit schneller arbeiten. Gruß
Also mit dem Sendeinterrupt komme ich irgendwie nicht ganz zu Recht, ich blick da nicht durch. Ich habs jetzt mal anders probiert. Allerdings funzt es nicht...Ist von diesem Lösungsansatz abzuraten? Bzw. was hab ich verbockt, dass es nicht funktioniert? Generell passt doch alles!?
Louis schrieb: > ich verbockt, dass es nicht funktioniert? Generell passt doch alles!? Geh deinen Code mit dem Finger durch (kein Scherz). Und dann stell dir die Frage: Wo wird eigentlich jemals GET_CHAR aufgerufen? Also jetzt nicht statisch. Schon klar, dass GET_CHAR aus GET_STRING heraus aufgerufen wird. was ich meine ist, wenn du mit dem Finger die Programmausführung simulierst, landest du dann jemals in GET_CHAR?
if(UART_STRING_COMPLETE == 1) Wo wird UART_STRING_COMPLETE jemals 1 gesetzt? Mein Gott, ist das denn so schwer?
Hmmmm, mein Ziel war es ja eigentlich, den String in get_string in den Buffer zu schreiben und dann mit Hilfe von uart_string_complete = 1 dem Hauptprogramm zu sagen, das der Buffer in einen Hilfsbuffer kopiert werden soll. Danach setze ich uart_string_complete wieder Null und dadurch lande ich wieder in get_string. Au Backe, ich steh auf dem Schlauch! Ich möchte ja nicht nur die seriellen Daten auf UART0 untersuchen, sondern auch bei Bedarf von UART1. Ich will also mit UART0 die Strings einlesen, die ich in der Hauptschleife in einen Buffer kopiere. Ich möchte aber auch, bspw. nach einer bestimmten Anzahl kopierter Strings, die Hauptschleife verlassen und die Strings von UART1 einlesen bzw. diese bearbeiten. Ich dachte bisher immer, dass durch das Anlegen einer globalen Variablen solche Sprünge problemlos möglich sind. Oje, trotzdem Danke für die Antworten. VG, Louis
Das geht auch. Allerdings solltes du auf den ersten Blick zunächst einmal das hier.... DATA = GET_CHAR(); if(UART_STRING_COMPLETE == 0) { ...vertauschen. Nur nicht gleich die Flinte ins Korn werfen. Grüße und noch frohes Schaffen!
Hallo Malte,
Danke für deine Antwort. Allerdings klappts immer noch nicht:-(
>> Nur nicht gleich die Flinte ins Korn werfen
...doch, bald ist es soweit. Grüße
Hallo, ich weiß das pushen nicht schön ist. Aber wäre vielleicht jemand bereit mit weiterzuhelfen? Ich komm nicht drauf...Vielen Dank
Den Hinweis von Karl Heinz hast du wohl nicht ganz ernst genommen, wie? Dann also nochmal im Klartext: In der Hauptschleife wartest du darauf, dass der String komplett ist. Wie soll er aber jemals komplett werden, wenn innerhalb dieser Schleife niemals GET_STRING aufgerufen wird?
Uffff, da habe ich Interrupt und while völlig vertauscht, vermischt, etc. Gut, ich muss also mein get_string in die Schleife einbauen. Ich habs mal mit einer If-else-Anweisung probiert, funktioniert aber noch nicht. Aber das wäre so schon der richtige Weg, oder? Euch vielen Dank für die Antworten und Hinweise! VG, Louis
Du terminierst deinen String nicht richtig
uart_rx_buffer[uart_rx_counter] = ' ';
Damit ist der String immer noch nicht gültig, weil er kein
abschliessendes \0 Zeichen hat
> Ich habs mal mit einer If-else-Anweisung probiert
Wozu?
1 | while(1) |
2 | {
|
3 | get_string(); |
4 | |
5 | if(uart_string_complete == 1) |
6 | {
|
7 | strcpy(stringbuffer, uart_rx_buffer); |
8 | send_string(stringbuffer); |
9 | uart_string_complete = 0; |
10 | }
|
11 | }
|
machs doch nicht komplizierter als notwendig! Jetzt musst du nur noch deine get_string richtig stellen.
Hallo Karl-Heinz, spitze es funktioniert. Danke für deine Hilfe! VG, Louis
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.