Skyduino:~#
Articles
arduino, Corrigé, programmation

[Arduino] Afficheur 7 segments / 16 digits (base TM1640)

Bonjour tout le monde !

Aujourd’hui je vous propose un article bien sympa à base d’arduino (pas d’article ROHS ce coup ci ;))

Le sujet du jour porte sur cet afficheur 7 segments à 16 digits de DealExtreme :
http://dx.com/jy-mcu-16x-digital-tube-yellow-led-module-104311

L’afficheur :

Que dire dessus …
– 21cm de long,
– 16 digits,
– 7 segments + point décimal par digits,
– une led témoin d’alimentation,
– un contrôleur série TM1640.

16 digits pour même pas 8$ … que demander de plus ?
C’est vraiment une belle bête, je suis très satisfait de mon achat 🙂

Le contrôleur :

La petite bête qui contrôle tout le bouzin est un TM1640. C’est un ci « made in china » de la société « Titan micro electronics ».
Le TM1640 permet de contrôler jusqu’à 16 digits via un interface proche du SPI (plus d’info plus bas).

Problème :
Aucun datasheet dans une autre langue que le chinois n’est disponible sur internet … c’est ennuyeux …
Et oui pour moi le chinois … c’est du chinois … 🙂

Mais un mec malin du forum DX en a fait une traduction partielle avec google trad !
http://club.dx.com/forums/Forums.dx/threadid.1013434?page=2
Youpi ! Let’s go !

Tout est détaillé, forme du signal, protocole, commandes, … TOUT 🙂

Voici le plus intéressant :

Forme du signal et protocole :

Carte de la mémoire ram :

Détail de l’agencement des éléments des digits et de la luminosité :

Avec tout ça il est possible de faire un code de test sans problème !

A première vue, en analysant la forme du signal on peut voir qu’il s’agit d’une sorte de communication SPI, mais avec la particularité que le signal SS (slave select) est fusionné avec DIN.
Cette particularité étant utilisé comme bit de start / stop.

En gros au lieu d’avoir un port SPI « normal » (SCLK, MOSI, SS) on obtient un port à deux fils SCLK / DIN.
Il suffit donc que je reprenne mon code de « software SPI » et que je fusionne DIN et SS … trop simple 🙂

Bon ok, ma première idée fut d’utiliser une librairie toute prête comme celle ci :
http://code.google.com/p/tm1638-library/

Mais cela n’aurait pas été drôle, c’est tellement plus marrant de partir à l’aventure et de coder le tout soit même 😉

Voici donc ma version maison qui émule un port SPI avec la particularité de fusionner SS et DIN :

/* Pinmapping */
#define PORT_REG PORTB  // Port de sortie sur lequel est câblé le TM1640
#define DDR_REG DDRB    // Registre DDR associé au port de sortie
#define PIN_DIN 4  // PB4 -> D12 // Broche DIN
#define PIN_SCLK 5 // PB5 -> D13 // Broche SCLK

/* Data pin macro*/
#define dataLow() PORT_REG &= ~(1 << PIN_DIN)
#define dataHigh() PORT_REG |= (1 << PIN_DIN)
#define dataSet(x) (x) ? dataHigh() : dataLow()

/* Clock pin macro */
#define clockLow() PORT_REG &= ~(1 << PIN_SCLK)
#define clockHigh() PORT_REG |= (1 << PIN_SCLK)

/* Switch display macro */
#define displayOff() M1640_command(0x80)

/* Table de correspondance chiffre / lettre -> binaire */
byte numericTable[] = {
/* dp, g, f, e, d, c, b, a */
  B00111111, // 0
  B00000110, // 1
  B01011011, // 2
  B01001111, // 3
  B01100110, // 4
  B01101101, // 5
  B01111101, // 6
  B00000111, // 7
  B01111111, // 8
  B01101111, // 9
  B01110111, // A
  B01111100, // B
  B00111001, // C
  B01011110, // D
  B01111001, // E
  B01110001  // F
};

/* Envoi un octet au TM1640 */
void TM1640_write(byte raw) {
  for (byte i = 0; i < 8; ++i) {
    clockLow();
    dataSet(raw & 1);
    raw >>= 1;
    clockHigh();
  }
}

/* Efface le contenu de la ram du TM1640 (= efface l'écran) */
void TM1640_clear(void) {
  dataLow();
  TM1640_write(0xC0);
  for (byte i = 0; i < 16; ++i) TM1640_write(0x00);
  dataHigh();
}

/* Envoi une commande au TM1640 */
void TM1640_command(byte cmd) {
  dataLow();
  TM1640_write(cmd);
  dataHigh();
}

