2016年10月10日

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 LeonardoPWM出力できるピンは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

として下さい。
タグ:Arduboy
posted by boochow at 01:48| Comment(0) | Arduino | このブログの読者になる | 更新情報をチェックする

2016年10月04日

スケッチファイルのフォルダの中にフォルダを作ってはいけないらしい

Arduino IDEは今まで1.6.9を使っていたのですが、1.6.12が出たのでそちらをインストールしてみました。
ところが、1.6.12で今開発中のabshellをコンパイルするとコンパイルが通りません。

調べてみると、スケッチの中にフォルダを作って入れてあったNT-Shellがコンパイルされておらず、リンクエラーになっていました。
1.6.9ではコンパイルできていたので、仕様が変わったようです。
テンポラリフォルダを調べると、ソースコード自体はディレクトリごとコピーされていましたが、コンパイルはされていませんでした。

ネットで情報を漁ってみても、どうやら「スケッチを入れるフォルダの中にサブディレクトリは作れない」というほうが常識のようです。(文句を言っている人も多いのですが)

もしかすると1.6.12のバグで、今後修正されるのかもしれませんが、待ってもいられないのでフォルダを無くしてファイルをすべてトップ階層に移動しました。
また、ついでにこれまでフラッシュに格納していなかった定数配列を3つほど、フラッシュ側へ移動しました。
これでRAM使用率は68%まで下がったので、安定動作が期待できそうです。
posted by boochow at 00:00| Comment(0) | Arduino | このブログの読者になる | 更新情報をチェックする

2016年10月02日

Arduboy用シェルにビットマップ転送コマンドを追加



先週作成したArduboy用コマンドシェルですが、ビットマップ画像を表示させる命令についてはペンディングにしていました。
あれこれ考えた末、

・表示位置とデータは別命令とする
・データは16進数の文字列で送る

ということに決めました。

ビットマップはデータがとても重いので、1行の命令で記述しようとするとあまり大きな画像は表現できません。
1行は100文字程度、と考えると最大でも100バイトしか、一度には送れません。
すると、何回かに分割して送ることになります。
それなら、1度しか必要ない表示位置の指定と、何度も行うデータの転送は分けるのが理にかなっています。

表示位置は矩形(座標、幅、高さ)で指定し、それ以降に送られるデータはその矩形の中へ詰めていくことにします。
データはバイナリなので、テキストに変換する必要がありますが、今回は16進数にしました。
16進数だと1文字で4ビットしか送れないので、効率は悪いですが、覚えやすいのがメリットです。
ソースコードに書く値との互換性もあります。
uuencodeやbase64などを使えば、文字数の3/4程度のバイナリデータを送れますが、そこまで速度に拘る必要性は低いと判断しました。

とはいえ将来的に拡張も考えられるように、16進数のデータ転送命令は「.x」としました。
「x」が16進数を表しているつもりです。

書式は単純な16進数の連続で、たとえば
「.x aa55aa55」

「{ 0xaa, 0x55, 0xaa, 0x55 }」
というデータを表します。

実際の使い方はこんな感じです。
bitmap 52 20 24 24
.x 8080808888888888888c8c8880ffff81
.x 8082849c98c0c080000000f8f80800fe
.x fe222030200f7ff08000000000000000
.x 20e0607f7f30301f1f08080404000001
.x 070e1c7870e0fe60


これは、左上が(52, 20)で幅と高さが24ピクセルのビットマップを描画します。
続く.x命令が実際のビットマップのデータを現しています。

このデータは、たとえば以下で公開されているツールなどで生成したデータを、"0x"や","、余計なスペースなどを削除し、行の頭に".x "を付加すれば簡単に作ることができます。

Web-based bitmap editor - Arduboy / Development - Community

とはいえ、以前にも書いたように変換ツールも作る予定です。
とりあえずカラー画像をディザリングして白黒画像に変換するところまではできました。

abImage.png
タグ:Arduboy
posted by boochow at 23:35| Comment(0) | Arduino | このブログの読者になる | 更新情報をチェックする

2016年10月01日

Arduino(AVR)でのグローバル変数の扱い

先日Arduinoへ移植したNT-Shellですが、どうも動作が不安定で画面にゴミが出ることがあります。
RAMが逼迫しているという警告が出るので、グローバル変数の状況を調べてみました。
調べ方ですが、avr-objdump.exeというコマンドを使います。
このコマンドはArduinoのフォルダ内の「hardware\tools\avr」に入っています。
使用状況を調べるには、ソースコードをコンパイルして作成される.elfファイルが必要です。
(通常はテンポラリフォルダの中に保存されています。)

avr-objdumpの使い方は以下の通りです。

avr-objdump -S -j .bss project.elf

avr-objdump -S -j .data project.elf


初期化されない変数は.bss、初期化される変数は.dataで調べます。

これで調べてみたら、コマンド名の配列(入力されたコマンドを調べるために使っていた)が意外と領域を食っていたので、ちょっと手間ですがフラッシュのほうへ格納し直しました。
これで現時点ではRAM使用率が72%まで下がり、警告も出なくなりました。

また、avr-nmというコマンドを使うとシンボルテーブルの出力ができます。
こちらは使い方は
.\avr-nm.exe -C project.elf

です。
posted by boochow at 16:55| Comment(0) | Arduino | このブログの読者になる | 更新情報をチェックする

2016年09月29日

Arduboyでスケッチが書き込めなくなったときは

単なるTipsです。

Natural Tiny Shell(NT-Shell)をArduinoに移植してみたの記事ではシリアルポートを常時使っています。

開発中、ときどきスケッチが書き込めなくなることがありました。
スケッチの書き込みもシリアルポート経由なので、スケッチの動作がおかしいと書き込みモードに切り替わらない場合があるようです。

Arduboyはハードリセットボタンがありますが、Arduboyのライブラリにも対策がされています。

それは、「電源オンのときにUPボタンとLEFTボタンを押しているとセーフモードになる」というものです。
セーフモードでは、ユーザのスケッチは実行されません。
そのため、スケッチ書き込みの邪魔をするようなコードがArduboyに書き込まれてしまっていても、それを回避することができます。

これはライブラリのコードを見ていたら偶然見つけました。
Arduboy-1.1.1/src/core/core.hに以下のような記載があります。
    /// Safe mode
/**
* Safe Mode is engaged by holding down both the LEFT button and UP button
* when plugging the device into USB. It puts your device into a tight
* loop and allows it to be reprogrammed even if you have uploaded a very
* broken sketch that interferes with the normal USB triggered auto-reboot
* functionality of the device.
*
* This is most useful on Devkits because they lack a built-in reset
* button.
*/
void static inline safeMode() __attribute__((always_inline));


safeMode() の中身はcore.cppにありますが、何もしない命令(nop)の無限ループです。

これでもだめだったら、ハードウェアリセットという手段があります。
タグ:Arduboy
posted by boochow at 00:11| Comment(2) | Arduino | このブログの読者になる | 更新情報をチェックする
人気記事