Forum: Mikrocontroller und Digitale Elektronik USART Sende Problem - 168


von cyrexo (Gast)


Lesenswert?

Hallo,

mein Usart empängt alles einwandfrei beim senden des ersten bytes wird
das interrupt auch ausgelöst doch dies nur beim ersten => kann keine
weiteren bytes versenden

ich hoffe ihr könnt mir weiterhelfen.

**************************************************************
#include <avr/io.h>
#include <avr/interrupt.h>
#include <avr/signal.h>
#include <avr/pgmspace.h>
#include <stdio.h>

// Function prototypes
//
int UART_putchar(char c);
int UART_getchar(void);
void UART_first_init(void);

// Buffer sizes must be 2^n
//
#define TBUFSIZE   32
#define RBUFSIZE   32

#define TMASK      (TBUFSIZE-1)
#define RMASK      (RBUFSIZE-1)

// Static variables
//
volatile unsigned char tbuf[TBUFSIZE];  // TX buffer
volatile unsigned char rbuf[RBUFSIZE];  // RX buffer

volatile unsigned char t_in;            // TX buffer in index
volatile unsigned char t_out;           // TX buffer out index

volatile unsigned char r_in;            // RX buffer in index
volatile unsigned char r_out;           // RX buffer out index

SIGNAL(SIG_USART_RECV) {
//******************
// RX interrupt handler
//
   char c;
   c = UDR0;                     // Get received char
   rbuf[r_in & RMASK] = c;
   r_in++;
}

SIGNAL(SIG_USART_DATA) {
//*******************
// Data register empty interrupt handler.
// Indicates that next char can be transmitted
//
   if(t_in != t_out) {
      UDR0 = tbuf[t_out & TMASK];
      t_out++;
   }
   else {
      UCSR0B &= ~(1<<UDRIE0);
   }
}

char tbuflen(void) {
//****************
// Retrieve pending chars in TX buffer
//
   return(t_in - t_out);
}

int UART_putchar(char c) {
//*********************
// Fills the transmit buffer, if it is full wait
//
   while((TBUFSIZE - tbuflen()) <= 2);  // Wait...

   // Add data to the transmit buffer, enable TXCIE
   //
   tbuf[t_in & TMASK] = c;
   t_in++;
   UCSR0B |= (1<<UDRIE0);
   //UCR |= (1<<UDRIE);         // Enable UDR empty interrupt
   return(0);
}

char rbuflen(void) {
// ***************
// Retrive pending chars in RX buffer
//
   return(r_in - r_out);
}

int UART_getchar(void) {
//*******************
// Retieves character from UART. This function is to be passed
// to fdevopen
//
   unsigned char c;
   while(rbuflen() == 0);     // Wait...
   c = rbuf[r_out & RMASK];
   r_out++;
   return(c);
}

void UART_first_init(void) {
//***********************
// The function fdevopen(..) must contain as parameters the
// corresponding  ..putchar() and  ..getchar() functions, defined
before.
//
   UCSR0B;
   UBRR0H = ((16000000UL/(38400 * 16L)) >> 8);
   UBRR0L = (16000000UL/(38400 * 16L)) - 1;

   //       (Empfänger)  |  (Sender)
   UCSR0B = (1<<RXEN0) | (1<<TXEN0) | (1<<RXCIE0);

   //       (      8 Bit Befehl    )
   UCSR0C = (1<<UCSZ01) | (1<<UCSZ00);
   //UBRR = 25;                               // 19200 BPS
   //UCR = (1<<RXCIE)|(1<<TXEN)|(1<<RXEN);    // 8 Databits, receive
and transmit enabled, receive and transmit complete interrupt enabled

   fdevopen(UART_putchar, UART_getchar);
   sei();                                  // Global interrupt enable
}

