MachiKaniaでの音楽の再生

MachiKaniaマイコンの限界まで詰め込んだビデオゲームシステムですが、ビデオは素晴らしいのですが音楽がチープだったのでそれを改善するためのプロジェクト。

まず元々のプロジェクトでは音声出力はOC(OutCompare)を使ってコンペアマッチイベントでピンをトグルすることで目的の波形を生成しています。負荷率はほぼ0%に近く、おかげに常にDutyRatioが50%というものですが1chでおかげに出力が矩形波のみです。エンベロープはなしです。

ということで改善させてみました。使用を以下のようにまとめます。

・任意波形形式(テーブル参照方式)をとり、矩形波のみならず様々な波形を出力する。

例えば矩形波、オルガン、三角波、波形ではありませんがホワイトノイズなどを扱います。まあ2-3chですかね?負荷が結構高いのであまり同時に再生ができません。

エンベロープを実装し、音の減衰を再現する。

これが実は音の要素としてかなり重要になります。FF3のファミコン音源が私は大好きでいまも聞きながら書いていますが(ww)、音の減衰がかなり精密に作られていて、とてもすばらしいです。

・サンプリング16kで、出力周波数は200khz付近、周波数が高いのでテレビ内蔵のフィルターなどを用いて、元の回路そのままで再現可能にする。

当たり前ですが回路の拡張が必要だったら誰も使いません。そんなわけで回路はそのまま、機能のみ拡張(負荷も上がりますが)。とします。

まずはWaveTable方式について説明します。PCM音源とは異なる点がバッファのサイズです。本システムでは音源を再生するための十分な容量が確保できません。なので小さな一周期分のテーブルを用意してそれをぐるぐる回して再生します。テーブルはリング状になっていてそれの中をぐるぐるとインデックスが(以後Thetaと呼ぶ)を進めていきます。このテーブルが例えば矩形波や、三角波だったりするわけでテーブルの内容をそのまま出力します。ただし音程がありますのでThetaに加算する量は調整する必要があります。この辺の話は過去の記事を参考にしてください。まああくまでも個人的なメモであり、あまり読みやすくはないですが。http://elm-chan.org/works/mxb/report_j.htmlここがわかりやすいですね。ただし今回は波形の出始めに別の波形を搭載するというのはなしで。本当なら入れたいのですが処理が多くなりそうなので。。。

で、目標の音色を出せたとして、減衰計算も行います。これもここをみてください(雑)。

原理は以上ですがこれをそのまま実装するとものすごく負荷が高いです。ので簡単な方法に直します。まず、出力区間を以下とします。img_2682

この時の増減分を定義して、各区間にて現在の値に足し合わせます。

とりあえずソースコードはここからどうぞ。

https://github.com/elect-gombe/WAON_onMachiKania

コードはリファクタリングしますので。今はダーティーです。

CubeMX コードジェネレータチュートリアルもどき

CubeMXはST社のコードジェネレータです。このジェネレータはライブラリとその設定(初期化)、クロックの設定、RTOSなどのミドルウェアを生成します。そのことについてメモメモ。

Read more

マイコンのDMAでの断続的な転送


DMA 単一のアドレス空間上にはFlashペリフェラル(周辺機器)のレジスタ、RAMメモリがある。この空間の間は通常CPUなどがアクセスしてメモリ間転送している。

img_2582

これはCPUにやらせてもいいのだがCPUはリソースを取られるし一定時間内で転送しなければならない項目が重なるとデータをこぼしかねない。なのでその代わりにDMAを使ってCPUを返さず直接的にメモリとペリフェラル(周辺機器)間をアクセスするのがDMAである。

Read more

久しぶりのオルゴールについて

今回は減衰について説明しましょうかね。

オルゴールでは大抵音が鳴り始めたら減衰します。この音の減衰にあたる部分まで演算して求めようという考え方です。

人間の聴覚では音の大きさはdBで表されることが多いですね。音が相対的に大きさが触れられるのもかなり大きいです。そこで実際に操作するときはexp(指数関数)に通してから減衰させることも多いです。これは感覚的なものですがね。

最初に音がで始めるときは大きな音が出ることが多いです。そこからある一点に落ち着いてからゆっくりと減衰する。これを電子的にも再現することが今回の目的であります。

Read more

時計の設計Elecrow基板発注

img_2528

Elecrowに基板発注しました。しばらくして写真が送られて来ました。これからどのくらいで届くかな?楽しみですね。

2016/11/12追記

到着しました。

img_2597 img_2598

はい。まあまあですね。シルクが少しずれているところがありました。でもFusionPCBよりかは綺麗です。ユニクラフトはやっぱり良すぎた。。。

宇宙展行く

幕張の宇宙展に行ってきました

img_2492

撮影スポットはここだけであとは見るだけでした。

内容は様々でしたが月の石も見れました。軽石にゴマがたくさんくっついているみたいな感じです。貸し出しもなかなかしてくれないレア物なので見れてよかったです。

