Forum: Mikrocontroller und Digitale Elektronik Mehrere vorhandene Funktionen für bis zu 4 UART's nutzen?


von AVRli .. (avrli)


Lesenswert?

Hallo,

erstmals möchte ich nun 2 UART's mit einem ATmega2560 nutzen. Für den 
UART0 habe ich mir sämtlich Ausgabe Funktionen bereits geschrieben und 
möchte nun UART1 (später vlt. auch noch 2 und 3) mit diesen Funktionen 
nutzen.

Im Kern münden alle Ausgaben von UART0 in dieser Funktion:
1
void uart0_char(char chrtx)
2
{
3
  uart0_buf_tx[pin] = chrtx;
4
  pin++;
5
  pin = pin & (UART0_TX_BUFFER_SIZE - 1);
6
7
  SET_BIT(UCSR0B, UDRIE0);
8
}

Die die nun diese Funktion aufrufen, sehen so aus (um eine 
stellvertreten zu zeigen)...
1
void uart0_str(char *buftx)
2
{
3
  while(*buftx) uart0_char(*buftx++);
4
}

Jetzt wird man ja wohl kaum ALLE 4x schreiben um damit sinnlos den 
Speicher voll zu machen sondern es wird sicher einen Weg geben, wie man 
die Ausgabefunktion so umbauen kann, dass die universell nutzbar für 
UART0, UART1, UART2 und UART3 wird.

Wie stellt man das nun an?

Baut man sich einen "switch/case" Block mit ein oder wie macht ihr das?


Grüße AVRli...

von Patrick C. (pcrom)


Lesenswert?

ich benutze (f)printf. Nur auf lowest level sind die ports dann separat.

von Jim M. (turboj)


Lesenswert?

AVRli .. schrieb:
1
  uart0_buf_tx[pin] = chrtx;
2
  pin++;
3
  pin = pin & (UART0_TX_BUFFER_SIZE - 1);

Hui, das kann aber knallen bei (extrem) hoher Interrupt Last.

Das sollte besser so aussehen:
1
  unsigned char temp = pin;
2
  uart0_buf_tx[temp ] = chrtx;
3
  temp ++;
4
  pin = temp & (UART0_TX_BUFFER_SIZE - 1);

Dann wird nämlich pin nicht mal kurz auf einen ungültigen Wert gesetzt.

AVRli .. schrieb:
> Wie stellt man das nun an?

Man legt eine Struktur an, die sowohl den UART als auch die nötigen 
Puffer enthält.

Ungefähr so:
1
void uart0_char(uart_buffer_t *uartbuf,  char chrtx)
2
{
3
   unsigned char temp = uartbuf->pin;
4
   uartbuf->uart0_buf_tx[temp ] = chrtx;
5
   temp ++;
6
   uartbuf->pin = temp & (UART0_TX_BUFFER_SIZE - 1);
7
   SET_BIT(uartbuf->uart->UCSR0B, UDRIE0);
8
}

Oft hat sich der Hersteller dafür schon was überlegt, denn er muss ja 
Beispielcode liefern.

von Jim M. (turboj)


Lesenswert?

Habe vergessen zu erwähnen: Bei 4 schnellen UARTs will man DMA nutzen, 
und müsste dann einiges umbauen. Dann hat man eventuell doch den Code 
viermal - weil er jedesmal was anderes tut.

von Stefan F. (Gast)


Lesenswert?

Ich verstehe, worauf du hinaus willst.

a) Man kann sich Strukturen anlegen, die Zeiger auf die Register des 
jeweiligen UART enthalten, sowie ggf. die Puffer. Dann übergibst du der 
Funktion uart_str() als ersten Parameter die Adresse dieser Struktur.

Dieses Konstrukt bringt aber auch einen gewissen Overhead mit sich. Ganz 
ehrlich: Ich würde ernsthaft überlegen, die paar Zeilen Code doch 
einfach zu kopieren.

b) Du baust in deine Funktionen switch/case Konstrukte ein, die abhängig 
von der UART-Nummer auf unterschiedliche Register zugreifen. Etwa so:
1
void uart_char(uint8_5 uartNr, char chrtx)
2
{
3
  switch (uartNr)
4
  {
5
    case 0:
6
      uart0_buf_tx[pin] = chrtx;
7
      pin++;
8
      pin = pin & (UART0_TX_BUFFER_SIZE - 1);
9
      SET_BIT(UCSR0B, UDRIE0);
10
      break;
11
    case 1:
12
      ...
13
    case 2:
14
      ...
15
    case 3:
16
      ...
17
  }
18
}
Ist aber letztendlich auch nicht viel schöner, als code zu kopieren.

