Controller un OPL2 avec un Arduino Mega (YM3812)

Bonjour,

Depuis un moment j'essaie de contrôler un OPL2 avec un Arduino MEGA 2560.
J'ai effectué les branchement sur une plaque d'expérimentation, normalement les branchement sont OK.

Le problème est la lecture/ecriture des registres de la puce, les commandes de lecture (OPL-> arduino) reste muette et je ne sais donc pas si les commandes d'écriture fonctionne.

Voici le code que j'ai écrit pour communiquer avec l'OPL:

//PORT use for data/address line
#define DATA_BUS PORTK
#define CONF_BUS DDRK
 
//Pin use for command
const int pin_a0 = A0;
const int pin_cs = A1;
const int pin_wr = A2;
const int pin_rd = A3;
const int pin_ic = A4;
 
void setup() {
  ym3812_init();
 
  ym3812_write2(0xA5, 0xAA);
  delay(500);
  ym3812_read2(0xA5);
 
}
 
void loop() {
 
  delay(1000);
}
 
void ym3812_init() {
  pinMode(pin_a0, OUTPUT);
  pinMode(pin_cs, OUTPUT);
  pinMode(pin_wr, OUTPUT);
  pinMode(pin_rd, OUTPUT);
  pinMode(pin_ic, OUTPUT);
   
  CONF_BUS = 0xFF;
  DATA_BUS = 0x00;
   
  //Trigger
  digitalWrite(pin_a0, 0);
  delay(10);
  digitalWrite(pin_a0, 1);
  delay(100);
}
 
 
 
void ym3812_write2(unsigned char reg, unsigned char data) {
  CONF_BUS = 0xFF; //OUTPUT
 
  //Address of Register
  digitalWrite(pin_a0, 0);
  delay(1);
  digitalWrite(pin_cs, 1);
  delay(1);
  digitalWrite(pin_wr, 1);
  delay(1);
   
  DATA_BUS = reg;
   
  delay(10);
  digitalWrite(pin_wr, 0);
  delay(1);
  digitalWrite(pin_cs, 0);
  delay(1);
  digitalWrite(pin_a0, 1);
   
  //reset DATA_BUS
  DATA_BUS = 0x00;
  
  //Data of register
  delay(100);
  digitalWrite(pin_cs, 1);
  delay(1);
  digitalWrite(pin_wr, 1);
  delay(1);
   
  DATA_BUS = data;
   
  delay(10);
  digitalWrite(pin_wr, 0);
  delay(1);
  digitalWrite(pin_cs, 0);
   
  //reset DATA_BUS
  DATA_BUS = 0x00;
}
 
unsigned char ym3812_read2(unsigned char reg) {
  unsigned char buffer;
  CONF_BUS = 0xFF; //OUTPUT
 
  //Address of Register
  digitalWrite(pin_a0, 0);
  delay(1);
  digitalWrite(pin_cs, 1);
  delay(1);
  digitalWrite(pin_wr, 1);
  delay(1);
   
  DATA_BUS = reg;
   
  delay(10);
  digitalWrite(pin_wr, 0);
  delay(1);
  digitalWrite(pin_cs, 0);
  delay(1);
  digitalWrite(pin_a0, 1);
   
  //reset DATA_BUS
  DATA_BUS = 0x00;
   
  delay(50);
 
  //read
   //CONF_BUS = 0x00;
   
  digitalWrite(pin_a0, 0);
  delay(1);
  digitalWrite(pin_cs, 1);
  delay(1);
  digitalWrite(pin_rd, 1);
   
  buffer = DATA_BUS;
   
  delay(10);
  digitalWrite(pin_rd, 0);
  delay(1);
  digitalWrite(pin_cs, 0);
  delay(1);
  digitalWrite(pin_a0, 1);
 
  return buffer;
 
}

Voici également une capture des transmissions entre arduino et OPL (4bits de données sur 8 + 4 signaux de commandes.)