人は休日にしては少なかった気がしますが。。。

時計の完成(ハード側)

時計が完成しました。ソフトはまだ調節中です。(時刻調節未実装)

img_2489

左の6は輝度を示します。CdSで自動的に輝度を変更します。時刻カウントについてはほぼ完璧でした。まあずれても困るしね(w。

アクリル板もレーザーカットしました。裏のパネルに彫刻も入れたりとか。

img_2503

まずはTouchSensingで調節してみるか。

それではタッチセンシングのやり方についてまとめておきます。TouchSensingについては多数のやり方が存在します。本マイコン(PIC16F1938)にはタッチセンサーが8つくらいついてた気がします。それらは浮遊容量の変化としてマイコンはタッチを検出します。なんか空いているIOを触るとグラグラしますよね?それと原理は同じです。以下の画像はアプリケーションノートからの転用の転用です。img_2491

センサーの反応値はアナログ的に出力されます。一定期間内に反応した回数であるためです。そのためノイズに対しては非常に弱くなります。何らかの形で様々なノイズ対策が求められます。今回はメーカーの指定通りのPCBパターンで作りました。センシングは容量の変化による充放電の時間変化をもち入ります。そのため元から容量成分が大きいとうまく検出しにくいのであらかじめパターンの周辺のベタグランドを両面とも抜いておきます。また、センシングまでの経路は極力最短で配線し、ノイズの影響を減らします。ガードパターンを敷いてクロスストークを極力減らしますが、センサーの裏にはベタパターンは敷かずに禁止ゾーンとして抜いておきます。これは浮遊容量を極力減らすためです。本来はtmr1を用いて計測します。これは成分における感度、振れ幅を大きくするためであり、比較的ラフに組んでも平気ですが、Tmr1はすでに時刻計測に用いています。だからパターンの設計時から考慮を重ねておく必要があるとの判断です。

タッチセンサーでは機械式でないためチャタリング等の誤作動が起きないものの、値はノイズなどで上下するためアナログ的なもなのでヒステリシスを設ける必要があります。ヒステリシスについての説明をします。

img_2494

上記の図は閾値を赤の線でとったときの反応図です。HLそれぞれ繰り返していて不安定です。これは得られるデータの中に不安定なデータが混入してしまった場合の例です。以下の図はヒステリシスを設けたときの反応です。

img_2495

一回の変化で一回のみ反応するようになります。これはHのときの閾値とLのときの閾値の差によるものでこれがあるおかげで大幅にシステムを安定化されることができます。このヒステリシス幅はソフトウェアで実施する際は通常かなり小さいです。例えばボタン式スイッチでは押すまでに必要な力とそれを維持する力は異なりますよね。それをSoftwareで実装するということです。

これは量産する際にも非常に重要です。例えば今回は何台か作りますがその状態によってはアナログ値が少々上下するかもしれません。これを吸収と言ったら大げさでしょうか?をすることもできます。反応するところとしないところまで指を近づけたときの動作を確定させるということになります。

ボタンを増やす際はさらに多くの課題を解決する必要があります。今回は1つだけですが、ボタンが複数あるということは同時にセンシングできるのが一つだけという制約上切り替えての使用になります。今回は使わないですが例えば4Portだけ持ちいれば最大10個のセンサーを実装することができます。同時押しなどは非対応になりますが。。。詳しくはApplicationNoteを見てください。それにしても素晴らしい機能ですよね。

さらに今回は移動平均を考えていきます。移動平均は移動する平均つまり直近n回のデータの平均を取る作業を示します。この移動平均を取ることで瞬間的なノイズに対して強くなりますがレスポンスが遅れるのが欠点です。ここでは10msごとのサンプリングを4回の移動平均をとってそのヒステリシスを設けたアルゴリズムになります。

入力したいポートはアナログ入力に設定しておきます。ADみたいに同時にサンプリングができずに複数ポートでは一つずつサンプリングします。サンプリング時間という明確なものはなく、実際に実装するときはオーバーフローしない範囲で適当に定めます。Tmr0の時に最低感度ならば10msが妥当でしょうか?200ほど触れます。触ると100ほどまで下がります。

では実際に実装することを考えてコードを書きます。

[c] タッチセンシング値読み出し後再スタート uint8_t _getTouchValue(void){ uint8_t data;

CPSON=0; data=TMR0; TMR0=0; CPSON=1;

return data; } 押下確認で10msごとに呼び出す。 int8_t getTouch(void){ static uint8_t data[4]; static uint8_t idat; uint8_t sum; static uint8_t result;

data[idat++] = _getTouchValue(); idat&=3;

sum=0; for(i=0;i<4;i++){ sum+=data[i]; }

if(!result&&sum<Border-Hysteresis){ result=1; } else if(!result){ result=0; } else if(sum<Border+Hysteresis){ result=1; } else{ result=0; } [/c]