Forum: Mikrocontroller und Digitale Elektronik Uart und Interrupt


von Tilo (Gast)


Lesenswert?

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

von Karl heinz B. (kbucheg)


Lesenswert?

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.

von Stefan (Gast)


Lesenswert?

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...

von Tilo (Gast)


Lesenswert?

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.

von Tilo (Gast)


Angehängte Dateien:

Lesenswert?

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?

von Stefan (Gast)


Lesenswert?

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.

von Tilo (Gast)


Lesenswert?

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
Noch kein Account? Hier anmelden.