Forum: Mikrocontroller und Digitale Elektronik Eigener Versuch eines UART-Puffers


von Alex V. (bastel_alex) Benutzerseite


Lesenswert?

Hallo, sorry für den bereits zweiten Code-Beitrag heute:

Ich versuche gerade das erste mal UART-Kommunikation zwischen PC und 
AtMega644 umzusetzen. Einzelne Bytes senden und empfangen hat bereits 
geklappt - Hardware und UART-Konfiguration sollte soweit also stimmen.

Nun Habe ich versucht einen Sende-Puffer einzurichten. Das Ziel:
Es sollen immer 3 Byte in den Puffer geschrieben und nacheinander 
gesendet werden, während die nächsten drei Bytes in Vorbereitung sind. 
Das ganze soll Interrupt-Gesteuert und ohne Polling passieren.

Für den Code habe ich mir auch schon 
http://www.mikrocontroller.net/articles/Interrupt zu Gemüte geführt und 
mich auch grob daran orientiert, wollte das ganze aber noch 
spartanischer auf mich zugeschnitten einmal selber umsetzen.

Nur: Der Code läuft (natürlich) nicht, mal wieder die Frage warum.
1
// IM HEADER DEFINIERT: 
2
void transmit_string(volatile uint8_t * uart_tx_flag, char * uart_tx_buffer[], char data[3]);
3
4
// IN EINER C FILE DEFINIERT: 
5
/* Transmit a String */
6
void transmit_string(volatile uint8_t *uart_tx_flag, char *uart_tx_buffer[], char data[3])        
7
{
8
  if (&uart_tx_flag==1)              // check whether last string has been sent completely
9
  {
10
    for (int i=0; i<3; i++)
11
    {
12
      uart_tx_buffer[i] = data[i];      // Copy String into send buffer
13
    }
14
    uart_tx_flag = 0;              // Delete "Sent-Flag"
15
    UCSR0B |= (1<<UDRIE0);            // UDRIE Interrupt on
16
  }
17
}
18
// 
19
20
// MAIN FILE:
21
22
#include <avr/io.h>
23
#include <avr/interrupt.h>
24
#include <stdlib.h>
25
#include <string.h>
26
27
#include "Mainboard_UART_functions.h"
28
#include <util/delay.h>
29
30
#define uart_buffer_size 3
31
32
// VARIABLES
33
34
unsigned char byte_buffer;
35
volatile uint8_t uart_rx_flag=0;            // Flag, complete string received
36
volatile uint8_t uart_tx_flag=1;            // Flag, complete string sent
37
volatile uint8_t rx_buff_cnt=0;        // RX buffer counter
38
volatile uint8_t tx_buff_cnt=0;        // TX buffer counter
39
char uart_rx_buffer[uart_buffer_size];      // Receive-Buffer
40
char uart_tx_buffer[uart_buffer_size+16];   // Send-Buffer
41
42
43
44
// UART TX data register empty interrupt:
45
// loading new data into UART sending register
46
ISR(USART0_UDRE_vect) 
47
{
48
  if (tx_buff_cnt > 2)      // Buffer completely sent (counter >2)?
49
  {
50
    tx_buff_cnt = 0;      // reset counter
51
    uart_tx_flag = 1;      // set String-Sent Flag
52
    UCSR0B &= ~(1<<UDRIE0);    // UDRIE Interrupt Flag off
53
  }
54
  else
55
  {
56
    UDR0 = uart_tx_buffer[tx_buff_cnt];    // put next byte into UDR
57
    tx_buff_cnt++;              // increase tx buffer counter
58
  }
59
}
60
61
62
int main(void)
63
{
64
  
65
  char stringbuffer[64];  // general buffer for Strings
66
  char data[3] ={0xFF, 0xAA, 0xBB};
67
  uint8_t buffer_full=0;  // buffer full flag
68
  char * charpointer;     // pointer
69
    
70
  /*********************** Initialization ***********************/
71
  //Port Directions
72
                    
73
  DDRC= 0b01010001;        // SPI Slave select, LEDs                    
74
  DDRD= 0b00001010;        // UART
75
  
76
  UART_Init();          // UART Initialisierung
77
  
78
  sei();              // Globale Interrupts enable
79
  
80
  
81
    while(1)
82
    {
83
    
84
    transmit_string(&uart_tx_flag, &uart_tx_buffer, data);
85
  
86
    }
87
}

