Forum: Mikrocontroller und Digitale Elektronik PIC: UART-Transmitter beendet das Senden nicht


von Master S. (snowman)


Lesenswert?

Hallo

Hardwaremässig kommunizieren (vorerst) ein Master und ein Slave via 
LIN-Topologie miteinander. Protokollmässig weiche ich vom LIN-Standard 
ab (bei mir: Break, Sync (0x55), 10 Bytes Nutzdaten vom Master, dann 15 
Bytes Antwort vom Slave). Das Ganze läuft reibungslos ....bis der Slave 
das Senden nicht terminiert und den Bus ständig auf 'low' zieht.

ps: Der Master sendet noch andere Pakete, welche aber vom Slave 
ignoriert werden. Der Master versucht auch dann weiter zu senden, wenn 
der Bus permanent auf 'low' ist (TX-Pin am Master-uC toggelt).

Im obigen Fall, wenn der Slave die Leitung permanent auf 'low' zieht, 
ist das Registerbit TXSTA->TRMT auf '0', was soviel bedeutet, wie dass 
das UART-Modul das TSR-Byte noch am "raus-shiften" ist. BAUDCON->RCIDL 
ist dann auch auf '0', was soviel heisst, wie dass das UART-Modul am 
Daten empfangen ist. TXSTA->TRMT und BAUDCON->RCIDL können nicht 
beschrieben werden (read-only). Andere Register ändern ihre Werte nicht. 
Das Baudraten-Register hat immer gültige (die richtigen) Werte.

Wenn ich im "nicht-funktionierenden" Betrieb TXSTA->TXEN mit dem 
Debugger auf '0' setze (UART-Transmitter disable), setzt der uC das 
TXSTA->TRMT auf '1' (UART-Modul sendet nicht mehr). Wenn ich dann 
TXSTA->TXEN wieder aktiviere, funktioniert meistens die Verbindung 
wieder, und wenn nicht, haben die Register wieder obige Werte.

Datenblatt PIC16F1509 (Seite 240 oben und Seite 246)
http://ww1.microchip.com/downloads/en/DeviceDoc/41609A.pdf (4.0MB)


Meine Frage: Wie kann es dazu kommen, dass das Senden beim UART nicht 
abgeschlossen wird? Im Datenblatt bin ich nicht fündig geworden (und das 
Errata enthält auch nichts dazu). Oder was könnte sonst die Ursache 
sein?

Vielen Dank für jegliche Hilfe


edit: Bei einem SW-Reset beim Slave, funktioniert die Verbindung wieder.

von W.S. (Gast)


Lesenswert?

Ja was sendet er denn da?

Normalerweise hört das Senden auf, wenn der gesamte TX leergelaufen ist. 
Wenn aber eine Interrupt-Routine auf jeden Interrupt vom TX damit 
reagiert, daß ein neues Byte in den Sender geschoben wird, dann geht das 
eben ewig.

Also:
Wenn das letzte Byte in den TX geschoben worden ist und es nix mehr zu 
senden gibt, dann mußt du eben das zugehörige ..IE Bit rücksetzen. Beim 
nächsten anfallenden Byte setzt du es dann pauschal wieder auf 1

Benutzest du irgendwelche Fremdquellen, die du noch nicht gelesen und 
verstanden hast?

W.S.

von Master S. (snowman)


Lesenswert?

Danke für die schnelle Antwort.

> Ja was sendet er denn da?
Nichts: Bus ist permanent auf 'low'. Ich werde aber noch verifizieren ob 
der TX-pin auch immer 'low' ist. [ edit: Ja, der ist immer auf 'low' ]

> ..dann mußt du eben das zugehörige ..IE Bit rücksetzen.
Sollte so sein (letzte Zeile des ersten Codes).

Meine Funktion, die im Interrupt aufgerufen wird:
1
inline void ISRTransceiveLin() {
2
    unsigned char c, x;
3
    if (PIR1bits.RCIF) {
4
/*** Interrupt Receive ****/
5
        c = RCREG;
6
        if ((PIE1bits.RCIE) && (BAUDCONbits.ABDEN == 0)) {
7
            LinRxBuff[LinRxBuffPos++] = c;
8
            if (LinRxBuffPos == 1) {    // reset 16ms-counter (Timer 0)
9
                TMR0 = 0;
10
                LinIdleCounter = 0;
11
            }
12
            else if (LinRxBuffPos > (ReceiveLength + ReceiveOffstet)) { // receiving finished
13
                PIE1bits.RCIE = 0;
14
                c = 0;
15
                for (x = 1; x < (ReceiveLength + ReceiveOffstet); x++)  // calculate dummy "CRC"
16
                    c += LinRxBuff[x];
17
                if (c == LinRxBuff[ReceiveLength + ReceiveOffstet]) {   // if "CRC" matches
18
                    LED_Orange = 1;
19
                    LinReceptionCorrect = 1;
20
                    TXREG = LinTxBuff[0];
21
                    LinTxBuffPos = 1;
22
                    PIE1bits.TXIE = 1;
23
                    ParseLinReception();
24
                }
25
            }
26
        }
27
    }
28
/*** Interrupt Transmit ****/
29
    else if ((PIR1bits.TXIF) && (LinTxBuffPos < TransmitLength))
30
        TXREG = LinTxBuff[LinTxBuffPos++];
31
    else {
32
        PIE1bits.TXIE = 0;
33
    }
34
}

16ms nach einer Break/Sync-Übertragung wird folgender Code aufgerufen, 
um für das Empfangen einer neuen Übertragung bereit zu sein (Master 
sendet alle 20ms eine neue Übertragung und eine ganze Übertragung geht 
max. 10ms).
1
    LinReceptionCorrect = 0;
2
    LED_Orange = 0;    
3
    for (tmp8a = 0; tmp8a < (ReceiveLength + ReceiveOffstet + 1); tmp8a++)
4
        LinRxBuff[tmp8a] = 0xFF;
5
    PIE1bits.TXIE = 0;
6
    LinRxBuffPos = 0;
7
    tmp8a = RCREG; // clear RCIF
8
    BAUDCONbits.WUE = 1;
9
    BAUDCONbits.ABDEN = 1;
10
    PIE1bits.RCIE = 1;

> Benutzest du irgendwelche Fremdquellen, die du noch nicht gelesen
> und verstanden hast?
nein.

von Max H. (hartl192)


Lesenswert?

Das PIR1bits.RCIF wird nirgends wieder gelöscht...

von Master S. (snowman)


Lesenswert?

"The RCIF interrupt flag bit is read-only, it cannot be set or cleared
by software. [...] The RCIF interrupt flag bit will be set when there is 
an
unread character in the FIFO, regardless of the state of
interrupt enable bits"

hmm, müsste ich zuerst das RCIE deaktivieren (auch den RCSTA->SPEN (Rx 
enable), und dann sicherstellen, dass der Rx-Buffer leer ist? Wäre ja 
echt mühsahm.

edit:
Ich sehe gerade, dass ich 'SPEN' nicht abschalten darf, da dies auch den 
Tx-Pin deaktiviert. Aber genügt denn das Abschalten des Receivers nicht?
c = RCREG;
PIE1bits.RCIE = 0;

von Master S. (snowman)


Lesenswert?

...push...

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.