Forum: Mikrocontroller und Digitale Elektronik TJA1020 mit ESP32 betreiben


Announcement: there is an English version of this forum on EmbDev.net. Posts you create there will be displayed on Mikrocontroller.net and EmbDev.net.
von Olli Z. (z80freak)


Angehängte Dateien:

Lesenswert?

Ich möchte mir einen LIN-Sniffer mit einem ESP32 (S3) bauen und als 
Transceiver einen TJA1020 verwenden und habe dazu noch ein paar Fragen.

Für meine Anwendung reicht ja erstmal nur den RXD zu verbinden. Dieser 
ist laut Schaltbild Open-Drain, muss also mit einem Pullup auf IO-Pegel 
gebracht werden. Sollte ich das mit einem externen Widerstand (10 k) 
tun, oder reicht da auch der interne Pullup des ESP32 auf einem GPIO?

Der LIN-Bus hat einen 12V Pegel, da muss ich mich ja eigentlich nur 
anklemmen. Der TJA selbst benötigt ja eine Betriebsspannung, die liegt 
laut Datenblatt im Bereich 5 bis 27 V. Ich habe jetzt nicht 
herausgelesen das die irgendwie von der LIN-Spannung abhängt, also 
sollte es doch reichen die 5V vom ESP-Board dafür zu nutzen?

Für einen normalen Betriebsmodus ohne irgendwelche Energiesparfunktionen 
muss laut Datenblatt NSLP auf 1 (3,3 V) gezogen werden. Mehr ist für den 
einfachen Lesebetrieb scheinbar nicht notwendig?

Nun zum ESP32. Der hat ja keinen LIN-Controller also muss man das 
entweder per Bit-Banging erledigen oder per UART. Bei UART sehe ich 
besonders die Verarbeitung sowie Erzeugung des BREAK als kritischen 
Punkt an. Der Rest wäre ja gleich? Also Baudrate mit 8N1 sollte bei LIN 
grundsätzlich Bytes liefern. Das SYNC-Byte sollte dann als 0x55 daher 
kommen. Macht es Sinn das mit UART zu tun? Ein "Trick" für das senden 
eines BREAKs ist wohl die Baudrate zu halbieren, aber das empfangen 
eines 13 Bit langen BREAK mit 1 Bit rezessiv in Folge ist wohl ein 
Problem für UARTs.

In meinem konkreten Fall habe ich mal einen Logic-Analyzer an den Bus 
gehangen und bin nach den gemessenen Werten der Meinung das die Baudrate 
9.600 sein müsste. Wenn ich vom LA den ASYNC-Serial Analyzer auf 9600 
8N1 einstelle und den Pegel auf Negativ (weil ja im Ruhezustand HIGH), 
erhalte ich auch ein vermeintlich brauchbares Datenbild.

: Bearbeitet durch User
von Soul E. (soul_eye)


Lesenswert?

Solange Du keine EMV-Tests machen willst, spricht nichts dagegen den 
internen Pullup zu verwenden. Echte Widerstände sind robuster, das 
bringt in Deiner Anwendung wahrscheinlich keine Vorteile.
Schau Dir die steigende Flanke mit dem Oszilloskop an. Wenn die zu sehr 
abgerundet ist, ist der Widerstand zu groß.

Der Transceiver muß mit der Betriebsspannung des Netzwerkes versorgt 
werden. Referenz für High/Low ist die halbe Betriebsspannung.

Beim Empfang mit einem UART liefert das Sync Break einen frame error. Je 
nach Controller kann man damit einen Interrupt auslösen.

LIN mit 9k6 gibt es, üblich ist 19200.

von Rainer W. (rawi)


Lesenswert?

Olli Z. schrieb:
> Ich habe jetzt nicht herausgelesen das die irgendwie von der
> LIN-Spannung abhängt, also sollte es doch reichen die 5V vom
> ESP-Board dafür zu nutzen?

Das ist nicht so richtig toll. An BAT liegt normalerweise die gleiche 
Spannung, mit der auch der Bus läuft. Die Schwelle für den Empfänger 
(receiver threshold voltage V_th(rx)) liegen irgendwo zwischen 0.4 ... 
0.5 V_Bat @ V_Bat 7.3 ... 27 V (Datenblatt Philips/NXP s.11), d.h. wenn 
du den TJA1020 mit 5V betreibst, ist damit zu rechnen, dass die Schwelle 
wahrscheinlich bei um die 2 ... 3V liegt (nicht mehr spezifiziert). Du 
hast also einen kräftig reduzierten Störabstand. Sobald der LIN-Pegel 
nicht weit genug auf 0V gezogen wird, kann es eng werden, bei 
kapazitiver Belastung verschieben sich Signalflanken.

: Bearbeitet durch User
von Frank K. (fchk)


Lesenswert?

Olli Z. schrieb:

> Für meine Anwendung reicht ja erstmal nur den RXD zu verbinden. Dieser
> ist laut Schaltbild Open-Drain, muss also mit einem Pullup auf IO-Pegel
> gebracht werden. Sollte ich das mit einem externen Widerstand (10 k)
> tun, oder reicht da auch der interne Pullup des ESP32 auf einem GPIO?

Sehe in jedem Fall externe Widerstände vor. Nicht bestücken kannst Du 
immer noch.

> Der LIN-Bus hat einen 12V Pegel, da muss ich mich ja eigentlich nur
> anklemmen. Der TJA selbst benötigt ja eine Betriebsspannung, die liegt
> laut Datenblatt im Bereich 5 bis 27 V. Ich habe jetzt nicht
> herausgelesen das die irgendwie von der LIN-Spannung abhängt, also
> sollte es doch reichen die 5V vom ESP-Board dafür zu nutzen?