Die Funktionalität sollte eigentlich einfach sein:
Ich habe einen default 3-Wort-block "data" (1. Byte 0xFF, 2. Byte 0xAA, 
3. Byte 0xBB), der mit einem counter stück für stück gesendet wird - 
unter Verwendung des UDRE (USART Data register empty= Flag-Interrupts. 
Immer wenn ein Byte raus ist wird der Interrupt neu aufgerufen und das 
nächste Byte verschickt, bis der counter >2 ist - dann wird der counter 
auf 0 gesetzt und der UDRIE ausgeschaltet.

Wo habe ich einen fehler gemacht? Es scheint so, als ob die 
ISR(USART0_UDRE_vect)  Routine nicht aufgerufen wird (mal wieder 
LED-Blink-Test)..

von hufnala (Gast)


Lesenswert?

Hi,

UART Interrupt freigegeben? Hab das Datenblatt gerade nicht aber TIMSK 
koennte das richtige register sein. Dort steht auch ob der Interrupt 
ueberhaupt beim Senden geht, ich hab ihn bisher nur fuer RX verwendet.

//hufnala

von Jonas B. (jibi)


Lesenswert?

>transmit_string(&uart_tx_flag...

Warum dieser Umweg mit der Adresse vom flag? Übergebe doch einfach das 
Byte?! Deine Variante erzeugt sogar noch mehr Code und längere Laufzeit, 
wenngleich das hier nicht wichtig ist.

Ich würde das Rad auch nicht neuerfinden. Saubere Implementationen für 
Ringbuffer findest du in der Softwaresammlung.

Gruß Jonas

: Bearbeitet durch User
von Alex V. (bastel_alex) Benutzerseite


Lesenswert?

hufnala schrieb:
> UART Interrupt freigegeben?

ja ist freigegeben,
in der  UART_Init(); die nicht im code drin ist oben:
1
 void UART_Init()
2
 {
3
   // baud Calculation: baud = (fosc/ 16*Baudrate)-0.5
4
   // baud = (20 MHz / 16*9600)-0.5 = 129.708 --> = baud = 130
5
   unsigned int baud = 130;
6
   
7
   //* Set baud rate */
8
   UBRR0H = (unsigned char)(baud>>8);
9
   UBRR0L = (unsigned char)baud;
10
   /* Enable receiver and transmitter, Set Interrupts: RXCIE - RX-Complete, TCVIE - TX-Complete */
11
   UCSR0B = (1<<RXEN0)|(1<<TXEN0)|(1<<RXCIE0);
12
   /* Set frame format: 8data, 2stop bit */
13
   UCSR0C = (1<<USBS0)|(3<<UCSZ00);
14
   
15
 }

Jonas Biensack schrieb:
> Warum dieser Umweg mit der Adresse vom flag? Übergebe doch einfach das
> Byte?!

ich bin nicht sicher ob ich dich richtig verstehe aber:
Ich übergebe die adresse vom flag (und nicht das flag) nur, weil die 
methode in einem anderen file steht und die variable dort nicht 
deklariert ist.
Das mit flag und buffer habe ich eigentlich von der idee her (wenn ichs 
denn richtig gemacht habe) von 
http://www.mikrocontroller.net/articles/Interrupt abgeguckt...
Die transmit funktion enabled ja im wesentlichen nur den UDRE-Interrupt, 
damit so lange vom buffer in das UDR0 register geschaufelt wird, bis der 
buffer leer ist.

Ich würde mich aber nicht wundern wenn es klügere implementierungen gibt 
als meine, ich bin da ja anfänger. nur trotzdem sollte der code ja 
laufen, wenn auch nicht zeitoptimal meinetwegen...?

von Karl H. (kbuchegg)


Lesenswert?

Alex v. L. schrieb:

> ich bin nicht sicher ob ich dich richtig verstehe aber:
> Ich übergebe die adresse vom flag (und nicht das flag) nur, weil die
> methode in einem anderen file steht und die variable dort nicht
> deklariert ist.

Wen kümmerts?

Die Funktion kriegt ja bei
1
void foo( int i )
2
{
3
  printf( "%d", i );
4
}
5
6
int main()
7
{
8
  int j = 7;
9
10
  foo( j );
11
}

ja sowieso nie zu sehen, dass der Aufruf von foo mit j gemacht wurde. 
Der Funktion wird der Wert 7 übergeben, der vom Aufrufer aus j geholt 
wird, an die Funktion übergeben wird, und den die Funktion sich für sich 
selbst in i ablegt.
Die Funktion muss nicht wissen, dass es in main ein j gibt und das von 
dort der Wert für ihren Aufruf herstammt.



> Die transmit funktion enabled ja im wesentlichen nur den UDRE-Interrupt,
> damit so lange vom buffer in das UDR0 register geschaufelt wird, bis der
> buffer leer ist.

Das müsste man jetzt genauer analysieren.
Grundsätzlich ist die Versteifung auf die 3-er Sequenzen schon mal 
etwas, was man anzweifeln kann. Bringt eigentlich nichts. D.h. man würde 
einen Buffer Mechanismus schreiben, der erst mal mit einzelnen Bytes 
umgehen kann. Das du als Aufrufer dann jeweils gleich immer 3 Aufrufe 
machst um Bytes einzustellen, ja das ist dein Bier als Aufrufer. Aber es 
ist nichts, was den Buffermechanismus jetzt groß kümmern müsste.

Es gibt derartige IMplentierungen. Zb ist in der UART Lib vom P.Fleury 
eine ethalten. Vielleicht möchtest du ja die mal studieren wie die 
arbeitet, ehe du dich dann selber daran versuchst?

: Bearbeitet durch User
von Oliver (Gast)


Lesenswert?

Wenigstens folgendes ist falsch:
if (&uart_tx_flag==1)
Richtig wäre:
if (*uart_tx_flag==1)

Oliver

von Uwe (Gast)


Lesenswert?

Hi,

>ja ist freigegeben,
>in der  UART_Init(); die nicht im code drin ist oben: void UART_Init()
.
.
>   /* Enable receiver and transmitter, Set Interrupts: RXCIE - RX->Complete, 
TCVIE - TX-Complete */
>   UCSR0B = (1<<RXEN0)|(1<<TXEN0)|(1<<RXCIE0);
>   /* Set frame format: 8data, 2stop bit */
>   UCSR0C = (1<<USBS0)|(3<<UCSZ00);

Das sehe ich aber anders, oder kann ich nicht lesen?

RXCIE ist nicht UDRIE
Bit 7 – RXCIEn: RX Complete Interrupt Enable
Bit 5 – UDRIEn: USART Data Register Empty Interrupt Enable

Bit 7      6       5     4     3      2     1     0
  RXCIEn TXCIEn UDRIEn RXENn TXENn UCSZn2 RXB8n TXB8n UCSRnB

viel Erfolg, Uwe

von Alex V. (bastel_alex) Benutzerseite


Lesenswert?

Herzlichen Dank für alle Rückmeldungen! Mal sehen was damit der heutige 
Tag bringt! ;)

von Karl H. (kbuchegg)


Lesenswert?

Alex v. L. schrieb:


Da ist auch noch ein Bock

> void transmit_string(volatile uint8_t *uart_tx_flag, char
> *uart_tx_buffer[], char data[3])
> {
...
>     uart_tx_flag = 0;              // Delete "Sent-Flag"


Ich denke ehrlich gesagt nicht, dass derartige Mechanismen momentan 
schon etwas für dich sind.
Soalnge du derart eklatante Schwächen in grundlegenden C Dingen hast, 
solltest du ehrlich gesagt besser ein C-Buch durcharbeiten und die 
Übungen da drinnen machen (auf dem PC), als dich mit weiterführenden 
Konzepten auf einem AVR beschäftigen. Da klafft ein scheinentorgrosses 
Wissensloch, das erst gestopft werden sollte.


Und ja. Das Argument muss ein Pointer sein. Wenn sich auch deine 
Begründung dafür ein wenig hahnebüchern liest.

: Bearbeitet durch User
von Alex V. (bastel_alex) Benutzerseite


Lesenswert?

Eine ganz kurze generelle Frage, die mich beim datenblattlesen und coden 
gerade noch einmal stutzig gemacht hat:

Das UDR-Register besteht aus eigentlich zwei 8-Bit registern - einem 
Empfangs- und einem Senderegister - oder nur EINEM 8 Bit register?

Ich lese ja aus UDR wenn ich einen RXC-Interrupt bekomme und schreibe 
wenn mir der UDRE-Interrupt zeigt, dass das register leer ist - aber 
doch wohl nicht beide male auf das selbe register?

Wenn es zwei sind - wieso haben sie dann nur einen namen? - 
unterscheidet der µC durch die Lese/schreibaktion dann auf welches 
zugegriffen wird, dient das ganze also der simplifizierung?
Im Datenblatt des Atmega644 auf s.165 wird im USART-Block Diagramm ja 
UDR(Transmit) und UDR(Receive) unterschieden.

Wenn es ein und das selbe ist: wie wird dann verindert, dass beim senden 
und empfangen gleichzeitig das selbe register beschrieben wird?

von Karl H. (kbuchegg)


Lesenswert?

Alex v. L. schrieb:

> Das UDR-Register besteht aus eigentlich zwei 8-Bit registern - einem
> Empfangs- und einem Senderegister - oder nur EINEM 8 Bit register?

Genau

> Ich lese ja aus UDR wenn ich einen RXC-Interrupt bekomme und schreibe
> wenn mir der UDRE-Interrupt zeigt, dass das register leer ist - aber
> doch wohl nicht beide male auf das selbe register?

Nö. Wozu auch.
Aber es ist völlig klar, dass beim Schreiben das eine Register gemeint 
ist und beim Lesen das andere. Also kann man die beiden Register an eine 
gemeinsame Speicheradresse legen und durch die Art des Zugriffs 
(Schreiben oder Lesen) entscheiden, welches von beiden gemeint ist.

>
> Wenn es zwei sind - wieso haben sie dann nur einen namen?

Weil das Adressen einspart und der "Preis" dafür akzeptabel ist. Man 
verliert die Möglichkeit aus dem Sende-UDR auslesen zu können, was 
eigentlich gesendet wird. Das interessiert aber sowieso keinen, denn das 
Programm hat ja den Wert selber reingeschrieben. Der muss aber von 
irgendwoher gekommen sein und daher weiß man auch was man 
reingeschrieben hat ohne im Schreibe-UDR nachsehen zu müssen.
Auf der anderen Seite macht es keinen Sinn, etwas in den Lese-UDR 
einschreiben zu wollen. Aus diesem UDR kommt das raus, was die UART 
empfangen hat. Wozu soll ich da was reinschreiben?

: Bearbeitet durch User
von Jonas B. (jibi)


Lesenswert?

>Das mit flag und buffer habe ich eigentlich von der idee her (wenn ichs
>denn richtig gemacht habe) von
>http://www.mikrocontroller.net/articles/Interrupt abgeguckt...
>Die transmit funktion enabled ja im wesentlichen nur den UDRE-Interrupt,
>damit so lange vom buffer in das UDR0 register geschaufelt wird, bis der
>buffer leer ist.

Doch wird dort nirgends ein einzelnes Byte über einen Pointer als 
Parameter übergeben, das macht auf 8-Bit Systemen einfach keinen Sinn 
weil
>Deine Variante erzeugt sogar noch mehr Code und längere Laufzeit,
>wenngleich das hier nicht wichtig ist.

: Bearbeitet durch User
von Alex V. (bastel_alex) Benutzerseite


Lesenswert?

Danke!

von Jonas B. (jibi)


Lesenswert?

Aber hey, jeder Anfang war schwer und da mussten wir alle durch bzw. 
stecken wir noch drin...

Gruß Jonas

von Karl H. (kbuchegg)


Lesenswert?

Jonas Biensack schrieb:

> Parameter übergeben, das macht auf 8-Bit Systemen einfach keinen Sinn
> weil
>>Deine Variante erzeugt sogar noch mehr Code und längere Laufzeit,
>>wenngleich das hier nicht wichtig ist.


Es würde dann Sinn machen, wenn man hier
1
void transmit_string(volatile uint8_t *uart_tx_flag, char *uart_tx_buffer[], char data[3])        
2
{
3
  if (&uart_tx_flag==1)              // check whether last string has been sent completely
4
  {
5
    for (int i=0; i<3; i++)
6
    {
7
      uart_tx_buffer[i] = data[i];      // Copy String into send buffer
8
    }
9
    uart_tx_flag = 0;              // Delete "Sent-Flag"
den Kapitalen Fehler beseitigen würde.
Auf der anderen Seite liegt da noch ein Konzeptfehler drüber.
Dieses uart_tx_flag hat im Grunde genommen in main() überhaupt nichts 
verloren, sondern gehört zu den UART Routinen wodurch sich die ganze 
Notwendigkeit es zu übergeben, sofort in Luft auflösen würde.

Hier fehlt es einfach unter anderem an grundlegenden Fertigkeiten bzw. 
Übung und Erfahrung, wie man sich Module baut, speziell wenn die in ein 
eigenes C-File ausgelagert werden. Gepaart mit einigen C-Schwächen ist 
das dann eben eine brisante Mischung.

: Bearbeitet durch User
von greg (Gast)


Lesenswert?

Alex v. L. schrieb:
> Nun Habe ich versucht einen Sende-Puffer einzurichten. Das Ziel:
> Es sollen immer 3 Byte in den Puffer geschrieben und nacheinander
> gesendet werden, während die nächsten drei Bytes in Vorbereitung sind.
> Das ganze soll Interrupt-Gesteuert und ohne Polling passieren.

Das hört sich umständlich an. Nimm lieber einen kleinen Ringpuffer/FIFO, 
das ist die Standardlösung, und braucht neben dem Buffer nur zwei 
Indizes für Producer und Consumer.

So ein Puffer ist beim Empfangen aber wichtiger, denn da gibt der Sender 
das Timing vor; wenn der ohne Pause ein Byte nach dem anderen sendet, 
musst du hinterher kommen. Wenn du selbst sendest, ist das Timing 
unkritisch.

von Jonas B. (jibi)


Lesenswert?

>Das hört sich umständlich an. Nimm lieber einen kleinen Ringpuffer/FIFO,
>das ist die Standardlösung, und braucht neben dem Buffer nur zwei
>Indizes für Producer und Consumer.

sag ich doch:

>>Ich würde das Rad auch nicht neuerfinden. Saubere Implementationen für
>>Ringbuffer findest du in der Softwaresammlung.

>Auf der anderen Seite liegt da noch ein Konzeptfehler drüber.
>Dieses uart_tx_flag hat im Grunde genommen in main() überhaupt nichts
>verloren, sondern gehört zu den UART Routinen wodurch sich die ganze
>Notwendigkeit es zu übergeben, sofort in Luft auflösen würde.

Eben.

>Hier fehlt es einfach unter anderem an grundlegenden Fertigkeiten bzw.
>Übung und Erfahrung, wie man sich Module baut. Gepaart mit einigen
>C-Schwächen ist das dann eben eine brisante Mischung.

100 % ack

Gruß Jonas

: Bearbeitet durch User
von Jonas B. (jibi)


Lesenswert?

Die Ringbuffer-Implementierungen haben schon einige Tricks, die die 
Performance massiv steigern, auf die kommst DU nie. Also wenn man keine 
Ahnung hat lieber mal über den leeren Tellerrand schauen.

Gruß Jonas

von Alex V. (bastel_alex) Benutzerseite


Lesenswert?

Also ich habe mir eure Kommentare zu Herzen genommen und seit heute 
morgen mal eine Ringpufferlösung programmiert. die funktioniert auch 
grundsätzlich, nur bin ich jetzt am Macken ausbessern - wenn ich nämlich 
den unten eingefügten code zur kontrolle durchlaufen lasse, kommt am 
Terminal die dezimal-zahlenreihe (ACHTUNG, LANG):
0
0
1
2
3
4
5
6
7
8
9
10
11
12
13
0
16
17
18
19
20
21
22
23
24
25
26
27
28
29
0
0
32
33
34
35
36
37
38
39
40
41
42
43
44
45
0
0
48
49
50
51
52
53
54
55
56
57
58
59
60
61
0
0
64
65
66
67
68
69
70
71
72
73
74
75
76
77
0
0
80
81
82
83
84
85
86
87
88
89
90
91
92
93
0
0
96
97
98
99

an. Man sieht: Da sind Nullen wo keine hingehören! ;-) Für tips bin ich 
außerordentlich dankbar, auch wie man die hier nun gepostete 
-->bisherige Lösung noch weiter verbessern kann:
1
/*.............RINGPUFFER.............*/
2
struct circ_buffer {
3
  uint8_t data[buffer_size];
4
  uint8_t read_ptr;      // points to last input data
5
  uint8_t write_ptr;      // always points to empty field
6
  uint8_t fillcount; 
7
  };
8
9
void buffer_init (struct circ_buffer *buf)          // Initialization of circular buffer values
10
{
11
  buf->fillcount=0;
12
  buf->read_ptr=buffer_size;
13
  buf->write_ptr=0;
14
}
15
16
void buffer_write(struct circ_buffer *buf, uint8_t byte)  // Write byte into buffer, increment fillcounter and write pointer
17
{
18
  buf->data[buf->write_ptr] = byte;
19
  buf->fillcount++;
20
  
21
  if (buf->write_ptr>= buffer_size)
22
  buf->write_ptr = 0;  
23
  else
24
  buf->write_ptr = buf->write_ptr + 1;
25
}
26
27
uint8_t buffer_read(struct circ_buffer *buf)        // Read byte out of buffer, decrement fillcounter, increment read ptr
28
{
29
  if (buf->fillcount>0)
30
  {
31
    buf->fillcount--;
32
    
33
    buf->read_ptr = buf->read_ptr + 1;
34
    if (buf->read_ptr >= buffer_size)
35
    {
36
      buf->read_ptr = 0;
37
      return buf->data[buffer_size];
38
    }
39
    else
40
    return buf->data[buf->read_ptr-1];
41
  }
42
  else return 0;
43
}
44
45
bool buffer_full (struct circ_buffer *buf)
46
{
47
  if (buf->fillcount == buffer_size)
48
  return true;
49
  else return false;
50
}
51
52
bool buffer_empty (struct circ_buffer *buf)
53
{
54
  if (buf->fillcount == 0)
55
  return true;
56
  else return false;
57
}
58
59
*/ ........... UART FUNCTIONS ........... */
60
61
 void UART_Init()
62
 {
63
   // baud Calculation: baud = (fosc/ 16*Baudrate)-0.5
64
   // baud = (20 MHz / 16*9600)-0.5 = 129.708 --> = baud = 130
65
   unsigned int baud = 130;
66
   
67
   //* Set baud rate */
68
   UBRR0H = (unsigned char)(baud>>8);
69
   UBRR0L = (unsigned char)baud;
70
   /* Enable receiver and transmitter, Set Interrupts: RXCIE - RX-Complete */
71
   UCSR0B = (1<<RXEN0)|(1<<TXEN0)|(1<<RXCIE0);
72
   /* Set frame format: 8data, 2stop bit */
73
   UCSR0C = (1<<USBS0)|(3<<UCSZ00);
74
 }
75
 
76
void UART_start_transmit (void) 
77
{
78
  UCSR0B |= (1<<UDRIE0);              // Enable UDRIE- UART Data Register empty Interrupt - UDRE-ISR is going to put data in UDR
79
}
80
81
void UART_stop_transmit (void) 
82
{
83
  UCSR0B &= ~(1<<UDRIE0);              // Disable UDRIE- UART Data Register empty Interrupt - UDRE-ISR is going to put data in UDR
84
}
85
86
/* .................. MAIN ................ */
87
#include <avr/io.h>
88
#include <avr/interrupt.h>
89
#include <stdlib.h>
90
#include "stdbool.h"
91
#include <string.h>
92
93
#include "UART_functions.h"
94
#include "circbuf.h"
95
#include <util/delay.h>
96
97
98
99
// VARIABLES
100
101
unsigned char byte_buffer;
102
bool transmission_flag = false;
103
104
struct circ_buffer uart_rx_buffer;            // Receive-Circular-Buffer
105
struct circ_buffer uart_tx_buffer;            // Send-Circular-Buffer
106
107
108
109
// UART RX-Complete interrupt: 
110
// Receiving data + buffering (saving)
111
ISR(USART0_RX_vect) 
112
{
113
  buffer_write(&uart_rx_buffer, UDR0);
114
}
115
116
// UART Data register empty interrupt:
117
// loading new data into UART sending register
118
ISR(USART0_UDRE_vect) 
119
{
120
  if (buffer_empty(&uart_tx_buffer))          // Is buffer empty?
121
  {
122
    UART_stop_transmit();              // If yes: Disable UDRE Interrupt
123
    transmission_flag = false;
124
  }
125
  else
126
  UDR0 = buffer_read(&uart_tx_buffer);
127
  
128
  
129
}
130
131
132
133
int main(void)
134
{
135
  /* Buffer Initialization */
136
  buffer_init(&uart_rx_buffer);
137
  buffer_init(&uart_tx_buffer);
138
  
139
  /* Variables */
140
  uint16_t number =0;
141
    
142
  /*********************** Initialization ***********************/
143
  //Port Directions
144
  
145
  DDRA= 0b11011011;        // NIRS-Module 1-3 Communication                  
146
  DDRB= 0b10100110;        // NIRS-Module 3-4 Communication, SPI                    
147
  DDRC= 0b01010001;        // SPI Slave select, LEDs                    
148
  DDRD= 0b00001010;        // UART
149
  
150
  UART_Init();          // UART Initialisierung
151
  
152
  sei();              // Globale Interrupts enable
153
  
154
    while(1)
155
    {
156
    
157
    if (number < 100)
158
    {
159
      buffer_write(&uart_tx_buffer, number);
160
      number++;
161
      _delay_ms(100);
162
    }
163
    
164
    UART_start_transmit();
165
    
166
    }
167
}

Das sieht finde ich schon einigermaßen viel übersichtlicher aus.
In der testschleife nutze ich nur den TX-Buffer und lese keine bytes. 
Grundsätzlich ist es aber so gedacht, dass Interrupt-Gesteuert UDR 
gelesen und beschrieben wird (aus und in die zwei Ringpuffer) und in der 
main diese puffer bearbeitet. Sinnvoll?

von Alex V. (bastel_alex) Benutzerseite


Lesenswert?

Jonas Biensack schrieb:
> Also wenn man keine
> Ahnung hat lieber mal über den leeren Tellerrand schauen.

Ich bin nicht sicher, was du mir damit sagen willst - ganz so leer ist 
der teller gar nicht - nur die Seite vom Gemüse (C-Programmierung) ist 
bisher eben beschränkt und muss noch gefüllt werden. Zum Füllen aber bin 
ich ja HIER! ;)

von RomanK (Gast)


Lesenswert?

Hallo Alex,

dann such doch mal auf der Seite von Atmel nach dem Dokument 1307. Da 
steht alles notwendige drin.

Gruss

Roman

von Alex V. (bastel_alex) Benutzerseite


Lesenswert?

Karl Heinz schrieb:
> Ich denke ehrlich gesagt nicht, dass derartige Mechanismen momentan
> schon etwas für dich sind.
> Soalnge du derart eklatante Schwächen in grundlegenden C Dingen hast,
> solltest du ehrlich gesagt besser ein C-Buch durcharbeiten und die
> Übungen da drinnen machen (auf dem PC), als dich mit weiterführenden
> Konzepten auf einem AVR beschäftigen.

Dem würde ich an der Stelle mal klar widersprechen und zwar:
Ich habe schon C/C++ Bücher durchgearbeitet, nur ist das schon ganz 
schön her. Die µC Programmierung ist für mich bisher immer Mittel zum 
Zweck gewesen - und auch hier wieder. Ich muss mich dann allerdings 
jedes mal wieder neu reinfummeln - und mache auch immer erstmal wieder 
ähnliche Fehler bis ich mich wieder eingearbeitet habe.
Das UART-Kommunikationsmodul brauche ich eigentlich nur um eine ganz 
andere Hardware zu unterstützen, damit ich die damit gemessenen Signale 
auf den PC bekomme.
Ich finde es eigentlich eher spannend mir verschiedene grundlagen durch 
größere Projekte anzueignen, auch wenn der weg umso steiniger ist (durch 
mehr flüchtigkeits und dummheitsfehler), sonst fehlt mir die Motivation.
Gerade bin ich sehr motiviert! ;-)

von greg (Gast)


Lesenswert?

Alex v. L. schrieb:
> void buffer_init (struct circ_buffer *buf)          // Initialization of
> circular buffer values
> {
>   buf->fillcount=0;
>   buf->read_ptr=buffer_size;
>   buf->write_ptr=0;
> }

read_ptr und write_ptr sollten beide mit 0 initialisiert werden, sonst 
haut das nicht hin. fillcount ist unnötig.

Alex v. L. schrieb:
> if (buf->write_ptr>= buffer_size)
>   buf->write_ptr = 0;
>   else
>   buf->write_ptr = buf->write_ptr + 1;

buf->write_ptr = (buf->write_ptr + 1) % buffer_size;

ist kürzer und klarer.

Keine Ahnung was da konkret nicht klappt, hab nur mal kurz 
drübergeschaut und das ist mir aufgefallen.

von Alex V. (bastel_alex) Benutzerseite


Lesenswert?

greg schrieb:
> read_ptr und write_ptr sollten beide mit 0 initialisiert werden, sonst
> haut das nicht hin.

ok, gemacht. kommt allerdings das selbe raus am terminal wie vorher

greg schrieb:
> fillcount ist unnötig.

Ja ich weiß, auf der mikrocontroller.net-FiFo Seite ists auch ohne 
gemacht. Ich habs drin weil mein Kleingeist das besser versteht ;-)

