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
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.
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
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
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
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.
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.
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
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
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 :-)
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
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.
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.
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
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.
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
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.
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
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
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
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.
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; |
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
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
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
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.