Nein, das ist so nicht vorgesehen. VBAT kommt direkt an die 
LIN-Busspannung (12 oder 24V je nach System un Transceiver).

> Für einen normalen Betriebsmodus ohne irgendwelche Energiesparfunktionen
> muss laut Datenblatt NSLP auf 1 (3,3 V) gezogen werden. Mehr ist für den
> einfachen Lesebetrieb scheinbar nicht notwendig?

ja.

> Nun zum ESP32. Der hat ja keinen LIN-Controller also muss man das
> entweder per Bit-Banging erledigen oder per UART. Bei UART sehe ich
> besonders die Verarbeitung sowie Erzeugung des BREAK als kritischen
> Punkt an. Der Rest wäre ja gleich? Also Baudrate mit 8N1 sollte bei LIN
> grundsätzlich Bytes liefern. Das SYNC-Byte sollte dann als 0x55 daher
> kommen. Macht es Sinn das mit UART zu tun? Ein "Trick" für das senden
> eines BREAKs ist wohl die Baudrate zu halbieren, aber das empfangen
> eines 13 Bit langen BREAK mit 1 Bit rezessiv in Folge ist wohl ein
> Problem für UARTs.

LIN ist im Prinzip UART mit ein paar Spezialitäten. Laut Datenblatt 
müsste der S3 Breaks senden können. Beim Empfangen eines Breaks wirst Du 
wohl ein Nullbyte plus einen Framing Error bekommen.

> In meinem konkreten Fall habe ich mal einen Logic-Analyzer an den Bus
> gehangen und bin nach den gemessenen Werten der Meinung das die Baudrate
> 9.600 sein müsste. Wenn ich vom LA den ASYNC-Serial Analyzer auf 9600
> 8N1 einstelle und den Pegel auf Negativ (weil ja im Ruhezustand HIGH),
> erhalte ich auch ein vermeintlich brauchbares Datenbild.

9600 und 19200 sind gängige Bitraten.

fchk

von Alexander (alecxs)


Lesenswert?

Frank K. schrieb:
> Beim Empfangen eines Breaks wirst Du wohl ein Nullbyte plus einen
> Framing Error bekommen.

Dafür gibt's was (ungetestet)

https://github.com/CW-B-W/ESP32-SoftwareLIN

von Frank O. (frank_o)


Lesenswert?

Mal eine allgemeine Frage zum dem Sniffer: Was erwartest du da zu sehen?

Jeder vernünftige Tester kann bestimmte Daten auslesen und die 
angeschlossenen Geräte darstellen, das sogar als Kurve, falls mal 
sporadische Fehler sind. Aber die sagt dir das Auto sowieso und der 
Tester kann sie lesen.
Deshalb würde ich gerne wissen, ob es Gründe gibt, die mir so nicht 
bekannt sind.

von Soul E. (soul_eye)


Lesenswert?

Frank O. schrieb:
> Jeder vernünftige Tester kann bestimmte Daten auslesen und die
> angeschlossenen Geräte darstellen, das sogar als Kurve,

Man kann LIN-Tools auch fertig kaufen, das ist richtig. Ich nutze hier 
Vector CANoe mit einem VN1640. Aber ich hatte den TO so verstanden, dass 
er selber etwas bauen wollte?

Zwischen den beiden Lösungen liegen ca 26 dB Preisunterschied.

von Frank O. (frank_o)


Lesenswert?

Mir ging es eher um das Sniffen. Da ich am Auto alles mit Launch Testern 
mache und machen kann, verstehe ich nicht was man da sehen will.
Man kann dann vielleicht die Nachrichten sehen und das alles 
funktioniert,  aber außer man will aus solchen Erkenntnissen selbst 
irgendeinen Tester bauen und verkaufen,  verstehe ich den Sinn nicht.
Wenn ich irgendein Gerät oder Sensor überwachen will, lege ich den auf 
das Display,  mit sinnvollen anderen Werten und das ist besser als "nur" 
ein LIN Tester.
Deshalb frage ich, weil der TO vielleicht etwas weiß,  auf das ich 
selbst gerade nicht komme und vielleicht nicht einmal kenne.
Ich möchte nur verstehen warum. Ebenso will ich auch keinen bekehren was 
anderes,  wie so einen Launch Tester zu nehmen.

: Bearbeitet durch User
von Olli Z. (z80freak)


Angehängte Dateien:

Lesenswert?

Soul E. schrieb:
> Solange Du keine EMV-Tests machen willst, spricht nichts dagegen den
> internen Pullup zu verwenden. Echte Widerstände sind robuster, das
Ok, ich verwende jetzt einen 10k Pullup auf Vcc (3,3V).

> Der Transceiver muß mit der Betriebsspannung des Netzwerkes versorgt
> werden. Referenz für High/Low ist die halbe Betriebsspannung.
Ja, irgendwie auch logisch (im Datenblatt steht Vo(reces) = 0.9 - 
1.0VBAT und Vo(dom) 0.6 - 2,0 V) Irgendwie hab ich das überlesen. Danke 
für die Aufklärung! Also klemme ich Pin 7 (VBAT) an +12V.

Damit auf RXD was kommt muss NSLP ja auf HIGH, was laut Datenblatt 2-7V 
sind. Also kann ich den direkt, ohne Widerstand auf +3,3V legen.