Danke für deine Anmerkungen!

von greg (Gast)


Lesenswert?

Eine Sache noch: immer erst die Daten lesen/schreiben, und danach erst 
read_ptr/write_ptr/fillcount aktualisieren. Sonst hast du eine race 
condition! Wenn der Interrupt in einem ungünstigen Augenblick feuert, 
dann hat dein Ringbuffer sonst einen inkonsistenten Zustand. Das ist 
insgesamt recht tricky.

Einfachere Lösung: Interrupts kurzzeitig deaktivieren.

von Jonas B. (jibi)


Lesenswert?

>Eine Sache noch: immer erst die Daten lesen/schreiben, und danach erst
>read_ptr/write_ptr/fillcount aktualisieren. Sonst hast du eine race
>condition! Wenn der Interrupt in einem ungünstigen Augenblick feuert,
>dann hat dein Ringbuffer sonst einen inkonsistenten Zustand. Das ist
>insgesamt recht tricky.

>Einfachere Lösung: Interrupts kurzzeitig deaktivieren.

Um wieder nur einen Teil der ganzen Wahrheit preiszugeben.

Also wenn es wirklich nur darum geht, den Algorithmus der 
Implemtierungen zu verstehen, verstehe ich deine Mühe. Aber ganz ehrlich 
das ist doch unwichtig.
Ich meine, ein Ringbuffer ist doch meist ein nur kleiner Teil einer viel 
größeren Anwendung. Oder in welchem Rahmen findet das alles statt?

