[戻る]

AVR ATtiny2313 ビデオ出力 ブロック崩し

2008年11月
前回のAVR ATtiny2313 ビデオ出力テストの続きで、AVRを使ったビデオ信号出力が
テーマの製作です。アプリとしてブロック崩しを作りながら、何が問題になるのか、
どう解決したかを紹介します。ブロック崩しはスイッチ操作で、アナログコントロールの
研究は次回の予定です。
回路図と部品一覧、ソフトを公開しています。

※画像拡大はここをクリック

ビデオ出力テストの続き

↓ビデオキャプチャ画像
↓クリックで拡大


ATtiny2313内蔵8MHz


セラロック20MHz

コンポジットビデオ信号(NTSCビデオ信号)を作るにあたり、まずはクロックをAVR内蔵クロックから外部供給へ変更します。動作速度を速くしたいことと、安定したクロックを供給することが目的です。

AVR ATtiny2313 ビデオ出力テストの映像をPCでビデオキャプチャしたものが左・上の画像です(6行表示に変更)。ATtiny2313内蔵クロック8MHz。なんとなく規則性があるギザギザで乱れています。

20MHzのセラロック(セラミック発振子)に変更したものが左・下の画像です。乱れなく文字が出力されます。クロック任せで処理能力も上がり、1行で6文字表示は余裕でした。

ビデオ信号の出力には比較的シビアなタイミング制御が必要です。
時計や周波数カウンタやUSB制御など、特に安定したクロックが必要なときはクリスタル(水晶振動子)/クリスタル発振器を使いますが、ビデオ信号の出力ではセラロックで十分目的を果たせるようです。

※クリスタル+コンデンサより安価なセラロックを使いたかった。


「A」の乱れが目立つ


右へ行くほど乱れる


1行3文字が読める限度か


読めそうにない文字も

ここまでで表示していたソフトの内容を整理し、作り直し、改めて出力実験したものが左・1番目の画像です。
1文字出力する処理を変更して、これまでより文字幅が狭くなっています。幅を決める具体的な制御はしていません。処理時間がそのまま幅表現となって現れます。

ここで微妙な文字の乱れに気付きます。
文字は5x5のフォントで「A」は下図のようになっていますが、
□□■□□ :1ライン目
□■□■□ :2ライン目
□■■■□ :3ライン目
■□□□■ :4ライン目
■□□□■ :5ライン目
出力された映像では3,4,5ライン目が1,2ライン目より狭いです。
走査線1ライン毎にもわずかなブレが見えます。

これをハッキリ見るため、ソフト側で文字幅を広げる調整をしたものが左・2番目の画像です。1行10文字にしました。
映像の右へ行くほど乱れが大きくなります。しかもズレ方がわかりません。例えば同じ色が続くと狭くなるとか、白は広がるとか分かれば対策できるかもしれませんが、そういったルールが見えません。
走査線1ライン毎のブレも1番目の画像とちょっと様子が違うようです。

「A」「V」「R」ばかりで飽きたので数字、アルファベットA-Z、記号のフォントを作ってテスト表示したものが左・3,4番目の画像です。
1行3文字くらいなら乱れが目立ちません。全部出力してみると、やはり乱れがひどく、右半分くらいでは読めそうにない文字、記号があります。

1行10文字5行表示にして、TVメッセージボードや、マイコン開発のデバッグ出力をTVに、などと考えていたのですが、これでは役に立ちません。お手上げ、放置か? ……実は最初から、WinAVRの強力な最適化が原因だろうと見当は付いていました。
ここまで最適化オプションを「-Os」か「-O2」にしていました。最適化を切って比較したいところですが、「-O0」だとプログラムサイズが2KBを軽く超え、ATtiny2313に書き込めません。

アセンブラを使わず何とかCでねじ伏せようとソフトをいじくり回した結果、やはりCで記述してるだけでは解決できないという結論に至りました。

※一週間くらい格闘したが、無駄な時間とは思っていない。

ブロック崩しを作る


開発イメージ
ただマス目を並べただけ
で全く動かせず、描画の
ズレも解決していない。

ビデオ信号出力のアプリとして、メッセージ表示系の他にゲームを作りたいと考えていました。ビデオゲーム(TVゲーム)の始祖としてPONGが有名で、AVRやPICでの開発例もあります。
私はPONGに馴染みがなく、TVゲームの元祖としてはドライブゲーム(車らしき形のものがまっすぐな道路上で障害物を避けながら走る)や、ブロック崩しに親しみがあります。ということで見た目がダイナミックなブロック崩しを作ります。

