ArduboyでのPWMサウンド出力

Arduinoのライブラリには標準でtone命令が用意されていますが、一定音量の矩形波しか出せません。
PWM(パルス幅変調)を使うと、アナログ波形を出力することができるので、より多彩な音を出すことができます。
I/Oピンからは1か0の信号しか出せないのですが、PWMは1と0を高速に出力して、その時間平均を取ることにより中間の値を表現します。

直感的には「白」と「黒」を交互に切り替える操作を高速に行うと、「灰色」に感じられる、というようなものです。時間軸に沿って「白」と「黒」を見せる割合を変化させると、「灰色」の濃さも変化させることができるわけです。

Arduboy内蔵のスピーカーでも、この機能は使えます。
圧電スピーカーなので再生能力は限定されますが・・・。

ということで、ちょっと関連の情報を調べてみたので整理しておきます。

まず、Arduino UNOではPWMを使った音声出力の実験がいろいろ行われています。

・FM音源
Arduino スケッチ 「FMmelody」
Pakurino (1) – シンセ・アンプラグド

・ポリフォニックサウンドジェネレータ
PWMDAC_Synthライブラリ | CAmiDion blog
PWMDAC_Synthライブラリ Wiki – OSDN

・解説記事ほか
[WIP] Arduinoで音を出したい(メモ) – Qiita
Audio via Arduino 16-bit PWM | Sand, software and sound
Secrets of Arduino PWM
TimerのPWMモードで Sin波形を発生
シグマデルタ変調 PWM (1) – シンセ・アンプラグド

これらの記事では実際に動作するスケッチやライブラリが紹介されています。
ただし、そのほとんどはArduino UNO用で、Arduboyにはそのままでは使えません。
ArduboyはArduino Leonardoベースであり、UNOのCPU(ATmega328)とは異なるCPU(ATmega32u4)が使われており、利用できるハードウェアタイマーが異なるためです。
以下に解説があります。

R6500: Fast PWM on Arduino Leonardo
Arduino Timer | TONYLABS

Arduino UNOおよびArduino Leonardoのピンアサイン図は以下にあります。

Arduino Pinout Diagrams – marcusjenkins.com

Arduino UNOでPWM出力できるピンは3, 5, 6, 9, 10, 11です。
ただし、上述のPWMDAC_Synthライブラリの作者さんによると、5と6が連動するTIMER0はArduino の millis() などのために使われているため、利用できないようです。

Arduino LeonardoでPWM出力できるピンは3, 5, 6, 9, 10, 11, 12, 13となっています。
また、32u4はタイマー2が無く、タイマー3と4があります。

PWMで利用する、値比較(Output Compare)用のレジスタと出力ピンの関係について、UNOとLeonardoで対比すると以下のようになっています。

Timer 0 (8 bit)
・OC0A → UNO(6)、Leonardo(11)
・OC0B → UNO(5)、Leonardo(3, 18)
Timer 1 (16 bit)
・OC1A → UNO(9)、Leonardo(9)
・OC1B → UNO(10)、Leonardo(10)
・OC1C → UNO(-)、Leonardo(11)
Timer 2 (8 bit)
・OC2A → UNO(11)、Leonardo(-)
・OC2B → UNO(3)、Leonardo(-)
Timer 3 (16 bit)
・OC3A → UNO(-)、Leonardo(5)
Timer 4 (10 bit)
・OC4A → UNO(-)、Leonardo(13)
・OC4B → UNO(-)、Leonardo(10)
・OC4D → UNO(-)、Leonardo(6)
・#OC4A → UNO(-)、Leonardo(5)
・#OC4B → UNO(-)、Leonardo(9)
・#OC4D → UNO(-)、Leonardo(12)

なお、#OCnxは、OCnxのNOTで、かつOCnxの立ち上がりより前・立下がりより後に変化させることができるようです。このへんは詳しいことは良く分かりません。データシートには詳しく書かれています。
また、以下のサイトに日本語訳があります。

AVR.jp

AVRのタイマーに関しては、以下の解説も詳しいです。

AVRでのタイマとPWMの使い方 | うしこlog

ではArduboyはどのようなハードウェア設計になっているかですが、Arduboyの開発過程でも、PWMによる音声出力を想定してスピーカー周辺の設計(主に、どの出力ピンとタイマーを割り当てるか)については、相当に議論が行われていたようです。

Music library brainstorm – Arduboy / Development – Community

その結果、圧電ブザーはPC6(Arduino的には5番ピン)とPC7(Arduino的には13番ピン)の間に接続することになりました。
(下の引用した回路図はPD6につながっていますが、その後の議論でPC7に変わりました。なお、最終的な完全な回路図はこちら

arduboy-schematic.png

Arduboy Kickstarter version design discussion – Arduboy / Development – Communityより引用)

従って、Arduboyの圧電ブザーを鳴らす信号はタイマー3で作ることもできるし、タイマー3とタイマー4を組み合わせて使うこともできるし、タイマー4だけで作ることもできるようになっています。

もっとも、「Pin 6とPin 12にしておけば圧電ブザーから入力することもできたのに!」という意見も書かれていました。スピーカーは電力を運動(音波)に変換しますが、逆に音波を電力に変換することもできるので、ADC入力として使えるPin6を割り当てれば、音を拾えたかもしれないということのようです。

実際にPWMを使ってサウンド再生をするプログラムもいくつか作られています。
もっとも単純なものは、上記のコミュニティ議論の中でノコギリ波を生成するサンプルとして提示されているもので、25番目の書き込みにあります。
似たコードが二つ提示されていますが、1つ目はタイマー4のみ、2つ目はタイマー3とタイマー4を使って信号を生成しています。
タイマー4のみのほうが音量が大きめです。
タイマー4のみを使う場合はスピーカーの2つの端子に逆相の信号を与えられますが、タイマー3と4を使う場合は2つの信号が打ち消しあっているかもしれない、と記事のコメントには書かれています。

Music library brainstorm – Arduboy / Development – Community

また、Arduboy用にさまざまなゲームを作っているTEAM a.r.g.さんが4和音のライブラリを開発中です。現在はまだ完成版ではないようですが、ArduboyBasedTrackerというサンプルが動作しています。

[WIP] 4 channel Music – Arduboy / Development – Community

ということで、私も試しに上述のPWMDAC_SynthライブラリをArduboyに移植してみました。
出力は、とりあえず5番ピン側でタイマー3を使うことにしました。
オリジナルはタイマー1またはタイマー2を使用するようになっていますので、その部分をタイマー3に書き換えます。

6和音だと処理能力の余力が小さそうなので、4和音にしました。
ファイルは以下のGitHubに置いてあります。
動作させるには「Arduboy2」ライブラリが必要です。(Arduboy2はサウンド関連が分離されているため、競合が起こりません。)

boochow/pwmtest: PWM sound synthesize test for Arduboy

一応音は出るようになりましたが、結構小さい音です。
下のビデオではICレコーダーと重ねて録音レベルを最大にして録っていますが、それでもこの程度です。
ゲームの効果音に使うには、ちょっと迫力に欠けるかもしれません。

録音した音声の波形です。音声に強弱が付いていることが分かります。

pwmwave.png

2016/10/11追記:
タイマー4を使うコードも追加しました。やはりタイマー4のみのほうが音は大きいです。

スケッチの中で

#define PWMDAC_OUTPUT_PIN 13
#define PWMDAC_OUTPUT_PIN2 5

とするとタイマー4を使います。タイマー3を使う場合は、

#define PWMDAC_OUTPUT_PIN 5
#define PWMDAC_OUTPUT_PIN2 13

として下さい。

コメント