Forum: Mikrocontroller und Digitale Elektronik LPC2148 Problem mit UART Interrupt


von Thomas Haslinger (Gast)


Lesenswert?

Hallo,

ich verwende einen LPC2148 und sende Messwerte per Interrupt über den 
UART.
Dazu hab ich einen vorhandenen Quellcode mit Software FIFO (256Byte). 
Außerdem hat der Controller noch einen Hardware-FIFO (16Byte).

Ich beschreibe meinen SoftwareFIFO mit einer bestimmten Datenrate und 
dann wird immer der UART Interrup ausgelöst, und vom SoftwareFIFO in den 
HardwareFIFO des LPC geschoben.

Mein Problem ist, das nach einer bestimmten Zeit plötzlich kein UART 
Interrupt mehr ausgelöst wird. Der Software FIFO füllt sich dann 
natürlich bis er voll ist und nichts geht mehr.

Ich konnte das Problem schon etwas eingrenzen, dazu hier ein Teil des 
Quellcodes:

Hiermit beschreibe ich den SoftwareFIFO:

void UART1_PutStgToIntBuf(char *Buf, int len)
{
char *pBuf = Buf, ch;
LPC_Uart_Buffer_t *pUartBuf;
unsigned long IntFlagsHold;

  pUartBuf = &Uart1Config.UartBuffer;
  while (len--)
  {
    // if FIFO is full pending here, wait for send characters
    if (pUartBuf->TxCount >= TXBUFSIZE)
    {
      break;
    }
    ch = *pBuf & 0xff;
    pUartBuf->TxBuf[pUartBuf->TxTailPoint] = ch;
    pUartBuf->TxTailPoint = (pUartBuf->TxTailPoint+1)%TXBUFSIZE;

    IntFlagsHold = disable_IRQ();
    pUartBuf->TxCount++;
    restore_IRQ(IntFlagsHold);

    pBuf = pBuf + 4;
  }
  U1IER |= IER_THRE;
}



Und das ist meine Interruptroutine:

void UART1_ISR (void)
{
 IO0SET  = (1<<10);  //ZUM TESTEN; MUSS WIEDER WEG

int i;
char temp;
LPC_Uart_Buffer_t *pUartBuf = &Uart1Config.UartBuffer;
  switch((U1IIR>>1)&0x7)
  {
  case IIR_THRE:  // continue sending data
    // Check for Transmitter FIFO enable
    if (Uart1Config.FIFOEnable)
      // when FIFO is enable load FIFODEEP bytes
      i = FIFODEEP;
    else
      // when FIFO is disable load 1 byte
      i = 1;
    do
    {
      // Check for software FIFO state and load data into transmitter 
hold register
      // disable interups imediatly aftre write when FIFO is empty
      if(!UART_ReadTxBuf(pUartBuf, (char*)&U1THR) || (pUartBuf->TxCount 
== 0))
      {
        // Disable interrup when FIFO is empty
        U1IER_bit.THREIE = false;
        break;
      }
    }
    while(--i);
    break;
  case IIR_RSL:  // error manage
    temp = U1LSR;
    pUartBuf->RxFlag |= temp & 0x9E;
    break;
  case IIR_RDA:  // receive data


  case IIR_CTI:  // time out

    pUartBuf->RxBuf[pUartBuf->RxTailPoint] = U1RBR;
    if (++pUartBuf->RxCount > RXBUFSIZE)
    {
      pUartBuf->RxFlag |= RC_FIFO_OVERRUN_ERR;
    }
    pUartBuf->RxTailPoint = (pUartBuf->RxTailPoint+1)%RXBUFSIZE;
    break;
  default:
    break;
  }
  IO0CLR  = (1<<10);  //ZUM TESTEN; MUSS WIEDER WEG
  VICVectAddr = 0;    // Clear interrupt in VIC.
}


Ich bin schon soweitgekommen, dass es daran liegt, wenn ein Interrupt 
auftritt, während ich den SoftwareFIFO beschreibe.
Beim beschreiben des SoftwareFIFOs werden die Interrupts kurzzeitig 
deaktiviert mit "IntFlagsHold = disable_IRQ();" und danach wieder 
aktiviert. Wenn der Interrup des UART sofort nach dem reaktivieren der 
Interrupts kommt, tritt mein Fehler auf.
Wenn ich die Zeile mit dem deaktiveren des Interrupts weglasse, dann 
passiert der Fehler nicht mehr??

