Forum: Mikrocontroller und Digitale Elektronik PIC12F1572: UART TRMT vs. TXIF - Bytes sporadisch doppelt übertragen?!


von Christoph S. (mcseven)


Lesenswert?

Grüße Forum,

mein kleines LED-Projekt besteht aus einem Haufen PIC12F1572, die mit 
ihrem UART ge-daisy-chained sind und dem WS2812-Protokoll folgen: 6 
Bytes rein, nimm 3 für RGB und schiebe 3 raus. Repeat. Synchronisation 
zusätzlich über TMR2, der nach 1ms UART-Inaktivität einen Interrupt 
bekommt. ISR siehe Code.

Leider passiert es etwa ab der vierten Lampe, daß Bytes sporadisch 2x 
gesendet werden. Zumindest, wenn ich TXIF abfrage.

Warte ich tatsächlich, bis TRMT die Leere des TSR signalisiert, läuft's 
bombig; leider aber nur mit 10 Lampen, bei 11 hängt sich die erste auf, 
weil der UART RX FIFO überläuft.

Kurze Suche förderte zutage:
* http://www.microchip.com/forums/m829947.aspx
* http://www.microchip.com/forums/m608851.aspx

Hat jemand mal das gleiche Problem gehabt und eventuell einen 
Work-Around? 12% Flash sind noch frei...

Danke.

1
#include <stdio.h>
2
#include <stdlib.h>
3
#include "mcc_generated_files/mcc.h"
4
5
void TMR2_ISR(void);
6
7
   const uint16_t gammaTable_red[256]   = {0, 3, 11, 22, 37, 55, 77, 101, 129, 159, 193, 229, 267, 309, 353, 400, 449, 501, 555, 612, 671, 732, 796, 863, 931, 1002, 1076, 1151, 1229, 1309, 1392, 1476, 1563, 1652, 1743, 1837, 1932, 2030, 2130, 2232, 2336, 2442, 2550, 2660, 2773, 2887, 3004, 3122, 3243, 3366, 3490, 3617, 3745, 3876, 4009, 4143, 4280, 4419, 4559, 4701, 4846, 4992, 5141, 5291, 5443, 5597, 5753, 5911, 6070, 6232, 6396, 6561, 6728, 6897, 7068, 7241, 7416, 7592, 7771, 7951, 8133, 8317, 8503, 8690, 8880, 9071, 9264, 9459, 9655, 9854, 10054, 10256, 10460, 10665, 10873, 11082, 11292, 11505, 11719, 11936, 12153, 12373, 12595, 12818, 13043, 13269, 13497, 13728, 13959, 14193, 14428, 14665, 14904, 15144, 15386, 15630, 15875, 16123, 16372, 16622, 16874, 17128, 17384, 17641, 17900, 18161, 18423, 18687, 18953, 19220, 19489, 19760, 20032, 20306, 20582, 20859, 21138, 21419, 21701, 21985, 22271, 22558, 22847, 23137, 23429, 23723, 24018, 24315, 24613, 24914, 25215, 25519, 25824, 26130, 26439, 26748, 27060, 27373, 27687, 28004, 28322, 28641, 28962, 29285, 29609, 29935, 30262, 30591, 30921, 31253, 31587, 31922, 32259, 32597, 32937, 33279, 33622, 33967, 34313, 34661, 35010, 35361, 35713, 36067, 36423, 36780, 37138, 37499, 37860, 38224, 38588, 38955, 39323, 39692, 40063, 40436, 40810, 41185, 41562, 41941, 42321, 42703, 43086, 43470, 43857, 44244, 44634, 45024, 45417, 45810, 46206, 46603, 47001, 47401, 47802, 48205, 48609, 49015, 49422, 49831, 50241, 50653, 51067, 51481, 51898, 52315, 52735, 53155, 53578, 54001, 54427, 54853, 55281, 55711, 56142, 56575, 57009, 57444, 57881, 58320, 58760, 59201, 59644, 60089, 60534, 60982, 61431, 61881, 62332, 62786, 63240, 63696, 64154, 64613, 65073, 65535 };
