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).
  1. // Teste com KC89C72 / AY-3-8910  
  2.   
  3. // Conexões da placa de teste ao Arduino  
  4. const int pinData = 5;  
  5. const int pinLatch = 6;  
  6. const int pinClock = 7;  
  7. const int pinBC1 = 10;  
  8. const int pinBCDIR = 12;  
  9.   
  10. // Frequencia do clock do KC89C72  
  11. const long freqKC = 2000000L;  
  12.   
  13. // Registradores do KC89C72  
  14. const unsigned char regToneA_Lo = 0;  
  15. const unsigned char regToneA_Hi = 1;  
  16. const unsigned char regToneB_Lo = 2;  
  17. const unsigned char regToneB_Hi = 3;  
  18. const unsigned char regToneC_Lo = 4;  
  19. const unsigned char regToneC_Hi = 5;  
  20. const unsigned char regNoisePeriod = 6;  
  21. const unsigned char regEnable = 7;  
  22. const unsigned char regAmpA = 8;  
  23. const unsigned char regAmpB = 9;  
  24. const unsigned char regAmpC = 10;  
  25. const unsigned char regEnv_Lo = 11;  
  26. const unsigned char regEnv_Hi = 12;  
  27. const unsigned char regEnvShape = 13;  
  28.   
  29. // iniciação  
  30. void setup ()  
  31. {  
  32.   // configura direção dos pinos  
  33.   pinMode(pinLatch, OUTPUT);  
  34.   pinMode(pinData, OUTPUT);    
  35.   pinMode(pinClock, OUTPUT);  
  36.   pinMode(pinBC1, OUTPUT);  
  37.   pinMode(pinBCDIR, OUTPUT);          
  38.     
  39.   // inicia o KC89C72  
  40.   writeReg (regNoisePeriod, 0);  // desliga noise generator  
  41.   writeReg (regEnable, 0x3E);    // somente Tom no A  
  42.   writeReg (regToneA_Lo, 0);     // desliga o tom  
  43.   writeReg (regToneA_Hi, 0);  
  44.   writeReg (regAmpA, 0x0F);      // amplitude máxima, sem envelope  
  45.     
  46.   // incia a serial  
  47.   Serial.begin (9600);  
  48.   Serial.println ("KC89C72");  
  49. }  
  50.   
  51. // programa principal  
  52. void loop ()  
  53. {  
  54.   if (Serial.available() > 0)  
  55.   {  
  56.     Serial.read();  
  57.       
  58.     // Gerar um dó - 523Hz  
  59.     long val = ((freqKC/16L) / 523L);  
  60.     Serial.println (val);  
  61.     writeReg (regToneA_Lo, (unsigned char) (val & 0xFF));  
  62.     writeReg (regToneA_Hi, (unsigned char) ((val >> 8) & 0xFF));  
  63.       
  64.     while (Serial.available() == 0)  
  65.       ;  
  66.     Serial.read();  
  67.     
  68.    // Encerra o tom  
  69.    writeReg (regToneA_Lo, 0);  
  70.    writeReg (regToneA_Hi, 0);  
  71.   }  
  72. }  
  73.   
  74. // rotina para escrever um valor em um registrador do KC89C72  
  75. void writeReg (unsigned char reg, unsigned char valor)  
  76. {  
  77.   // coloca no modo inativo  
  78.   digitalWrite(pinBC1, LOW);  
  79.   digitalWrite(pinBCDIR, LOW);      
  80.     
  81.   // Coloca na via o endereço do registrador  
  82.   digitalWrite(pinLatch, LOW);  
  83.   shiftOut(pinData, pinClock, MSBFIRST, reg);    
  84.   digitalWrite(pinLatch, HIGH);  
  85.   
  86.   // indica escrita de endereço  
  87.   digitalWrite(pinBC1, HIGH);  
  88.   digitalWrite(pinBCDIR, HIGH);  
  89.     
  90.   // volta ao modo inativo  
  91.   digitalWrite(pinBC1, LOW);  
  92.   digitalWrite(pinBCDIR, LOW);      
  93.     
  94.   // indica escrita de dado  
  95.   digitalWrite(pinBC1, LOW);  
  96.   digitalWrite(pinBCDIR, HIGH);  
  97.     
  98.   // Coloca na via o valor do registrador  
  99.   digitalWrite(pinLatch, LOW);  
  100.   shiftOut(pinData, pinClock, MSBFIRST, valor);    
  101.   digitalWrite(pinLatch, HIGH);  
  102.   
  103.   // volta ao modo inativo  
  104.   digitalWrite(pinBC1, LOW);  
  105.   digitalWrite(pinBCDIR, LOW);      
  106. }  
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:
  1. unsigned freq[7] = {523, 587, 659, 698, 784, 880, 988 };  
  2. unsigned nota[7];   
  3.   
  4. int musica[] = {   
  5.   0, 1, 2, 3, 3, 3, 0, 1, 0, 1, 1, 1, 0, 4, 3, 2, 2, 2,   
  6.   0, 1, 2, 3, 3, 3, -1   
  7. };  
  8.   
  9. // iniciação  
  10. void setup ()  
  11. {  
  12.   // ...  
  13.   
  14.   // calcula programação das notas  
  15.   for (int i = 0; i < 7; i++)  
  16.   {  
  17.     nota[i] = (unsigned) ((freqKC/16L) / (long) freq[i]);  
  18.   }  
  19. }  
  20.   
  21. // programa principal  
  22. void loop ()  
  23. {  
  24.   int c;  
  25.     
  26.   if (Serial.available() > 0)  
  27.   {  
  28.     c = Serial.read();  
  29.       
  30.     if ((c >= '1') && (c <= '7'))  
  31.     {  
  32.       writeReg (regToneA_Lo, (unsigned char) (nota[c-'1'] & 0xFF));  
  33.       writeReg (regToneA_Hi, (unsigned char) ((nota[c-'1'] >> 8) & 0xFF));  
  34.       delay (500);  
  35.       writeReg (regToneA_Lo, 0);  
  36.       writeReg (regToneA_Hi, 0);  
  37.     }  
  38.     else if (c == ' ')  
  39.     {  
  40.       for (int i = 0; musica[i] != -1; i++)  
  41.       {  
  42.         int n = musica[i];  
  43.         writeReg (regToneA_Lo, (unsigned char) (nota[n] & 0xFF));  
  44.         writeReg (regToneA_Hi, (unsigned char) ((nota[n] >> 8) & 0xFF));  
  45.         delay (500);  
  46.         writeReg (regToneA_Lo, 0);  
  47.         writeReg (regToneA_Hi, 0);  
  48.         delay (200);  
  49.       }  
  50.     }  
  51.       
  52.   }  
  53. }  
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!