Viele Grüße

P.S. Unterschätzt nicht dein Wissen! :D

von Karl H. (kbuchegg)


Lesenswert?

Alex v. L. schrieb:

> Dem würde ich an der Stelle mal klar widersprechen und zwar:
> Ich habe schon C/C++ Bücher durchgearbeitet, nur ist das schon ganz
> schön her.

red nicht um den heissen Brei rum.
Wer bei Argumentübergabe an Funktion bzw. deren Weiterverwendung in der 
Funktion Schwächen hat, hat ganz einfach ein Basis-Problem.
Da diskutiere ich nicht drüber.

: Bearbeitet durch User
von Jonas B. (jibi)


Lesenswert?

>red nicht um den heissen Brei rum.
>Wer bei Argumentübergabe an Funktion bzw. deren Weiterverwendung in der
>Funktion Schwächen hat, hat ganz einfach ein Basis-Problem.
>Da diskutiere ich nicht drüber.

Jetzt wird'r böss.

Auch noch'm Karl-Heinz sein Middag versauen und kein C-können. Oh oh, 
ich seh schwarz :D

Nur Spass, schönes Wochenende euch!

AFK Jonas

von Alex V. (bastel_alex) Benutzerseite


Lesenswert?

greg schrieb:
> Eine Sache noch: immer erst die Daten lesen/schreiben, und danach erst
> read_ptr/write_ptr/fillcount aktualisieren. Sonst hast du eine race
> condition! Wenn der Interrupt in einem ungünstigen Augenblick feuert,
> dann hat dein Ringbuffer sonst einen inkonsistenten Zustand. Das ist
> insgesamt recht tricky.

