PIC18 : Problem mit serieller Kommunikation UART mittels Interrupt

Gast #6151860
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.
Angehängte Dateien:
#6151898
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();
    }


  }

}
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.
#6155680
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)
Gast #6155682
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.
#6155702
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.
#6155885
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!
#6156062
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.

Antwort schreiben

Bitte melde dich an, um einen Beitrag zu schreiben.

oder

Mit Google-Account einloggen

Die Registrierung ist kostenlos und dauert nur eine Minute.

Jetzt registrieren