/* Envoi un octet de données au TM1640 chip */
void TM1640_data(byte address, byte data) {
  TM1640_command(0x44);
  dataLow();
  TM1640_write(0xC0 | address);
  TM1640_write(data);
  dataHigh();
}

/* Initialise le TM1640 */
void TM1640_setup(byte intensity) {

  /* Place les broches DIN et SCLK en sortie et à HIGH*/
  DDR_REG |= (1 << PIN_DIN) | (1 << PIN_SCLK);
  PORT_REG |= (1 << PIN_DIN) | (1 << PIN_SCLK);

  /* Initialise le TM1640 et règle la luminosité */
  TM1640_command(0x40);
  TM1640_command(0x88 | (intensity & 7));

  /* Efface le contenu de la ram du TM1640 */
  TM1640_clear();
}

/* setup() */
void setup() {
  TM1640_setup(5); // Initialise le TM1640 avec une luminosité de 5 / 7
}

/* loop() */
void loop() {
  static unsigned long i = 0; // Variable s'incrémentant à l'infini
  static unsigned long t = 0; // Temps en ms du dernier affichage

  /* Rafraîchissement de l'affichage toute les 250ms */ 
  if(millis() - t > 250) {    
    TM1640_data(15, numericTable[i % 10]);
    TM1640_data(14, numericTable[(i / 10) % 10]);
    TM1640_data(13, numericTable[(i / 100) % 10]);
    TM1640_data(12, numericTable[(i / 1000) % 10]);
    TM1640_data(11, numericTable[(i / 10000) % 10]);
    TM1640_data(10, numericTable[(i / 100000) % 10]);
    TM1640_data(9, numericTable[(i / 1000000) % 10]);
    TM1640_data(8, numericTable[(i / 10000000) % 10]);
    TM1640_data(7, numericTable[(i / 100000000) % 10]);
    TM1640_data(6, numericTable[(i / 1000000000) % 10]);
    TM1640_data(5, 0);
    TM1640_data(4, 0);
    TM1640_data(3, 0);
    TM1640_data(2, 0);
    TM1640_data(1, 0);
    TM1640_data(0, 0);

    t = millis();
  }

  ++i;
}

Ce code est très simple, il fournit une série de 5 fonctions bas niveau :
void TM1640_write(byte raw) pour envoyer un octet brute,
void TM1640_clear(void) pour effacer la ram du TM1640,
void TM1640_command(byte cmd) pour envoyer une commande,
void TM1640_data(byte address, byte data) pour envoyer un octet de données à une adresse précise,
void TM1640_setup(byte intensity) pour initialiser le TM1640 et régler la luminosité.

Au passage vous remarquerez la présence d’une table de conversion lettres / chiffres -> binaire.
Merci google pour les associations 8 => abcdefg, … et mon script python tiré d’un précédant article sur les afficheurs 7 segments.
Faire cette table à la main aurait été une pure galère …

Niveau hardware / câblage :

Vous avez besoin :
- une carte arduino,
- un afficheur 16 digits sur base TM1640,
- 4 fils mâle / mâle

Le câblage :
Arduino -> TM1640
D13 -> SCLK
D12 -> DIN
VCC -> VCC
GND -> GND

C’est pas compliqué 😉

Hop hop hop ! Montage !

Et au final voila ce que ça donne :

Enjoy ! 🙂

Discussion

