ブログトップ 記事一覧 ログイン 無料ブログ開設

電子ガジェットいろいろ 開発メモ このページをアンテナに追加 RSSフィード Twitter

主に自分用に色々なアプリ開発のメモや電子工作や色々な開発関連のメモを書いてます。
他にもゲームや便利アプリ等の紹介や、iPhoneOS以外の話題も書いていきたいと思います。


ブログランキング・にほんブログ村へ
にほんブログ村

2016-07-03

YAMAHAの音源IC(YMZ294)の使い方の基礎

いつか使ってみようと思いながら、なかなか使わずに部品箱にずっと入れている人も多いと思われる音源ICですが、やってみると意外と簡単なので、基礎を説明しようと思います。

今回は、秋月電子通商でも長年販売しているYAMAHAPSG(SSG)音源のYMZ294で説明します。
YAMAHAの音源ICはどれも使い方の基本は同じなので、FM音源も同じような方法で使うことができるので、音源ICの基本として適切と思われる音源ICです。

今回はArduino Unoで制御しますが、理解すればmbedでもPICAVRで直接制御するにせよ同じ方法で使うことができるはずです。
初期処理から基準のA(ラ,440Hz)の音を出力するまでの手順を説明します。




個々のレジスタの情報等の詳細な仕様はデータシートを参照ください。

http://akizukidenshi.com/download/ds/yamaha/ymz294.pdf

ハードウェア


下の回路図のようにArduinoとYMZ294を接続しています。
f:id:uosoft:20160703000316p:image:w480


Arduino2:9-YMZ294D0:D7
Arduino10-YMZ294WR,CS
Arduino11-YMZ294A0
Arduino12-YMZ294IC

同じIOに複数個のICを接続しない限りはWRとCSは同じ制御でよさそうなので、Arduinoの同じピンに接続しています。
SOからの出力は結構大きいので、ボリュームとして10kΩの可変抵抗を間に入れてスピーカ(ヘッドホンミニジャック)に接続しています。

実際に動作させるまでの処理


IOの設定

まず、Arduinoに接続されたピンに対して便宜上名前を付けます。

const byte WRCS_PIN = 10;
const byte A0_PIN = 11;
const byte RESET_PIN = 12;
const int DATA_PIN[] = { 2, 3, 4, 5, 6, 7, 8, 9 };

setup()内ですべて出力に設定します。

  for (int i = 0; i < 8; i++) {
    pinMode(DATA_PIN[i], OUTPUT);
  }
  pinMode(WRCS_PIN, OUTPUT);
  pinMode(A0_PIN, OUTPUT);
  pinMode(RESET_PIN, OUTPUT);

レジスタ書き込みファンクション

音源ICの機能は全てレジスタにアクセスすることで制御できます。
これは同じYAMAHA製のFM音源ICも含めて基本的なことなので、この概念を理解すると他の音源ICもレジスタの仕様をチェックするだけで使えるようになります。

WRCS_PINがLOWの状態でDATA_PINにバイトデータをセットし、WRCS_PINをHIGHにするとバイトデータがYMZ294に送られます。このときA0_PINがLOWの場合はレジスタのアドレス、A0_PINがHIGHの場合はレジスタに入れる値となります。

この方法でレジスタに値をセットするファンクションを定義します。

void set_register(byte addr, byte value)
{
  // addr
  digitalWrite(WRCS_PIN, LOW);
  digitalWrite(A0_PIN, LOW);
  for (int i = 0; i < 8; i++) {
    digitalWrite(DATA_PIN[i], bitRead(addr, i));
  }
  digitalWrite(WRCS_PIN, HIGH);

  // value
  digitalWrite(WRCS_PIN, LOW);
  digitalWrite(A0_PIN, HIGH);
  for (int i = 0; i < 8; i++) {
    digitalWrite(DATA_PIN[i], bitRead(value, i));
  }
  digitalWrite(WRCS_PIN, HIGH);
}||<

*** YMZ294の初期化

動作の初めにYMZ294を初期化します。
全てのレジスタに0をセットし、ハードウエアリセットします。
ハードウエアリセットはWRCS_PINがHIGH、A0_PINがLOWの状態でRESET_PINをLOWにし、少し間をおいてからRESET_PINをHIGHにします。

この方法で初期化するファンクションを定義します。

