109 – Doper les sorties d’un Attiny

S’il est une chose qu’un Attiny85 a du mal à gérer, c’est un nombre « important » de sorties.
Et oui, 6 c’est déjà très important pour lui, il faut déjà choisir entre l’entrée/sortie et le reset…

Un attiny, trop petit ?

P9130038-800Alors, piloter des paquets de leds, pfiou…
Pourtant, c’est sympa un attiny85: petit, pas besoin de quartz, 16Mhz sous 5v; pour pas mal d’applications ça suffit largement.
Et puis si on veut piloter des tas d’E/S, même un arduino nano, ça ne suffit pas. Il faut sortir la grosse artillerie des Méga et Due, ou se reposer sur des circuits d’extensions.

Alors justement, comme j’ai joué avec le MM5451 (cf la fiche MM5451 et le tuto Arduino pour 35 leds), pas de raison de faire des jaloux, j’ai voulu voir ce que ça donnait avec un Attiny. J’ai utilisé pour cela ma carte de dev attiny « custom »

Je pense notamment à une utilisation pour un système d’afficheur à persistance rétinienne en couleur.

Je n’ai pas été déçu !

Non, j’ai pas été déçu !

Avec l’Arduino (atmega328) et du bitbanging par manipulation directe de ports, on arrivait à une fréquence d’horloge de 220Khz environ et une trame qui dure 120µs.
Le MM5451 est donné pour une fréquence d’horloge max de 500Khz.

Avec un attiny en mode SPI hardware, qu’est ce que ça donne ?
voici:
mm5451-TimingHardw
On remarque déjà les différents blocs : les bits sont shiftés par Octet, d’où les groupes de 8.
L’horloge n’est pas régulière, mais le MM5451 n’est pas géné pour autant.

Mieux… Durée d’une trame, moins de 16µs, et une fréquence d’horloge mesurée à 2Mhz, soit 4 fois la fréquence max annoncée par le fabricant.

Je ne sais pas si cet overclocking est de rigueur avec ce type de composant ou si je suis tombé sur une exception.
En tout cas, ça montre qu’on peut prendre des libertés avec la doc, et qu’il ne faut pas craindre de pousser un peu les circuits dans leurs limites.

Le code

Voilà le code de la librairie que j’ai utilisé (tinySPI.h, elle n’est pas de moi mais je l’ai un peu adaptée)
Elle utilise le « USI » matériel de l’attiny: une interface série matérielle.
Comme ici on n’a besoin que de deux fils, j’ai un peu taillé dans le code de la librairie.

#include 
// lib Attiny SPI
// Written by Nick Gammon
// March 2013

namespace tinySPI {
const byte DI   = 0;  // D0, pin 5  Data In
const byte DO   = 1;  // D1, pin 6  Data Out (this is *not* MOSI)
const byte USCK = 2;  // D2, pin 7  Universal Serial Interface clock
const byte SS   = 3;  // D3, pin 2  Slave Select

void begin () {
  // digitalWrite (SS, HIGH);  // ensure SS stays high until needed
  pinMode (USCK, OUTPUT);
  pinMode (DO,   OUTPUT);
  // pinMode (SS,   OUTPUT);
  //  pinMode (DI,   INPUT);
  USICR = bit (USIWM0);  // 3-wire mode
}  // end of tinySPI_begin

// What is happening here is that the loop executes 16 times.
// This is because the 4-bit counter in USISR is initially zero, and then
// toggles 16 times until it overflows, thus counting out 8 bits (16 toggles).
// The data is valid on the clock leading edge (equivalent to CPHA == 0).

byte transfer (const byte b) {
  USIDR = b;  // byte to output
  USISR = bit (USIOIF);  // clear Counter Overflow Interrupt Flag, set count to zero
  do  {
    USICR = bit (USIWM0)   // 3-wire mode
            | bit (USICS1) | bit (USICLK)  // Software clock strobe
            | bit (USITC);   // Toggle Clock Port Pin
  } while ((USISR & bit (USIOIF)) == 0);  // until Counter Overflow Interrupt Flag set
  return USIDR;  // return read data
}    // end of tinySPI_transfer
};  // end of namespace tinySPI

Et le code principal :

/*
Test du MM5451 avec un Attiny85 en mode hardware SPI
*/

#include "tinySPI.h"

void setup (void)  {
  tinySPI::begin ();
}

void loop (void)  {
  while (1) {
    delay(1000);
    // le SPI envoi MSB en premier.
    // 1 bit de start, 3 bits utiles => 4 bits vides et inutiles au début
    tinySPI::transfer (B00001000 | B010);
    // reste 32 bits
    tinySPI::transfer (B10101010);
    tinySPI::transfer (B10101010);
    tinySPI::transfer (B10101010);
    tinySPI::transfer (B10101010);
    delay(1000);
    // 1 bit de start, 3 bits utiles => 4 bits vides et inutiles au début
    tinySPI::transfer (B00001000 | B111);
    // reste 32 bits
    tinySPI::transfer (255);
    tinySPI::transfer (255);
    tinySPI::transfer (255);
    tinySPI::transfer (255);
    delay(1000);
    // 1 bit de start, 3 bits utiles => 4 bits vides et inutiles au début
    tinySPI::transfer (B00001000 | B000);
    // reste 32 bits
    tinySPI::transfer (0);
    tinySPI::transfer (0);
    tinySPI::transfer (0);
    tinySPI::transfer (0);
  }
}  

Comme pour la démo Arduino: on allume une led sur deux, puis tout, puis on efface tout et on recommence.

Prochaine étape : le système POV (persistence rétinienne).

Des questions, des remarques ? n’hésitez pas !

Laisser un commentaire