Beitrag #5403058 wurde von einem Moderator gelöscht.
von Peter D. (peda)


Lesenswert?

AVRli .. schrieb:
> Jetzt wird man ja wohl kaum ALLE 4x schreiben um damit sinnlos den
> Speicher voll zu machen

Flash ist meistens nicht das Problem, daher ist es schon sinnvoll, den 
Code zu kopieren. Ansonsten müßte man wertvollen SRAM opfern, um für 
jedes 8Bit-Register einen 16Bit-Pointer anzulegen. Und die Funktionen 
müßten dann auch erst ein Pointerregister (X,Y,Z) laden (+Push/Pop 
Overhead), anstatt direkt auf das IO-Register zuzugreifen. D.h. die 
Laufzeit wird größer und Flash wird auch kaum gespart.

von J Zimmermann (Gast)


Lesenswert?

Nehme immer XMEGA, weiß nicht ob das mit mega auch geht:

#include <avr/io.h>

ini_usart(USART_t *myusart)
{
  myusart->CTRLA=0;
  ...
}


int main(void)
{
    ini_usart(&USARTC0);
    while (1)
    {
    }
}

von Stefan F. (Gast)


Lesenswert?

Bei Xmega Controllern hast du den Vorteil, dass die Register von allen 
gleichartigen Funktionseinheiten gleich aufgebaut sind und in der selben 
Reihenfolge angeordnet sind.

Dort kann man die Register selbst als Struct anlegen (was die avr-libc 
auch tut) und braucht der Funktion nur einen Zeiger auf das erste 
Register zu übergeben.

Leider ist es bei ATmegas nichtso einfach, dort sind die Register 
"Hysterisch" gewachsen, wie ein Ex-Chef von mir zu sagen pflegte.

von spess53 (Gast)


Lesenswert?

Hi

>Leider ist es bei ATmegas nichtso einfach, dort sind die Register
>"Hysterisch" gewachsen, wie ein Ex-Chef von mir zu sagen pflegte.

Bei allen neueren ATMegas ist da nichts 'hysterisches'. Da sind die 
USART-Register recht einheitlich angeordnet.

MfG Spess

von Wilhelm M. (wimalopaan)


Lesenswert?

Es ist zwar nicht explizit im Post des TO zu erkennen, doch bei solchen 
Fragen kann es eigentlich nur um C gehen. Trotzdem der Hinweis: in C++ 
löst man das mit templates.

von Mitlesa (Gast)


Lesenswert?

AVRli .. schrieb:
> Jetzt wird man ja wohl kaum ALLE 4x schreiben um damit sinnlos den
> Speicher voll zu machen

Die paar Bytes für viermal die UART-Routinen machen das Kraut
nun wirklich nicht fett. Aber dafür effizient schnell. Ich
verstehe nicht wie man so verblendet auf diese Art von Erspar-
nis aus ist wo man doch wie die Made im Speck leben kann.

Du hast im ATMega256 genug (Code-) Speicher dafür. Was viel
mehr (RAM-) Speicher kostet sind die Buffer die du sowieso
viermal anlegen musst.

von AVRli .. (avrli)


Lesenswert?

Danke für alle Anregungen!

Es scheint also keine generelle, einfache Lösung möglich zu sein.

Da mein Programm aber wie an einem Faden abläuft und ich der Reihe nach 
die UART's aus dem MAIN bedienen werde, finde ich den Weg mit der Ziel 
UART Variablen gar nicht so schlecht. Da muss ich dann jeder Routine...
1
void uart0_char(char chrtx);
2
void uart0_sp(void);
3
void uart0_cr(void);
4
void uart0_crlf(void);
5
void uart0_0x(void);
6
void uart0_buf(char *buftx, uint16_t cnt);
7
void uart0_str(char *buftx);
8
void uart0_str_P(PGM_P str);
9
void uart0_str_P_FAR(uint_farptr_t str);
10
void uart0_byte_P(PGM_P byteOut);
11
void uart0_byte_in_hex_P(PGM_P byteOut);
12
13
void uart0_uint8(uint8_t value, uint8_t width);
14
void uart0_int8(int8_t value, uint8_t width);
15
void uart0_uint8_bin(uint8_t value, uint8_t width);
16
void uart0_uint8_hex(uint8_t value, uint8_t width);
17
void uart0_uint16(uint16_t value, uint8_t width);
18
void uart0_int16(int16_t value, uint8_t width);
19
void uart0_uint16_bin(uint16_t value, uint8_t width);
20
void uart0_uint16_hex(uint16_t value, uint8_t width);
21
void uart0_uint32(uint32_t value, uint8_t width);
22
void uart0_int32(int32_t value, uint8_t width);