>|C|
void reset() {
  set_register(0x00, 0);
  set_register(0x01, 0);
  set_register(0x02, 0);
  set_register(0x03, 0);
  set_register(0x04, 0);
  set_register(0x05, 0);
  set_register(0x06, 0);

  set_register(0x07, 0);
  set_register(0x08, 0);
  set_register(0x09, 0);
  set_register(0x0a, 0);

  set_register(0x0b, 0);
  set_register(0x0c, 0);
  set_register(0x0d, 0);

  digitalWrite(WRCS_PIN, HIGH);
  digitalWrite(A0_PIN, LOW);
  digitalWrite(RESET_PIN, LOW);
  delay(10);
  digitalWrite(RESET_PIN, HIGH);
}

このファンクションをsetup()内で実行します

  reset();

周波数を指定して出力

指定した周波数の音を出力する場合は、125000/周波数(HZ)の整数値を12ビットで各チャンネルのレジスタにセットします。
レジスタは8ビット構成なので、上位4ビットと下位8ビットに分けて格納します。


チャンネル上位4ビットレジスタ下位8ビットレジスタ
A#01#00
B#03#02
C#05#04

チャンネルと周波数を設定するファンクションを定義

void SetFrequency(int ch, word freq) {
  word cal_freqency = 0;
  if (freq != 0) {
    cal_freqency = 125000 / freq;
  }
  cal_freqency &= 0b0000111111111111;
  set_register(0x00 + (ch * 2), cal_freqency & 0xff);
  set_register(0x01 + (ch * 2), (cal_freqency >> 8) & 0xff);
}

演奏設定

今回はチャンネルABC共にトーンのみ出力します。
この設定はレジスタ#07へ0b00111000の値をセットすることにより行えます。

今回はチャンネルABC共にボリュームを0にします。
この設定はレジスタ#08,#09,#0Aへ0の値をセットすることにより行えます。

今回はチャンネルABC共に周波数を0にします。

この処理をsetup()内で実行します

  set_register(0x07, 0b00111000);
  set_register(0x08, 0);
  set_register(0x09, 0);
  set_register(0x0a, 0);
  SetFrequency(0, 0);
  SetFrequency(1, 0);
  SetFrequency(2, 0);

演奏

チャンネルAにA(ラ)の音程の周波数の440Hzをセットします。
チャンネルAのボリュームを15にします。
1秒後にチャンネルAのボリュームを0にし、チャンネルAの周波数に0をセットして演奏を止めます。
周波数に0をセットしただけでは想定外の音が出力されたので、ボリュームも0にしました。
この処理は1回だけ実行するつもりだったので、setup()内の最後に追加しました。

  SetFrequency(0, 440);
  set_register(0x08, 15);
  delay(1000);
  set_register(0x08, 0);
  SetFrequency(0, 0);

これで起動後、1秒間だけ音が出力されるはずです。

最後にソース全文を掲載するので、参考にどうぞ。

// Output Pins
const byte WRCS_PIN = 10;
const byte A0_PIN = 11;
const byte RESET_PIN = 12;
const int DATA_PIN[] = { 2, 3, 4, 5, 6, 7, 8, 9 };

void setup() {

  for (int i = 0; i < 8; i++) {
    pinMode(DATA_PIN[i], OUTPUT);
  }
  pinMode(WRCS_PIN, OUTPUT);
  pinMode(A0_PIN, OUTPUT);
  pinMode(RESET_PIN, OUTPUT);
  
  reset();
  
  set_register(0x07, 0b00111000);
  set_register(0x08, 0);
  set_register(0x09, 0);
  set_register(0x0a, 0);
  SetFrequency(0, 0);
  SetFrequency(1, 0);
  SetFrequency(2, 0);

  delay(100);

  SetFrequency(0, 440);
  set_register(0x08, 15);
  delay(1000);
  set_register(0x08, 0);
  SetFrequency(0, 0);

}

void loop() {

}

// 初期化
void reset() {
  set_register(0x00, 0);
  set_register(0x01, 0);
  set_register(0x02, 0);
  set_register(0x03, 0);
  set_register(0x04, 0);
  set_register(0x05, 0);
  set_register(0x06, 0);

  set_register(0x07, 0);
  set_register(0x08, 0);
  set_register(0x09, 0);
  set_register(0x0a, 0);

  set_register(0x0b, 0);
  set_register(0x0c, 0);
  set_register(0x0d, 0);

  digitalWrite(WRCS_PIN, HIGH);
  digitalWrite(A0_PIN, LOW);
  digitalWrite(RESET_PIN, LOW);
  delay(10);
  digitalWrite(RESET_PIN, HIGH);

}

