Autoradio Tuner List (4) : Écran LCD

Date: 2018-03-17

Tags: Renault_tuner_list arduino auto elec jacky reverse_engineering

J’ai toujours voulu afficher n’importe quoi sur l’afficheur au centre du tableau de bord, qui affiche normalement la station de radio et l’heure, et il se trouve que c’est tout à fait possible.

Affichage de mots amusants

Hardware

Ici, on a un autoradio Renault/VDO Tuner List (modèle 22DC259/62T, 77 00 434 422) et un afficheur Renault (82 00 028 364), avec un connecteur gris connecté au tableau de bord et à la radio, et un connecteur rouge connecté aux commandes situées sous le volant.

Je n’ai pas eu confiance dans les documentations que j’ai pu trouver, avec des couleurs de fils incorrectes, des brochages de connecteurs parfois à l’envers, différentes version de l’afficheur ou de l’autoradio.

Identification des pins

Mais ce n’est pas un problème, il suffit de démonter pour vérifier qu’on ne va rien faire de dangereux. On peut rapidement identifier les pins à la masse, ceux des alimentations, et certains signaux de puissance.

Connecteur Gris :

  1. NTC
  2. GND
  3. NC
  4. NC
  5. GND
  6. Éclairage feux? (in)
  7. Éclairage backlight? (in)
  8. +12V (contact?, in)
  9. +12V (permanent?, in)
  10. NC
  11. GND
  12. LCD_ENABLE (12V, in)
  13. SDA (TTL, io)
  14. SCL (TTL, io)
  15. !MRQ (TTL, io, active-low)

Connecteur Rouge (TTL) :

  1. NC
  2. NC
  3. NC
  4. NC
  5. NC
  6. NC
  7. NC
  8. NC
  9. Commun 1
  10. Retour A
  11. Commun 2
  12. Retour B
  13. Commun 3
  14. Retour C
  15. NC

Ça permet de comprendre la majorité du câblage :

Connections LCD, commodo, autoradio

Et on peut aussi reverse-engineerer la matrice des boutons utilisés pour les commandes au volant:

Fonction Pin commun Pin retour
OK A 1
VOL- A 2
VOL+ A 3
Source L B 1
Source R B 2
Pause B 3
Molette 1 C 1
Molette 2 C 2
Molette 3 C 3

Curieusement, la molette n’est qu’un interrupteur rotatif à 3 positions, ça suffit à détecter le sens de rotation ça donne l’impression de bien plus de positions (6 par tour).

Câble

Dans ce cas, la solution la plus pratique est de prendre une rallonge avec des câbles mini-ISO branchés sur l’autoradio, de couper chaque fils et de les brancher sur un connecteur à 0.1”, pour y mettre des cavaliers pour une connection directe, des fils en Y pour espionner le bus ou bien des fils vers un MCU ou un analyseur logique.

Câble Man in the middle

Connecteur jaune C1 :

  1. SDA (TTL 5V)
  2. SCL (TTL 5V)
  3. !MRQ (TTL 5V)
  4. LCD_ENABLE (+12V)
  5. GND On peut se contenter de n’utiliser que ce connecteur pour afficher ce que l’on veut, et reverse-engineerer une partie du protocole.
I2C bus reverse-engineering sur-place

Comme je ne voulais pas décharger la batterie ni passer plusieurs heures dans le froid, j’ai préféré démonter l’afficheur et l’autoradio pour les utiliser à l’intérieur.

I2C bus reverse-engineering sur-table

Protocole

Ça nous avance bien de connaître le matériel, mais il faut encore comprendre comment l’autoradio communique avec l’écran LCD. Pour ça, on va utiliser un analyseur logique (Cypress FX2 et sigrok/pulseview) et regarder tout ce qui passe sur le bus i2c.

Analyseur logique Cypress FX2

On peut voir que le bus i2c fonctionne à 7.14kHz et que le signal !MRQ est constamment tiré à 0 avant que quelque chose ne soit transféré sur le bus.

En débranchant le signal MRQ de chaque côté, on peut voir que l’afficheur le force à 0 en attendant une trame de l’autoradio, mais que l’autoradio le force aussi à 0 avant d’envoyer une tram à l’autoradio. On peut aussi voir que l’autoradio est maître sur le bus i2c, et identifier l’adresse de l’écran LCD (0x23).

On peut voir plusieurs messages de 2 octets de long [0x01, 0x10] ou [0x01, 0x11], demandés au moins toutes les 500ms par l’autoradio (initié par l’afficheur qui tire le signal MRQ à 0, puis répond lorsque l’autoradio envoie une requête).

I2C on idle

Commandes au volant