Je peux également fournir de photo des différents signaux (Horloge, etc..) via mon Oscilloscope analogique (EDIT : Faute de copié-coller j'ai bien en Analyseur logique !)

Datasheets :

YM3812 (OPL2): YM3812 Fiches technique(PDF) - YAMAHA CORPORATION

YM3014 (DAC) : http://www.datasheetspdf.com/PDF/YM3014B/865757/1

Merci d'avance !

Bonjour,
ton code manque de commentaires, difficile à lire

je ferais les choses dans l'ordre suivant :

// sélection du chip
CS ↓
// (1) sélection de l'adresse
A ↨
// si écriture (WR) mise en place des données
D7 à D0 ↨ au niveau de l'arduino
// lecture ou écriture, constitue en même temps l'impulsion d'horloge genre SPI
// attention : pas de lecture (RD↓) avec les broches de l'arduino en mode sortie !!!
RD↓ ou WR↓
// on attend le temps qu'il faut
delay(un peu)
// si lecture (RD↓)
lectures des lignes D7 à D0, au niveau de l'arduino, triturage pour assembler la valeur du byte
// on termine l'impulsion d'horloge SPI
RD↑ ou WR↑
// si pas fini
recommencer à (1)
// si fini
CS ↑

Merci de ta réponse, je vais essayer d'implémenter ta méthode.

Je vais en profiter pour ajouté suffisamment de commentaire pour une bonne compréhension de tous. (En fait ca fait 3 fois que je réécris ce code et j'avoue que l'ajout de commentaire a été omis pour cette troisième fois :slight_smile: )

EDIT : Pour la lecture du bus de données, il vaut mieux configurer le PORTK en entrée puis utilisé var = PORTK, ou plutôt var = PINK ?

Je dois bien avoué que je suis perdu pour les signaux de commande, le tableau page 3 dit : qu'il faut que CS, WR et A0 soient LOW et RD soit HIGH pour écrire l'adresse du registre.

CS, WR, A0 -> LOW et RD -> HIGH : Ecriture de l'adresse du registre.
CS, WR -> LOW et RD, A0 ->HIGH : Écritures des données dans le registre
CS, RD, A0 -> LOW et WR -> HIGH : OPL en mode lecture

Mais sur la page 8, il est dit que CS et WS (je suppose WR) sont conduit a un état haut. Je suppose que l'état de repos est donc HIGH, non ?

Je comprend pas vraiment comment tu veux que je fasse trimarco232, j'ai l'impression de faire ce que tu dit.

Par contre effectivement j'avais la mise en Entrée des pins de l'arduino en commentaire, ce n'est pas grave à mon sens puisque je visualise à chaque essaie les signaux avec l'analyseur logique.

Voici la dernière version, j'ai suivit a la lettre le tableau de la page 3.
J'écris dans le registre de test (addr : 01).
J'avais oublié de reset la puce via le pin IC, je l'ai ajouté dans init().

//PORT use for data/address line
#define DATA_BUS PORTK
#define CONF_BUS DDRK

//Pin use for command
const int pin_a0 = A0;
const int pin_cs = A1;
const int pin_wr = A2;
const int pin_rd = A3;
const int pin_ic = A4;

void setup() {
  ym3812_init();

  ym3812_write(0x01, 0xAA);
  delay(50);
  ym3812_read(0x01);

}

void loop() {

  delay(1000);
}

void ym3812_init() {
  //Command pin as OUTPUT
  pinMode(pin_a0, OUTPUT);
  pinMode(pin_cs, OUTPUT);
  pinMode(pin_wr, OUTPUT);
  pinMode(pin_rd, OUTPUT);
  pinMode(pin_ic, OUTPUT);
  
  //DATA_BUS mode : OUTPUT
  CONF_BUS = 0xFF;
  //Reset DATA_BUS
  DATA_BUS = 0x00;
  
  //Stand-by state of control signal
  digitalWrite(pin_a0, 1);
  digitalWrite(pin_wr, 1);
  digitalWrite(pin_rd, 1);
  digitalWrite(pin_cs, 1);
  
  //Reset OPLII
  digitalWrite(pin_ic, 0);
  delay(1);
  digitalWrite(pin_ic, 1);
  
  
  //Trigger (use for Logical Analyser)
  digitalWrite(pin_a0, 0);
  delay(5);
  digitalWrite(pin_a0, 1);
  delay(10);
}



void ym3812_write(unsigned char reg, unsigned char data) {
  CONF_BUS = 0xFF; //OUTPUT

  //Address of Register
  //Write addr of register : A0 : 0 / CS : 0 / WR : 0 / RD : 1
  digitalWrite(pin_a0, 0);
  digitalWrite(pin_rd, 1);
  digitalWrite(pin_wr, 0);
  digitalWrite(pin_cs, 0);
  
  delay(5);
  
  DATA_BUS = reg;
  
  delay(5);
  
  digitalWrite(pin_a0, 1);
  digitalWrite(pin_wr, 1);
  digitalWrite(pin_rd, 1);
  digitalWrite(pin_cs, 1);

  
  //reset DATA_BUS
  DATA_BUS = 0x00; 
  delay(10);
 
  //Data of register
  //Write addr of register : A0 : 1 / CS : 0 / WR : 0 / RD : 1
  digitalWrite(pin_a0, 1);
  digitalWrite(pin_wr, 0);
  digitalWrite(pin_rd, 1);
  digitalWrite(pin_cs, 0);
  
  delay(5);
  
  DATA_BUS = data;
  
  delay(5);
  
  digitalWrite(pin_a0, 1);
  digitalWrite(pin_wr, 1);
  digitalWrite(pin_rd, 1);
  digitalWrite(pin_cs, 1);
  
  //reset DATA_BUS
  DATA_BUS = 0x00; 
}

unsigned char ym3812_read(unsigned char address) {
  unsigned char buffer;
  CONF_BUS = 0xFF; //OUTPUT

  //Address of Register
  //Write addr of register : A0 : 0 / CS : 0 / WR : 0 / RD : 1
  digitalWrite(pin_a0, 0);
  digitalWrite(pin_rd, 1);
  digitalWrite(pin_wr, 0);
  digitalWrite(pin_cs, 0);
  
  delay(5);
  DATA_BUS = address;
  delay(5);
  
  digitalWrite(pin_a0, 1);
  digitalWrite(pin_wr, 1);
  digitalWrite(pin_rd, 1);
  digitalWrite(pin_cs, 1);
  
  //reset DATA_BUS
  DATA_BUS = 0x00; 
  delay(10);

  //READ DATA ON DATA_BUS
  CONF_BUS = 0x00; //INPUT
  
  digitalWrite(pin_a0, 0);
  digitalWrite(pin_wr, 1);
  digitalWrite(pin_rd, 0);
  digitalWrite(pin_cs, 0);
  
  delay(5);
  buffer = DATA_BUS;
  delay(5);
  
  digitalWrite(pin_a0, 1);
  digitalWrite(pin_wr, 1);
  digitalWrite(pin_rd, 1);
  digitalWrite(pin_cs, 1);


  return buffer;

}

plutôt var = PINK

l'adresse PINK est effectivement utilisée pour lire les entrées

Je suppose que l'état de repos est donc HIGH, non ?

oui, pour CS, WR et RD
mais si tu enchaines les opérations, je pense que tu peux laisser CS↓

j'ai l'impression de faire ce que tu dit

pas si je regarde ta capture d'analyseur : les signaux RD et WR sont presque actifs tout le temps, donc y compris simultanément, ce qui est streng verboten

ces signaux ne devraient être actifs que le temps d'une impulsion :

  • en écriture, après la présentation de l'adresses et des datas
  • en lecture, après la présentation de l'adresses et avant la lecture par l'arduino des datas

Par contre effectivement j'avais la mise en Entrée des pins de l'arduino en commentaire, ce n'est pas grave à mon sens puisque je visualise à chaque essaie les signaux avec l'analyseur logique.

Heureusement, parc qu'avec un RD presque tout le temps à 0, tu aurais pu fusiller les CI
Tu peux mettre ton portk en pull-up pour éviter que ses lignes soient en l'air quand l'analyseur n'y est pas connecté

les posts se sont croisés

void ym3812_init() {
//Command pin as OUTPUT
pinMode(pin_a0, OUTPUT);
pinMode(pin_cs, OUTPUT);
pinMode(pin_wr, OUTPUT);
pinMode(pin_rd, OUTPUT);
pinMode(pin_ic, OUTPUT);

achtung, comme tous ces signaux sont actifs à l'état bas, il faut les mettre à ↑ avant de les commuter en sortie

je lis la suite ...

Capture d'écran de l'analyseur avec le code de mon dernier post avant celui-ci

//Write addr of register : A0 : 0 / CS : 0 / WR : 0 / RD : 1
digitalWrite(pin_a0, 0);
digitalWrite(pin_rd, 1);
digitalWrite(pin_wr, 0);
digitalWrite(pin_cs, 0);

respecte l'ordre que je t'ai proposé
mets un délai entre, et pourquoi pas un petit commentaire (suffit de recopier ceux que j'ai fait)

delay(5);

DATA_BUS = reg;

delay(5);

vis à vis des 20 ou 30ns nécessaires, 1ms suffira

digitalWrite(pin_a0, 1);

pas besoin d'y retoucher à chaque fois : fixe le en début d'opération en fonction de ce que tu veux faire, puis laisse le jusqu'à la prochaine opération

//reset DATA_BUS
DATA_BUS = 0x00;

pareil, c'est inutile

Je viens de faire les modifications suites a ton derniers post, après rerelecture du post plus haut je vois (enfin) ce que tu voulais dire :slight_smile: .

//PORT use for data/address line
#define DATA_BUS PORTK
#define CONF_BUS DDRK

//Pin use for command
const int pin_a0 = A0;
const int pin_cs = A1;
const int pin_wr = A2;
const int pin_rd = A3;
const int pin_ic = A4;

void setup() {
  ym3812_init();
  ym3812_write(0x01, 0xAA);
  delay(50);
  ym3812_read(0x01);
}

void loop() {
  delay(1000);
}

void ym3812_init() {
  //Stand-by state of control signal
  digitalWrite(pin_a0, 1);
  digitalWrite(pin_wr, 1);
  digitalWrite(pin_rd, 1);
  digitalWrite(pin_cs, 1);
  
  //Command pin as OUTPUT
  pinMode(pin_a0, OUTPUT);
  pinMode(pin_cs, OUTPUT);
  pinMode(pin_wr, OUTPUT);
  pinMode(pin_rd, OUTPUT);
  pinMode(pin_ic, OUTPUT);
  
  //DATA_BUS mode : OUTPUT
  CONF_BUS = 0xFF;
  //Reset DATA_BUS
  DATA_BUS = 0x00;
  
  //Reset OPLII
  digitalWrite(pin_ic, 0);
  delay(1);
  digitalWrite(pin_ic, 1);
  
  
  //Trigger (use for Logical Analyser)
  digitalWrite(pin_a0, 0);
  delay(1);
  digitalWrite(pin_a0, 1);
  delay(10);
}

void ym3812_write(unsigned char reg, unsigned char data) {
  CONF_BUS = 0xFF; //OUTPUT

  //Address of Register
  //Write addr of register : A0 : 0 / CS : 0 / WR : 0 / RD : 1
  //Select Chip
  digitalWrite(pin_cs, 0);
  delay(1);
  
  //Select addr
  digitalWrite(pin_a0, 0);
  delay(1);
  
  //Set address
  DATA_BUS = reg;
 
  digitalWrite(pin_wr, 0);
  delay(1);
  
  digitalWrite(pin_wr, 1);
  delay(1);
  digitalWrite(pin_a0, 1);
 
  //Data of register
  //Write data of register : A0 : 1 / CS : 0 / WR : 0 / RD : 1
  
  //Set Data
  DATA_BUS = data;
  digitalWrite(pin_wr, 0);
  delay(1);
  
  digitalWrite(pin_wr, 1);
  delay(1);
  
  //Unselect chip
  digitalWrite(pin_cs, 1);
  
  //reset DATA_BUS
  DATA_BUS = 0x00; 
}

unsigned char ym3812_read(unsigned char address) {
  unsigned char buffer;
  CONF_BUS = 0xFF; //OUTPUT

  //Address of Register
  //Write addr of register : A0 : 0 / CS : 0 / WR : 0 / RD : 1
  
  //Select chip
  digitalWrite(pin_cs, 0);
  delay(1);
  
  //Select addr
  digitalWrite(pin_a0, 0);
  delay(1);
  
  //Set addr
  DATA_BUS = address;
  
  digitalWrite(pin_wr, 0);
  delay(1);
  
  digitalWrite(pin_wr, 1);
  
  // ------------------------------------
  //READ DATA ON DATA_BUS
  CONF_BUS = 0x00; //INPUT
  
  digitalWrite(pin_rd, 0);

  delay(1);
  buffer = PINK;
  
  digitalWrite(pin_a0, 1);
  delay(1);
  digitalWrite(pin_rd, 1);
  delay(1);
  
  //Unselect Chip
  digitalWrite(pin_cs, 1);
  
  return buffer;
}

En tous cas merci pour tes réponses (et ta patience aussi :wink: )

je vois (enfin) ce que tu voulais dire

pas toujours facile de trouver les mots qu'il faut ...

donc, peux-tu me mettre la capture d'écran, c'est plus facile à observer

c'est la capture d'écran de l'analyseur logique que je voulais :confused:

Ah, j'ai encore compris de travers XD.

La voila !

Edit : J'ai l'impression qu'il se passe quelque chose maintenant. Après j'ai peut-être griller le chip a force (au pire j'en recommande 2-3 c'est pas trop chère)

Ah, j'ai encore compris de travers

non, c'est moi qui était distrait, tu peux supprimer ton post avec les captures d'écran

on va s'en tenir à l'analyse logique :
vers les 10ms ça a l'air pas mal
après les 40ms, il semble qu'on passe assez brutalement d'une lecteur à un écriture, ce qui provoque notamment un espèce de glitch au niveau de D3
cela n’empêche pas forcément de fonctionner, mais c'est bizarre, car tu as mis des delay par ailleurs

la bonne nouvelle effectivement c'est qu'au moment de l'envoi de l'impulsion RD↓, les lignes D0 et D3 passent à 0, ce qui fait penser que le YM3812 répond aux questions

ce qu'il serait intéressant de faire maintenant, c'est un petit programme qui permet de lire et d'écrire dans le YM à partir du moniteur ?

Je dois avoir ca en stock, j'avais bidouillé un prog qui permet cela. Je crois meme que c'était pour le YM, je vais voir.

EDIT : faudrait regarder D5-D7 aussi voir ce qu'il se passe sur ces lignes la aussi.

EDIT 2 : Problème c'est que l'analyseur logique na pas de mode "temps réel" ou l'on peut voir ce qu'il ce passe en direct. Après rien n'empèche d'écrire plusieurs commande d'écriture-lecture directement dans le code.

Voila j'ai test sur le ligne D4-D7 et le "glitch" est aussi présent, comme sur le ligne D3.

J'ai écrit 0xFF dans le registre de test (0x01) mais aussi dans 0x02 et 0x03, même résultat.

EDIT : J'ai ajouté un delay de 1ms entre écriture de l’adresse et lecture du registre, résultat le glitch est le passage du mode sortie à entrée des pins de l'arduino et ensuite le YM passe les lignes a l'état LOW. Sur la capture2 c'est très flagrant avec le delay.

ok,
on peut laisser les delay, cela facilitera la lecture à l'analyseur

comme il n'y a pas grand chose à lire dans l'YM, je pense que le moment est venu d'y écrire et de voir (d'écouter pour ce qui te concerne) ce qui en sort
j'ai jeté un coup d’œil à la description des registres : c'est très pudique, on à l'impression qu'il manque des pages au datasheet ?

J'ai aussi l'impression qu'il manque des pages, la fin du datasheet est comme corrompu.

Ici j'avais obtenue plus d'info sur le fonctionnement : http://www.shikadi.net/moddingwiki/OPL_chip

Je vais tester avec ce que j'ai, malheureusement je sais pas si "écouter" est la prochaine étape car il manque quelque composant sur mon circuit après le DAC, mais je peux toujours "voir" ce qu'il se passe sur l'oscilloscope.

Il n’empêche que l'on devrait pouvoir relire les infos que l'on écrit dans les registres et pour le moment je n'ai pas l'impression que cela marche. Il se passe quelque chose sur le BUS de donnée, mais j'ai peut-être "cramer" la puce a force de mauvais traitement.

J'ai trouver plus d'info GitHub - DhrBaksteen/ArduinoOPL2: Arduino library for use with the OPL2 board (YM3812) and OPL3Duo (YMF262), mais je dois bien avoué c'est au dessus de mes compétences là. Bon j'ai des schéma pour le reste du circuit (niveau analogique), c'est déjà ça.

Il n'empêche que l'on devrait pouvoir relire les infos que l'on écrit dans les registres

-> ce n'est pas ce que le datasheet semble indiquer, et ce n'est normalement pas utile au fonctionnement du YM

je dois bien avoué c'est au dessus de mes compétences là

c'est sans doute la meilleure base pour que tu pisses apprendre
utilise au maximum ce qui a déjà été fait te permettra d'avancer

dans un premier temps il faudra se contenter d'utiliser des sons déjà faits
personnellement je n'ai jamais été capable de créer le moindre son écoutable en synthèse fm ..