Hallo Ich programmiere einen ADuC7026. Ich kann einen Text mit dieser Funktion ausgeben: int PutCharUart (char ch) { // Schreibe Char auf uart while ((COMSTA0 & 0x40)==0) {} // Warte bis Sendepuffer leer ist if (ch==LF) { // Wandle Zeilenende um COMTX = CR; // Sende Zeichen return 0; } COMTX = ch; // Sende Zeichen return 0; } Leider wartet die Funktion so lange, bis der Sendepuffer leer ist. Daher habe ich mir folgendes überlegt: * Sende das Zeichen, wenn Sendepuffer leer ist * Kopiere das Zeichen in Zwischenspeicher, wenn Sendepuffer voll ist So bald der Sendepuffer wieder leer ist, wird ein Interrupt ausgelöst. In der Interrruptfunktion wird ein Zeichen gesendet. const short int sizeofuartbuffer = 50; // Größe des Array 50==100 Zeichen // Speichere in einem "short int" 2 Zeichen. // -> Positionspuffer beinhaltet 2 Informationen: // posbuffer / 2 == Position im Array // posbuffer % 2 == Position im "short int" short int uarttxbuffer[50]; // Sendepuffer short int readuarttxbuffer; // Lese ab hier Messwerte auf Puffer short int writeuarttxbuffer; // Schreibe ab hier Messwerte in Puffer int PutCharUart (char ch) { if (ch==LF) ch=CR; // Wandele Zeilenende um. // while ((COMSTA0 & 0x40)==0) {} // COMTX = ch; if ((COMSTA0 & 0x40)==1) { // Sendepuffer leer, Sende nächstes Zeichen COMTX = ch; } else { // Kopiere Wert in Puffer writeuarttxbuffer++; writeuarttxbuffer = writeuarttxbuffer % (sizeofuartbuffer*2); int ArrayPos = writeuarttxbuffer / 2; int ShortIntPos = writeuarttxbuffer % 2; int highbyte; int lowbyte; if (ShortIntPos==0) { highbyte = ch << 8; lowbyte = uarttxbuffer[ArrayPos] & 0x00FF; } else { lowbyte = ch; highbyte = uarttxbuffer[ArrayPos] & 0xFF00; } uarttxbuffer[ArrayPos] = highbyte + lowbyte; } return 0; } void IrqUart (void) { if (writeuarttxbuffer!=readuarttxbuffer) { // Zeichen im Sendepuffer if ((COMSTA0 & 0x40)==1) { // Sendepuffer leer, Sende nächstes Zeichen readuarttxbuffer++; readuarttxbuffer = readuarttxbuffer % (sizeofuartbuffer*2); int ArrayPos = readuarttxbuffer / 2; int ShortIntPos = readuarttxbuffer % 2; if (ShortIntPos==0) COMTX = uarttxbuffer[ArrayPos] & 0xFF00; // high byte else COMTX = uarttxbuffer[ArrayPos] & 0x00FF; // low byte } } } void My_IRQ_Function (void) { IrqUart(); if ((IRQSTA & UART_BIT) != 0) { // Uart IrqUart(); } int main(void) { GP1CON |= 0x0011; // Aktiviere uart COMCON0 = 0x080; // Erlaube setzen der Baudrate int cd = (POWCON & 0x07); // aktuelle Taktrate // Formel zur Baudratenbestimmung aus uC Handbuch S. 62 int dl = (1305625 >> cd) / rate; COMDIV0 = (dl & 0x00FF); // Setze Lowbyte COMDIV1 = (dl & 0xFF00); // Setze Highbyte COMCON0 = 0x003; // Verwende 8bit Wörter mit 1 Stopbit // Setze Variablen auf 0, Startbedingung readuarttxbuffer=0; readuartrxbuffer=0; writeuarttxbuffer=0; writeuartrxbuffer=0; COMIEN0 = 0x03; // Aktiviere Interrupt, wenn RX oder TX Puffer voll for (int i=0; i<50; i++) PutCharUart('X'); } Leider erhalte ich gar keine Ausgabe mehr. Woran könnte das liegen? Vielen Dank, Tilo
Gibt es einen technischen Grund, warum du den Buffer so kompliziert mit unsigned shorts aufbaust? Warum nicht ein Array aus unsigned char. Dann würde dir mal geschätzte 60 bis 70% Code von der Sendefunktion wegfallen, was die Fehlerwahrscheinlichkeit drücken würde. Ich bin den Code nicht im Detail durchgegangen, aber das ist mir eher unangenehm aufgefallen.
main() sollte nicht enden. Dein Hauptprogramm ist vielleicht schon fertig und irgendwo im Startupcode gelandet bevor der Interrupt zum Senden kommt. Wo klinkst du deine UART-IRQ Funktion ein, so dass sie auch aufgerufen werden kann? Bei den AVR kenne ich die ISR(vektorname) Schreibweise. Bei anderen µC gibt es vielleicht eine "processorAicAttachSys()" (AT91, Procyon Armlib) oder "processorVicAttach" (LPC2000, Procyon Armlib), um die Funktion in der Vektortabelle einzutragen oder man muss die Routine im Startupcode oder im Linkercontrolskript eintragen...
Es gibt keinen Grund dafür. Ich denke nur oft viel zu kompliziert. :) Ich habe den Code wesentlich vereinfacht und versucht das Problem einzukreisen. Meine Interruptroutine wird nie ausgeführt. Klar, dass ich dann auch keine Ausgabe habe. Da ich noch Anfänger bin, bin ich gerade etwas überfordert. Meine IRQ-Funktion selbst wird bei Interrupts ausgeführt. In der Funktion muss ich selbst herausfinden, wer den Interrupt ausgelöst hat. Mit Timer2 funktioniert das ganze: void My_IRQ_Function (void) { if ((IRQSTA & WAKEUP_TIMER_BIT) != 0) { // Timer2? [...] } } Genau nach diesem Schema wollte ich es beim Uart machen: void My_IRQ_Function (void) { if ((IRQSTA & UART_BIT) != 0) { // Uart [...] } if ((IRQSTA & WAKEUP_TIMER_BIT) != 0) { // Timer2? [...] } } Laut Handbuch zu meinem uC muss im Register COMIEN0 einstellen, bei welchen Ereignissen ein Interrupt ausgelöst werden soll. Bei COMIEN0 = 0x03; sollte ein Interrupt ausgelöst werden, wenn der Sendepuffer leer oder der Empfangspuffer voll ist. Zum Testen habe ich mit HyperTerm mehrere Chars an den uC geschickt. Er hat darauf nicht reagiert. Die Beispielcodes die ich von Analog habe, warten einfach nur, bis COMTX wieder beschreibbar ist.
Stefan wrote: > main() sollte nicht enden. Dein Hauptprogramm ist vielleicht schon > fertig und irgendwo im Startupcode gelandet bevor der Interrupt zum > Senden kommt. Du hast Recht. Das kommt davon, wenn man die relevanten Teile aus einem vorhandenen Programm herauskopiert. main beendet sich im richtigen Programm nciht. > Wo klinkst du deine UART-IRQ Funktion ein, so dass sie auch aufgerufen > werden kann? Es gibt bei meinem ARM nur eine IRQ-Funktion, in der ich selbst herausfinden muss, wer den IRQ ausgelöst hat. Das ganze hab ich mit Timer2 hinbekommen aber leider nicht mit Uart. > Bei den AVR kenne ich die ISR(vektorname) Schreibweise. Bei anderen µC > gibt es vielleicht eine "processorAicAttachSys()" (AT91, Procyon Armlib) > oder "processorVicAttach" (LPC2000, Procyon Armlib), um die Funktion in > der Vektortabelle einzutragen oder man muss die Routine im Startupcode > oder im Linkercontrolskript eintragen... Ich hab in meinem Hauptprogramm nur meine Funktion zugewiesen: IRQ = My_IRQ_Handler; [...] void My_IRQ_Handler(void) { [...] } Was ich vergessen habe, den uart in das Register mit den erlaubten IRQs einzutragen. Das habe ich mit folgendem Befehl nachgeholt: IRQEN = IRQEN | UART_BIT; Der IRQ wird aber immer noch nicht ausgelöst?
Das hört sich doch schon gut an. Ich würde jetzt mit Debuggen weitermachen bzw. intensiv das Datenblatt wälzen. Zur Hardwareunterstützung würde ich eine Abfrage auf alle möglichen Interruptquellen einbauen und den Zustand jeweils auf einen Ausgabepin geben. Dort kann man seine Tests luxuriös mit Logikanalyzer oder Oszi oder spartanisch mit ein paar LEDs + Treiber überwachen. "Zum Testen habe ich mit HyperTerm mehrere Chars an den uC geschickt. Er hat darauf nicht reagiert." ;-) In dem Code oben sehe ich nichts, was etwas mit RX (empfangene Zeichen) macht. Da ist nur TX Senden behandelt.
Ich hab das Problem gefunden. Ich sollte natürlich nur dann den IRQ für einen leeren TX-Buffer aktivieren, wenn in meinem Puffer Daten sind. Vielen Dank für die Hilfe
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.