> Beim Empfang mit einem UART liefert das Sync Break einen frame error. Je
> nach Controller kann man damit einen Interrupt auslösen.
Tja, wenn ich meinen LA hinter den TJA klemme, habe ich ein sauberes 
Signal. Wenn ich das versuche mit meinem ESP32 über UART zu verarbeiten 
kommt nix.
Der wesentliche Teil der Initialisierung sieht so aus:
1
esp_err_t lin_sniffer_init(const lin_sniffer_config_t *config)
2
{
3
    // Configure UART for passive listening
4
    uart_config_t uart_config = {
5
        .baud_rate = config->baudrate,
6
        .data_bits = UART_DATA_8_BITS,
7
        .parity = UART_PARITY_DISABLE,
8
        .stop_bits = UART_STOP_BITS_1,
9
        .flow_ctrl = UART_HW_FLOWCTRL_DISABLE,
10
        .source_clk = UART_SCLK_DEFAULT,
11
    };
12
13
    esp_err_t ret = uart_param_config(config->uart_num, &uart_config);
14
    if (ret != ESP_OK) {
15
        ESP_LOGE(TAG, "UART param config failed: %s", esp_err_to_name(ret));
16
        return ret;
17
    }
18
19
    // Set RX-only pins (no TX pin for passive sniffing)
20
    ret = uart_set_pin(config->uart_num, UART_PIN_NO_CHANGE, config->rx_pin, 
21
                       UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE);
22
    if (ret != ESP_OK) {
23
        ESP_LOGE(TAG, "UART set pin failed: %s", esp_err_to_name(ret));
24
        return ret;
25
    }
26
27
    // Install UART driver
28
    ret = uart_driver_install(config->uart_num, UART_BUF_SIZE * 2, UART_BUF_SIZE * 2, 0, NULL, 0);
29
    if (ret != ESP_OK) {
30
        ESP_LOGE(TAG, "UART driver install failed: %s", esp_err_to_name(ret));
31
        return ret;
32
    }
33
34
    ...
35
}

: Bearbeitet durch User
von Olli Z. (z80freak)


Lesenswert?

Frank O. schrieb:
> Deshalb frage ich, weil der TO vielleicht etwas weiß,  auf das ich
> selbst gerade nicht komme und vielleicht nicht einmal kenne.
Auch wenn das hier eigentlich nichts zur Sache tut, es Dich aber 
sichtlich beschäftigt darf ich Doch beruhigen, ich bastle einfach gern, 
gehe Dingen auf den Grund und lerne gern neues. Ich bin sicher das man 
mit fertigen, teurem Equipment alles viel leichter hin bekommen könnte, 
ich habe halt eine Kiste voller Elektronikteile :-) Mein Ziel ist es die 
LIN-Kommunikation mit einem Batteriesensor zu untersuchen, aber auch das 
tut nichts zur Sache, ich könnte auch ein Türmodul oder sonst was 
nehmen.
Also, keine Geheimnisse, kannst ganz beruhigt sein :-)

von Olli Z. (z80freak)


Lesenswert?

Alexander schrieb im Beitrag #80047
> https://github.com/CW-B-W/ESP32-SoftwareLIN
Ja, kannte ich schon, danke. Ist für Arduino-IDE geschrieben und basiert 
auf weiteren Libs. Das müsste ich alles für ESP-IDF und VS Code 
portieren...
Ich habe mir wohl so ziemlich alle Libs angeschaut die es so gibt, aber 
das macht es dann irgendwann nicht besser, im Gegenteil. Daher ja meine 
Frage nach der grundsätzlichen Ausrichtung, mit Uart oder Bit-Banging 
oder doch einen externen LIN Controller?

: Bearbeitet durch User
von Soul E. (soul_eye)


Lesenswert?

Olli Z. schrieb:
> Frage nach der grundsätzlichen Ausrichtung, mit Uart oder Bit-Banging
> oder doch einen externen LIN Controller?

Ich kenne die UART von Deinem Controller nicht, aber grundsätzlich 
funktioniert LIN mit einer UART. Beim Empfang liefert der Sync Break 
einen frame error, den muss man halt bestätigen um danach das 0x55 
empfangen zu können. Beim Senden schaltet man entweder die Bitrate 
runter um die 11-13 Nullen rauszuschieben, oder man setzt den Pin für 
eine Weile auf GPIO und macht das mit einem Timer, oder man schaltet 
einen zweiten Pin als GPIO parallel und macht das mit dem.


Wenn Du mit Deiner UART was senden kannst und der LA das versteht, dann 
sollte die Initialisierung geklappt haben. Dann bleibt die Frage wo der 
Sync Break hingeht, ob der irgendwie den Controller durcheinanderbringt. 
Dazu könntest Du über den LIN-Transceiver ein normales UART-Signal 
einspeisen. Z.B. von einem FTDI mit einem zweiten Transceiver dahinter.

von Frank O. (frank_o)


Lesenswert?

Olli Z. schrieb:

> Also, keine Geheimnisse, kannst ganz beruhigt sein :-)

Da bin ich wirklich beruhigt. Ich dachte schon,  dass es vielleicht 
etwas gibt, wo ich mit dem Tester nicht dran komme.

von Soul E. (soul_eye)


Lesenswert?

Frank O. schrieb:
> Da bin ich wirklich beruhigt. Ich dachte schon,  dass es vielleicht
> etwas gibt, wo ich mit dem Tester nicht dran komme.

Unter "Tester" verstehst Du vermutlich ein OBD2- oder 
Werkstatt-Diagnosegerät? Diese Geräte sind dafür gebaut, um über ein 
Diagnose-Gateway über spezielle Protokolle wie UDS oder KWP auf 
ausgewählte Steuergeräte zuzugreifen. Üblicherweise sind sie nicht dazu 
geeignet, die Kommunikation auf einem der inneren Busse (hinter dem 
Gateway) komplett auszulesen oder zu beeinflussen.

Einen Transparent-Modus, d.h. alles was kommt mitlesen und auf dem 
Bildschirm ausgeben, bieten eher solche Tools wie CanCool, CANHacker 
oder eben CANoe.