int main(void) {
//************
//
    unsigned char s[20];

    UART_first_init();            // First init UART

   for(;;) {

      // Same function as printf() but the format string resides in
flash
      //
      printf_P(PSTR("Type text in TX window, then press
ENTER\r\n"));

      while(scanf("%s",s) == 0);  // Get text

      printf_P(PSTR("\r\nYou entered '%s'\r\n"),s);
   }
}

von cyrexo (Gast)


Lesenswert?

ahja es ist ein 168iger

von Rahul (Gast)


Lesenswert?

Ein Fehler:

   UBRR0H = ((16000000UL/(38400 * 16L)) >> 8);
   UBRR0L = (16000000UL/(38400 * 16L)) - 1;

Die Formel sollte in beiden Fällen bis auf das ">>8" gleich sein
(==>Makro verwenden!). Hat aber eher nichts mit deinem Problem zu tun.
Ansonsten finde ich dein Programm etwas wirr, weil du teilweise
Funktionen von hand implementierst, andererseits aber fertige
Funktionen benutzt bzw. Funktionen verwendest, die nicht in deinem
Quellcode vorkommen (sind die Teil der stdio.h?).

von cyrexo (Gast)


Lesenswert?

hallo,

ja das programm hab ich ja von menem restlichen programm ausgelagert
deswegen ein bisschen schlampig wollte sehen ob der fehler viel an
meinem restlichen programm liegt aber das war es auch nicht.

aber es wundert mich einfach wieso er den interrupt ein bzw 2 mal
ausführt, beim 2 mal wird der interrupt deaktivert ... wird aber wieder
aktiviert wenn in den puffer geschrieben wird... habe mir auch alle
register angeschaut vor dem interrupt und nachdem er wieder aktiviert
wird .. identisch :(

von Wolfram (Gast)


Lesenswert?

Also ich sehe da einiges unsauberes im Programm
rout,tin müssen nicht volatile sein
r_in & RMASK solltest du ersetzen durch module Arraygrösse bei 2^n wird
das dann auch ein AND ist aber wesentlich Fehlerunabhängiger
>return(r_in - r_out);
du willst doch mit Ringpuffer arbeiten? Da kann das auch negativ
werden!
In putchar und analog getchar machst du einfach r_out++;  sicher
maskierst du noch beim Zugriff aber das ist ...
besonders wenn man damit den Abstand im Ringpuffer bestimmen will.
(r_in - r_out)
Ich würde vorschlagen du räumst erstmal in deinem Programmcode mit
diesen Schlampigkeiten auf.
Der interrupt wird 2x ausgeführt aufgrund der Doppelpufferung.

von cyrexo (Gast)


Lesenswert?

so da is mal der code so wie ich ihn bei mir hab ohne meiner meinung
nach schlampigkeiten. das mit TX_BUF_MASK & tx_in find ich eig ganz
praktisch :)

das empfangen funktioniert auch einwandfrei nur halt beim senden wird
der interrupt nur 2 mal aufgerufen und dann nie wieder obwohl das
register auf empty steht und der interrupt auch aktiviert ist.

das uart_getc ist zu vergessen benutz es eh nicht geh da genau so über
einen interrupt..


uint8_t tx_buff[TX_BUF_SIZE];
uint8_t tx_in = 0;
uint8_t tx_out = 0;

uint8_t rn_buff[RN_BUF_SIZE];
uint8_t rn_in = 0;
uint8_t rn_out = 0;

/* Initialiesierung von Uart */
void uart_init(void){


  // Setzen der Baud Rate
  UBRR0H = (BAUD_RATE >> 8);
  UBRR0L = BAUD_RATE;

  //     (Empfänger)  |  (Sender)
  UCSR0B = (1<<RXEN0) | (1<<TXEN0) | (1<<RXCIE0);

  //     (    8 Bit Befehl   )
  UCSR0C = (1<<UCSZ01) | (1<<UCSZ00);

  fdevopen(uart_putc, uart_getc);


}

/* Senden aus dem Puffer */
SIGNAL(SIG_USART_DATA){

  if(tx_in != tx_out)
    UDR0 = tx_buff[tx_out++ & TX_BUF_MASK];
  else
    /* Sendebuffer ist leer, UDRE Interrupt deaktivieren */
    UCSR0B &= ~(1<<UDRIE0);


}

/* Senden eines Zeichen */
void uart_putc(unsigned char c)
{
  //Warten bis platz im Zwischenspeicher frei wird
    while( (RN_BUF_MASK - tx_in - tx_out) < 1)
    ;

  tx_buff[TX_BUF_MASK & tx_in++] = c;

  UCSR0B |= 1<<UDRIE0;

}

von Wolfram (Gast)


Lesenswert?

1. du hast 2 volatile zu viel weggenommen
2.(RN_BUF_MASK - tx_in - tx_out)
Wie schon erwähnt du erhöhst unsauber (ohne zu maskieren)
die Zeichen gehen schneller in den Puffer als heraus d.h. z.B.
RN_BUF_MASK=31 tx_in=40 (da du nicht maskierst) txout=2
31-40-2=mit sicherheit <1
Hör mit diesem Schlampigkeiten auf oder wiederhole sie nicht noch wenn
du darauf hingewiesen wirst!

von @wolfram (Gast)


Lesenswert?

verwirr den armen nicht mit deinem volatile das ist meistens unwichtig.
aber wenn man ne variable volatile macht dann schadet es auch nicht,
kostet nur ein bisschen mehr rechenzeit. manche compiler erkennen sogar
selbst wann eine variable volatile sein muss und wann nicht. also zu
viele volatiles schaden nicht, verlängern nur die ausführzeit des
programms

von cyrexo (Gast)


Lesenswert?

31-40-2=mit sicherheit <1

wenn das kleiner 1 is soll er solange warten bis es >1 dann weis er ja
ob platz frei is oder nicht

ah ich bin schon ganz verwirrt kannst du das den nicht kurz hincoden
wie du es meinst ... steht grad total aufm schlauch

von Rahul (Gast)


Lesenswert?

tx_out++ & TX_BUF_MASK

Müsste IMHO auch eher "++tx_out & TX_BUF_MASK" heissen.
(Der Übersicht wegen mache ich das gerne in zwei Zeilen.)

>RN_BUF_MASK - tx_in - tx_out

Dabei geht es doch "nur" darum, festzustellen, ob noch mindestens 1
Byte in den Puffer passt, oder?
"while (tx_in == tx_out);" sollte da sinnvoller sein, da die beiden
Variablen sowieo dauernd mit Puffergrösse-1 verundet werden.
Wolfram hat da schon ein schönes Rechenbeispiel geliefert.

von Karl heinz B. (kbucheg)


Lesenswert?

> 31-40-2=mit sicherheit <1
>
> wenn das kleiner 1 is soll er solange warten bis es >1 dann weis er
> ja ob platz frei is oder nicht

Warum soll er da warten?
Wenn der Puffer 40 Zeichen gross ist, mit 31 Zeichen gefüllt
wurde und erst 2 gesendet wurden, dann ist noch jede Menge
Platz im Puffer.

Schnapp dir Papier und Bleistift, mal dir einen Puffer auf

  tx_buf
  +---+---+---+---+---+---+--- ... ---+---+---+
  |   |   |   |   |   |   |           |   |   |
  +---+---+---+---+---+---+--- ... ---+---+---+

  tx_in
  +-----+
  |     |
  +-----+

  tx_out
  +-----+
  |     |
  +-----+

und spiel ein paar Fälle durch: Du spielst CPU und arbeitest
dein Programm ab. Du darfst nur die Aktionen ausführen, die
dir dein Program ansagt. Wenn dein Programm einen Vergleich
vorschreibt, dann schaust du auf den Zettel, liest die entsprechenden
Variablenwerte an und machst den Vergleich. Wenn dein Programm
eine Zuweisung vorschreibt, dann korrigierst du am Zettel die
entsprechenden Werte (daher Bleistift und nicht Kugelschreiber).
Alles ganz genauso wie es auch dein µC macht.

Und mach den Puffer nicht zu gross. Eine Puffergröße von
4 Byte ist für das Testen am Papier mehr als ausreichend.
Es geht nicht um den Puffer an sich, sondern es geht darum
dass du mal siehst, was dein Programm eigentlich macht.


Du musst dir mal selbst ein paar Definitionen zurechtlegen.
zb
  tx_in   Index im Ringpuffer an dem das nächste zu schreibende
          Zeichen abgelegt wird
  tx_out  Index im Ringpuffer welches das zuletzt gesendete Zeichen
          enthält

/* Senden eines Zeichen */
void uart_putc(unsigned char c)
{
  //Warten bis platz im Zwischenspeicher frei wird
    while( tx_in == tx_out )
      ;

  tx_buff[ tx_in ] = c;
  tx_in = ( tx_in + 1 ) % TX_BUF_MASK;

  UCSR0B |= 1<<UDRIE0;
}

Jetzt ist sichergestellt, dass tx_in nur Werte im Bereich
0 bis TX_BUF_MASK enthalten kann.

du musst dafür sorgen, dass tx_in und tx_out niemals den
erlaubten Wertebereich verlassen. Alles andere ist ein
sicherer Weg ins Chaos.

Dann musst du dir natuerlich ueberlegen, wie du rbuflen()
formulierst. Spiels einfach mal mit einer gedachten Buffe-
groesse von 8 Bytes durch. tx_in kann kleiner als tx_out
sein. D.h. aber nicht, dass der Buffer voll ist. Das heist
nur, dass wieder am Anfang dieses Ringbuffers eingefügt
wurde, während noch ein paar Zeichen aufs Senden warten.
Der Buffer ist nur dann voll, wenn tx_in == tx_out
Zumindest solltest du deinen Code dahin hintrimmen, dass
diese Vorgabe immer erfüllt ist
  wenn tx_in != tx_out     es gibt Platz im Buffer
  wenn tx_in == tx_out     es gibt keinen Platz im Buffer

Aber das wichtigste ist: Du musst tx_in und tx_out unter
allen Umständen im erlaubten Wertebereich halten.

von Karl heinz B. (kbucheg)


Lesenswert?

Ooops. Der letzte Teil haette es nicht mehr ins Forum machen
sollen. Bitte alles nach 'Du musst dir mal selbst ein paar
Definitionen zurecht legen' bitte ignorieren. Ich hab keine
Ahnung ob der Code so jetzt noch zum Rest passt.

von Rahul (Gast)


Lesenswert?

>  wenn tx_in != tx_out     es gibt Platz im Buffer
>  wenn tx_in == tx_out     es gibt keinen Platz im Buffer

Da gibt es aber Start-Probleme: Im ersten Moment sind tx_in und tx_out
gleich (== 0 oder je nach Initialisierung).
Besser ist es, entweder die freien Plätze im Puffer oder die zu
sendenden Bytes zu zählen. Ist die Anzahl der freien Pufferplätze = 0,
ist er voll...
Ansonsten sollte man sich eine Funktion (oder Makro) ausdenken, das die
Anzahl der freien Speicherplätze errechnet:

l = ((tx_in>ty_out) ? (tx_in-tx_out) : (tx_out-tx_in))) & TX_BUF_MASK;

(könnte so funktionieren... ausm Ärmel geschüttelt).

Ist l<RN_BUF_MASK, dann ist (sollte noch) noch Platz vorhanden
(sein)...

von cyrexo (Gast)


Lesenswert?

ich weis jetzt nicht ob ich meinen code anders sehe als ihr aber meiner
meinung is es so das durch

tx_buff[TX_BUF_MASK & tx_in++] = c;

tx_in immer hochgezzählt wird und somit die gesamtanzahl der bytes die
in den Puffer geschrieben worden sind ausgelesen wird

tx_out wird immer hochgezählt wenn ein byte rausgeschrieben wurde

tx_in: 60
tx_out: 40

=> 20 Byte im puffer

puffer: 30

puffer - (tx_in - tx_out) => 10
=> 10 platz frei

 => fehler im porg das die klammer fählt

=>
//warte bedingung
while( (TX_BUF_MASK - (tx_in - tx_out)) < 1)
    ;



sehe ich das jetzt so richtig von meinem eigen prog oder sieht ihr das
anders

von Karl heinz B. (kbucheg)


Lesenswert?

>>  wenn tx_in != tx_out     es gibt Platz im Buffer
>>  wenn tx_in == tx_out     es gibt keinen Platz im Buffer

> Da gibt es aber Start-Probleme: Im ersten Moment sind tx_in und
> tx_out gleich (== 0 oder je nach Initialisierung).

Kommt auf die Initialisierung an. Wenn ich mit tx_out = TX_BUF_MASK
loslege, stimmt obiges.

Ich wollte das auch mal so aus dem Ärmel geschüttelt, hab aber
dann schnell gemerkt, dass ich das nicht hier direkt programmieren
kann. Musste man aufmalen und mal ein Testprogramm auf
nem PC machen (damit man auch mit einem Debugger vernuenftig
ran kommt).

von Karl heinz B. (kbucheg)


Lesenswert?

> tx_in immer hochgezzählt wird und somit die gesamtanzahl der
> bytes die in den Puffer geschrieben worden sind ausgelesen wird

Und irgendwann ist tx_in so gross (255), dass beim nächsten
Addieren von 1, das Ding  zu 0 wird. Dann ist tx_in plötzlich kleiner
als tx_out. Was passiert dann?

Wie gesagt: am Zettel aufmalen, mit Papier und Bleistift
durchspielen.

von cyrexo (Gast)


Lesenswert?

aber wie gesgat ich hab mehr das problem das er den interrtupt nicht
wieder auslöst er kommt also gar nich wieder da rein

/* Senden aus dem Puffer */
SIGNAL(SIG_USART_DATA){

  if(tx_in != tx_out)
    UDR0 = tx_buff[tx_out++ & TX_BUF_MASK];
  else
    /* Sendebuffer ist leer, UDRE Interrupt deaktivieren */
    UCSR0B &= ~(1<<UDRIE0);


}

von Rahul (Gast)


Lesenswert?

>tx_in immer hochgezzählt wird und somit die gesamtanzahl der bytes >die
in den Puffer geschrieben worden sind ausgelesen wird
>tx_out wird immer hochgezählt wenn ein byte rausgeschrieben wurde

Gute Einwand, wenn tx_in unendlich groß werden könnte. Irgenwann gibt
es aber einen Überlauf, und dann ist tx_in definitiv (für eine gewisse
Zeit) kleiner als tx_out.

Ich meinte mit "aus dem Ärmel gschüttelt" die Berechnung (hab noch
nicht sooo oft mit "= ? :" gearbeitet).

von cyrexo (Gast)


Lesenswert?

stimmt da habt ihr recht ich schreib das schnell mal um so wie ihr es
machen würdet :)