... "nur" um eine 8 Bit Variable erweitern und diese bis zur Übergabe in 
den eigentlichen Buffer mitgeben. Beim übergeben in den eigentlichen 
Sendebuffer darüber entscheiden, in welchen es letztendlich geht?

Alternativ eine global sichtbare Variable mit der man vor dem 
Einschreiben "umschaltet"?

Ich möchte das einmal sinnvoll und so Ressourcensparend wie möglich 
umbauen.

Gruß AVRli...

von Lama (Gast)


Lesenswert?

Nur um mal die AVR-Leute ein wenig zu ärgern:

Beim STM32 geht das so:
1
void uart_char(USART_TypeDef* USART_x, char chrtx)

Wobei USART_TypeDef wie folgt definiert ist:

1
typedef struct
2
{
3
  __IO uint32_t CR1;    /*!< USART Control register 1,                 Address offset: 0x00 */ 
4
  __IO uint32_t CR2;    /*!< USART Control register 2,                 Address offset: 0x04 */ 
5
  __IO uint32_t CR3;    /*!< USART Control register 3,                 Address offset: 0x08 */
6
  __IO uint32_t BRR;    /*!< USART Baud rate register,                 Address offset: 0x0C */
7
  __IO uint32_t GTPR;   /*!< USART Guard time and prescaler register,  Address offset: 0x10 */
8
  __IO uint32_t RTOR;   /*!< USART Receiver Time Out register,         Address offset: 0x14 */  
9
  __IO uint32_t RQR;    /*!< USART Request register,                   Address offset: 0x18 */
10
  __IO uint32_t ISR;    /*!< USART Interrupt and status register,      Address offset: 0x1C */
11
  __IO uint32_t ICR;    /*!< USART Interrupt flag Clear register,      Address offset: 0x20 */
12
  __IO uint16_t RDR;    /*!< USART Receive Data register,              Address offset: 0x24 */
13
  uint16_t  RESERVED1;  /*!< Reserved, 0x26                                                 */
14
  __IO uint16_t TDR;    /*!< USART Transmit Data register,             Address offset: 0x28 */
15
  uint16_t  RESERVED2;  /*!< Reserved, 0x2A                                                 */
16
} USART_TypeDef;

Damit kann man beliebig viele UARTs mit dem selben Code ansprechen, da 
sich nur der Pointer für den jeweiligen UART ändert.

Geht natürlich auch mit allen anderen Schnittstellen genauso.

von Mitlesa (Gast)


Lesenswert?

AVRli .. schrieb:
> Ich möchte das einmal sinnvoll und so Ressourcensparend wie möglich
> umbauen.

Ja .... lieber sinnlos zu Tode gespart als das Ziel schnell
erreicht. Ach so ... das Ziel ist auf dem ATMega256 so wenig
wie möglich Platz zu brauchen? Na dann ....

Man hat ja sonst nix zu tun. Beschäftigungstherapie damit man
weg von der Strasse ist. Luxusproblem.

von Stefan F. (Gast)


Lesenswert?

> ... "nur" um eine 8 Bit Variable erweitern und diese bis zur Übergabe
> in den eigentlichen Buffer mitgeben.

Ich würde diese Funktionen ja nur einmal schreiben und als ersten 
Parameter einen Zeiger auf den Buffer übergeben. Der Buffer wäre dann 
eine Struktur mit Byte-Array sowie Zeiger auf Anfang und Ende.

von J Zimmermann (Gast)


Lesenswert?

Mitlesa:
> Die paar Bytes für viermal die UART-Routinen machen das Kraut
> nun wirklich nicht fett.

Du mußt dann aber auch jede Änderung 4x machen, hast Du dann noch viele 
andere Hardware-Einheiten in der "Mache" wird's schnell fitzig.

Lama:
> Nur um mal die AVR-Leute ein wenig zu ärgern...

xmega ist auch avr, siehe oben, nur mal die STM-Leute ein wenig zu 
ärgern (habe selbst mal STM probiert), schon mal das ATMEL-Studio 
angesehen?

von Mitlesa (Gast)


Lesenswert?

J Zimmermann schrieb:
> Du mußt dann aber auch jede Änderung 4x machen, hast Du dann noch viele
> andere Hardware-Einheiten in der "Mache" wird's schnell fitzig.

Ja schon klar. Die UART-Programmiererei ist ja soooo kompliziert
da muss man schon aufpassen, die Entwicklung kann sich über
Jahre hinweg erstrecken.

