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?
> 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).
> 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.
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?
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; }
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.
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
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.