Emillotron – parte 1

Update 20 Giu 2022 – All parts together and translated into English
https://hackaday.io/project/185954-use-yamaha-opl2-chip-with-esp8266-emillotron

Il titolo è un mix (una crasi) tra Emilio (il mio nome) e Mellotron. Il Mellotron andava forte negli anni ’60, ai tempi dei Beatles. In quegli anni, molto prima della “musica digitale”, qualcuno inventò un sistema meccanico che faceva scorrere un nastro magnetico davanti a una testina quando si premeva un tasto della keyboard, riproducendo così il suono registrato. Il dispositivo che sto per presentarvi non ha niente a che fare con tutto questo… Si tratta sempre di generare suoni e possibilmente musica, ma qui andremo a usare la sintesi FM (si tratta di Frequency Modulation, ma non è una radio privata).

Molti (MOLTI) anni fa, iniziai la mia carriera di progettista firmware / software / electronic designer in un’azienda che produceva strumenti musicali elettronici (tastiere, organi, ecc) ed ebbi quindi la possibilità di entrare in contatto con tutto ciò che di nuovo appariva sul mercato. All’inizio, era tutto molto strano, tanta parte analogica e quasi nulla di digitale, ma io avevo scritto del software per il micro Intel 8080 e chiesi ai titolari della ditta di poter condurre esperimenti usando un microcontrollore. Beh, me lo consentirono.

Si era proprio agli inizi: non c’era niente e il problema principale era di generare le note, in polifonia, con dei timbri adeguati. Ci si arrabbattava, era già tanto non dover più usare un oscillatore per ogni tasto, ma la tecnologia era abbastanza povera. Poi, arrivò Yamaha con la sintesi FM (teorizzata da Chowning) a portare scompiglio. Improvvisamente, si poteva creare una tastiera polifonica e politimbrica con un singolo chip! Certo, c’era da studiare parecchio, ma io amo farlo e così, insieme al consulente musicale, “smanettavo” i registri del chip per ottenere suoni decenti e talvolta anche indecenti, tipo richiami alieni!

Quello era il passato, si scrivevano i firmware in assembler ed è un modo di programmare che oggi è davvero desueto, ma uno che ha programmato in assembler, può scrivere programmi in qualsiasi linguaggio, perché conosce la macchina “deep inside” e sa come funziona.

Veniamo ad oggi: ho trovato in garage questo prototipo, quasi completamente privo di integrati. Era probabilmente una scheda di prova, perché sotto ci sono un paio di fili volanti. Trattandosi di circa 40 anni fa, non ricordo i dettagli, ma con ogni probabilità avevo progettato la scheda con il micro Rockwell 6502 (il mitico) dato che in quel periodo usavo un Apple II come sistema di sviluppo.

Gli unici integrati rimasti sulla scheda sono quelli relativi alla generazione del suono, quindi il chip OPL-II, il DAC e due operazionali con funzioni di buffer e filtro. Si noti che a quei tempi non esisteva il concetto di condivisione e si faceva di tutto per non farsi copiare i circuiti dalla concorrenza! Infatti, come si vede dall’immagine, le sigle degli integrati sono cancellate e non è un effetto del programma di grafica, ma di una piccola fresa 🙂

Insomma, veniamo a noi: ecco il progetto in questione. Questo dispositivo che sto per presentare, fa parte di un progetto più ampio che è una “macchina per rilassamento integrale”. La sto facendo per me, dato che sono stressatissimo dalla attuale mancanza di libertà per una strana “emergenza sanitaria” che tiene conto solo dei posti letto in ospedale e non dei danni mentali, a lungo termine, della privazione di molte delle libertà essenziali.

Insomma, questa macchina sarà in grado di generare stimoli visivi (striscia di led colorati), acustici (questo generatore di suoni) e tattili (scatolette da tenere in mano con motorini che generano vibrazioni).

Per la sintesi dei suoni FM, vi consiglio di leggere un mio articolo di qualche anno fa a questo link: https://ficara.altervista.org/?p=1381 . L’articolo ebbe un certo successo, anche perché pubblicai i files sorgenti in C (convertiti dai miei originali in assembler) e il tutto venne citato persino da hackaday che è un bellissimo sito per idee innovative e “hacks” di ogni genere.