Danke, umgesetzt.

Jonas Biensack schrieb:
> Oder in welchem Rahmen findet das alles statt?

Der Rahmen: Ein eigenes kleines ADC Board. Darauf: Ein Atmega644, ein 
LTC2468 16 Bit ADC und ein AMB2300 Bluetoothmodul.

Ziel: (beliebige) Analogdaten samplen und an den PC per bluetooth 
senden.

Board steht, software eben noch nicht. da gibts drei schritte
1. UART einarbeiten (grade dran)
2. SPI einarbeiten (ADC, folgt)
3. ADC-Werte über UART-Schnittstelle ans Bluetoothmodul.

Karl Heinz schrieb:
> Da diskutiere ich nicht drüber.

In ordnung. Ich werde mir meine Basisprobleme trotzdem an größeren 
zielen austreiben! Bislang hat das ganz gut funktioniert.. 
Frust-Tolerant bin ich ;-)
P.S. Die Fehler habe ich durchaus noch entdeckt.

von Jonas B. (jibi)


Lesenswert?

>Der Rahmen: Ein eigenes kleines ADC Board. Darauf: Ein Atmega644, ein
>LTC2468 16 Bit ADC und ein AMB2300 Bluetoothmodul.

Mit Rahmen meinte ich eher deinen "persönlichen Rahmen", Vorkenntnisse 
und Ausbildung etc. Sonst hätte ich vermutlich eher Board oder 
Evaluationsboard oder so geschrieben.