von Peter D. (peda)


Lesenswert?

AVRli .. schrieb:
> void uart0_char(char chrtx);
> void uart0_sp(void);
> usw.

Du brauchst je UART nur die 4 Grundfunktionen:
uart_x_init();
uart_x_get_status();
uart_x_putchar();
uart_x_getchar();
Und die 8 Interrupthandler für die FIFO-Buffer.

Die ganzen Formatierungsfunktionen schreibt man natürlich nur einmal, da 
sie ja nicht mehr auf UART-Register zugreifen, sondern auf Variablen 
oder Arrays.

von Wilhelm M. (wimalopaan)


Lesenswert?

Peter D. schrieb:
> AVRli .. schrieb:
>> void uart0_char(char chrtx);
>> void uart0_sp(void);
>> usw.
>
> Du brauchst je UART nur die 4 Grundfunktionen:
> uart_x_init();
> uart_x_get_status();
> uart_x_putchar();
> uart_x_getchar();
> Und die 8 Interrupthandler für die FIFO-Buffer.

Oh je! X x copy-n-paste!

Ist das nicht Grund genug, sich mal mit C++/templates auseinander zu 
setzen?

: Bearbeitet durch User
von Lama (Gast)


Lesenswert?

J Zimmermann schrieb:
> xmega ist auch avr

Ja, ist mir bekannt. Daher auch mein Einleitungssatz. Aber speziell für 
die Leute, die vor AVR-Liebe schon beinahe Blind durch die Gegend 
laufen: xmega = avr = "für die Leistung überteuert und altmodisch, wird 
aber am Leben erhalten für die Leute die sonst nichts anderes 
programmieren können".

Hat man ja mit der 8051 auch so gemacht.

:-)

J Zimmermann schrieb:
> nur mal die STM-Leute ein wenig zu
> ärgern

Wird für einen AVR-Menschen sehr schwer fallen :-)

J Zimmermann schrieb:
> schon mal das ATMEL-Studio
> angesehen?

Nicht nur angesehen, sondern früher jahrelang damit arbeiten müssen. 
G-R-A-U-S-A-M. (DebugWire ist ja wohl in dem Zusammenhang auch der Witz 
des Jahrhunderts, das hat Atmel selbst mit der EIGENEN IDE jahrelang 
nicht sauber ans Laufen bekommen. Toll das z.B. der 328P nur den als 
"Debug-Schnittstelle" hat)

Schon mal aus dem Profi-Bereich was probiert? Keil z.B. ist für die 
kleinen STM32F0 kostenlos. Dass du auch mal was anderes gesehen hast als 
den heißgeliebten ATMEL-Studio. Sowas soll den eigenen Horizont 
erweitern habe ich mal gehört.

von Amateur (Gast)


Lesenswert?

@AVRli
Du bist meiner Meinung nach bei den Grenzen des Copy & Past angekommen.
Jetzt ist ein sinnvoller Zeitpunkt gekommen, Dich mal mit dem Prozessor 
zu beschäftigen.

Soweit mir bekannt, haben diese Prozessoren, für jeden UART, einen 
eigenen Registersatz. Dieser spiegelt dann auch den aktuellen Status.
Ist z.B. der Sendepuffer von UART1 leer, so wird ein Flag gesetzt. So 
weit, so gut. Bist Du aber jetzt neugierig, wie es um UART2 steht, so 
musst Du ein anderes Flag abfragen. Dieses hat entweder eine andere 
Bitposition, im gleichen Register oder eine komplett andere Adresse.
Die Abfrage: Puffer leer ist also unsinnig.
Entweder Du verpackst das Ganze in eine Klasse oder Du verwendest 
passende Parameter für die jeweiligen Ports.

von Peter D. (peda)


Lesenswert?

Wilhelm M. schrieb:
> Oh je! X x copy-n-paste!

Naja, man könnte ein Include-File schreiben und 4-mal includieren:
1
#define UART_NO 0
2
#include <uart_x.inc>
3
#undef UART_NO

Wilhelm M. schrieb:
> Ist das nicht Grund genug, sich mal mit C++/templates auseinander zu
> setzen?

Wenn ich "templates" lese, kriege ich immer so nen Hals :-)
Hast Du mal nen Link auf Beispielcode, wie das bei der AVR-UART aussehen 
könnte?
Also mit Interrupthandlern für die FIFOs.

von Lama (Gast)


Lesenswert?

Um auch mal konstruktiv mitzuwirken:

