www.mikrocontroller.net

Forum: Mikrocontroller und Digitale Elektronik Uart und Interrupt


Autor: Tilo L. (katagia)
Datum:

Bewertung
0 lesenswert
nicht 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

Autor: Karl heinz Buchegger (kbucheg)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: Stefan (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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...

Autor: Tilo L. (katagia)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: Tilo L. (katagia)
Datum:
Angehängte Dateien:

Bewertung
0 lesenswert
nicht 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?

Autor: Stefan (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: Tilo L. (katagia)
Datum:

Bewertung
0 lesenswert
nicht 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

Antwort schreiben

Die Angabe einer E-Mail-Adresse ist freiwillig. Wenn Sie automatisch per E-Mail über Antworten auf Ihren Beitrag informiert werden möchten, melden Sie sich bitte an.

Wichtige Regeln - erst lesen, dann posten!

  • Groß- und Kleinschreibung verwenden
  • Längeren Sourcecode nicht im Text einfügen, sondern als Dateianhang

Formatierung (mehr Informationen...)

  • [c]C-Code[/c]
  • [avrasm]AVR-Assembler-Code[/avrasm]
  • [code]Code in anderen Sprachen, ASCII-Zeichnungen[/code]
  • [math]Formel in LaTeX-Syntax[/math]
  • [[Titel]] - Link zu Artikel
  • Verweis auf anderen Beitrag einfügen: Rechtsklick auf Beitragstitel,
    "Adresse kopieren", und in den Text einfügen




Bild automatisch verkleinern, falls nötig
Bitte das JPG-Format nur für Fotos und Scans verwenden!
Zeichnungen und Screenshots im PNG- oder
GIF-Format hochladen. Siehe Bildformate.
Hinweis: der ursprüngliche Beitrag ist mehr als 6 Monate alt.
Bitte hier nur auf die ursprüngliche Frage antworten,
für neue Fragen einen neuen Beitrag erstellen.

Mit dem Abschicken bestätigst du, die Nutzungsbedingungen anzuerkennen.