PONG、ドライブゲーム、ブロック崩しには共通点があります。それはボリューム(可変抵抗)で操作することです。ビデオ信号出力とアナログコントロール、面白そうな開発テーマです。


(ほぼ)初めてのアセンブラ
最適化はCのソースをアセンブラへ展開するときにかかります。アセンブラで記述したソースはそのままコンパイルされます。
C言語での開発を主体としたいので、プログラム全体ではなく画面に描画する部分だけをアセンブラで記述して、その関数をC側から呼び出すようにしました。
私はアセンブラのプログラミング経験がほとんど無いので、できた関数はアセンブラの特長を活かした効率的なものではないかもしれませんが、意図通り動作しているので、これでよしです。
ソースはこのページのもう少し下の方で公開しています。

※ソース中のコメントに間違いや勘違いがあるかもしれず。ご容赦。


描画ズレ修正の確認


遊べる形になった

左・上の画像で、ブロックの領域に描画ズレ確認の縦線を出力しています。ゲームが進行して、ブロックの状態によらずこれが綺麗に一直線に出ています。描画ズレの問題が解決しました

画面の描画処理は、ブロック領域、ブロックとバーの間の領域、バー、バーの下の領域、で分かれています。
一番左の縦線は描画開始のタイミングで出力していて、これが領域判定のif文ごとにずれていくのが面白いです。Visual CじゃないけどビジュアルなCだ!

これでブロック崩しの完成が見えました。とりあえずスイッチ入力で操作することにして、ゲームをプレイできる状態にした様子が、左・下の画像です。
ここまで1マスを縦8ラインで描画してきましたが、小さくてプレイしづらかったので10ラインに変更しました。ゲームで有効な画面範囲(プレイフィールドと呼ぶことにします)は横16マス、縦24マスです。ちなみにボールは1マス、バーは5マスです。
マスの幅は、TV画面を見ながら適当に時間待ち処理を入れて正方形になるよう調整してるだけです。キャプチャ画像では微妙に横長に見えますが、これは4:3の映像が720x480に引き伸ばされているためです。


完成画面
ブロック領域の上にも
空間を作れるよう変更
した。


左右端の縦線に満足

もう一苦労
一応遊べる形になっているし、AVRでビデオ信号出力というテーマはクリアできたので製作完了としてもよかったのですが、プレイしてみると我慢できない不満が1つ出てきました。「ボールが反射する左右端がどこか分からない」
プレイフィールドの縦方向は画面の高さ分、横方向はブロック領域の幅です。この幅をわかりやすくするために、ボールが反射する壁を描画することにします。

プレイフィールド全体をフレームバッファに持てれば描画は楽なのですが、ATtiny2313のSRAMは128byteしかありません。(*1)
1マス3色(白灰黒)を2bitで表現して横4マスを1byteに詰め込めますが、ブロックの色変更や、ボールやバーの移動をフレームバッファに反映させる処理が面倒になるので(シフト演算とビットマスクの大乱舞と予想)、1マス1byteで扱いたいです。とすると、プレイフィールド全体では1bytex横16マスx縦24マス=384byte必要となり、余裕でアウトです。

それで、ブロック領域だけバッファ(すなわち配列)に持ち、ボールとバー、残りの空間はタイミングを計りながら描画することにしました。
これがまた、先述の通りif文一つでタイミングがずれるわけですから、画面の上から下まで綺麗な縦線を引くのはかなり大変でした。静止状態でまっすぐ揃えてもボールが動くとその行はガタガタになったり。ボールの上の行、ボールの行、ボールの下の行で描画タイミングを揃えなければならないのです。if文と最適化の”妨害”を受けながら。
ボールとバーを狙い通りの横位置に出すためのささやかな工夫として、1行分のバッファ(16マス分の配列)を用意してブロック描画の関数を利用しています。
結局ほとんど_delay_us()(で使われる_delay_loop_1())とnop相当のインラインアセンブラで、強引に、かつ、ちまちまとタイミングを合わせました。この作業はもうやりたくないです。


(*1)…プログラムを書き込む領域はflashROMで、ATtiny2313には2KBあります。プログラムの実行に必要なメモリ領域はSRAMで、これはPCでいうとメインメモリにあたるものですが、ATtiny2313には128byteしかありません。ATmegaシリーズだともっと多く、例えばATmega88には1KBあります。