Il progetto presentato allora, era basato su un Microchip PIC a 40 pin che pilotava una scheda audio standard per PC (bus ISA, roba ormai vetusta). In questa nuova versione, ho deciso di usare semplicemente un chip Yamaha OPL-II (YM3812) con un circuito basato su Esp8266 (modulo mini D1) e periferiche adeguate (I2C + SPI) , sviluppando il firmware (con opportune conversioni dall’originale) su piattaforma Arduino. Se si usano solo gli OPL-II (due operatori) il codice è praticamente identico sia per i chip usati sulle schede audio ISA, sia per lo YM3812.

Vediamo cosa serve per interfacciarsi al chip OPL-II. Questo è il pinout:

I segnali necessari a scrivere i vari registri del chip sono: D7..D0 (il bus dati); A0 l’indirizzo di base (nota: di registri ce ne sono tanti, ma come mai c’è un solo bit di indirizzamento ? Semplice. Se si usa A0 = 0 si invia su D7..D0 l’indirizzo del registro da leggere o scrivere; se si usa A0 = 1, si scrive il registro precedentemente indirizzato, quindi è un’operazione in due fasi). Altri segnali di controllo sono il /CS (chip select), il /RD (read) e il /WR (write). Infine abbiamo /IC che è a tutti gli effetti il pin di reset e /IRQ che è il segnale di interrupt in uscita dal chip, che al momento non è usato.

Gli altri pin sono per l’alimentazione, per il DAC e per l’oscillatore di riferimento. Come fare a collegare tutti questi segnali alla scheda ESP8266 mini D1 ? Chiaramente, abbiamo bisogno di qualche espansione di I/O. Ho deciso di usare l’interfaccia I2C per il bus dati D7..D0, mentre la SPI per i vari segnali di controllo. Ho usato solo materiale che avevo a disposizione da precedenti esperimenti e così vedrete una interfaccia I2C originariamente creata per pilotare display LCD 2×16, con leggere modifiche:

Questo circuito permette di controllare un display LCD (a caratteri) tramite bus I2C. Utilizza il controller LCD nella modalità 4 bit, così con le 8 linee di I/O disponibili sull’integrato PCF8574T riesce a dare tutti i segnali necessari. Le modifiche che ho fatto servono solo per avere tutti i segnali su un unico connettore, come da schema qui sotto:

I segnali DB0..DB3 del connettore LCD erano originariamente liberi e ora invece sono collegati a D3, SDA e SCL. I segnali RS, R-/W ed E erano già collegati a D0, D1 e D2. Il P3 (pin 7 dell’integrato) era collegato ad una resistenza in base al transistor che pilotava la backlight. Ho rimosso i componenti non necessari e collegato con dei fili i segnali che mi servivano e in questo modo ora ho tutto su di un singolo connettore. Le porte di I/O dell’integrato PCF8574T sono di tipo “quasi bidirectional” che significa che ogni linea si può usare come input o output senza dover settare un registro di direzione. In pratica, per leggere un input, si deve prima scrivere un ‘1’ sull’output e questo attiva una sorta di “weak pull-up”, cosa molto diversa da una porta di uscita di tipo push-pull.  In ogni caso, esiste un altro modulino di espansione I/O via I2C che ha già tutti i pin disponibili su di un singolo connettore e volendo si può usare quello ed evitare questa modifica al modulo I2C – LCD. Ripeto, io avevo questo e l’ho usato!

In questa applicazione mi servono tutti gli 8 bit del PCF8574 per il bus dati D7..D0 e quindi dovrò usare un secondo circuito per espandere gli I/O ed avere le linee necessarie per i segnali di controllo. Per il momento, cominciamo a testare la sezione I2C. Questo è lo schema delle connessioni tra il modulo ESP8266 mini D1 e il modulo (modificato) I2c-LCD 8574:

Bene, è arrivato il momento di scrivere il primo programmino di prova. Eccolo qui:

// Test i2c I/O expander (output mode)

#include <Wire.h>  // this is for PCF8574T
uint8_t wdata; // data write to D7..D0 bus
 
void setup()
{
  delay(250); // power-on delay
  Wire.begin(4, 5); // begin with PCF8574 (sda, scl)
}

void loop()
{
  Wire.beginTransmission(0x27); // address of PCF8574T
  Wire.write(wdata++); // write to D7..D0 bus then incr. counter
  Wire.endTransmission();
  delay(5); // cycle time
}