: Bearbeitet durch User
von Frank O. (frank_o)


Lesenswert?

Soul E. schrieb:
> Unter "Tester" verstehst Du vermutlich ein OBD2- oder
> Werkstatt-Diagnosegerät?

Klar!
Mitlesen will ich auch nicht die Kommunikation,  sondern die Werte,  die 
beispielsweise ein Sensor ausgibt.
Hier sind ja ziemlich gute Leute dabei und ich dachte halt, dass es auf 
dem LIN Bus vielleicht irgendwas gibt, wo ich vielleicht nicht mit dem 
üblichen "Diagnosegerät" gar nicht erst hinkomme.
Aber mein neuer Launch sollte so ziemlich überall ran kommen.
Wir können das jetzt sein lassen und den TO seinen eigentlichen Zweck 
dieses Threads überlassen.

von Alexander (alecxs)


Lesenswert?

Frank O. schrieb:
> Man kann dann vielleicht die Nachrichten sehen und das alles
> funktioniert,  aber außer man will aus solchen Erkenntnissen selbst
> irgendeinen Tester bauen und verkaufen,  verstehe ich den Sinn nicht.

Ich denke das sind zwei verschiedene Schuhe. Mit dem Launch kommst Du in 
die Steuergeräte rein, dort siehst Du was immer Dir das Steuergerät 
zeigen möchte, und zwar ausschließlich über die dort hinterlegten 
Funktionen. Im Klartext, Du kannst eigentlich keinen Sensor auslesen, Du 
kannst Dir nur die Daten anschauen die das Steuergerät vom Sensor 
bekommen hat.

Den CAN Traffic (hier LIN) abzugreifen ist was völlig anderes. Dort 
siehst Du den Bus und die Kommunikation der Steuergeräte miteinander. 
Der für mich einzige Grund sich das anzutun ist, eigene zusätzliche 
Hardware zu vernetzen die sich über die vorhandene Hardware steuern 
lässt. In meinem Fall ein ESP32 der über die Lenkradtasten (CAN) bedient 
wird, und welcher seinerseits auch das Auto bedient, u.a. - quasi MITM 
mit WiFi.

In einem anderen Thread ging es um einen ~230V Inverter von VW welcher 
sich nur über LIN einschalten lässt, der TE aber nicht das passende 
Signal vom BMS verfügbar hatte. Da könnte ich mir vorstellen dass es 
Sinn macht das von einem anderen Fzg zu sniffen und dann Replay über 
einen mit ESP32 simulierten LIN auszugeben, also eine fehlende Hardware 
zu ersetzen.

Stelle ich mir mit einem Launch schwierig vor, ist der falsche Schuh 
dafür.

Also was auch immer hier über LIN empfangen werden soll, der ESP32 ist 
wohl dazu in der Lage die Daten auszuwerten und für eigene 
automatisierte Aktionen zu verwenden, so wie der TE das gerne hätte. Das 
sehe ich als den Benefit einer solchen Aktion ggü. einem käuflichen 
Tester.

: Bearbeitet durch User
von Soul E. (soul_eye)


Lesenswert?

Alexander schrieb:
> Den CAN Traffic (hier LIN) abzugreifen ist was völlig anderes.

Richtig. Man kann auch nicht WireShark, ein Programm zum mitlesen des 
Verkehrs in Computernetzwerken, ersetzen durch Firefox, ein Programm zum 
Abrufen von Daten über eben diese Netzwerke. Das sind völlig 
unterschiedliche Anwendungen.

von Olli Z. (z80freak)


Lesenswert?

Wenn ich das inzwischen richtig gelesen habe 1) hat der UART im ESP32 
eine Break-Detection mit der er einen "BRK_DET" Interrupt auslösen kann, 
der genau dafür gedacht ist.

Ich denke meinen Ansatz also mit UART nochmal neu. Der TJA1020 
funktioniert inzwischen, ich bekomme also ein sauberes Signal. Wichtig 
hierbei ist vielleicht noch das dieses dem LIN-Pegel folgt, also im 
Ruhezustand HIGH, aber ansonsten nicht invertiert ist. Eine BUS-0 ist 
also eine Logische-0.

UART0 wird intern fürs Flashen und Debuggen benutzt, also nehme ich 
UART1. Den initialisiere ich also mit 9.600 Baud, weise den GPIO4 dem 
RX-Signal zu. Für die Empfangsdetektion eines BREAK gibt es in ESP-IDF 
5.5 keine besonderen Funktionen (nur für das Senden mit BREAK), das wird 
also über Hardware und Events gelöst.

Ich muss also einen Empfangstask bauen der mit xQueueReceive() auf 
Events aus der "uart_queue" wartet und den Event-Typ auswertet 
(uart_event_t). Ist dieser Typ "UART_BREAK" dann hat es den auf dem Bus 
gegeben.

1) 
https://docs.espressif.com/projects/esp-idf/en/stable/esp32/api-reference/peripherals/uart.html

von Olli Z. (z80freak)


Angehängte Dateien:

Lesenswert?