Il y a aussi une trame répondue par l’afficheur lorsqu’on appuie sur un bouton des commandes au volant, et qui permet de déduire le code de chacune des commandes au volant. L’afficheur tire le signal MRQ à 0, l’autoradio envoie une requête, puis l’afficheur envoie la trame correspondant aux boutons pressés.

I2C on button press
Bouton octet 0 octet 1 octet 2 octet 3 octet 4 action
OK 0x04 0x82 0x91 0x00 0x00 press
OK 0x04 0x82 0x91 0x00 0x40 hold
Source R 0x04 0x82 0x91 0x00 0x01 press
Source R 0x04 0x82 0x91 0x00 0x81 hold
Source L 0x04 0x82 0x91 0x00 0x02 press
Source L 0x04 0x82 0x91 0x00 0x82 hold
Volume + 0x04 0x82 0x91 0x00 0x03 press
Volume + 0x04 0x82 0x91 0x00 0x43 hold
Volume - 0x04 0x82 0x91 0x00 0x04 press
Volume - 0x04 0x82 0x91 0x00 0x44 hold
Pause 0x04 0x82 0x91 0x00 0x05 press
Wheel up 0x04 0x82 0x91 0x01 0x41
Wheel down 0x04 0x82 0x91 0x01 0x01

Les valeurs 0x41 et 0x42 pour le dernier octet sont aussi interprétées par l’autoradio comme les boutons Source R et Source L maintenus appuyés, et il n’y a pas de code lorsqu’on maintient appuyé le bouton Pause ou pour la molette.

Affichage

L’afficheur est rafraichi uniquement en cas de besoin, on peut l’observer en appuyant sur un bouton de l’autoradio, qui va forcer le signal MRQ à 0, puis va envoyer une trame entre 13 et 16 octets avec les caractères à afficher.

Displaying line with I2C
Affichage 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
98.5 0x0f 0x90 0x7f 0x29 0xff 0x3f 0x35 0x81 0x20 0x20 0x20 0x20 0x39 0x38 0x35 0x20
CASS [=] 0x0f 0x90 0x7f 0x55 0xff 0xff 0x60 0x01 0x43 0x41 0x53 0x53 0x20 0x04 0x05 0x06
BAYERN 3 0x0f 0x90 0x7f 0x55 0xff 0x3f 0x75 0x01 0x42 0x41 0x59 0x45 0x52 0x4E 0x20 0x33

Bingo ! Les trames à partir de l’octet 9 ressemblent à de l’ASCII (les caractères clignotent si le MSB est à 1), l’octet 7 permet d’afficher le point décimal et le digit de mémoire/piste, et l’octet 6 permet d’afficher les pictogrammes Tuner Preset, Tuner Manu, Dolby et MSS.

Programme

On va remplacer l’autoradio par un Arduino Mega (n’importe quel micro-contrôleur avec un périphérique i2c et des IO TTL suffit), pour pouvoir écrire sur l’afficheur et lire l’état des boutons.

Avec un Atmel AVR, il faut forcer le bitrate/prescaler à environ 7kHz avec les lignes suivantes:

void conf() {
TWBR = 0xff;
TWSR = 0x01;
}

Comme c’est un programme de test qui n’a qu’une seule fonction et un MCU surpuissant pour son utilisation, il est possible d’écrire un code peu optimisé (polling au lieu d’interruptions, copy/paste).

On va initialiser l’écran en envoyant quelques trames [0x01, 0x10] et [0x01, 0x11], puis on peut écrire un peu ce qu’on veut :

void writerandom(byte *data, int len) {
    while(digitalRead(2)); // polling the MRQ line
    Wire.beginTransmission(0x23);
    conf(); // overwrites the bitrate/prescaler after the Wire lib configures the i2c
    Wire.write(data, len);
    Wire.endTransmission();
}

Dans ce cas, on peut commencer par copier/coller des trames récupérées en sniffant celles envoyées par l’autoradio, puis on va reverse-engineerer le protocole complet en bouclant sur tous les caractères entre 0x00 et 0xFF, et en testant les pictogrammes.

On va aussi pouvoir lire l’état des boutons, qui sont des trames de 5 octets :

void read01() {
    while(digitalRead(2)); // polling the MRQ line, pulling the MRQ line low and waiting 500us also works
    conf(); // overwrites the bitrate/prescaler after the Wire lib configures the i2c
    Wire.requestFrom(0x23, 5);
    TWBR = 0xff;
    TWSR = 0x01; // 0x00 appears to work as well
    for(i = 0; i < 5; i++) {
        READDATA_ = Wire.read();
    }
}

Références

Electronics Électronique puissance semiconducteur semiconductors power Hardware CPE INSA Xavier Bourgeois

Xavier B.