Erstelle dir einen Struct, in dem jeweils ein Pointer auf den 
Datenregister, sowie auf die benötigten Registerbits zeigt. Erzeuge 
diese Variable so oft wie du UARTs hast und initialisiere sie korrekt.

Schreibe die passenden get und set-Funktionen und fasse sie in einer 
generischen Funktion zusammen.

Rest dann analog zu dem oben von mir geposteten Lösung, nur dass du 
statt dem USART_TypeDef einen Funktionspointer verwendest.

Funktioniert dann auch mit beliebig vielen Schnittstellen, ist aber 
definitiv langsamer als alles "klassisch" mit eigenem Code zu behandeln. 
Hier stößt der AVR halt an die Grenze, dass die Register nicht im 
Adressraum gemappt sind.

Natürlich kann man in den jeweiligen structs auch die Buffer, eigene 
Statusbits, etc. unterbringen.

von Stefan F. (Gast)


Lesenswert?

>> Man kann sich Strukturen anlegen, die Zeiger auf die Register
>> des jeweiligen UART enthalten...

> Erstelle dir einen Struct, in dem jeweils ein Pointer auf den
> Datenregister, sowie auf die benötigten Registerbits zeigt...

Erzähle mal etwas Neues.

von Lama (Gast)


Lesenswert?

Stefanus F. schrieb:
> Erzähle mal etwas Neues.

Wenn das bekannt ist, wo genau liegt dann euer Problem???

von A. S. (Gast)


Lesenswert?

Weils grad aktuell ist und wenn Du kein C++ willst:

Packe alle öffentlichen Uart-Funktionen (und Flags, Speicher etc, was 
ein einzelner Uart so braucht) in eine Struktur struct sUart in uart.h;

Packe alle uart0_-Funktionen static in eine uart0.c, z.B. _chr statt 
uart0_char.

Alle Register-Definitionen (hier z.B. TX_REG_UART0) stehen in Uart0.h

Die uart0.c enthält dann z.B.:
1
#include <uart.h>
2
#include <Uart0.h>
3
4
static const struct sUart Uart0;
5
static ...; /* was auch immer noch */
6
7
static void chr(char c)
8
{
9
   ...
10
   TX_REG_UART0 = c;
11
   ...
12
}
13
14
/* einzige globale Funktion */
15
const struct sUart *GetUart0(...)
16
{
17
   return &Uart0;
18
}
19
20
static const struct sUart0 Uart0 = 
21
{
22
   _chr;
23
   _init;
24
   _...;
25
};

Diese Datei lässt Du von Deiner Build-Umgebung kopieren, nach uart1.c, 
uart2.c und uart3.c, wobei alle Uart0 durch Uart1/2/3 ersetzt werden 
(Textfresser, z.B. sed, awk oder cmd-kommandos ...)

Der Vorteil:
- Die Routinen sind maximal schnell, klein und lesbar (weil direkte 
Umsetzung, keine Indirektionen oder Switche)
- Gut, der Code ist 4mal da, aber insgesamt in Summe weniger als doppelt 
so groß. Zudem kannst Du den Code in eine Lib packen. Wenn Du dann in 
einem Projekt nur 2 Uarts brauchst, geht es kaum schneller oder kleiner.

- Du kannst Breakpoints auf einen dedizierten Uart setzen
- Du kannst einfach und elegant verschiedene Implementierungen parallel 
vorhalten, die per verschiedenen GetUartX ausgewählt werden. z.B. 
GetUart0_DMA() oder GetUart0_Blockierend().

Im Code sieht es dann z.B. so aus:
1
   struct sUart myUart = GetUart0();
2
      
3
      myUart->init(9600,8,1);
4
      myUart->chr('A');
du kannst statt GetUart0 auch die Struktur global machen, dann sieht der 
Code so aus:
1
      Uart0.init(9600,8,1);
2
      Uart0.chr('A');

von AVRli .. (avrli)


Lesenswert?

Peter D. schrieb:
> Du brauchst je UART nur die 4 Grundfunktionen:
> uart_x_init();
> uart_x_get_status();
> uart_x_putchar();
> uart_x_getchar();
> Und die 8 Interrupthandler für die FIFO-Buffer.
>
> Die ganzen Formatierungsfunktionen schreibt man natürlich nur einmal, da
> sie ja nicht mehr auf UART-Register zugreifen, sondern auf Variablen
> oder Arrays.

Darum geht es mir, richtig. Ich möchte nicht alle Schreibroutinen im 
maximalen Fall 4x pflegen und hinterherrennen. Wenn was auffällt oder 
vlt. noch was hinzukommt, möchte ich es universell für alle 4 einsetzen 
können. Ob ich es dann überhaupt brauche, also alle Routinen für alle 4 
UART'S, darüber möchte ich mir keine Gedanken machen müssen.