Ich habe angefügten Code geschrieben und in der Tat erhalte ich beim 
Anschluß an den LIN ein Ergebnis:
1
I (253) LIN_SNIFFER: Starting LIN Bus Sniffer
2
I (257) uart: queue free spaces: 10
3
I (8254) LIN_SNIFFER: UART_BREAK detected!
4
I (8263) LIN_SNIFFER: UART_BREAK detected!
5
I (8278) LIN_SNIFFER: UART_BREAK detected!
6
I (8288) LIN_SNIFFER: UART_BREAK detected!
7
I (8298) LIN_SNIFFER: UART_BREAK detected!
8
I (8309) LIN_SNIFFER: UART_BREAK detected!
9
I (8324) LIN_SNIFFER: UART_BREAK detected!
10
I (8334) LIN_SNIFFER: UART_BREAK detected!
11
I (8343) LIN_SNIFFER: UART_BREAK detected!
12
I (8354) LIN_SNIFFER: UART_BREAK detected!
13
I (8368) LIN_SNIFFER: UART_BREAK detected!
14
I (8378) LIN_SNIFFER: UART_BREAK detected!
15
I (8388) LIN_SNIFFER: UART_BREAK detected!
16
I (8398) LIN_SNIFFER: UART_BREAK detected!
17
I (8414) LIN_SNIFFER: UART_BREAK detected!
18
I (8424) LIN_SNIFFER: UART_BREAK detected!
19
I (8434) LIN_SNIFFER: UART_BREAK detected!
20
I (8444) LIN_SNIFFER: UART_BREAK detected!
21
I (8458) LIN_SNIFFER: UART_BREAK detected!
22
I (8462) LIN_SNIFFER: UART_DATA: 120 bytes
23
I (8468) LIN_SNIFFER: UART_BREAK detected!
24
...

Der Code initialisiert ganz normal eine UART mit 9600-8N1 und wartet 
dann auf einen UART Event UART_BREAK. Damit ich ohne angeschlossenen 
Transceiver keinen solchen Event erhalte habe ich den GPIO vom RX-Pin 
auf internen Pullup gestellt.

Als nächstes müsste ich also eine Frame-Erkennung implementieren. Die 
Bytes träufeln nach dem BREAK ja sofort weiter in den Buffer, lösen also 
dann valide UART_DATA Events aus. Das erste Byte müsste ja eigentlich 
immer ein SYNC sein, also 0x55.

Jetzt brauche ich noch ein wenig Logik um die Frames aus den eingehenden 
Datenbytes zu ermitteln und defekte auszusortieren. Ein Frame kann ja 
maximal 10 Bytes lang sein: SYNC + 8xDATA + CHKSUM.

: Bearbeitet durch User
von Olli Z. (z80freak)


Lesenswert?