Wie sieht's damit aus?

von Alex V. (bastel_alex) Benutzerseite


Lesenswert?

Uni Elektroingenieur in den letzten studienzügen. Schwerpunkt allerdings 
biomedizintechnik.

von Jonas B. (jibi)


Lesenswert?

Also für die Uni, oder für dich, dein Kumpel, oder Karl-Heinz ;)?

Man "Etwas jemandem aus der Nase ziehen" ist bei dir schon fast 
übertrieben. :D

Gruß Jonas

von Jonas B. (jibi)


Lesenswert?

Sag doch einfach du bist fast "Elektroingenieur" und kannst keinen MC 
dazubringen ein Signal in einen Buffer zu schreiben?
Das übliche halt.

von Alex V. (bastel_alex) Benutzerseite


Lesenswert?

Sorry ich scheine nur deine Fragen nie richtig zu deuten! :D
Eine Mischung. Das Gesamtprojekt nennt sich Masterarbeit, das was ich 
grade mache ist aber im Prinzip ein eigenes oben drauf (was mir auch 
evaluieren helfen soll) ;-)
Fehlt noch was?

von Alex V. (bastel_alex) Benutzerseite


Lesenswert?

Jonas Biensack schrieb:
> Sag doch einfach du bist fast "Elektroingenieur" und kannst keinen MC
> dazubringen ein Signal in einen Buffer zu schreiben?