Nur diese Zeile wird der Programmierer ja nicht umsonst eingefügt haben, 
oder?
Ich hab Angst, wenn ich meinen Fehler dadurch beseitige, dass ich diese 
Zeile weglasse, dass früher oder später ein anderer Fehler auftritt.

Irgendjemand eine Idee?

von Thomas Haslinger (Gast)


Lesenswert?

Keiner eine Idee?

von Helfer (Gast)


Lesenswert?

> Beim beschreiben des SoftwareFIFOs werden die Interrupts kurzzeitig
> deaktiviert mit "IntFlagsHold = disable_IRQ();" und danach wieder
> aktiviert.

Das stimmt nur für TxCount. Die anderen Elemente der Software-FIFO 
Struktur liest und beschreibst du fröhlich ohne gesperrten IRQ.

>    if (pUartBuf->TxCount >= TXBUFSIZE)
>    pUartBuf->TxBuf[pUartBuf->TxTailPoint] = ch;
>    pUartBuf->TxTailPoint = (pUartBuf->TxTailPoint+1)%TXBUFSIZE;

Das ist IMHO falsch, weil dir da die UART1_ISR dazwischen funken kann 
(Stichwort nicht atomarer Zugriff).

von Helfer (Gast)


Lesenswert?

> LPC_Uart_Buffer_t *pUartBuf;
> unsigned long IntFlagsHold;
>
>  pUartBuf = &Uart1Config.UartBuffer;
>  while (len--)
>  {
>    // if FIFO is full pending here, wait for send characters
>    if (pUartBuf->TxCount >= TXBUFSIZE)
>    {
>      break;
>    }

Der Kommentar stimmt so nicht. Durch das break wird das while 
beendet.

Dort gehört fürs Warten IMHO ein continue hin, dann aber darf das 
len-- nur gemacht werden, wenn ein Zeichen erfolgreich in den 
Software-FIFO geschrieben wurde.

Damit das asynchrone Update von pUartBuf->TxCount bei laufendem IRQ 
wirksam ist,

>    if (pUartBuf->TxCount >= TXBUFSIZE)

muss der C Compiler wissen, dass sich pUartBuf->TxCount ausserhalb der 
aktuellen Funktion ändern kann. D.h. mindestens das Element TxCount der 
Struktur LPC_Uart_Buffer_t muss volatile sein.

von Thomas Haslinger (Gast)


Lesenswert?

Vielen Dank schon mal.

Leider hab ich das Problem immer noch:

-Wenn ich während des gesamten beschreibens des FIFO´s 
(UART1_PutStgToIntBuf) die Interrupts deaktiviere, passiert der Fehler 
ebenso.

-Wenn ich TXCount als volatile deklaliere passiert es leider auch

- das mit dem continue, statt dem break hab ich auch ausprobiert. Soweit 
ich das richtig verstanden habe, bringt das nur was, sollte mein 
Software FIFO überlaufen, der ist aber so groß, dass das bei meiner 
Datenmenge eigenltich sowieso nie passieren kann. Hab es aber trotzdem 
ausprobiert

Irgendwas muss also passieren, warum meine UART1_ISR nicht mehr 
ausgeführt wird?

Oder hat vielleicht jemand einen funktionierenen ähnlichen Quellcode für 
mich?

von Thomas Haslinger (Gast)


Lesenswert?

So jetzt konnte ich meinen Fehler weiter einschränken.
Er passiert genau dann, wenn meine UART_ISR an dieser Stelle auftritt:


void UART1_PutStgToIntBuf(char *Buf, int len)
{
char *pBuf = Buf, ch;
LPC_Uart_Buffer_t *pUartBuf;
unsigned long IntFlagsHold;

  pUartBuf = &Uart1Config.UartBuffer;
  while (len--)
  {
    // if FIFO is full pending here, wait for send characters
    if (pUartBuf->TxCount >= TXBUFSIZE)
    {
      break;
    }
    ch = *pBuf & 0xff;
    pUartBuf->TxBuf[pUartBuf->TxTailPoint] = ch;
    pUartBuf->TxTailPoint = (pUartBuf->TxTailPoint+1)%TXBUFSIZE;

    IntFlagsHold = disable_IRQ();
    pUartBuf->TxCount++;
    restore_IRQ(IntFlagsHold);

    pBuf = pBuf + 4;
  }
  //WENN HIER DIE UART_ISR AUDTRITT!!!!!!!!!!!!!!!!!!!!!!!

  U1IER |= IER_THRE;
}

von Helfer (Gast)


Lesenswert?

Tipp: Es ist meistens einfacher, wenn man markierst, wo Originalcode ist 
und wo eigene Änderungen gemacht wurden.

Im Gegensatz zu deinem Code hat ist das break und der Kommentar dazu 
in einem Originalcode von UART_PutStringByInterrupt richtig. Beachte, 
dass dort die Zahl der gesendeten Zeichen SendCount zurück gegeben 
wird! Das Warten muss/kann die aufrufende Funktion machen, wenn sie den 
Rückgabewert auswertet.

http://read.pudn.com/downloads91/sourcecode/embed/351199/IAR_LPC2148_USB_Demo/modules/LPC_Uart.c__.htm
http://code.google.com/p/lpc-mt-2138/source/browse/trunk/IntModuls/LPC_Uart.h?spec=svn3&r=3
1
typedef struct {
2
  char RxBuf[RXBUFSIZE];
3
  char TxBuf[TXBUFSIZE];
4
  
5
  int RxHeadPoint;
6
  int RxTailPoint;
7
  
8
  int TxHeadPoint;
9
  int TxTailPoint;
10
  
11
  int RxCount;
12
  int TxCount;
13
14
  volatile int RxFlag;
15
} LPC_Uart_Buffer_t;

Die IRQ Sperrung muss selektiv sein. Nur dort wo es nötig ist.
Deine continue Versuche kann ich nicht checken, weil Code fehlt.
Ich würde es in etwa so machen:

[C]
void UART1_PutStgToIntBuf(char *Buf, int len)
{
  char *pBuf = Buf;
  char ch;
  LPC_Uart_Buffer_t *pUartBuf;
  unsigned long IntFlagsHold;

  pUartBuf = &Uart1Config.UartBuffer;

  while (len)
  {
    ch = *pBuf & 0xff;

    IntFlagsHold = disable_IRQ();
    // if FIFO is full pending here, wait for send characters
    if (pUartBuf->TxCount >= TXBUFSIZE)
    {
      restore_IRQ(IntFlagsHold);
      continue;
    }
    pUartBuf->TxBuf[pUartBuf->TxTailPoint] = ch;
    pUartBuf->TxTailPoint = (pUartBuf->TxTailPoint+1)%TXBUFSIZE;
    pUartBuf->TxCount++;
    restore_IRQ(IntFlagsHold);

    // *** Verstehe ich nicht. ***
    // Nur jedes 4. char aus char *Buf in den Software-FIFO schreiben?
    // char *pBuf rattert so u.U. in die Pampa!
    pBuf = pBuf + 4;

    len--;
  }

  // Nur wenn Software-FIFO gefüllt ist dann TX-IRQ einschalten
  IntFlagsHold = disable_IRQ();
  int fifo_filled = pUartBuf->TxCount;
  restore_IRQ(IntFlagsHold);
  if ( fifo_filled )
  {
    U1IER |= IER_THRE;
    // vgl. Code oben:
  }
}
[C]

Man kann verhandeln, ob man IRQs sperrt oder nicht, wenn aus der 
LPC_Uart_Buffer_t Struktur nur gelesen wird.
Ich würde es beim Schreiben und Lesen machen.

von Thomas Haslinger (Gast)


Lesenswert?

Vielen Dank,
jetzt hab ich das Problem einigermaßen im Griff.

Der Code von UART1_PutStgToIntBuf wurde von einem Vorgänger von mir 
geändert.
Deswegen auch dass nur jedes 4. char gesendet wird. Das hat was mit der 
Art der Messwerte die gesendet werden zu tun.

In meinem Programm wurde die UART1_PutStgToIntBuf 8 mal schnell 
hintereinander in einer for-schleife ausgeführt.
Bei jedem UART1_PutStgToIntBuf wird aber sofort ein UART-Interrupt 
ausgeführt, weshalb es überhaupt passieren kann, das die ISR während 
eines neuen UART1_PutStgToIntBuf auftreten kann.

Mittlerweile hab ich meinen Code so geändert, dass ich in der 
for-Schleife nur alle zu sendenden Bytes ansammle und anschließen nur 
einmal die UART1_PutStgToIntBuf ausführe. Es vergeht dann genügend Zeit, 
um alle UART Interrupts auszuführen, bis das nächste mal Werte gesendet 
werden müssen.

Vielen Dank schonmal!

Ist ech ein klasse Forum!

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.