15 réflexions sur “[Arduino] Afficheur 7 segments / 16 digits (base TM1640)

  1. Hello, merci pour cet article, très clair !
    Je suis tout nouveau en électronique et programmation arduino, ma question va peut être parraitre simple.
    Est-ce que je peux piloter d’autres périphérique I2C avec ce TM1640 ? Vu qu’il n’y a pas de pin SS, comment choisir le bon périphérique ?
    Pour être complet, j’aimerais également piloter un lecteur MP3 (VS1053B), et une horloge RTC1307.

    Merci !

    Publié par Rathouis Charles | 1 juin 2012, 11 h 39 min
    • Cet afficheur ce controle via un port « quasi » SPI.
      L’I2C et le SPI sont deux type de communication différents 😉

      Tu peut controler sans probléme tes périphériques I2C en même temps que l’afficheur vu que le bus I2C se trouve sur A4 et A5, alors que l’afficheur se trouve sur 2 broches de ton choix.
      Ce sont deux choses bien séparé.

      Publié par skywodd | 1 juin 2012, 13 h 57 min
      • Merci, les termes ne sont pas encore très clair et je confonds un peu. Donc en fait, j’ai 2 périférique SPI (l’afficheur et lecteur MP3), et 1 I2C (clock).
        D’après ce que j’ai compris, je dois utiliser une pin SS différente pour l’afficheur et le mp3. Comment faire pour selectionner le bon avec cet afficheur qui n’a pas de pin SS ?

        Publié par Rathouis Charles | 1 juin 2012, 14 h 56 min
  2. @Rathouis_Charles :
    L’afficheur n’as pas besoin d’être obligatoirement sur D13 / D12, tu peut changer les broches utilisé en modifiant les #define en haut du code 😉
    (Voir http://arduino.cc/it/Hacking/PinMapping168 pour les nom de port à utiliser)

    Exemple pour avoir A0 (DIN) et A1 (SCLK) :

    /* Pinmapping */
    #define PORT_REG PORTC  // Port de sortie sur lequel est câblé le TM1640
    #define DDR_REG DDRC    // Registre DDR associé au port de sortie
    #define PIN_DIN 0  // PC0 -> A0 // Broche DIN
    #define PIN_SCLK 1 // PC1 -> A1 // Broche SCLK
    

    Tu peut choisir quelles broches utiliser, c’est ça l’avantage du software SPI 😉

    Publié par skywodd | 1 juin 2012, 18 h 54 min
    • Super, merci ! Je test ca des reception.

      Publié par Rathouis Charles | 1 juin 2012, 19 h 01 min
    • Bonjour ! Vos conseils marchent parfaitement. Par contre, suite à une mauvaise manipulation, j’ai « touché les fils », et le condensateur C1 a coté de la puce TM1640 a brulé.
      Est-ce que quelqu’un peut me donner la valeur de ce composant pour que je le change ?

      Merci !

      Publié par ratwix | 17 juin 2012, 17 h 14 min
      • 47µF 10volts

        Ps: si le condensateur a brulé le TM1640 risque d’avoir pris chaud lui aussi.

        Publié par skywodd | 17 juin 2012, 21 h 29 min
      • Merci ! Je pense qu’il marche encore, vue que l’affichage fonctionne mais « clignotte »…

        Publié par ratwix | 17 juin 2012, 23 h 21 min
  3. Merci pour ce tuto, il m’a permis de vérifier le bon fonctionnement de l’afficheur, je ne sais pas encore à quoi il servira mais pour le prix on verra après. ^^
    Si ça peut intéresser j’ai un peu modifié le tuto, j’ai mis le tout dans une librairie où j’ai ajouté une table de caractère ascii trouvé sur la librairie que tu as cité. Le sketch affiche 4 chiffres en hexa générés aléatoirement par l’arduino et un compteur type horloge 24h.

    http://toutourien.org/Public/

    Publié par Jean RnR (@HyD_z) | 12 octobre 2012, 12 h 27 min
  4. Hi !
    Existe il une version de code source de commande du même TM1640 mais adaptée au Raspberry_Pi sur un port SPI ???

    MNY TNX

    Publié par F5ZZ | 14 février 2013, 16 h 36 min
    • Dans la mesure ou je n’ai pas de Raspberry Pi et que cet afficheur est plutôt difficile à trouver je ne pense qu’il existe de code tout fait pour le contrôler …

      Sinon tu as la doc des registres dans l’article et un exemple de code pour la génération du signal de commande.
      À toi de jouer avec les GPIO du Raspberry Pi pour faire la même chose.

      Ps: ce n’est pas du SPI ! C’est très proche mais ça n’en est pas !

      Publié par skywodd | 14 février 2013, 19 h 34 min
  5. OK Skywood pour les remarques.
    – Cet afficheur est disponible pour environ $8 en chine, ce n’est pas cher.
    -Je confirme qu’il n’y a pas de code spécifique SPI du TM1640 disponible sur le waibe. J’en suis le premier étonné!
    Si l’on utilise le GPIO du raspberry pour commander l’afficheur on peut transposer intégralement le soft de l’ Arduino mais cela monopolise les ports GPIO qui dans mon projet auront une fonction particulière. Aussi il me faut impérativement une solution SPI en épluchant et combinant les instruction de l’arduino et le data sheet du TM1640.
    Moralité, je bosse sur le sujet et si ça marche en ferai profiter la communauté.
    ++,
    Mike

    Publié par F5ZZ | 15 février 2013, 13 h 56 min
  6. Bonsoir,

    deux choses que je comprend pas trop :
    – comment afficher d’autres lettres
    – comment utiliser les points separateurs ?

    Merci

    Publié par Enky | 11 juin 2013, 22 h 45 min

Skyduino devient Carnet du Maker

Le site Carnet du Maker remplace désormais le blog Skyduino pour tout ce qui touche à l'Arduino, l'informatique et au DIY.