Bei den 4 Zugriffsfunktionen uart_x_ wird sich wohl nicht viel ändern. 
Die Register sind festgelegt und der einzelne UART0 macht seinen Dienst 
ja bereits sehr gut.


Achim S. schrieb:
> Weils grad aktuell ist und wenn Du kein C++ willst:
>
> Packe alle öffentlichen Uart-Funktionen (und Flags, Speicher etc, was
> ein einzelner Uart so braucht) in eine Struktur struct sUart in uart.h;

Der Ansatz mit den Strukturen finde ich auch nicht schlecht, wirkt 
aufgeräumter. Wenn ich es nun aber richtig verstanden habe, "kostet" das 
vlt. längere Ausführungszeiten durch die Pointer auf die Funktionen?
(Wie groß sind die dann eigentlich 16 oder vlt. sogar 24 oder noch 
größer?)

Mit Funktionen die in Strukturen mitgegeben werden habe ich noch nichts 
gemacht, das wäre was neues für mich.

von A. S. (Gast)


Lesenswert?

AVRli .. schrieb:
> "kostet" das
> vlt. längere Ausführungszeiten durch die Pointer auf die Funktionen?

nicht bei gleichen Randbedingungen, z.B. globale Objekte:

Uart_char(&Uart1, 'a'); kostet einen Ptr (&Uart1) auf den Stack legen 
und drüben wieder runternehmen und alles indirekt verarbeiten.

Uart1.chr('a'); kostet eine Indirektion (Uart1.chr auslesen und 
hinspringen, statt direkt zur Adresse von Uart_char), spart aber 
mindestens 2 Stack-Operationen und ggf. mehrfache Indirektion in der 
Funktion. .

Insgesamt wird der Code schneller, meist sogar deutlich schneller (weil 
vieles zur Compilezeit eindeutig ist). Ich habe das sogar auf 18er Pics 
so gemacht, die lange keine und später nur eingeschränkte und teure 
Funktionspointer hatten.

von J Zimmermann (Gast)


Lesenswert?

Lama:
> für die Leute die sonst nichts anderes programmieren können
ertappt!
> früher jahrelang
eben
> Schon mal aus dem Profi-Bereich was probiert?
Als Entwicklungs-Ing. - noch nie!
Vielen Dank für diese sehr sachlichen Bemerkungen.

von Lama (Gast)


Lesenswert?

J Zimmermann schrieb:
>> für die Leute die sonst nichts anderes programmieren können
> ertappt!

Wusste ichs doch, deine Kompetenz strahlt förmlich

J Zimmermann schrieb:
>> früher jahrelang
> eben

Ja, danach auf was brauchbares umgestiegen.

J Zimmermann schrieb:
>> Schon mal aus dem Profi-Bereich was probiert?
> Als Entwicklungs-Ing. - noch nie!

So, wie du auch die STM32 mal "probiert" hast? Na dann...


--> Wenn du schon versuchst ironisch zu sein, dann mach es richtig. 
Zumindest das solltest du ja hinbekommen :-)

von AVRli .. (avrli)


Lesenswert?

Achim S. schrieb:
> Uart1.chr('a'); kostet eine Indirektion (Uart1.chr auslesen und
> hinspringen, statt direkt zur Adresse von Uart_char), spart aber
> mindestens 2 Stack-Operationen und ggf. mehrfache Indirektion in der
> Funktion. .

Das klingt gut!
Dann habe ich noch 3 Fragen...

1. nehmen wie mal an ich habe in der struct Definition einen rx_buffer, 
mit RX_BUFFER_SIZE definiert, wie kann man die Größe dann von "außen" 
beliebig festlegen?
1
struct sUart =
2
{
3
    ...
4
    char rx_buffer[];
5
    ...
6
}
7
8
#define RX_BUFFER_SIZE 128
9
10
static const struct sUart Uart0;

Ich weiß wie groß der sein muß, einer muss 256 Zeichen vorhalten können, 
ein anderer auf UART1 halt nur 64 Zeichen.

Kann man das berücksichtigen oder muß man alle Buffer die gleiche Größe 
bei der Struct Lösung verpassen?

2.
Die Formatierungsroutinen bekommen dann alle eine Erweiterung bei der 
Übergabe, welche auf die entsprochene z.B. UART0 Struct zeigt?
1
Uart_str(&Uart0, "Auch mal an die frische Luft gehen!");