8
   const uint16_t gammaTable_green[256] = {0, 3, 11, 22, 37, 55, 77, 101, 129, 159, 193, 229, 267, 309, 353, 400, 449, 501, 555, 612, 671, 732, 796, 863, 931, 1002, 1076, 1151, 1229, 1309, 1392, 1476, 1563, 1652, 1743, 1837, 1932, 2030, 2130, 2232, 2336, 2442, 2550, 2660, 2773, 2887, 3004, 3122, 3243, 3366, 3490, 3617, 3745, 3876, 4009, 4143, 4280, 4419, 4559, 4701, 4846, 4992, 5141, 5291, 5443, 5597, 5753, 5911, 6070, 6232, 6396, 6561, 6728, 6897, 7068, 7241, 7416, 7592, 7771, 7951, 8133, 8317, 8503, 8690, 8880, 9071, 9264, 9459, 9655, 9854, 10054, 10256, 10460, 10665, 10873, 11082, 11292, 11505, 11719, 11936, 12153, 12373, 12595, 12818, 13043, 13269, 13497, 13728, 13959, 14193, 14428, 14665, 14904, 15144, 15386, 15630, 15875, 16123, 16372, 16622, 16874, 17128, 17384, 17641, 17900, 18161, 18423, 18687, 18953, 19220, 19489, 19760, 20032, 20306, 20582, 20859, 21138, 21419, 21701, 21985, 22271, 22558, 22847, 23137, 23429, 23723, 24018, 24315, 24613, 24914, 25215, 25519, 25824, 26130, 26439, 26748, 27060, 27373, 27687, 28004, 28322, 28641, 28962, 29285, 29609, 29935, 30262, 30591, 30921, 31253, 31587, 31922, 32259, 32597, 32937, 33279, 33622, 33967, 34313, 34661, 35010, 35361, 35713, 36067, 36423, 36780, 37138, 37499, 37860, 38224, 38588, 38955, 39323, 39692, 40063, 40436, 40810, 41185, 41562, 41941, 42321, 42703, 43086, 43470, 43857, 44244, 44634, 45024, 45417, 45810, 46206, 46603, 47001, 47401, 47802, 48205, 48609, 49015, 49422, 49831, 50241, 50653, 51067, 51481, 51898, 52315, 52735, 53155, 53578, 54001, 54427, 54853, 55281, 55711, 56142, 56575, 57009, 57444, 57881, 58320, 58760, 59201, 59644, 60089, 60534, 60982, 61431, 61881, 62332, 62786, 63240, 63696, 64154, 64613, 65073, 65535 };
9
   const uint16_t gammaTable_blue[256]  = {0, 3, 11, 22, 37, 55, 77, 101, 129, 159, 193, 229, 267, 309, 353, 400, 449, 501, 555, 612, 671, 732, 796, 863, 931, 1002, 1076, 1151, 1229, 1309, 1392, 1476, 1563, 1652, 1743, 1837, 1932, 2030, 2130, 2232, 2336, 2442, 2550, 2660, 2773, 2887, 3004, 3122, 3243, 3366, 3490, 3617, 3745, 3876, 4009, 4143, 4280, 4419, 4559, 4701, 4846, 4992, 5141, 5291, 5443, 5597, 5753, 5911, 6070, 6232, 6396, 6561, 6728, 6897, 7068, 7241, 7416, 7592, 7771, 7951, 8133, 8317, 8503, 8690, 8880, 9071, 9264, 9459, 9655, 9854, 10054, 10256, 10460, 10665, 10873, 11082, 11292, 11505, 11719, 11936, 12153, 12373, 12595, 12818, 13043, 13269, 13497, 13728, 13959, 14193, 14428, 14665, 14904, 15144, 15386, 15630, 15875, 16123, 16372, 16622, 16874, 17128, 17384, 17641, 17900, 18161, 18423, 18687, 18953, 19220, 19489, 19760, 20032, 20306, 20582, 20859, 21138, 21419, 21701, 21985, 22271, 22558, 22847, 23137, 23429, 23723, 24018, 24315, 24613, 24914, 25215, 25519, 25824, 26130, 26439, 26748, 27060, 27373, 27687, 28004, 28322, 28641, 28962, 29285, 29609, 29935, 30262, 30591, 30921, 31253, 31587, 31922, 32259, 32597, 32937, 33279, 33622, 33967, 34313, 34661, 35010, 35361, 35713, 36067, 36423, 36780, 37138, 37499, 37860, 38224, 38588, 38955, 39323, 39692, 40063, 40436, 40810, 41185, 41562, 41941, 42321, 42703, 43086, 43470, 43857, 44244, 44634, 45024, 45417, 45810, 46206, 46603, 47001, 47401, 47802, 48205, 48609, 49015, 49422, 49831, 50241, 50653, 51067, 51481, 51898, 52315, 52735, 53155, 53578, 54001, 54427, 54853, 55281, 55711, 56142, 56575, 57009, 57444, 57881, 58320, 58760, 59201, 59644, 60089, 60534, 60982, 61431, 61881, 62332, 62786, 63240, 63696, 64154, 64613, 65073, 65535 };