war das jetzt ironie? ;)

von Jonas B. (jibi)


Lesenswert?

Sagen wir so mit Augenzwinkern. Du musst, wenn du ein guter Ingenieur 
werden willst, verstehen, dass nur Erfahrung hilft so cool zu bleiben 
das man alle Probleme überblicken kann. Im Umkehrschluss verfängst du 
dich ohne Erfahrung in allen möglichen nur erdenklichen Problemen. Oder 
um es mal anders zu formulieren, ein fleißiger Bastler bringt das Ding 
an einem Tag zum laufen, ohne jemals eine Uni betretten zu haben. Um 
also das massive Fachwissen was die Uni mit sich bringt zu nutzen, musst 
du tun. Du braucht Interesse, das ist viel wichtiger, such dir eigene 
Projekte. Nur dass Zeug für die Uni reicht nicht, das willst du auch gar 
nicht. geb gas man.

Gruß Jonas

von Alex V. (bastel_alex) Benutzerseite


Lesenswert?

Jonas Biensack schrieb:
> Nur dass Zeug für die Uni reicht nicht, das willst du auch gar
> nicht. geb gas man.

Haha danke, ganz meine Einstellung. Womit wir wieder bei dem Grund 
meiner Anwesenheit und dem Forenbeitrag sind :D

von Jonas B. (jibi)


Lesenswert?

>Haha danke, ganz meine Einstellung. Womit wir wieder bei dem Grund
>meiner Anwesenheit und dem Forenbeitrag sind :D

Ok digga. Dann ziehen wir das jetzt durch. Ich helf dir.

Anforderung nochmal genau, bitte jetzt mit den neuen Erkenntnissen:

-Samplen...
-Ringbuffer...
...

Gruß jonas

von Jonas B. (jibi)


Lesenswert?

>Es sollen immer 3 Byte in den Puffer geschrieben und nacheinander
>gesendet werden, während die nächsten drei Bytes in Vorbereitung sind.
>Das ganze soll Interrupt-Gesteuert und ohne Polling passieren.

DAs ganze macht nur Sinn wenn die berechnung der 3 bytes fast unendlich 
dauert, was wird da berechnet der Bauchumfang von cyblords ma?

gruß Jonas

von Alex V. (bastel_alex) Benutzerseite


Lesenswert?

;)

Also der µC wird zum schluss

- einzelne (wenige ~16) Konfigurationsbytes vom PC verarbeiten müssen, 
der empfangspuffer ist also unkritisch. Das wird eine einfache 
Switch-Case geschichte, die z.B. den Kanal des ADC wählt. unkritisch.

- Mit vorgegebenem Takt (über Timer, kenne ich auch - unkritisch) den 
ADC wert eines kanals des LTC2468 abrufen über SPI (kenne ich noch nicht 
- wird kritisch ;-) und direkt in den Sendepuffer des UART kanals 
packen. Der ADC hat 16 bit werte, davor soll ein Byte konfigdaten (kanal 
etc). Also: Pro Messung drei Bytes.

Tatsächlich ist die UART geschichte für mich soweit vollständig geklärt 
und umgesetzt, sobald der Puffer einwandfrei funktioniert. Und der 
knarzt gerade nur noch an einer Stelle, die mit den write/read pointern 
zu tun haben muss. Wenn du mir ein schafes auge für den Debug leihen 
willst freue ich mich natürlich sehr. Der aktuelle Code auf dem PC 
(kommt unten nochmal) inkrementiert eine zahl und steckt sie in den 
puffer, damit ich sehe ob alles richtig klappt. Tut es noch nicht - und 
zwar immer bei den Sprüngen auf den Anfang des Puffers zurück - dann 
kommt eine Null. Mit einer Ausnahme: Dem ersten Durchlauf, der klappt.

Konkreter auszug:
0
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
0
18
19
20
21
22
23
24
25
0
27
28
29
30
31
32
33
34
0
...

