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
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???
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
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
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.
>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...
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?
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
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.