回路図とプログラム


この先アナログコントロールの実験があるので、現段階では基板で作らずブレッドボード上に組んで完成としています。

回路図はここをクリック
部品一覧はこのページの一番下にあります。

プログラムはここをクリック。(VBlock ver1.10)
プログラムはZIPで圧縮してあります。解凍するとVBlock.c(プログラム本体)、aryPaint.s(ブロックの描画関数)が出てきます。
AVR StudioでVBlockというプロジェクトを新規作成し、2つのソースをプロジェクトに加え、最適化オプション「-O2」でビルドしてください。ソースが不要な人は同梱のVBlock.hexをATtiny2313に書き込めばok.


ブレッドボード上に組んだ回路


拡大

(動画)
AVR ATtiny2313 ビデオ出力 ブロック崩し
ビデオ出力 ブロック崩し




(*2)…次回製作予定のアナログコンパレータ(を応用したA/Dコンバータ)利用時にもう一つ割り込みを使いそうで、そうするとスリープを利用する方法は使えなくなります。描画タイミングと関係なしに、アナログコンパレータ用の割り込み発生時点でスリープが解除されてしまうからです。
それを見越してスリープを使わなかったのですが、今回は今回、次回は次回で割り切りました。
さてアナログコンパレータ、多重割り込みで行けるか、割り込みを使わずに応用できる方法があるか……どうなりますかね。

回路について
・白レベル1.0V(これ以下でこの付近の値。例えば0.9V)
・黒レベル0.286V(これ以上でこの付近の値。例えば0.3V)
ビデオ信号を生成する抵抗2本の値はVCCを5Vとして決めています。3V(乾電池2本)で動かすならR1=680Ω、R2=220Ωでどうでしょうか。試していませんが
[Video Output] H=3v, L=0v
Video1(R1=680Ω) H L H L
Video2(R2=220Ω) H H L L
Output 0.93v 0.76v 0.3v 0v
White Gray Black Sync

現段階ではボリューム(可変抵抗)ではなくスイッチにしています。操作性は良好、ハードもソフトも簡単です。

プログラムについて
水平同期周波数15.734kHz→1ラインの周期は63.56us、これをタイマ/カウンタ割り込みで作り、描画処理しています。走査線1ラインの開始でまず描画、続いてデータ処理(ボールやバーの移動、ブロックとの当たり判定)、の繰り返しです。
データ処理後、次のラインの描画開始(割り込み発生)までスリープ状態にしています。実は開発後期までスリープを使わず、割り込み処理完了時にフラグを立て、main()側while(1)内でフラグ監視という方法をやっていました。(*2)
しかし最適化の影響なのか、それだと描画タイミングが不揃いになり、画面が細かくブレました。ブレを抑えることはできましたが消すことはできませんでした。

コンポジットビデオ信号は本来なら偶数フィールド/奇数フィールドで1画面(525ライン)を構成しますが、この開発では1フィールド(262ライン)で1画面としています。そのためTV画面で見ると走査線の隙間がよく分かります。

アセンブラのソースはブロックを描画する関数です。配列に格納した色番号を見て、その値によらず出力までのタイミングが等しくなるよう調整してポートに出力する、という処理内容です。
この関数ではプログラムの対称性がポイントになります。
配列には色番号「0,1,2」が格納されていて、この判定を次のようにしています。

 if (値==0) 色番号0の処理へジャンプ →出力処理へ
 if (値==1) 色番号1の処理へジャンプ →出力処理へ
 if (値==2) 色番号2の処理へジャンプ →出力処理へ
 それ以外の値は出力処理へジャンプ


値が0,1,2どれでも同じ判定でその後の処理経路も同様です。この判定順では値が小さいほど先に出力処理へ到着することになりますが、対称性のおかげで時間差が等しくなるのでタイミング待ちの処理が簡単になります。

一方、次のように判定した場合…

 if (値==0) 色番号0の処理へジャンプ →出力処理へ
 if (値==1) 色番号1の処理へジャンプ →出力処理へ
 それ以外の値は色番号2として出力処理へジャンプ


値が0,1のときと2のときでは判定と処理経路が異なり(対称性が損なわれた)、出力処理のタイミングが合わせにくくなります。


