Forum: Mikrocontroller und Digitale Elektronik PIC18 : Problem mit serieller Kommunikation UART mittels Interrupt


von netfriend (Gast)


Angehängte Dateien:

Lesenswert?

Hallo,

ich habe einen PIC18F2685 vorliegen, der u.a. seriell kommunizieren 
kann.
Die serielle Kommunikation ist über Interrupts gelöst. Mein Problem 
besteht aktuell darin, dass nach Senden des Stopbits des letzten Byte 
die TX-Leitung nochmal kurz (ca. 10 µs) auf 0 gezogen wird, bevor diese 
in Ruhelage = 1 geht.
Nachfolgende Schaltungen (MAX232) geben diese weiter, und der Empfänger 
hat damit ein Problem. Daher habe ich mich auf die Suche gemacht, woher 
dieses Verhalten kommt. Andere PICs, die ich kenne (PIC16) haben dieses 
Verhalten nicht.

Ich vermute, dass es vielleicht ein Reihenfolgenproblem mit dem 
Interrupt oder dem Setzen von den notwendigen Registern ist.

Ich versuche, den wesentlichen Teil der Kommunikation herauszulösen, um 
das nachvollziehbar darzustellen:


void interruptGlobalLow()
{
  //rs232 byte send
  if(TXIF)
  {
    rs232Com_check();
    TXIF = 0;
  }
}


void rs232_Init()
{
  TRISC=0b.1000.0011;

  TXIE = 0;
  RCIE = 0;
  CREN = 0;
  SPEN = 0;
  SYNC = 0;
  BRGH = 1;
  BRG16 = 1;
  ADDEN=0;
  ABDEN=0;
  WUE=0;

  RCIF=TXIF=0; // clear interrupt flags

  //Baudrateneinstellung (9600,8,'n',1)
  SPBRGH=4;
  SPBRG=16;

  RX9 = 0; // no 9th bit (parity)
        TX9 = 0;

  TXIE = 1;
  RCIE = 1;
  CREN = 1;
  RCIF=TXIF=0;
  SPEN = 1;

  TXIP = RCIP = 0; // low interrupts
  RCIF=TXIF=0;
}


void rs232Com_check()
{
  if (rs232_sending==0)
    return;

  if (rs232_inxSendBuffer>rs232_sizeSendBuffer)
  {
    rs232_sending=0;
    TXEN = 0;
  }
  else
  {
    uns8 send = rs232_sendBuffer[rs232_inxSendBuffer];
    ++rs232_inxSendBuffer;
    send_with_parity( send );
  }
}

rs232Com_StartSend()
{
  // rs232_inxSendBuffer = Index des Sendepuffers
  // rs232_sendBuffer = Sendepuffer, wird woanders gefüllt und es ist 
sichergestellt, dass dieser zu dieser Zeit auf gefüllt ist

  rs232_sizeSendBuffer = rs232_inxSendBuffer;
        rs232_inxSendBuffer=1;
        rs232_sending=1;

        // start send first byte
  TXEN = 1;
  TXREG = rs232_sendBuffer[0];
}


void main()
{


  while(1)
  {

    if(rs232_sending == 0)
    {
      rs232Com_StartSend();
    }


  }

}

Anbei noch 2 Screenshots der Oszi-Kommunikation. Letztes Byte ist 0xEF, 
danach sieht man den negativen Peak, der meiner Meinung hier nicht 
hergehört.

Hat jemand ne Idee, woran das liegen kann. Ich habe mir das Datenblatt 
schon mehrfach angeschaut, komme aber aktuell nicht weiter.

Vielen Dank.

:
von Stefan (netfriend)


Lesenswert?

Es hat sich im zusammenkopieren des Quellcodes noch ein Fehler 
eingeschlichen. Nachdem ich keinen "Editieren"-Button gefunden habe, 
hier nochmal der aktuelle Stand:

void interruptGlobalLow()
{
  //rs232 byte send
  if(TXIF)
  {
    rs232Com_check();
    TXIF = 0;
  }
}