Il programma è davvero molto semplice. Scrive un byte sulla porta (espansione i2c) e poi ne incrementa il valore. Il risultato che vedremo sui pin D0..D7 sarà una serie di onde quadre, ognuna di periodo doppio di quella del pin precedente. Dato che abbiamo usato un delay di 5mS, la frequenza che avremo sul pin D0 è di circa 100Hz (10mS, un cambio di stato ogni 5mS). Una volta compilato il sorgente per ESP8266 mini D1 e trasferito l’eseguibile nel modulino, avremo questo risultato:

Il tempo della funzione delay non è compensato (non si tiene conto della durata delle operazioni che seguono); per avere i tempi più precisi conviene usare la funzione millis() nel loop, ma in questo caso ci serve solo per vedere se tutto funziona e va bene così. L’indirizzo dell’espansione i2c è fissato a 0x27, che è quello del PCF8574 con i pin A0..A2 tutti a livello alto (senza ponticelli).

Se non abbiamo un oscilloscopio, possiamo costruire questo piccolo attrezzo (ci verrà utile subito dopo per il test degli inputs)

Come si vede dallo schema, il led si accende se tocchiamo con il puntale un pin a livello basso, sempre che il coccodrillo rosso sia collegato a una tensione positiva. In questo caso, essendo il PCF8574 alimentato a 5V, possiamo collegare il coccodrillo rosso a tale tensione. Dato che il pin D0 commuta a 100Hz, se misuriamo tale pin vedremo il led sempre acceso (se non siamo vulcaniani…) e quindi converrà spostarci almeno sul pin D2, dove vedremo un lampeggio veloce, ma perfettamente distinguibile. Il coccodrillo nero non sarà collegato, oppure potrà essere collegato a GND, ma il ponticello dovrà essere aperto, così come il pulsantino. Essendoci comunque una resistenza da 1K, c’è una certa protezione in caso di distrazioni (I=5V/1KOhm), ma il led non sarà mai spento! Avremo, invece, una specie di luce modulata, più intensa quando il pin è a livello 0, meno quando è a livello 1.

Passiamo ora al test della parte di input. Anche qui abbiamo un piccolissimo programma:

// Test i2c I/O expander (input mode)

#include <Wire.h> // this is for PCF8574T
uint8_t rdata; // data read from D7..D0 bus
uint8_t count; // counter for n prints on the same line
char pbuff[6]; // buffer for formatted serial print
  
void setup()
{
  delay(250); // power-on delay
  Wire.begin(4, 5); // begin with PCF8574 (sda, scl)

  Serial.begin(38400); // init serial port (we use serial monitor for test)
  Serial.println("\r\nHW reset");

  Wire.beginTransmission(0x27); // address of PCF8574T
  Wire.write(0xFF); // all data high (quasi-bidirectional port can be used as input)
  Wire.endTransmission();

  count = 0; // init byte counter for print
}

void loop()
{
  delay(500); // cycle time
  Wire.requestFrom(0x27, 1); // address of PCF88574, 1 byte data
  rdata = Wire.read(); // read data in rdata
  Wire.endTransmission();
  sprintf(pbuff, "%02X ", rdata ^ 0xFF); // pbuff contains the HEX data (complemented)
  Serial.print(pbuff); // print to serial
  count = (++count & 0x0F); // increment counter in range 0..0x0F
  if(count == 0) // if counter is 0
    Serial.println(); // print crlf (16 bytes on a row)    
}

Il programma inizializza il PCF8574 e scrive 0xFF su D0..D7. Le linee quasi-bidirectional sono così nello stato adatto per l’utilizzo come input. Nel loop principale (temporizzato a 500mS) eseguiamo una lettura del bus e quindi una stampa in HEX del valore letto. Ho fatto il complemento perché è più facile capire 01 che FE… In effetti, usando il circuitino di test proposto prima e toccando con il puntale i vari pin D0..D7, avremo dei valori come 00 (il puntale non tocca nessun piedino) oppure 01, 02, 04, 08, 10, 20, 40, 80 toccando da D0 fino a D7. Ecco cosa leggiamo sul serial monitor:

In questo caso, possiamo scollegare il coccodrillo rosso dal positivo, mentre collegheremo quello nero a GND e chiuderemo il ponticello. Possiamo lasciare anche il coccodrillo rosso collegato al +5; il valore in tensione sul pin di input sarà un po’ più alto, ma dovrebbe essere letto ancora come ‘0’ quando tocchiamo con il puntale i pin D0..D7.

La parte 1 finisce qui. Seguirà la parte 2 con il dettaglio dell’altra espansione di output, realizzata con bus SPI, con il circuito integrato 74HC595A. A presto…