Habe jetzt einfach mal die empfangenen Bytes ausgeben lassen und die 
BREAKs ignoriert:
1
I (257) uart: queue free spaces: 10
2
I (5939) LIN_SNIFFER: UART_DATA: 120 bytes
3
I (5939) LIN_SNIFFER: 00 55 06 00 00 ff 00 55 20 84 80 00 00 fa 00 55
4
I (5940) LIN_SNIFFER: 06 00 00 ff 00 55 85 40 09 b6 00 55 06 00 00 ff
5
I (5944) LIN_SNIFFER: 00 55 50 00 0a f5 00 55 06 00 00 ff 00 55 85 40
6
I (5951) LIN_SNIFFER: 09 b6 00 55 06 00 00 ff 00 55 20 84 80 01 00 f9
7
I (5957) LIN_SNIFFER: 00 55 06 00 00 ff 00 55 85 40 08 b7 00 55 06 00
8
I (5963) LIN_SNIFFER: 00 ff 00 55 0d a4 1f c5 34 42 00 55 06 00 00 ff
9
I (5969) LIN_SNIFFER: 00 55 85 40 02 bd 00 55 06 00 00 ff 00 55 20 84
10
I (5975) LIN_SNIFFER: 80 01 00 f9 00 55 06 00
11
I (6147) LIN_SNIFFER: UART_DATA: 120 bytes
12
I (6147) LIN_SNIFFER: 00 ff 00 55 85 40 02 bd 00 55 06 00 00 ff 00 55
13
I (6148) LIN_SNIFFER: 8e 00 b2 11 ff 3c 00 55 06 00 00 ff 00 55 85 40
14
I (6154) LIN_SNIFFER: 02 bd 00 55 06 00 00 ff 00 55 20 84 80 01 00 f9
15
I (6160) LIN_SNIFFER: 00 55 06 00 00 ff 00 55 85 40 02 bd 00 55 06 00
16
I (6167) LIN_SNIFFER: 00 ff 00 55 cf 9d 82 ac ff 33 00 55 06 00 00 ff
17
I (6173) LIN_SNIFFER: 00 55 85 40 02 bd 00 55 06 00 00 ff 00 55 20 94
18
I (6179) LIN_SNIFFER: 80 01 00 e9 00 55 06 00 00 ff 00 55 85 40 02 bd
19
I (6185) LIN_SNIFFER: 00 55 06 00 00 ff 00 55
20
I (6338) LIN_SNIFFER: UART_DATA: 105 bytes
21
I (6338) LIN_SNIFFER: 8e 00 b2 11 ff 3c 00 55 06 00 00 ff 00 55 85 40
22
I (6339) LIN_SNIFFER: 02 bd 00 55 06 00 00 ff 00 55 20 94 80 01 00 e9
23
I (6343) LIN_SNIFFER: 00 55 06 00 00 ff 00 55 85 40 02 bd 00 55 06 00
24
I (6349) LIN_SNIFFER: 00 ff 00 55 0d 07 1e c2 34 e3 00 55 06 00 00 ff
25
I (6356) LIN_SNIFFER: 00 55 85 40 02 bd 00 55 06 00 00 ff 00 55 20 94
26
I (6362) LIN_SNIFFER: 80 01 00 e9 00 55 06 00 00 ff 00 55 85 40 02 bd
27
I (6368) LIN_SNIFFER: 00 55 06 00 00 ff 00 55 08
28
I (6456) LIN_SNIFFER: UART_DATA: 64 bytes
29
I (6456) LIN_SNIFFER: 00 55 06 00 00 ff 00 55 85 40 02 bd 00 55 06 00
30
I (6456) LIN_SNIFFER: 00 ff 00 55 20 94 82 03 00 e5 00 55 06 00 00 ff
31
I (6461) LIN_SNIFFER: 00 55 85 40 02 bd 00 55 06 00 00 ff 00 55 8e 00
32
I (6467) LIN_SNIFFER: b2 11 ff 3c 00 55 06 00 00 ff 00 55 85 40 02 bd
33
I (6692) LIN_SNIFFER: UART_DATA: 120 bytes
34
I (6692) LIN_SNIFFER: 00 55 06 00 00 ff 00 55 20 94 82 03 00 e5 00 55
35
I (6693) LIN_SNIFFER: 85 40 02 bd 00 55 50 00 4a b5 00 55 2b 02 41 47
36
I (6699) LIN_SNIFFER: 39 4e 00 00 00 ed 00 55 6a ff ff ff ff ff ff 00
37
I (6705) LIN_SNIFFER: 00 55 06 00 00 ff 00 55 97 b8 c7 e8 c3 d2 00 55
38
I (6711) LIN_SNIFFER: 20 94 82 03 00 e5 00 55 85 40 02 bd 00 55 a6 86
39
I (6718) LIN_SNIFFER: c3 69 c2 89 00 55 e7 ff ff ff ff 00 00 55 06 00
40
I (6724) LIN_SNIFFER: 00 ff 00 55 f5 02 42 56 36 54 00 00 00 da 00 55
41
I (6730) LIN_SNIFFER: 20 94 82 03 00 e5 00 55
42
I (6768) LIN_SNIFFER: UART_DATA: 33 bytes
43
I (6768) LIN_SNIFFER: 85 40 02 bd 00 55 06 00 00 ff 00 55 20 94 82 03
44
I (6768) LIN_SNIFFER: 00 e5 00 55 85 40 02 bd 00 55 06 00 00 ff 00 55
45
I (6773) LIN_SNIFFER: 99
46
I (6788) LIN_SNIFFER: UART_DATA: 3 bytes
47
I (6788) LIN_SNIFFER: 00 55 61
48
I (7018) LIN_SNIFFER: UART_DATA: 120 bytes
49
I (7018) LIN_SNIFFER: 00 55 20 94 82 03 00 e5 00 55 85 40 02 bd 00 55
50
I (7018) LIN_SNIFFER: 06 00 00 ff 00 55 20 94 82 03 00 e5 00 55 85 40
51
I (7023) LIN_SNIFFER: 02 bd 00 55 50 00 4a b5 00 55 2b 03 31 30 43 36
52
I (7029) LIN_SNIFFER: 37 39 00 b1 00 55 6a ff ff ff ff ff ff 00 00 55
53
I (7035) LIN_SNIFFER: 06 00 00 ff 00 55 97 b8 c7 e8 c3 d2 00 55 20 94
54
I (7042) LIN_SNIFFER: 82 02 00 e6 00 55 85 40 02 bd 00 55 a6 86 c3 69
55
I (7048) LIN_SNIFFER: c2 89 00 55 e7 ff ff ff ff 00 00 55 06 00 00 ff
56
I (7054) LIN_SNIFFER: 00 55 f5 03 31 37 44 35
57
I (7123) LIN_SNIFFER: UART_DATA: 47 bytes
58
I (7123) LIN_SNIFFER: 34 37 00 af 00 55 20 84 80 00 00 fa 00 55 85 40
59
I (7123) LIN_SNIFFER: 02 bd 00 55 06 00 00 ff 00 55 20 84 80 00 00 fa
60
I (7128) LIN_SNIFFER: 00 55 85 40 02 bd 00 55 06 00 00 ff 00 55 99
61
I (7143) LIN_SNIFFER: UART_DATA: 3 bytes
62
I (7143) LIN_SNIFFER: 00 55 61
63
I (7373) LIN_SNIFFER: UART_DATA: 120 bytes
64
I (7373) LIN_SNIFFER: 00 55 20 84 80 00 00 fa 00 55 85 40 02 bd 00 55
65
I (7374) LIN_SNIFFER: 06 00 00 ff 00 55 20 84 80 00 00 fa 00 55 85 40
66
I (7378) LIN_SNIFFER: 02 bd 00 55 50 00 0a f5 00 55 2b 04 44 45 00 00
67
I (7385) LIN_SNIFFER: 00 00 00 72 00 55 6a ff ff ff ff ff ff 00 00 55
68
I (7391) LIN_SNIFFER: 06 00 00 ff 00 55 97 b8 c7 e8 c3 d2 00 55 20 84
69
I (7397) LIN_SNIFFER: 80 00 00 fa 00 55 85 40 02 bd 00 55 a6 86 c3 69
70
I (7403) LIN_SNIFFER: c2 89 00 55 e7 ff ff ff ff 00 00 55 06 00 00 ff
71
I (7409) LIN_SNIFFER: 00 55 f5 04 41 44 00 00

Ich muss also auch das durch den BREAK erzeugte 0x00 am Anfang 
ignorieren.
Laut LIN Protokoll ist die Anzahl der Datenbytes (Nutzlast) eines Frame 
abhängig von seiner ID:
ID  1..31 (0x1F) = 2 Bytes
ID 32..47 (0x2F) = 4 Bytes
ID 48..63 (0x3F) = 8 Bytes
Dem folgt dann noch das Checksum-Byte.

Ich könnte nun eine Frame-Queue bauen in der ich die Frames reinstecke, 
erstmal ungeprüft, aber ohne BREAK+SYNC. Oder ich prüfe zuerst die 
Checksum ob der Frame valide ist und stopfe nur PID und Nutzlast in die 
Queue? Davor muss ich aber immer auch prüfen ob der Frame überhaupt 
vollständig im Buffer liegt.