von Wolfram (Gast)


Lesenswert?

while( (RN_BUF_MASK - tx_in - tx_out) < 1);
Hast du schonmal daüber nachgedacht was passiert wenn txin über läuft
31-0-255 was wird das für ein Datentyp? Ich habe da irgendwie leichte
Probleme. Also wie war das Es wird in den grössten verwendeten Datentyp
konvertiert. defines sind standardmäßig ohne Kennzeichnung int ?
Das heisst wir rechnen mit int 31-0-255=-224
Warum läßt du die Variable durchlaufen und hältst nicht in ihr den
direkten Pufferindex? Das würde auch die Fehlersuche sehr erleichtern.

von Karl heinz B. (kbucheg)


Lesenswert?

> Ich meinte mit "aus dem Ärmel gschüttelt" die Berechnung (hab
> noch nicht sooo oft mit "= ? :" gearbeitet).

Sieht doch gut aus.
Ich hab mich auch lange gegen ?: gewehrt. Solange man das
nicht übertreibt, ist das aber echt praktisch. zb.
in Argumentlisten:

   foo( i > j ? 3 : 1 );

Wenn man sich dran gewöhnt hat, liest sich das genauso leicht,
wie

  if( i > j )
    foo( 3 );
  else
    foo( 1 );

Eventuell noch ein paar Klammern um die Bedingung, damit
sie etwas rausleuchtet, und 'passt schon'.
Nur rate ich davon ab, sowas mit komplexeren Bedingung
zu machen. Das wird seeeeehr schnell unübersichtlich.

