Hallo zusammen, ich versuche gerade mich in das Ringbufferprinzip einzuarbeiten. Ich benutze Codevision, und hieraus die Ringbufferfunktionen des Programmwizzard. Über Studio4 und JTAG-AVR progge ich einen mega128. Meine Codeschnipsel: Das was in main.h steht: #define RXB8 1 #define TXB8 0 #define UPE 2 #define OVR 3 #define FE 4 #define UDRE 5 #define RXC 7 #define FRAMING_ERROR (1<<FE) #define PARITY_ERROR (1<<UPE) #define DATA_OVERRUN (1<<OVR) #define DATA_REGISTER_EMPTY (1<<UDRE) #define RX_COMPLETE (1<<RXC) // USART0 Receiver buffer #define RX_BUFFER_SIZE0 16 char rx_buffer0[RX_BUFFER_SIZE0]; char rx_wr_index0,rx_rd_index0,rx_counter0; // This flag is set on USART0 Receiver buffer overflow bit rx_buffer_overflow0; Das was in main.c steht: // USART0 Receiver interrupt service routine interrupt [USART0_RXC] void usart0_rx_isr(void) { char status,data; status=UCSR0A; data=UDR0; if ((status & (FRAMING_ERROR | PARITY_ERROR | DATA_OVERRUN))==0) { rx_buffer0[rx_wr_index0]=data; if (++rx_wr_index0 == RX_BUFFER_SIZE0) rx_wr_index0=0; if (++rx_counter0 == RX_BUFFER_SIZE0) { rx_counter0=0; rx_buffer_overflow0=1; }; }; } #ifndef DEBUG_TERMINAL_IO // Get a character from the USART0 Receiver buffer #define ALTERNATE_GETCHAR #pragma used+ char getchar(void) { char data; while (rx_counter0==0); data=rx_buffer0[rx_rd_index0]; if (++rx_rd_index0 == RX_BUFFER_SIZE0) rx_rd_index0=0; #asm("cli") --rx_counter0; #asm("sei") return data; } #pragma used- endif Die einzelnen Arbeitsweisen von der Interruftroutine und von getchar() sind mir klar. Aber das Zusammenspiel versteh ich nicht. Meine Frage ist: getchar() wird zu einer alternativen Funktion, aber was geschiet damit? Wie komme ich an meine Daten die im Ringbuffer stehen? Kann man mir das mit wenigen Worten erklären? Vielen Dank Thomas
Wenn du einen normalen Ringbuffer verwendest, sollst du mit dem Lesen oder Schreiben immer am Anfang des Buffers starten und dann bis zum Ende gehen. Das heisst, das du ganz am Ende des Buffers nur knappe Bytes zur Verfuegung hast, obwohl fast der ganze Buffer schon nicht mehr besetzt ist. Wenn du dagegen einen Ringbuffer nimmst, dann kannst du irgendwo am Ende des Buffers lesen und gleichzeitig am Anfang was schreiben. Natuerlich ist es ein gewaltiges Vorteil. Nachteile: du brauchst schon mal nicht 2, sondern 3 Variablen - Index fuer das Lesen, Index fuer das Schreiben und eine Variable fuer die Anzahl der Bytes, die du noch nicht gelesen hast. Und da kommt noch eine Verifizierung dazu - du sollst aufpassen, dass dein Buffer nicht ueberfluttet wird. Im Prinzip funktioniert es so - du schreibst bis zum Ende des Buffers und liest gleichzeitig deine Daten aus, dann erreichst du mit dem Schreiben das Ende, wenn es ein normaler Buffer waere, dann ist es schon aus, aber jetzt hast du noch bisschen Platz am Anfang des Buffers, weil du einige Daten schon mal gelesen hast.
Ich habe noch eine zusätzliche Variable eingefügt. Sowie etwas im Ringpuffer steht wird diese Variable gesetzt. Anhand dieser Variable kann man feststellen ob man getchar() ausführen muss. Erst wenn der Buffer wieder leer ist wir die Variable zurück gesetzt.
1 | // USART0 Receiver interrupt service routine
|
2 | interrupt [USART0_RXC] void usart0_rx_isr(void) |
3 | {
|
4 | char status,data; |
5 | status=UCSR0A; |
6 | data=UDR0; |
7 | if ((status & (FRAMING_ERROR | PARITY_ERROR | DATA_OVERRUN))==0) |
8 | {
|
9 | rx_buffer0[rx_wr_index0]=data; |
10 | if (++rx_wr_index0 == RX_BUFFER_SIZE0) rx_wr_index0=0; |
11 | if (++rx_counter0 == RX_BUFFER_SIZE0) |
12 | {
|
13 | rx_counter0=0; |
14 | rx_buffer_overflow0=1; |
15 | };
|
16 | |
17 | ZEICHEN_IM_BUFFER = 1; |
18 | |
19 | };
|
20 | }
|
21 | |
22 | #ifndef _DEBUG_TERMINAL_IO_
|
23 | // Get a character from the USART0 Receiver buffer
|
24 | #define _ALTERNATE_GETCHAR_
|
25 | #pragma used+
|
26 | char getchar(void) |
27 | {
|
28 | char data; |
29 | while (rx_counter0==0); |
30 | data=rx_buffer0[rx_rd_index0]; |
31 | if (++rx_rd_index0 == RX_BUFFER_SIZE0) rx_rd_index0=0; |
32 | #asm("cli")
|
33 | --rx_counter0; |
34 | #asm("sei")
|
35 | |
36 | if (rx_counter==0) |
37 | ZEICHEN_IM_BUFFER = 0; |
38 | |
39 | return data; |
40 | }
|
41 | #pragma used-
|
42 | endif
|
Man hat dann zwar wieder nen Polling. Allerdings hat man den Vorteil bei seltener Übertragung, dass man das Bit nur recht selten abfragen muss. Durch den Ringpuffer gehen die Daten ja erst mal nicht verloren.
@Aleksej Das was ich habe ist ja ein endlos Buffer mit den drei, von dir beschriebenen Variablen. @Stefan Muss ich wirklich die neue Variable ZEICHEN_IM_BUFFER benutzen? Reicht es nicht aus , wenn ich rx_counter abfrage? Bei rx_counter != 0 sind noch ungelesene Zeichen im Buffer.
@Stefan: Ganz verstehe ich Deine Variable ZEICHEN_IM_BUFFER nicht. Was bringt Dir diese, was Dir die Abfrage von rx_counter0 nicht schon bringt? Um auf Zeichen im Buffer zu testen, reicht doch if (rx_counter0){ } Übrigens hast Du in Deiner Abfrage einen kleinen Schönheitsfehler: if (rx_counter==0) ********************** wenn hier ein RX-Interrupt auftritt, gibt ********************** es Probleme! ZEICHEN_IM_BUFFER = 0; Angenommen, ein Zeichen steht im Puffer. Das holst Du mit getchar ab und machst --rx_counter0. Dann fragst Du (rx_counter0 == 0) (== TRUE) und setzt ZEICHEN_IM_BUFFER = 0. Wenn zwischen diesen Zeilen ein RX_IR auftritt, wird ein Zeichen in den Buffer geschrieben, rx_counter0 erhöht und ZEICHEN_IM_BUFFER = 1 gesetzt. Merkst Du was? Danach steht ZEICHEN_IM_BUFFER == 0 und rx_counter0 == 1 Wenn Du wirklich ZEICHEN_IM_BUFFER verwenden willst, dann schreibe: #asm("cli") if (rx_counter==0) ZEICHEN_IM_BUFFER = 0; #asm("sei") Gruß, Stefan
Alles klar. Sehe ich ein. Hatte bisher zwar noch keine Probleme werde ich aber beim nächsten mal berücksichtigen.
Man braucht doch nur 2 Pointer, bzw. Indexe. Sind sie gleich, ist nichts im Puffer.
1 | char rx_buf[256]; |
2 | unsigned char rx_tail, rx_head; |
3 | |
4 | |
5 | char kbhit(void) |
6 | {
|
7 | return rx_head != rx_tail; |
8 | }
|
9 | |
10 | |
11 | char getchar(void) |
12 | {
|
13 | char c; |
14 | |
15 | while( !kbhit() ); |
16 | |
17 | c = rx_buf[rx_tail]; |
18 | ++rx_tail; |
19 | return c; |
20 | }
|
In diesem Beispiel ist der Index rx_tail ein unsigned char und der Puffer genau 256 Byte groß. D.h. ganz ohne Überlauftest rollt der Puffer über. Peter
P.S.: Und wenn der Puffer nur 32Byte groß sein soll, auch kein Problem:
1 | rx_tail = (rx_tail + 1) & 0x1F; |
Ein Undierung ist codesparender und schneller als ein bedingter Sprung. Daher werden gerne 2-er Potenzen als Puffergrößen genommen. Peter
@peter: ich interessiere mich auch gerade für eine Ringbuffer-Implementation. kannst du bitte mal deinen Rountine kbhit() erläutern. rx_head ist sicherlich der Lesezeiger, oder? Mario
@Mario: Falsch geraten... rx_head ist der Schreibzeiger. Kann man sich relativ leicht an einer Eselsbrücke mit einem Hund merken: Der Schwanz (tail) folgt dem Kopf (head). Sobald der tail den head eingeholt hat, ist der Hund 0 Elemente groß... "rx_head" wird in der Empfangs-Interrupt-Routine geschrieben. Irgendwo hat hier auch jemand ein paar Makros zu dem Thema gepostet... @Peter: durch rx_tail++ % 100 könnte man einen 100 Elemente grossen Ringspeicher produzieren, oder?
...durch rx_tail++ % 100 könnte man... Könnte man machen, ist aber nicht schön, da eine Division notwendig wird. Da ist ein if(x >= y) schneller. Neben den Schreib- Lesezeigern auch noch die Anzahl der Zeichen im Puffer mitzuführen hat dann Vorteile, wenn z.B. Xon-Xoff Protokoll verwendet wird, welches erst bei fast vollem Puffer wirksam werden soll. Ferner kann man vor der Ausgabe testen, ob die auszugebenden Zeichen komplett in den Puffer passen oder nicht. Bei Zustandsautomaten (state machine) kann es besser sein, die Ausgabe zunächst zurückzustellen, bis der Puffer hinreichend entleert wurde. Anderfalls hängt das Programm in der Ausgabe fest, bis alle Zeichen übergeben werden konnten.
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.