void rs232_Init()
{
  TRISC=0b.1000.0011;

  TXIE = 0;
  RCIE = 0;
  CREN = 0;
  SPEN = 0;
  SYNC = 0;
  BRGH = 1;
  BRG16 = 1;
  ADDEN=0;
  ABDEN=0;
  WUE=0;

  RCIF=TXIF=0; // clear interrupt flags

  //Baudrateneinstellung (9600,8,'n',1)
  SPBRGH=4;
  SPBRG=16;

  RX9 = 0; // no 9th bit (parity)
    TX9 = 0;

  TXIE = 1;
  RCIE = 1;
  CREN = 1;
  RCIF=TXIF=0;
  SPEN = 1;

  TXIP = RCIP = 0; // low interrupts
  RCIF=TXIF=0;
}

void rs232_send(uns8 byte)
{
  TXEN = 1;
  TXREG = byte;
}


void rs232Com_check()
{
  if (rs232_sending==0)
    return;

  if (rs232_inxSendBuffer>rs232_sizeSendBuffer)
  {
    rs232_sending=0;
    TXEN = 0;
  }
  else
  {
    uns8 send = rs232_sendBuffer[rs232_inxSendBuffer];
    ++rs232_inxSendBuffer;
    rs232_send( send );
  }
}

rs232Com_StartSend()
{
  // rs232_inxSendBuffer = Index des Sendepuffers
  // rs232_sendBuffer = Sendepuffer, wird woanders gefüllt und es ist 
sichergestellt
  // dass dieser zu dieser Zeit auf gefüllt ist

  rs232_sizeSendBuffer = rs232_inxSendBuffer;
    rs232_inxSendBuffer=1;
    rs232_sending=1;

    // start send first byte
  rs232_send(rs232_sendBuffer[0]);
}


void main()
{

  while(1)
  {

    if(rs232_sending == 0)
    {
      rs232Com_StartSend();
    }


  }

}

von Volker S. (vloki)


Lesenswert?

Vielleicht liegt es daran, dass der Transmitter komplett abgeschaltet 
wird (TXEN = 0). Hast du schon mal probiert, das über das TXIE zu lösen?

von Stefan (netfriend)


Lesenswert?

Ja, das hatte ich auch schon probiert.
Wenn ich TXIE nutze, sehe ich schon vor dem Senden mehrmals das TXIF=1 
gesetzt wird. Auch nach dem Senden wird TXIF=1 sehr oft gesetzt, obwohl 
keine Kommunikation mehr stattfindet.

Beitrag #6155633 wurde von einem Moderator gelöscht.
Beitrag #6155634 wurde von einem Moderator gelöscht.
Beitrag #6155638 wurde von einem Moderator gelöscht.
Beitrag #6155639 wurde von einem Moderator gelöscht.
Beitrag #6155642 wurde von einem Moderator gelöscht.
Beitrag #6155645 wurde von einem Moderator gelöscht.
Beitrag #6155647 wurde von einem Moderator gelöscht.
Beitrag #6155653 wurde von einem Moderator gelöscht.
Beitrag #6155659 wurde von einem Moderator gelöscht.
Beitrag #6155666 wurde von einem Moderator gelöscht.
Beitrag #6155674 wurde von einem Moderator gelöscht.
Beitrag #6155677 wurde von einem Moderator gelöscht.
Beitrag #6155679 wurde von einem Moderator gelöscht.
von Volker S. (vloki)


Lesenswert?

Net F. schrieb:
> Auch nach dem Senden wird TXIF=1 sehr oft gesetzt, obwohl
> keine Kommunikation mehr stattfindet

Genau dafür ist es ja da.
(TXIF zeigt an, dass das TXREG frei ist und mit einem neuen Byte befüllt 
werden kann)

Wenn mehrere Bytes gesendet werden sollen, dann schiebt man das erste in 
TXREG und aktiviert den Interrupt mit TXEN = 1. Sobald TXREG frei wird, 
wird der IR ausgelöst und in diesem schiebt man das nächste Byte in 
TXREG. Handelt es sich dabei um das letzte zu übertragende Byte, dann 
deaktiviert man den IR wieder (TXIE = 0)

von kyrk (Gast)


Lesenswert?

if(TXIF)

sollte eher
if ((TXIF) && (TXIE))


prinzipiell solltest du den TXIF flag meiner Meinung nach nicht löschen. 
Sondern eher:
- Im Interrupt Daten schicken
- Wenn es keine Daten mehr gibt dann den TXIE auf 0 setzen