3.
Wie behandel ich Variablen, die ich bisher als volatile deklariert habe?
pos_uart_tx_buf und pos_uart_rx_buf sind ja solche.

Sind die durch die STRUCT Deklaration automatisch immer volatile?

Grüße AVRli...

: Bearbeitet durch User
von AVRli .. (avrli)


Lesenswert?

Habe mich "durchgebissen" und es läuft! ;-)

Buffer ist natürlich doch einstellbar, wenn man in der Struct nur den 
Zeiger auf diesen aufnimmt und ihn dann intern einfach festlegt.

Funktionen habe ich die von Euch angesprochenen realisiert.
Läuft nun mit  zwei UART's und weil es mich interessiert hat, die 
Erweiterung schlägt mit 630 Byte mehr Programmspeicher zu buche.

Ich versuche da noch etwas zu optimieren, vielleicht geht noch was.

Danke, zumindest bis hier hin, an alle konstruktiven Kommentare!
Grüße AVRli...

von AVRli .. (avrli)


Lesenswert?

Nachtrag... editieren geht nicht mehr...

Ich glaube ich habe noch einen Bock drin...
Denn die 640 Byte mehr, kommen mir etwas viel vor, die sind auch dazu 
gekommen, wenn ich nur einen UART nutzen würde.

Ich habe mir eine STRUCT angelegt mit:
1
struct sUart
2
{
3
  /*Buffer, Variablen und Zeiger auf die
4
    Funktionen init und put_char */
5
};
6
typedef const struct sUart* UART_P;
7
8
extern struct sUart UART0;

Die Funktion mit der ein Zeichen in den TX Buffer geschrieben wird sieht 
so aus.
1
static void _uart0_put_char(char chrtx)
2
{
3
  static uint8_t pin;
4
5
  pin = UART0.pos_in_tx;
6
  UART0.buf_tx[pin] = chrtx;
7
  pin++;
8
  pin = pin & (UART0_TX_BUFFER_SIZE - 1);
9
  UART0.pos_in_tx = pin;
10
11
  SET_BIT(UCSR0B, UDRIE0);
12
}

Um nun eine Zeichenkette einzuspielen nutze ich...
1
void uart_char(UART_P uart, char chrtx)
2
{
3
  uart->put_char(chrtx);
4
}
5
6
void uart_str(UART_P uart, char *buftx)
7
{
8
  while(*buftx) uart->put_char(*buftx++);
9
}

Aus meinem Main übergebe ich nun wie folgt...
1
uart_Str(&UART0, "Zeichenkette");

Alle anderen Funktionen wo ich die Werte übergebe haben auch diesen 
UART_P als ersten Parameter erhalten.

Grüße AVRli...

von A. S. (Gast)


Lesenswert?

AVRli .. schrieb:
> 1. nehmen wie mal an ich habe in der struct Definition einen rx_buffer,
> mit RX_BUFFER_SIZE definiert, wie kann man die Größe dann von "außen"
> beliebig festlegen?

Das ist ein ganz eigenes Thema, die Größe von Puffern.

Wenn der Treiber keine "Standardgröße" haben soll, dann
 - per malloc reservieren --> sehr unschön in kleinen embedded
 - vom Aufrufer bereitstellen lassen. Also z.B. in main
1
static unsigned char buf[128];
2
3
   Uart0.init(9600, 8, 1, buf, sizeof(buf));


> 2.
> Die Formatierungsroutinen bekommen dann alle eine Erweiterung bei der
> Übergabe, welche auf die entsprochene z.B. UART0 Struct zeigt?
> Uart_str(&Uart0, "Auch mal an die frische Luft gehen!");
eigentlich sollte solche Routinen Teil von Uart0 sein, also
1
Uart0.str("...");.

Und wenn nicht, dann übergib nur, was die Funktion braucht, hier also 
.chr:
1
Uart_str(Uart0.chr, "..."); /* beachte das fehlende &, da chr ein ptr */

> 3.
> Wie behandel ich Variablen, die ich bisher als volatile deklariert habe?
> pos_uart_tx_buf und pos_uart_rx_buf sind ja solche.

Die gehören nicht in die Struktur, denn die enthält nur vom Nutzer zu 
verwendende Elemente, keine Internas.

in uart0.c steht entweder
1
static volatile unsigned char pos_uart_tx_buf @0x4711; /* oder wie Du das Register kennzeichnest*/
oder in uart0.h direkt:
1
#define POS_UART_TX_BUF (*((volatile unsigned char *) 0x4711))

von AVRli .. (avrli)


Lesenswert?

