quinta-feira, janeiro 16, 2014

Chip de Som AY-3-8910 / KC89C72 - Parte 4

Agora que consegui acertar o hardware, vamos começar a fazer experiências com o KC89C72 ligando a placa de teste a um Arduino.


A ligação elétrica é muito simples: o Arduino fornece a alimentação da placa através dos pinos +5V e GND e os sinais de controle da placa são ligados às portas digitais 3 a 7 do Arduino.

Como primeiro teste, vamos programar o KC89C72 para soar um tom de 523Hz (um dó). As informações de programação estão no datasheet, basicamente precisamos escrever valores apropriados nos registradores do KC89C72. A escrita em um registrador é feita através dos seguintes passos, lembrando que temos um shift register 595 ligado à via de dados do KC:
  • Os sinais BC1 e BCDIR são colocados em zero para colocar a via de dados no modo inativo (alta impedância).
  • O endereço do registrador é colocado na via. Para isto é preciso colocar o sinal Latch do 595 em nível baixo, colocar os bits um a um no sinal Data, pulsando o sinal Clock a cada bit (para colocar o endereço dentro do 595) e depois colocar o sinal Latch no nível alto para o 595 atualizar os seus pinos de dados com o valor que foi enviado serialmente.
  • Os sinais BC1 e BCDIR sãi colocados em um, para o KC registrar o dado na via como um endereço de registrador.
  • Os sinais BC1 e BCDIR são retornados a zero para voltar a via de dados ao modo inativo.
  • O sinal BC1é mantido em zero e BCDIR colocado em um para inciar a escrita no registrador.
  • O valor a ser escrito é colocado na via, de volta igual à feita com o endereço.
  • Os sinais BC1 e BCDIR são retornados a zero para concluir a escrita.
Ok, já sabemos como escrever um valor em um registrador, precisamos agora verificar no datasheet quais valores precisamos escrever e em quais registradores. para este primeiro teste, vamos manipular somente os registradores 0, 1, 7 e 10.


Os registradores 0 e 1 definem a frequência (ou o período se preferirem) do tom que será emitido no canal A. O KC89C72 pega o sinal de clock (2MHz), divide por 16 e depois divide pelo valor de 12 bits que está nestes registradores. Portanto, para gerar uma frequência f, precisamos programar o valor 2MHz/16/f. O valor zero é usado para gerar silêncio (na realidade o KC89C72 gera uma frequência muito alta).

O registrador 7 determina quais canais de tom e ruído estão inibido, no nosso caso queremos inibir tudo menos o tom do canal A. Este registrador determina também o uso das portas de I/O adicionais (não usadas na placa), vamos deixá-las como entradas.

O registrador 10 determina o volume do canal A e o uso ou não do envelope. Vamos programar a amplitude máxima, sem envelope.

Para não perturbar muito, vamos usar a serial do Arduino para ligar e desligar o tom. O programa está abaixo e pode ser baixado dos arquivos do blog (KC89C72_A.zip).
// Teste com KC89C72 / AY-3-8910

// Conexões da placa de teste ao Arduino
const int pinData = 5;
const int pinLatch = 6;
const int pinClock = 7;
const int pinBC1 = 10;
const int pinBCDIR = 12;

// Frequencia do clock do KC89C72
const long freqKC = 2000000L;

// Registradores do KC89C72
const unsigned char regToneA_Lo = 0;
const unsigned char regToneA_Hi = 1;
const unsigned char regToneB_Lo = 2;
const unsigned char regToneB_Hi = 3;
const unsigned char regToneC_Lo = 4;
const unsigned char regToneC_Hi = 5;
const unsigned char regNoisePeriod = 6;
const unsigned char regEnable = 7;
const unsigned char regAmpA = 8;
const unsigned char regAmpB = 9;
const unsigned char regAmpC = 10;
const unsigned char regEnv_Lo = 11;
const unsigned char regEnv_Hi = 12;
const unsigned char regEnvShape = 13;

// iniciação
void setup ()
{
  // configura direção dos pinos
  pinMode(pinLatch, OUTPUT);
  pinMode(pinData, OUTPUT);  
  pinMode(pinClock, OUTPUT);
  pinMode(pinBC1, OUTPUT);
  pinMode(pinBCDIR, OUTPUT);        
  
  // inicia o KC89C72
  writeReg (regNoisePeriod, 0);  // desliga noise generator
  writeReg (regEnable, 0x3E);    // somente Tom no A
  writeReg (regToneA_Lo, 0);     // desliga o tom
  writeReg (regToneA_Hi, 0);
  writeReg (regAmpA, 0x0F);      // amplitude máxima, sem envelope
  
  // incia a serial
  Serial.begin (9600);
  Serial.println ("KC89C72");
}

