Hallo zusammen,
ich beschäftige mich seit kurzem mit der Schnittstellenprogrammierung.
Also bitte ich zunächst einmal um Entschuldigung, falls diese Frage sich
für manch anderen etwas dümmlich anhört.
Ich möchte gerne 70 Zeichen lange Strings empfangen ( Gerät--> uC --->
Hyperterminal). Allerdings empfange ich nur jeden zweiten String. Es
scheint, dass mein Programm einfach nicht schnell genug ist, diese
Strings weiter zu versenden. Ich möchte die Daten gerne ohne
Interruptsteuerung empfangen und versenden.
Ich habe nun gelesen das ein FIFO-Buffer hierbei Abhilfe schaffen
könnte. Sprich der Datendurchsatz wird erhöht und die CPU Last wird
verringert. Allerdings versteh ich noch immer nicht ganz wie ich das in
das vorhandene Programm integrieren kann. Im Grunde werden die Daten ja
in einen globalen Buffer geschrieben und das erste geschriebene Zeichen
wird als erstes ausgelesen bzw. zur Verarbeitung weitergegeben.
Wäre wirklich klasse, wenn mir hier jemand weiterhelfen könnte.
Grüße
Urban schrieb:> die CPU Last wird verringert.
Das heißt auf gut Deutsch: es wird nicht unnötig gewartet...
Und das passiert jetzt aber in deinem Code immer noch:
1
voidsend_char(unsignedchardata)
2
{
3
while(!(UCSR0A&(1<<UDRE0)));// warten und Däumchen drehen...
4
UDR0=data;
5
}
6
7
8
voidsend_string(char*string)
9
{
10
while(*string)// ein while(), das deutet auf Rechenzeitverbrauch hin...
11
{
12
send_char(*string);// und jetzt kommts: Warten, warten...
Hier kann der uC nichts anderes tun, als zu warten, bis das Senden
fertig ist. Klar, dass er in der Zwischenzeit keine ankommenden Zeichen
verarbeiten kann. Da hilft dein ganzer hübscher Fifo nicht!
> Ich möchte die Daten gerne ohne Interruptsteuerung empfangen und> versenden.
Warum?
Das ist, wie wenn du sagst: ich würde gerne Einparken, aber den
Rückwärtsgang nicht verwenden (obwohl du einen hast!!!)...
Wenn du mal in dich gegangen bist, such nach P. Fleurys UART-Lib...
BTW:
#define fosc 16000000
Ich schreibe da immer ein UL hinter die Zahl, weil 16000000 nicht in 16
Bit passen...
Die while - schleifen sind wie mein Vorgänger erwähnt hat, tödlich.
Du solltest in der Hauptschleife einen Zustandsautomaten einbauen, der
anstatt auf While -(TUE GARNICHTS BIS DIE SCHNITTSTELLE FERTIG IST) eine
If-Abfrage einbauen, die nur bei fertiger Schnittstelle ein Datenbyte
nachlegt.
Wäre das Senden oder Empfangen noch nicht fertig, ist der Prozessor in
der Lage an anderer Stelle im Programm weiter zu machen. Phasenprobleme
werden so vermieden.
ANregung OHNE ANSPRUCH AUF COMPILIERFÄHIGKEIT !!!
define MAX_STING 70
uint8_t *SpeicherString[] = "IRGEND_ETWAS_VON_IRGENDWOHER\0"
uint8_t SpeicherStringZaehler=0
mode = 0;
// Hier noch die Initialisierungen
Main
{
while(1)
{
switch(mode)
{
case 0:
if(SpeicherStringZaehler++ < MAX_STRING)
{
/* IF erspart das Sinnlos Warten ...*/
if ((UCSR0A & (1 << UDRE0))
UDR0 = *SpeicherStringe++;
}
mode ++;
break;
case 1:
Empfangen irgendwie analog wie oben.
mode ++;
break;
default:
mode = 0;
// tue noch irgendetwas anderres sinnvolles und warte nicht.
}
}
}
Ich habe mir von je her angewöhnt, so ein kooperatives Scheduling zu
integrieren. Die Statemachines machen zwar die Funktion des Codes
unübersichtlich. Aber es lohnt sich. CAN - Grafik-Display und Modem
betreiben etc. , lassen sich so quasi parallel betreiben.
Und man kann so dem AVRs mit nur 16MHz richtig etwas abverlangen.
Viel erfolg.
Hallo zusammen,
vielen Dank für die schnellen Antworten. Ich habe es nun mal mit einer
ISR probiert und es klappt. Zunächst einmal passen die so, oder
muss/sollte ich da etwas anders machen? Ich möchte das Programm so
aufbauen, dass beim Auftreten eines bestimmten
Zustandes/Zeichenkombination ein Befehl verendet wird.
Zudem hätte ich noch folgende Frage:
Ich habe zwei UARTS mit UART0 will ich die Daten kontrollieren. Mit dem
Zweiten will ich beim Auftreten einer bestimmten Situation, eine SMS
versenden.
Die GSM-Initialisierung habe ich ebenfalls mittels Polling realisiert.
Diese kann ich ja nun einfach durch ISR´s ersetzen. Muss ich dabei auf
etwas spezielles achten? Ich weiß nicht inwieweit die dann zueinander im
Konflikt stehen.
Bin für jeden Hinweis und Tipp dankbar...
VG
Hallo, ich bins nochmal.
Das Empfangen und Versenden der Daten klappt auf jeden Fall besser, wie
mit der while-Schleife. Ich empfange und versende meine 70 Zeichen lange
Strings nun nacheinander. Allerdings wird der letzte String der nur 14
Zeichen lang ist nicht versendet. Am obigen Programm hab ich nichts
verändert. VG
wenn deine UART nicht sendebereit ist, zb. weil noch eine andere
Übertragung läuft, dann .... lässt du den String einfach unter den Tisch
fallen und machst gar nichts. Und das ist dann das was du siehst: Ein
einzelner String wird nicht gesendet.
Hallo KH, vielen dank für die Antwort!
Wenn ich Funktion get_log_data um
void get_log_data(void)
{
if( log_daten == 1 && uart_tx_flag == 1)
{
send_string(stringbuffer);
log_daten = 0;
}
}
uart_tx-flag == 1 erweitere, erhalte ich das gesamte Protokoll. Ich muss
also noch festlegen: Wenn letzte Übertragung beendet, dann können neue
Daten gesendet werden?!
Muss ich also immer, wenn ich ein Befehl senden will, mit uart_tx_flag
== 1 die Sendebereitschaft prüfen? Beim Polling habe ich meine
Sendefunktion,
void send_string(char *string)
{
while(*string)
{
send_char(*string);
string++;
}
send_char('\r');
}
mit der ich einfach die Befehle innerhalb eines Programms versenden
kann.
if(log_data == 1)
send_string("Log Daten vorhanden");
Bspw. wenn ich nach dem Erreichen eines Counters == 12 einen Befehl
versenden will, oder wenn ich auf die Antwort des GSM Moduls reagieren
will. Muss ich das dann immer mittels Flags realisieren? Sprich Flag
setzen und schauen ob uart_tx_flag auch gesetzt, wenn ja, Senden?
if(counter == 12)
counter_flag = 1;
.
.
.
if(strcmp(stringbuffer, "OK") == 0)
gsm_answer_flag = 1;
.
.
.
if(counter_flag == 1 && uart_tx_flag == 1)
{
send_string("Counter = 12");
counter flag = 0;
}
if(gsm_answer_flag == 1 && uart_tx_flag == 1)
{
send_string("12345");
gsm_answer_flag = 0;
}
Ich kapiere das immer noch nicht so richtig.
VG
Urban schrieb:> Ich muss also noch festlegen: Wenn letzte Übertragung beendet,> dann können neue Daten gesendet werden?!
Nein, eigentlich mußt du nur eine etwas bessere Pufferverwaltung
einführen. Ein Ringpuffer wäre da gut geeignet (das ist übrigens ein
Fifo). Wenn der ausreichend groß ist, dann kannst du von deinem
"Hauptprogramm" einfach Zeichenketten ausgeben, und die ISR
(+Pufferverwaltung) sorgt dann schon dafür, dass die Zeichenketten
irgendwann nacheinander auf der SIO ausgegeben werden.
Die Logik mit den Flags bringt dich irgendwann garantiert zur
Verzweiflung.
Lothar Miller schrieb:> Die Logik mit den Flags bringt dich irgendwann garantiert zur> Verzweiflung.
Das unterstreiche ich ganz dick und mit roter Farbe!
Hallo,
zunächst einmal vielen Dank für eure Antworten.
Ich habe mich mal anhand den Anmerkungen von Lothar an die Arbeit
gemacht und versucht die Pufferverwaltung zu realisieren.
Für mich als Anfänger auf jeden Fall etwas schwieriger als wie beim
Einsatz von Polling. Es klappt nicht wirklich. Mit Hilfe meines C-Buches
und über das Internet hab ich versucht die Fehler ausfindig zu machen,
ohne Erfolg.
Es wäre nett, wenn der ein oder andere erfahrenen Programmierer einen
Tipp oder Hinweis hätte...Es wäre schon mal gut zu wissen, ob das, was
ich gemacht hab in die Richtung geht, die mit Lothar geraten hat.
Meine Strings, die ich empfange Enden immer mit '\r', also würde ich
dieses Zeichen als Terminator verwenden. In meinem Programm möchte ich
die bereits oben erwähnte Flag-Problematik vermeiden um im Haupt- oder
Nebenprogramm Befehle zu versenden.
Viele Grüße,
Urban
Das ist doch kein Ringbuffer, was du da hast.
Weder beim Senden noch beim Empfangen
> Meine Strings, die ich empfange Enden immer mit '\r', also würde ich> dieses Zeichen als Terminator verwenden.
Ein Tip: Halte den Ringbuffer aus jeglicher Dateninterpretation raus.
Ein Ringbuffer soll einfach nur ein Zwischenspeicher sein, der Zeichen
solange aufnimmt, bis die Verarbeitungseinheit Zeit hat, die
zwischengespeicherten Zeichen abzuarbeiten. Mehr nicht.
Ein Ringbuffer heißt deswegen so, weil die Speicherfläche logisch
gesehen zu einem Ring 'gebogen' wird.
Stell dir eine Uhr vor. An jeder Minutenposition ist eine
Speicherstelle. Wenn Zeichen in den Ringbuffer gestellt werden, dann
werden sie einfach reihum in das nächste Minutenfach gestellt. Wenn es
dabei über die Stundengrenze (=60 Minuten) geht, dann wird einfach
wieder vorne angefangen. So wie der Minutenzeiger einer Uhr auch einfach
nur immer im Kreis läuft und trotzdem immer von 0 bis 59 zählt.
Wenn die verarbeitende Einheit Zeit dazu hat, dann holt sie sich ein
Zeichen nach dem anderen, solange bis sie entweder genug hat, oder
nichts mehr Abzuholendes im Ringbuffer vorliegt.
D.h. der Ringbuffer hat 2 ausgezeichnete Positionen:
* Wo wird das nächste Zeichen eingefügt, beim Einstellen in den
Ringbuffer
* Von wo wird das nächste Zeichen gelesen, beim Wiederauslesen aus dem
Ringbuffer
(Und ja du brauchst 2 Ringbuffer. Einen fürs Senden und einen fürs
Empfangen)
Schau dir die UART LIb vom Peter Fleury an. Der verwendet Ringbuffer und
interrupt gesteuertes Senden/Empfangen.
Verblüffend: Wir haben gar keinen Artikel in der Artikelsammlung zum
Thema Ringbuffer
Hallo Karl-Heinz,
danke für deine Antwort.
Ufff,das war wohl ein Griff ins Klo. Ich habe mir die Lib mal angeschaut
und bin nicht so richtig durchgestiegen. Dann werde ich mir sie nochmals
zu Gemüte führen und hoffen das ich es kapier. Nochmals Danke!
VG,
Urban
Ist ja kein Beinbruch. Deswegen hat PF die Lib ja veröffentlicht, damit
man sie einfach nur benutzen kann, ohne sich um die Details kümmern zu
müssen.
Es gibt eine Funktion, die ein empfangenes Zeichen holt und es gibt eine
Funktion, die ein Zeichen versendet. Mehr braucht man nicht um daraus
deine gewünschte Funktionalität hochzuziehen.
Hallo,
ich habe nun versucht mit der von KH vorgeschlagenen UART Lib von Peter
Fleury einen FIFO zu realisieren.
Ich verwende eine Arduino Board Mega mit einer 16MHz Taktfequenz. Die
Daten die ich empfange werden mit 9600bps 8Bit, keine Parität und einem
Stoppbit versendet. Bisher konnte ich mit Polling meine kleine Anlage
ansteuern.
Zwar seh ich jetzt den versendeten String am Hyperterminal, meine Anlage
reagiert allerdings nicht mehr wenn ich die Befehle über den UART
versende.
Das lies mich vermuten, dass etwas nicht mit der Einstellung des
Datenformates(Parität, bps, etc.) stimmt. Allerdings ist soweit alles in
Ordnung.
Des weiteren wollte ich die empfangenen Zeichen, sofort wieder über die
UART versenden (siehe Programm). Allerdings wird nur 0x00 versendet. Ich
weiß nicht mehr weiter.
Bevor ich mir einen Wolf suche:
Hast du die Fleury Funktionen so gelassen wie sie waren?
(Warum hast du sie nicht in ihrem eigenen File unverändert gelassen. Du
brauchst dieses C-File doch nur ins Projekt mit aufnehmen und schon hast
du die Funktionen zur Verfügung)
Hallo Karl-Heinz,ich wollte das nicht einfach so übernehmen, sondern
versuchen das zu verstehen und selbst zu probieren...Klappt allerdings
noch nicht ganz. ich bleib hartnäckig
Urban schrieb:> Hallo Karl-Heinz,ich wollte das nicht einfach so übernehmen, sondern> versuchen das zu verstehen und selbst zu probieren...Klappt allerdings> noch nicht ganz. ich bleib hartnäckig
OK.
Extra langsam, nur für dich:
S i e h
d i r
d i e
b e i g e l e g t e
D e m o
a n
!
Der Fleury Peter macht das nicht zum Spass, dass er eine Demo schreibt,
an der man sieht, wie man die einzelnen Funktionen zu verwenden hat!
Sein uart_getc ist ein nicht wartendes getc. Das kommt auch dann zurück,
wenn es kein Zeichen an der UART vorgefunden hat. Es ist aber so
freundlich, dem Aufrufer mitzuteilen, dass genau dieser Fall vorliegt:
UART_NO_DATA (Ich hab nichts für dich).
Nur MUSST DU DIESE INFORMATION AUCH AUSWERTEN
Und damit man sieht wie das geht, hat der Peter Fleury seiner Lib eine
Demo beigelegt, in der er unter anderem genau das zeigt.
NUR TUN MUSST DU ES!