void SetFrequency(int ch, word freq) {
  word cal_freqency = 0;
  if (freq != 0) {
    cal_freqency = 125000 / freq;
  }
  cal_freqency &= 0b0000111111111111;
  set_register(0x00 + (ch * 2), cal_freqency & 0xff);
  set_register(0x01 + (ch * 2), (cal_freqency >> 8) & 0xff);
}

// レジスタセット
void set_register(byte addr, byte value)
{
  // addr
  digitalWrite(WRCS_PIN, LOW);
  digitalWrite(A0_PIN, LOW);
  for (int i = 0; i < 8; i++) {
    digitalWrite(DATA_PIN[i], bitRead(addr, i));
  }
  digitalWrite(WRCS_PIN, HIGH);

  // value
  digitalWrite(WRCS_PIN, LOW);
  digitalWrite(A0_PIN, HIGH);
  for (int i = 0; i < 8; i++) {
    digitalWrite(DATA_PIN[i], bitRead(value, i));
  }
  digitalWrite(WRCS_PIN, HIGH);
}


【永久保証付き】Arduino Uno

【永久保証付き】Arduino Uno

サウンドプログラミング入門――音響合成の基本とC言語による実装 (Software Design plus)

サウンドプログラミング入門――音響合成の基本とC言語による実装 (Software Design plus)

2016-06-21

Arduino互換機を購入する際の注意事項

Arduinoを購入する際に価格が安い互換機も探す場合に注意事項を紹介します。
特にUNOの互換機にはUSB-Serial変換チップに純正品と同じATmega16U2ではなく、CH340Gが使われている場合があります。
これはArduino開発環境のドライバとは別にドライバインストールが必要になり面倒なので、避けたほうが良いです。
コストを下げるためにCH340Gが使われており、この手の物は他の部分でも作りが雑で同じ互換機でもATmega16U2が使われているものよりが質が悪いことが多いです。
AliExpress等で中華互換機を探すと全体的にATmega16U2と比較して極端に安いものが多いですが、手間と質を考えるとATmega16U2の方が色々と楽になります。
(ATmega16U2の物は安くても800円前後するのに対して、CH340Gのものは300円ほどからある。)
またCH340Gは機能ICでUSB-Serial専用チップです。汎用コントローラのATmega16U2と違い、ファームウェアを書き換えてAVRライタにして遊んだりはできません。
始めは割り切って買ったとしても、後から色々と不具合が出るので、ATmega16U2が使われていることをチェックして購入することをお勧めします。



【永久保証付き】Arduino Uno

【永久保証付き】Arduino Uno

2016-06-19

mbedで迷ったらこれ STM32 NUCLEO-F446RE



mbedを使いたいと思った時に迷ったら、ST Nucleo Board STM32F446REがおすすめです。

このボードは低価格Arduino互換のピンソケットを持ったSTM32 NUCLEOシリーズのボードです。

CPUはFPUも内蔵されているARM32bit Cortex-M4を搭載し、動作周波数もmbedの中では比較的高速な最大180MHzです。

メインメモリも128KBでフラッシュも512KBと十分です。さらにDAC2ch搭載しているので、ステレオの自作音源制作もできます。NUCLEOは低価格で前から人気だったのですが、メモリを大きいものを選択するとCPUが遅いものだったり、DACをあきらめたり、融通の利く汎用的なスペックがなかなかなかったのですが、今回の機種でやっと一通り満足できるスペックで良いものが発売されたと思います。

もしmbedで迷ったらぜひST Nucleo Board STM32F446REを検討してください。

ST Nucleo Board STM32F446REのスペック


CPUSTM32F446RET6
CPUコアARM32bit Cortex-M4(+FPU)
周波数最大180MHz
CPU電源1.7V-3.6V
フラッシュ512KB
SRAM128KB+4KB(Buckup)
タイマー(AdvancedControl)x2
タイマー(General Purpose)x10
タイマー(Basic)x2
SPIx4
I2Sx2
USARTx4
UARTx2
USB OTGx1
CANx2
SAIx2
SPDIF-Rxx1
HDMI-CECx1
QuadSPIx1
GPIOx50
ADC12bitx3 16ch
DAC12bit 2ch