von cyrexo (Gast)


Lesenswert?

so hab das mal angepasst sollte so jetzt sein oder? torzdem immer noch
das problem mit dem interrupt... :(


#include "uart.h"


uint8_t tx_buff[TX_BUF_SIZE];
uint8_t tx_in = 0;
volatile uint8_t tx_out = 0;

uint8_t rn_buff[RN_BUF_SIZE];
volatile uint8_t rn_in = 0;
uint8_t rn_out = 0;
uint8_t *rn_parameter[MESSAGE_BUF_SIZE];

uint8_t parameter = 0;
uint8_t temp_parameter = 0;
uint8_t temp = 0;

void uart_putc(unsigned char c);
uint8_t uart_getc();



/* Initialiesierung von Uart */
void uart_init(void){


  // Setzen der Baud Rate
  UBRR0H = (BAUD_RATE >> 8);
  UBRR0L = BAUD_RATE;

  //     (Empfänger)  |  (Sender)
  UCSR0B = (1<<RXEN0) | (1<<TXEN0) | (1<<RXCIE0)| (1<<TXCIE0);

  //     (    8 Bit Befehl   )
  UCSR0C = (1<<UCSZ01) | (1<<UCSZ00);

  fdevopen(uart_putc, uart_getc);


}


/* Senden aus dem Puffer */
SIGNAL(SIG_USART_DATA){

  if(tx_in != tx_out){
    tx_out = ( tx_out + 1 ) & TX_BUF_MASK;

    UDR0 = tx_buff[tx_out];
  }else
    /* Sendebuffer ist leer, UDRE Interrupt deaktivieren */
    UCSR0B &= ~(1<<UDRIE0);


}




/* Senden eines Zeichen */
void uart_putc(unsigned char c){

   uint8_t tmphead;

   tmphead = ( tx_in + 1 ) & TX_BUF_MASK;


   //Warten bis platz im Zwischenspeicher frei wird
    while( tmphead == tx_out )
      ;

    tx_buff[ tmphead ] = c;
    tx_in = tmphead;

    UCSR0B |= 1<<UDRIE0;

}

von Wolfram (Gast)


Angehängte Dateien:

Lesenswert?

Hier mal zum Vergleich die Uartroutinen die ich verwende
(Mega16, 8Mhz bis 500kBit)

von cyrexo (Gast)


Lesenswert?

mich wundert des weil den ir löss ich ja eig aus und eig mach ich ja
alles richtig wird der ir nur 2 mal ausgelöst :(

von cyrexo (Gast)


Lesenswert?

also ich hab grad rumgespielt

das problem tritt durch das deaktivern und aktivern des interrupts
auf..

wenn ich ein belibiges zeichen sende und den interrupt laufen lasse
geht das ohne probleme sobald ich ihn aber deaktiver und wieder
aktivier wird er nicht mehr ausgeführt .. an was kann das liegen?


SIGNAL(SIG_USART_DATA){

  if(tx_in != tx_out){
    tx_out = ( tx_out + 1 ) & TX_BUF_MASK;

    UDR0 = tx_buff[tx_out];

  }else
    UDR0 = '.';
    //UCSR0B &= ~(1<<UDRIE0);


}

von Rahul (Gast)


Lesenswert?

Sollte das:

    tx_out = ( tx_out + 1 ) & TX_BUF_MASK;
    UDR0 = tx_buff[tx_out];

nicht lieber andersherum stehen:

    UDR0 = tx_buff[tx_out];
    tx_out = ( tx_out + 1 ) & TX_BUF_MASK;

Übrigens müsste die Abfrage

RN_BUF_MASK - tx_in - tx_out

so funktionieren:

RN_BUF_MASK - ((tx_in - tx_out) & TX_BUF_MASK)

von cyrexo (Gast)


Lesenswert?

ne des funtz schon da ich auch am anfag um eins erhöhe hab nur noch das
dumme interrupt prob :(

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.