Dsnn ist die Übergabe der gesamten Structur nicht richtig...
Gut ich danke Dir für Deine Antwort. Ich schau mir das heute Abend 
wieder an und werde dann nochmal umbauen.

Gut wäre dann, wenn ich einen Typen hätte, der dann &UART0.put_char 
abbildet, das wäre kürzer zu schreiben bei den ganzen Aufrufen.

Kannst Du vlt. noch ganz kurz auf die Zusammenhänge mit "static const 
struct" eingehen?

Das habe ich noch nie so verwendet. Vlt. ist ja das auch schon ein Grund 
warum es nun so "aufgebläht" wurde.

Ich kenne...

const char pgm_ans_invalid[]  PROGMEM = "invalid command";

Das macht man wohl so weil sich der Inhalt nie ändern wird?

Grüße AVRli...

von Karl M. (Gast)


Lesenswert?

Hallo,

AVRli .. schrieb:
> const char pgm_ans_invalid[]  PROGMEM = "invalid command";

Bitte nicht raten, sondern nachsehen, was beim avr gcc PROGMEM 
bedeutet.

von J Zimmermann (Gast)


Lesenswert?

Lama:
> Wusste ichs doch, deine Kompetenz strahlt förmlich
wow, Deine Kenntnis Anderer ist bemerkenswert, vielleicht sollte eine 
Rubrik "Übersinnnliches" oder "Hellsehen" für Dich eingerichtet werden?

von Lama (Gast)


Lesenswert?

J Zimmermann schrieb:
> wow, Deine Kenntnis Anderer ist bemerkenswert, vielleicht sollte eine
> Rubrik "Übersinnnliches" oder "Hellsehen" für Dich eingerichtet werden?

Was ist los Jacqueline? Tage?

Langsam wirst du irrational :-)

von Marc V. (Firma: Vescomp) (logarithmus)


Lesenswert?

J Zimmermann schrieb:
> Mitlesa:
>> Die paar Bytes für viermal die UART-Routinen machen das Kraut
>> nun wirklich nicht fett.
>
> Du mußt dann aber auch jede Änderung 4x machen, hast Du dann noch viele
> andere Hardware-Einheiten in der "Mache" wird's schnell fitzig.

 Welche Änderung denn, es sind Low level Routinen ?
 Meinst du, die Register Adressen ändern sich irgendwann ?

 Aber wenn man sonst nichts zu tun hat...

Peter D. schrieb:
> Flash ist meistens nicht das Problem, daher ist es schon sinnvoll, den
> Code zu kopieren. Ansonsten müßte man wertvollen SRAM opfern, um für
> jedes 8Bit-Register einen 16Bit-Pointer anzulegen. Und die Funktionen
> müßten dann auch erst ein Pointerregister (X,Y,Z) laden (+Push/Pop
> Overhead), anstatt direkt auf das IO-Register zuzugreifen. D.h. die
> Laufzeit wird größer und Flash wird auch kaum gespart.

 Ja, aber bei nur 256KB muss man schon gewaltig sparen.
 Egal wie langsam es wird und wieviel mehr RAM man verbraucht - mit
 FLASH muss man sparsam umgehen...

von AVRli .. (avrli)


Lesenswert?

Es funktioniert ja nun soweit, das Einzige ist in der Tat, dass ein 
Aufruf von z.B.
1
uart_cr(&UART0);

Nun 6 Byte, (zuvor 2 Byte) FLASH verbraucht. Da fehlt mir das Wissen, 
was da nun genau passiert und ob man da noch was machen kann.

Wenn ich einen weiteren UART einbinde, erhöht sich der RAM Verbrauch um 
260 Byte, wobei davon 128 RX und 128 TX Buffer sind.

Beide UART's lassen sich mit den gleichen Routinen ansprechen...
1
uart_cr(&UART0);
2
uart_cr(&UART1);

Ich möchte mich nun auch nicht verzetteln, der Flash "Verbrauch" ist das 
eine (nicht ganz so schlimm) doch Geschwindigkeit büße ich mit +4 Byte 
pro Ausgabe schon ein. Wenn man das noch verbessern könnte...

Gruß AVRli...

von S. R. (svenska)


Lesenswert?

Ich werfe mal das Thema "schreib die Funktionen doch als Makros und 
instanziiere dann viermal" in den Raum und verschwinde dann leise 
wieder, nachdem ich Wilhelm einmal zuzwinkerte. :-)

Die 6 Bytes werden ein "pop arg" und ein "push arg" oder sowas sein, 
wobei der restliche Unterschied nicht weiter auffällt. Oder so. ;-)

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.