: Bearbeitet durch User
von Frank O. (frank_o)


Lesenswert?

Sieht schon mal nach Erfolg aus.
Gut gemacht!

von Soul E. (soul_eye)


Lesenswert?

Olli Z. schrieb:
> Ich muss also auch das durch den BREAK erzeugte 0x00 am Anfang
> ignorieren.

Klar, die UART weiß ja bei den ersten acht bit noch nicht, dass das 
gleich einen Überlauf geben wird.


> Ich könnte nun eine Frame-Queue bauen in der ich die Frames reinstecke,
> erstmal ungeprüft, aber ohne BREAK+SYNC. Oder ich prüfe zuerst die
> Checksum ob der Frame valide ist und stopfe nur PID und Nutzlast in die
> Queue? Davor muss ich aber immer auch prüfen ob der Frame überhaupt
> vollständig im Buffer liegt.

Meist macht man sich Mailboxen, wo immer die gleiche Botschaft 
reinkommt. Die Applikation findet dann da immer die neusten Daten vor.

Also empfangen, prüfen, Interupts sperren, Nutzdaten in die Mailbox, 
Interrupts wieder freigeben.

von Olli Z. (z80freak)


Lesenswert?

Der UART des ESP32 hat einen Hardware-Buffer von 128 Byte und der 
Library-Default löst bei 120 Bytes einen UART_DATA Event aus, oder nach 
einer gewissen Timeout-zeit. Ich habe beide Werte etwas nach unten 
justiert damit ich schneller auf die Daten reagieren/parsen kann. So 
oder so weiß man nie genau was im Empfangsbuffer liegt, man muss sich 
das wohl Byte für Byte anschauen.

Daher habe ich mir nun einen Parser über eine Statemachine gebaut die 
valide Frames aus dem empfangenen Bytestrom erkennt und diese dann in 
eine Queue (aka Mailbox) steck. Das Grundgerüst ist so:
1
void lin_parse_byte(lin_parser_t *parser, uint8_t byte) {
2
    switch(parser->state) {
3
        case WAIT_FOR_BREAK:
4
            // Was tun wenn wir 0x00 sehen?
5
            break;
6
            
7
        case WAIT_FOR_SYNC:
8
            // Was tun wenn wir 0x55 erwarten?
9
            break;
10
            
11
        case READ_PID:
12
            // PID lesen, Parity prüfen, data_len setzen
13
            break;
14
            
15
        case READ_DATA:
16
            // Datenbytes sammeln
17
            break;
18
            
19
        case READ_CHECKSUM:
20
            // Checksum validieren, Frame ausgeben
21
            break;
22
    }
23
}

Mein nächstes Etappenziel ist es, valide, vollständige Frames zu 
erhalten und keinen zusammenhanglosen Bytestrom. Die Frames enthalten 
die ID und die Nutzdaten, welche ich dann in einem anderen Task 
verarbeiten, aber erstmal einfach nur ausgeben werde. Für die Frames 
habe ich mir diese Struktur überlegt:
1
typedef struct {
2
    lin_parser_state_t state;
3
    uint8_t pid;          // Protected ID (mit Parity)
4
    uint8_t id;           // Frame ID (ohne Parity)
5
    uint8_t data[8];
6
    uint8_t data_len;     // Erwartete Länge
7
    uint8_t data_idx;     // Aktueller Index
8
    uint8_t checksum;
9
} lin_parser_t;

von Olli Z. (z80freak)


Lesenswert?

Ok, ich bekomme nun solche Ergebnisse mit meinem Parser:
1
I (5281) LIN_SNIFFER: UART_DATA event: received 120 bytes
2
I (5281) LIN_SNIFFER: Valid frame- ID: 0x06
3
I (5281) LIN_SNIFFER: 00 00
4
I (5281) LIN_SNIFFER: Valid frame- ID: 0x20
5
I (5285) LIN_SNIFFER: 84 80 00 00
6
I (5288) LIN_SNIFFER: Valid frame- ID: 0x06
7
I (5292) LIN_SNIFFER: 00 00
8
I (5294) LIN_SNIFFER: Valid frame- ID: 0x05
9
I (5298) LIN_SNIFFER: 40 09
10
I (5301) LIN_SNIFFER: Valid frame- ID: 0x06
11
I (5305) LIN_SNIFFER: 00 00
12
I (5307) LIN_SNIFFER: Valid frame- ID: 0x10
13
I (5311) LIN_SNIFFER: 00 0a
14
I (5314) LIN_SNIFFER: Valid frame- ID: 0x06
15
I (5318) LIN_SNIFFER: 00 00
16
I (5320) LIN_SNIFFER: Valid frame- ID: 0x05
17
I (5324) LIN_SNIFFER: 40 09
18
I (5327) LIN_SNIFFER: Valid frame- ID: 0x06
19
I (5330) LIN_SNIFFER: 00 00
20
I (5333) LIN_SNIFFER: Valid frame- ID: 0x20
21
I (5337) LIN_SNIFFER: 84 80 01 00
22
I (5340) LIN_SNIFFER: Valid frame- ID: 0x06
23
I (5344) LIN_SNIFFER: 00 00
24
I (5346) LIN_SNIFFER: Valid frame- ID: 0x05
25
I (5350) LIN_SNIFFER: 40 08
26
I (5353) LIN_SNIFFER: Valid frame- ID: 0x06
27
I (5357) LIN_SNIFFER: 00 00
28
W (5359) LIN_SNIFFER: Checksum error - ID: 0x0D
29
I (5363) LIN_SNIFFER: Valid frame- ID: 0x06
30
I (5367) LIN_SNIFFER: 00 00
31
I (5370) LIN_SNIFFER: Valid frame- ID: 0x05
32
I (5374) LIN_SNIFFER: 40 02
33
I (5376) LIN_SNIFFER: Valid frame- ID: 0x06
34
I (5380) LIN_SNIFFER: 00 00
35
I (5383) LIN_SNIFFER: Valid frame- ID: 0x20
36
I (5387) LIN_SNIFFER: 84 80 01 00
37
I (5488) LIN_SNIFFER: UART_DATA event: received 120 bytes
38
I (5489) LIN_SNIFFER: Valid frame- ID: 0x06
39
I (5489) LIN_SNIFFER: 00 00
40
I (5489) LIN_SNIFFER: Valid frame- ID: 0x05
41
I (5493) LIN_SNIFFER: 40 02
42
I (5495) LIN_SNIFFER: Valid frame- ID: 0x06
43
I (5499) LIN_SNIFFER: 00 00
44
W (5502) LIN_SNIFFER: Checksum error - ID: 0x0E
45
...