10
volatile  uint8_t byteCounter           = 0;
11
volatile  uint8_t byteRead              = 0;
12
13
/*
14
 * 
15
 */
16
int main(int argc, char** argv) {
17
    SYSTEM_Initialize();
18
    INTCONbits.GIE  = 1; // Enable global interrupts
19
    INTCONbits.PEIE = 1; // Enable Peripheral Interrupts
20
    
21
    while (1) {
22
        // Did we receive a byte?
23
        while (!PIR1bits.RCIF) {}
24
        // Yes, let's handle the received byte
25
        byteRead = RCREG; //RCREG;
26
        // Increment byte counter...
27
        byteCounter++;
28
        // Clear timer counter
29
        TMR2=0;
30
        // Start timer to find end of a UART-frame by 1000us delay...
31
        T2CONbits.TMR2ON = 1;
32
        // Are we in forward-only mode? byte 0,1,2 is for us, all following bytes we pipe through!
33
        if (byteCounter>3) {
34
            // Fourth and all following bytes forward as is even on error...
35
            byteCounter--; // Keep at 3
36
            // Wait for TSR to become empty. Polling PIR1bits.TXIF was NOT enough.
37
            // Received characters twice often!
38
            while (0 == TXSTAbits.TRMT) {}
39
            // Forward Byte to next light...
40
            TXREG = byteRead;
41
            // and repeat.
42
            continue;
43
        }
44
        if (RCSTAbits.OERR||RCSTAbits.FERR) continue; // Don't use on error
45
        // Read LUT value from flash
46
        if (byteCounter==1) {
47
            // First byte of frame, apply and continue
48
            uint16_t lutValue = gammaTable_red[byteRead];
49
            PWM2DCH = (lutValue>>8);  //writing 8 MSBs to PWMPRH register
50
            PWM2DCL = (lutValue);
51
            PWM2LDCONbits.LDA = 1;
52
            continue;
53
        }
54
        if (byteCounter==2) {
55
            // Second byte of frame, apply and continue
56
            uint16_t lutValue = gammaTable_green[byteRead];
57
            PWM1DCH = (lutValue>>8);  //writing 8 MSBs to PWMPRH register
58
            PWM1DCL = (lutValue);
59
            PWM1LDCONbits.LDA = 1;
60
            continue;
61
        } 
62
        // Third byte of frame, apply and continue
63
        uint16_t lutValue = gammaTable_blue[byteRead];
64
        PWM3DCH = (lutValue>>8);  //writing 8 MSBs to PWMPRH register
65
        PWM3DCL = (lutValue);
66
        PWM3LDCONbits.LDA = 1;
67
    // */
68
    }
69
}
70
71
void TMR2_ISR(void) {
72
    // clear the TMR2 interrupt flag
73
    PIR1bits.TMR2IF = 0;
74
    // Stop the Timer by writing to TMRxON bit
75
    T2CONbits.TMR2ON = 0;
76
    // Reset the timer
77
    TMR2=0;
78
    // Reset the byte counter
79
    byteCounter=0;
80
}

: Bearbeitet durch User
von Christoph S. (mcseven)


Lesenswert?

In den Errata für andere Chips hab ich das hier gefunden:

> === Module: EUSART ===
> ======================
> In rare situations, one or more extra zero bytes have been observed
> in a packet transmitted by the module operating in Asynchronous mode.
> The actual data is not lost or corrupted; only unwanted (extra) zero
> bytes are observed in the packet.

