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;
}
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!