Sieht doch schon sehr gut aus, finde ich. Jetzt analysiere ich noch 
warum es so häufig Checksum-Fehler gibt, das kommt mir noch etwas 
seltsam vor...

Und dann fange ich an die Frames zu analysieren.
Die LIN ID 0x06 scheint mir sowas wie ein Keepalive zu sein?
In dem Datenstrom habe ich mir auch mal die Bytes als ASCII anzeigen 
lassen, das ist immer recht aufschlußreich und was finde ich da? Diese 
Teilenummer "BV6T-17D547-AD". Das ist aber ein Regensensor bei Ford. 
Laut Schaltplan dachte ich, der Batteriesensor liegt alleine auf dem 
LIN-Bus zum BCM, aber scheinbar ist dort wenigstens noch der Regensensor 
mit dabei! Das erklärt mir nun auch als ich mal etwas mit dem TJA1020 
rumgespielt hatte das plötzlich der Scheibenwischer "auslöste". 
Interessant! ;-)

: Bearbeitet durch User
von Olli Z. (z80freak)


Lesenswert?

Ah, ich hab da einen Verdacht! Das BCM (LIN Master) fragt verschiedenste 
IDs ab um Daten von den Slaves zu lesen, aber manche IDs bleiben einfach 
unbeantwortet weil die Slaves auf dem Bus nicht existieren (Fahrzeug 
ausstattungsabhängig). Dann habe ich natürlich nur sowas wie 0x00 0x55 
0x08 auf dem Bus, quasi nur den Request, ohne Antwort.
Auf diese Situation ist mein Code noch nicht vorbereitet, das muss ich 
also irgendwie nachholen... Das Problem wird sein ein dann später 
folgendes Frame 0x00 0x55 ... mit gültigen Daten von einer Antwort eines 
Slaves welche zufällig 0x00 0x55 als Daten hat zu unterscheiden.
Der Master wird einen Timeout haben um auf die Antwort zu warten, evtl. 
ist das sogar im Protokoll definiert. Das wird auch der Grund sein warum 
ich z.B. die PID 0x08 immer am Ende eines RX-Buffers vorfinde weil der 
UART Timeout zuschlagen wird. Setze ich diesen UART-Timeout zu hoch an, 
wären wohl auch nachfolgende Frames im Buffer. Es ist also unter 
Umständen wichtig den Timeout so klein wie möglich zu stellen, also auch 
etwas über der "Response Space" damit wir nicht beantwortete Requests 
sauber ausfiltern können?
Ich sehe jedenfalls keinen wirklich guten Weg das rein auf 
Bytestrom-Basis zu unterscheiden, ob die einer PID folgenden Daten zum 
Frame gehören oder eben selbst ein neuer Frame sind?

: Bearbeitet durch User
von Soul E. (soul_eye)


Lesenswert?

Olli Z. schrieb:
> Ah, ich hab da einen Verdacht! Das BCM (LIN Master) fragt verschiedenste
> IDs ab um Daten von den Slaves zu lesen, aber manche IDs bleiben einfach
> unbeantwortet weil die Slaves auf dem Bus nicht existieren (Fahrzeug
> ausstattungsabhängig). Dann habe ich natürlich nur sowas wie 0x00 0x55
> 0x08 auf dem Bus, quasi nur den Request, ohne Antwort.

Das wäre ein Rx Error, den sollte man behandeln können. Tx Error ist 
wenn der vom Master gesendete Header beschädigt wird, z.B. durch EMV 
oder Kurzschluß. Das kann natürlich nur der Master selbst erkennen.


> (...) Das Problem wird sein ein dann später
> folgendes Frame 0x00 0x55 ... mit gültigen Daten von einer Antwort eines
> Slaves welche zufällig 0x00 0x55 als Daten hat zu unterscheiden.

Genau dafür gibt es den Sync Break. Bzw bei Dir den Interrupt oder das 
frame error-Flag. Das muss man halt abfragen und als Start der Botschaft 
werten.



> Wie immer liegen die wahren Probleme in der Dynamic eines Systems.

Schalte vor dem letzten Teilnehmer 100 Ohm in Reihe mit dem LIN. Dann 
gehen dessen dominanten Pulse nicht so weit runter wie die des Masters. 
So kann man am Oszi unterscheiden was woher kommt. Bei mehreren 
Teilnehmern geht man die Slaves der Reihe nach durch. Auspinnen, 
Widerstand rein, IDs aufschreiben. Dann weisst Du schonmal, welche Daten 
wo her kommen.

: Bearbeitet durch User
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.