Der Puffer ist jetzt unter Berücksichtigung der meisten Rückmeldungen so 
aufgebaut:
1
struct circ_buffer {
2
  uint8_t data[buffer_size];
3
  uint8_t read_ptr;      // points to last input data
4
  uint8_t write_ptr;      // always points to empty field
5
  uint8_t fillcount; 
6
  };
7
8
#include "circbuf.h"
9
10
void buffer_init (struct circ_buffer *buf)          // Initialization of circular buffer values
11
{
12
  buf->fillcount=0;
13
  buf->read_ptr=0;
14
  buf->write_ptr=0;
15
}
16
17
void buffer_write(struct circ_buffer *buf, uint8_t byte)  // Write byte into buffer, increment fillcounter and write pointer
18
{
19
  buf->data[buf->write_ptr] = byte;
20
  
21
  buf->fillcount++;
22
  if (buf->write_ptr >= buffer_size)
23
  buf->write_ptr = 0;
24
  else
25
  buf->write_ptr = buf->write_ptr + 1;
26
}
27
28
uint8_t buffer_read(struct circ_buffer *buf)        // Read byte out of buffer, decrement fillcounter, increment read ptr
29
{
30
  uint8_t returnval =0;
31
  
32
  if (buf->fillcount>0)
33
  {
34
    returnval= buf->data[buf->read_ptr];
35
    
36
    if (buf->read_ptr >= buffer_size)
37
    {
38
      buf->read_ptr = 0;
39
    }
40
    else 
41
    buf->read_ptr = buf->read_ptr + 1;
42
  
43
    buf->fillcount--;
44
    return returnval;
45
  }
46
  else return 0;
47
}
48
49
bool buffer_full (struct circ_buffer *buf)
50
{
51
  if (buf->fillcount == buffer_size)
52
  return true;
53
  else return false;
54
}
55
56
bool buffer_empty (struct circ_buffer *buf)
57
{
58
  if (buf->fillcount == 0)
59
  return true;
60
  else return false;
61
}

wie gesagt - und wie ganz oben im codebeispiel: In der main passiert im 
wesentlichen
1
    if (number < 100)
2
    {
3
      buffer_write(&uart_tx_buffer, number);
4
      number++;
5
      _delay_ms(100);
6
    }

Wenn das läuft bin ich schon ziemlich glücklich, der rest ist 
fleißarbeit.
Offen ist nur noch die Anmerkung von greg mit den race-conditions:
Ich habe jetzt lesen/schreiben immer vor den 
dekrementierungen/inkrementierungen gemacht aber

greg schrieb:
> Einfachere Lösung: Interrupts kurzzeitig deaktivieren.

die interrupts noch laufen...

von Alex V. (bastel_alex) Benutzerseite


Lesenswert?

Ah kurze Anmerkung zu oben noch:
Im zahlenbeispiel ist die Buffersize 8.

von Jonas B. (jibi)


Lesenswert?

greg schrieb:
> Einfachere Lösung: Interrupts kurzzeitig deaktivieren.

die interrupts noch laufen...

Um erst mal den Ball wieder zurück zu spielen, du weisst wie man 
Interrupts global aktiviert und deaktiviert?
Davon sehe ich gerade nichts?

Oder hast du den Hazard gelöst?

Gruß Jonas

: Bearbeitet durch User
von Alex V. (bastel_alex) Benutzerseite


Lesenswert?

ja weiß ich, ist aber ja noch nicht umgesetzt.
ich kann gerne mal ein sei() und cli() einbauen aber ich dachte erstmal 
nicht dass das am ringpufferverhalten in diesem fall was ändert - aber 
vll ist das ja tatsächlich eine periodische race condition?.. mal 
probieren

von Jonas B. (jibi)


Lesenswert?

>ich kann gerne mal ein sei() und cli() einbauen aber ich dachte erstmal
>nicht dass das am ringpufferverhalten in diesem fall was ändert - aber
>vll ist das ja tatsächlich eine periodische race condition?.. mal
>probieren

Das sind keine Endzeit-Phänomäne, sondern sowas bringt Raketen zum 
Absturz. Da kann Morphy noch weiter schlafen und da hast Ärger damit.
Siehst du doch gerade :) So geil...

Gruß Jonas

: Bearbeitet durch User
von Alex V. (bastel_alex) Benutzerseite


Lesenswert?

ja, wars leider nicht.
habe am anfang der buffer_read und _write funktionen global 
ausgeschaltet und am ende wieder ein. Das so zu lassen ist aber wegen 
den race-c empfehlenswert?

und nein - hazard ist damit noch nicht gelöst. ;)

von Jonas B. (jibi)


Lesenswert?

Alex sorry, bin erstmal afk from keyboard. Mein wauwau muss raus. BIn 
heute abend wieder one, dann helf ich dir weiter gerne - aber du bist ja 
der .Ing. :D

Gruß Jonas

von Alex V. (bastel_alex) Benutzerseite


Lesenswert?

;) Danke soweit und ich schreib hier rein wenn ichs gelöst habe - sonst 
bist du herzlich eingeladen heute abend noch beizusteuern!!
LG

von Jonas B. (jibi)


Lesenswert?

So, Hund schon an der Leine...

Gerne - viel Erfolg!
P.S. Softwareentwicklung ist nicht wie Schule - Abschauen ist 
ausdrücklich erlaubt!


Gruß Jonas

von Alex V. (bastel_alex) Benutzerseite


Lesenswert?

Habs gelöst. UNNGH.
1
.. >= buffer_size -1!!

von jibi (Gast)


Lesenswert?

Freut mich dann schönes Wochenende! Wetter ist doch Hammer

gruß Jonas

von Alex V. (bastel_alex) Benutzerseite


Lesenswert?

Dir auch! :)

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
Noch kein Account? Hier anmelden.