> This situation has only been observed when the contents of the
> transmit buffer, TXREG, are transferred to the TSR during the
> transmission of a Stop bit. For this to occur, three things must
> happen in the same instruction cycle:
> • TXREG is written to;
> • the baud rate counter overflows (at the end of the bit period); and
> • a Stop bit is being transmitted (shifted out of TSR).

Work-Around 1
>> If possible, do not use the module’s double-buffer capability. Instead,
>> load the TXREG register when the TRMT bit is set, indicating the TSR
>> is empty.

Das scheint zu funktionieren, kostet aber natürlich ein bißchen Zeit.
Kommt nun ein kontinuierlicher Datenstrom herein, hängt sich mit obigem
Code die erste Lampe irgendwann auf ("RX Overflow"), man müßte den RX
aus- und wieder einschalten.

>> If double-buffering is used and back-to-back transmission is
>> performed, then load TXREG immediately after TXIF is set, or wait
>> 1-bit time after TXIF is set. Both solutions prevent writing TXREG
>> while a Stop bit is transmitted. Note that TXIF is set at the
>> beginning of the Stop bit transmission.

Hmmmm, das sollte ja im Interrupt-basierten Betrieb funktionieren. In
meinem Falle aber nicht, weil ja der Datenstrom von der vorigen Lampe
abhängt. In den hinteren ist der Jitter dann so groß, daß das nächste
Byte nicht rechtzeitig zur Verfügung steht und dann gehts schief.

Work-Around 2
>> Use a free timer resource to time the baud period.
>> Set up the timer to overflow at the end of Stop bit, then start the
>> timer when you load the TXREG. Do not load the TXREG when timer is
>> about to overflow.

Klingt auch machbar, hab ich nicht probiert.

Meine Lösung
Ich warte jetzt tatsächlich auf TRMT, aber nutze die RX-Ringbuffer-
Implementierung von Microchip. Damit kann ich bis zu 50 RGB-Lampen
hintereinanderschalten, das sollte reichen. Mehr Strom kann die Leitung
eh nicht. Chip zu 96% voll. RAM bei 72%.

von neuer PIC Freund (Gast)


Lesenswert?

Im Errata deines Chips hab ich das hier gefunden:

Work around

When transmitting bytes, it is common practice to
check the TXIF bit before writing to the TXREG
register. To avoid the issue of duplicate bytes
being transmitted, a NOP should be placed before
the write to the TXREG register. This changes the
timing so that the issue does not occur. The TRMT
bit can also be checked in addition to or instead of
the TXIF bit to determine if TXREG can be written
without causing a duplicate-byte transmission. If
the transmit interrupt is enabled then, inside the
ISR, testing the TRMT bit will avoid transmission of
a duplicate byte.

von Christoph S. (mcseven)


Lesenswert?

Hi,

Danke Dir. Wo hast du denn die Errata gefunden? Sind die speziell für 
den 12F1572? Würdest Du den (PDF-)Link posten?