ゲーム内容的には
ボールの斜め反射が45度だけなので、単調なプレイになります。若干の小細工はしてありますが。
今回の製作においてゲームの作り込みは二の次です。次回製作時に余力があれば改良しましょう。

参考回路(予告編?)


ボリューム(可変抵抗)

スイッチではなくボリューム(可変抵抗)で操作する回路も作って試しました。ATtiny2313のアナログコンパレータ機能を使ってみる実験です。
ボリュームを左へ回すとバーはずーっと左端まで動き、右へ回すとずーっと右端まで動きます。A/Dコンバータではないので、途中の位置で止めることはできません。ゲームとして問題ありです。
回路図はここをクリック

ボリュームはBカーブのものを使います。軸の回転量に比例して抵抗値が変化するものです。
ちなみに音量調節で使うタイプはAカーブです。これは抵抗値が対数的に変化します。

PB0(AIN0)に5V(VCCの電圧)を二分した電圧を入力します。これが基準電圧になります。
PB1(AIN1)にはボリュームで0〜5Vまでの電圧が入力されるようにします。
これで2.5Vを境に左/右の判定ができます。

プログラムを変更します。
ポートやタイマ/カウンタの設定をしている付近でアナログコンパレータの設定をします。

//アナログコンパレータの設定
ACSR = 0b00000000;

キー入力判定のところを次のように変更します。

//アナログコンパレータで操作判定
barDirecX = DIREC_NEUTRAL;
if ((ACSR & _BV(ACO)) && 0 < barX) //if (AIN0 > AIN1), _BV(ACO)→(1<<5)
{
barX--;
barDirecX = DIREC_LEFT;
}
if (!(ACSR & _BV(ACO)) && barX < FIELD_W - BAR_W)
{
barX++;
barDirecX = DIREC_RIGHT;
}

部品について

基板ではなくブレッドボード上に組むとします。
そのため電源コネクタやISP用のピンヘッダは部品一覧には書きませんが、
必要に応じて用意してください。

抵抗は R2 470Ω [黄紫茶金] の値を確かめるときに注意することがあります。
抵抗に印刷された色で紫と茶の区別が付きません。光にあててよーく見ないと分かりません。
お店のケースの中に間違った値の物が混ざっていることは無いと思いますが、
買うときに念のため確認してください。

クリスタル発振用のコンデンサの値はATtiny2313のデータシートを参考にしてください。
22pFでよいと思います。
ただのセラミックコンデンサと積層セラミックコンデンサのどちらでも構いません。

今回の製作物においてコンデンサ内蔵のセラロックで正常に動作しています。
コストの面からこちらをお勧めします。
回路図の点線囲みの部分、「クリスタル+コンデンサ2個」をセラロックに置き換えてください。

RCAジャックはお馴染みの黄色いビデオケーブルを差し込むジャックです。
基板実装用やケース取り付け用などの形状がありますが、
ブレッドボードに取り付けるならどっちもどっちです。好みで決めてください。

ATtiny2313 ビデオ出力 ブロック崩し 部品一覧 (回路図はここをクリック
部品名 部品番号 個数 参考価格/備考
AVR(マイコン) ATTINY2313 ATtiny2313 1 100円(秋月電子
抵抗 R1 1.2kΩ [茶赤赤金] 1 1個5円/100個100円
抵抗 R2 470Ω [黄紫茶金] 1 1個5円/100個100円
抵抗 R3 75Ω [紫緑黒金] 1 1個5円/100個100円
積層セラミックコンデンサ C 0.1uF [104] 1 10個100円
タクトスイッチ SW1,SW2 2 10個180円
千石電商 店頭価格)
RCAジャック J 黄色 1 30円
クリスタル XTAL 20MHz 1 10個500円(秋月電子)
(積層)セラミックコンデンサ クリスタル発振用 22pF 2 1個20円
セラロック 点線囲み部分 20MHz 1 40円
【参考】ボリューム操作のテスト
ボリューム(可変抵抗) VR Bカーブ10kΩ 1 90円
抵抗 R4,R5 10kΩ [茶黒橙金] 2 1個5円/100個100円

次回は…
ATMEL社のサイト内、Application Notesのページに、
アナログコンパレータをA/Dコンバータに応用する方法が紹介されています。
http://www.atmel.com/dyn/products/app_notes.asp?family_id=607#General%20Purpose
AVR400: Low Cost A/D Converter

次回はこれに挑戦する予定です。上手く応用できるかどうか楽しみです。


[戻る]
inserted by FC2 system