// programa principal
void loop ()
{
  if (Serial.available() > 0)
  {
    Serial.read();
    
    // Gerar um dó - 523Hz
    long val = ((freqKC/16L) / 523L);
    Serial.println (val);
    writeReg (regToneA_Lo, (unsigned char) (val & 0xFF));
    writeReg (regToneA_Hi, (unsigned char) ((val >> 8) & 0xFF));
    
    while (Serial.available() == 0)
      ;
    Serial.read();
  
   // Encerra o tom
   writeReg (regToneA_Lo, 0);
   writeReg (regToneA_Hi, 0);
  }
}

// rotina para escrever um valor em um registrador do KC89C72
void writeReg (unsigned char reg, unsigned char valor)
{
  // coloca no modo inativo
  digitalWrite(pinBC1, LOW);
  digitalWrite(pinBCDIR, LOW);    
  
  // Coloca na via o endereço do registrador
  digitalWrite(pinLatch, LOW);
  shiftOut(pinData, pinClock, MSBFIRST, reg);  
  digitalWrite(pinLatch, HIGH);

  // indica escrita de endereço
  digitalWrite(pinBC1, HIGH);
  digitalWrite(pinBCDIR, HIGH);
  
  // volta ao modo inativo
  digitalWrite(pinBC1, LOW);
  digitalWrite(pinBCDIR, LOW);    
  
  // indica escrita de dado
  digitalWrite(pinBC1, LOW);
  digitalWrite(pinBCDIR, HIGH);
  
  // Coloca na via o valor do registrador
  digitalWrite(pinLatch, LOW);
  shiftOut(pinData, pinClock, MSBFIRST, valor);  
  digitalWrite(pinLatch, HIGH);

  // volta ao modo inativo
  digitalWrite(pinBC1, LOW);
  digitalWrite(pinBCDIR, LOW);    
}
Sofisticando um pouco, vamos gerar vários tons e tocar uma musiquinha conforme o caractere recebido pela serial ((KC89C72_B.zip nos arquivos do blog). As alterações no programa são:
unsigned freq[7] = {523, 587, 659, 698, 784, 880, 988 };
unsigned nota[7]; 

int musica[] = { 
  0, 1, 2, 3, 3, 3, 0, 1, 0, 1, 1, 1, 0, 4, 3, 2, 2, 2, 
  0, 1, 2, 3, 3, 3, -1 
};

// iniciação
void setup ()
{
  // ...

  // calcula programação das notas
  for (int i = 0; i < 7; i++)
  {
    nota[i] = (unsigned) ((freqKC/16L) / (long) freq[i]);
  }
}

// programa principal
void loop ()
{
  int c;
  
  if (Serial.available() > 0)
  {
    c = Serial.read();
    
    if ((c >= '1') && (c <= '7'))
    {
      writeReg (regToneA_Lo, (unsigned char) (nota[c-'1'] & 0xFF));
      writeReg (regToneA_Hi, (unsigned char) ((nota[c-'1'] >> 8) & 0xFF));
      delay (500);
      writeReg (regToneA_Lo, 0);
      writeReg (regToneA_Hi, 0);
    }
    else if (c == ' ')
    {
      for (int i = 0; musica[i] != -1; i++)
      {
        int n = musica[i];
        writeReg (regToneA_Lo, (unsigned char) (nota[n] & 0xFF));
        writeReg (regToneA_Hi, (unsigned char) ((nota[n] >> 8) & 0xFF));
        delay (500);
        writeReg (regToneA_Lo, 0);
        writeReg (regToneA_Hi, 0);
        delay (200);
      }
    }
    
  }
}
No próximo post vamos brincar um pouco com o gerador de ruídos.

Um comentário:

RobsonFrança disse...

Uma experiência bacana seria fazer um tocador portátil de arquivos .AY - populares em micros como Sinclair ZX Spectrum e Atari ST. Bastaria incluir um leitor de cartões SD e um display LCD de duas linhas e pronto!