Und, falls Du das hier meinst (#1):
===================================
1
            // Received characters twice often!
2
            while (0 == PIR1bits.TXIF) {}
3
            // Questionable nop instruction?!
4
            asm("nop");
5
            // Forward Byte to next light...
6
            TXREG = byteRead;

hat reproduzierbar nicht funktioniert. Wie auch, das eine popelige NOP 
hat eine Execution time von 0,03µs gegen die Stop-Bit-Dauer von 52µs; 
das bringt also afaict nichts...

Und, falls Du das hier meinst (#2):
===================================
Interrupt-Betrieb für TX hat leider auch nichts gebracht, weil eben bei 
den hinteren Lampen, so ab #5 oder #6 der Jitter der Zeiten zwischen den 
einzelnen Bytes schon so groß ist, daß die Fehlerbedingung eben 
sporadisch eintritt. Und dann ist es egal, ob ich busy-waite oder eben 
interrupt-getrieben in die Problemzone gerate.

Leider ist auch die Bitdauer bei 19k2 mit eben 52µs im Verhältnis zur 
T_cy "sehr" lang; damit renne ich potentiell wahrscheinlicher in die 
Fehlervoraussetzungen hinein, nicht wahr...

von Volker S. (vloki)


Lesenswert?

Die Errata sind bei MCHP auf der gleichen Seite wie die Datenblätter. 
Genau darunter!

Das Problem tritt vermutlich verstärkt auf, weil bei deiner Kette der 
Abstand immer genau passt.
Du könntest testen was passiert, wenn die Daten mit 2 Stopbits 
losgeschickt werden.

Die Baudrate sollte eigentlich nur am Rande eine Rolle spielen, da das 
Problem laut Errata auftritt, wenn genau in dem Takt das TXREG 
beschrieben wird, wenn das Schriftregister leer wird.
Ein Nop() wird wegen dem Jitter wohl zu wenig sein. Eine längere 
Wartezeit (viele Nop() ) könnten helfen aus dem kritischen Bereich 
heraus zu kommen.

: Bearbeitet durch User
von Christoph S. (mcseven)


Lesenswert?

Volker S. schrieb:
> Die Errata sind bei MCHP auf der gleichen Seite wie die Datenblätter.
> Genau darunter!
Danke.

> Das Problem tritt vermutlich verstärkt auf, weil bei deiner Kette der
> Abstand immer genau passt. Du könntest testen was passiert, wenn die Daten
> mit 2 Stopbits losgeschickt werden.
Das kann zwar der PC als Master, aber der UART des 12F1572 leider nicht.
Und an der ersten LED gibt's bisher nie Probleme :) Spannend wird's erst
ab der 5. oder 6. LED. Und wie gesagt, weder 2 Stopbits noch 
automatisches Parity drin in dem Kisterl...

Man könnte natürlich noch einen Timer verbraten, der noch einmal ~1,5 
Bit-Dauern abwartet, bevor nach TXIF das TXREG neu befüllt wird.

von Klaus (Gast)


Lesenswert?

Christoph S. schrieb:
> Das kann zwar der PC als Master, aber der UART des 12F1572 leider nicht.

Das kann der schon. Einfach auf 9-Bit Transmit einstellen und das neunte 
Bit dauerhaft auf den gleichen Wert wie das Stop-Bit setzen. Der 
Receiver bleibt bei 8 Bit.

MfG Klaus

von Volker S. (vloki)


Lesenswert?

Nur den PC auf 2 Stopbits, sonst hat man doch das gleiche Problem 
wieder!
Die zwei Stopbits bewirken einfach eine relativ lange Pause zwischen den 
Bytes, die auch innerhalb der Kette besten bleiben sollte. Dann fällt 
das Eintreffen des neuen Bytes "vermutlich" nicht mehr mit dem Ende des 
Abschicken des vorherigen überein.

von Peter D. (peda)


Lesenswert?

Christoph S. schrieb:
>> The actual data is not lost or corrupted; only unwanted (extra) zero
>> bytes are observed in the packet.

Die haben wohl nen Clown gefrühstückt. Wenn in einen Datenstrom 0-Bytes 
eingefügt werden, ist der sehr wohl korrumpiert. Der Empfänger weiß doch 
nicht, ob die 0-Bytes echt oder Fake sind.

von Volker S. (vloki)


Lesenswert?

Peter D. schrieb:
> Die haben wohl nen Clown gefrühstückt...

Soll wahrscheinlich nur das Problem näher beschreiben. Dass die 
Übertragung im Ar*** ist, ist ja klar...

von Klaus (Gast)


Lesenswert?

Peter D. schrieb:
> Der Empfänger weiß doch
> nicht, ob die 0-Bytes echt oder Fake sind.

Der Vorlauf und Nachlauf bei einem Lochstreifen sind alles 0x00. Damit 
ist ein Teletype immer klar gekommen. So what

MfG Klaus

von Christoph S. (mcseven)


Lesenswert?

Und was ist mit "Mittendrinlauf"? Ich halte das für einen extremen 
Major-Bug, vor allem, daß es noch keine neue Silicone-Version gibt (?), 
die den behebt. Die zusätzliche Check-Logik verbraucht wertvollen Flash.

Naja, auf alle Fälle euch vielen Dank, jetzt funktioniert es für meine 
Anwendung zufriedenstellend gut, und vielleicht hilft der Thread ja noch 
jemandem in der Zukunft.

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.