Daten verschicken sollte über einen Ringbuffer gehen. Versenden guckt ob 
der Transmitt schon läuft, wenn ja dann daten richtung Ringbuffer 
schreiben, wenn nicht, dann daten ins TX register schreiben.

void putCharRS232(unsigned char data) {
  if (ringBuffer_addItem(&myRingBuffer_rs232_tx, data) != -1) {
    if (PIE1bits.TXIE == 0) {
      PIE1bits.TXIE = 1;
    }
  } else {
    missedTxCharRS232++;
  }
}


int isrSerialTx(void) {
  unsigned char data = 0;
  if (ringBuffer_getItem(&myRingBuffer_rs232_tx, &data) != -1) {
    TXREG = data;
  } else {
    PIE1bits.TXIE = 0;
  }
  return 0;
}

Beitrag #6155684 wurde von einem Moderator gelöscht.
Beitrag #6155688 wurde von einem Moderator gelöscht.
Beitrag #6155699 wurde von einem Moderator gelöscht.
Beitrag #6155700 wurde von einem Moderator gelöscht.
von Volker S. (vloki)


Lesenswert?

kyrk schrieb:
> sollte eher
> if ((TXIF) && (TXIE))
>
> prinzipiell solltest du den TXIF flag meiner Meinung nach nicht löschen.

Guter Einwand mit dem zusätzlichen TXIE. Mache ich auch immer, damit ich 
es nicht vergesse, falls ein zweiter IR in diesem Vektor behandelt 
werden soll.

TXIF ist übrigens read-only und kann nicht gelöscht werden.
Es geht automatisch auf 0, wenn das TXREG beschrieben wird und auch 
automatisch wieder auf 1, sobald TXREG frei wird.

Beitrag #6155703 wurde von einem Moderator gelöscht.
Beitrag #6155704 wurde von einem Moderator gelöscht.
Beitrag #6155707 wurde von einem Moderator gelöscht.
Beitrag #6155709 wurde von einem Moderator gelöscht.
von Stefan (netfriend)


Lesenswert?

Volker S. schrieb:
> kyrk schrieb:
>> sollte eher
>> if ((TXIF) && (TXIE))
>>
>> prinzipiell solltest du den TXIF flag meiner Meinung nach nicht löschen.
>
> Guter Einwand mit dem zusätzlichen TXIE. Mache ich auch immer, damit ich
> es nicht vergesse, falls ein zweiter IR in diesem Vektor behandelt
> werden soll.
>
> TXIF ist übrigens read-only und kann nicht gelöscht werden.
> Es geht automatisch auf 0, wenn das TXREG beschrieben wird und auch
> automatisch wieder auf 1, sobald TXREG frei wird.

Dass TXIF read-only ist, hatte ich in der Doku gelesen. Allerdings ist 
der Quellcode nicht von mir, den hatte ich geerbt.
Ich war der Meinung, dass der TXIF-Interrupt nur gerufen wird, wenn der 
Sendepuffer wieder frei ist. Ich hatte wie hier schon mehrfach 
geschrieben, nachdem ich das letzte Byte in TXREG geschoben hatte, 
TXIE=0 gesetzt.
Trotzdem bin ich mehrfach in den TXIF-Interrupt gekommen.
Mit der zusätzlichen if ((TXIF) && (TXIE)) - Abfrage sieht das Ganze 
besser aus.
Danke für den Tipp!

von Volker S. (vloki)


Lesenswert?

Net F. schrieb:
> Trotzdem bin ich mehrfach in den TXIF-Interrupt gekommen.
> Mit der zusätzlichen if ((TXIF) && (TXIE)) - Abfrage sieht das Ganze
> besser aus.

Dann gibt es da aber evtl. ein anderes Problem!

Der IR wird wohl noch von einer anderen Quelle ausgelöst und das ist 
dann ganz schlecht, weil er ja nicht behandelt wird. (der kommt immer 
wieder)

Zum Testen einfach eine Endlosschleife in die ISR:
1
void interruptGlobalLow()
2
{
3
  //rs232 byte send
4
  if(PIE1bits.TXIE && PIR1bits.TXIF)
5
  {
6
    ...
7
    return;
8
  }
9
  while(1){
10
      Nop(); // ??? Breakpoint ???
11
  }
12
}
Falls das Programm da rein läuft, muss du die Ursache dafür finden.

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.