yskwiki
ATTiny13Aでビデオ信号生成(NTSC) その1
最終更新:
yskwiki
-
view
やろうとしたきっかけ
PICマイコンを触る機会があったんですが、その時にPIC16F84Aでテトリスを作った人のページを見つけたのがきっかけです。
PIC18F84Aといえば
PIC18F84Aといえば
プログラムメモリ | 1kbytes |
SRAM | 68bytes |
こんな感じ。
これってATTiny13Aとほとんど同じでは?しかも13Aは8pinだから16F84Aよりも小さいものが作れる!と思って試そうと思いました。
しかもPICは1命令実行するのに最短4クロックかかるのに対して、AVRは1命令を1クロック(最長でも4クロック)で実行できるので、単純に考えてPICの4倍の速さで処理できるんですよね。
これってATTiny13Aとほとんど同じでは?しかも13Aは8pinだから16F84Aよりも小さいものが作れる!と思って試そうと思いました。
しかもPICは1命令実行するのに最短4クロックかかるのに対して、AVRは1命令を1クロック(最長でも4クロック)で実行できるので、単純に考えてPICの4倍の速さで処理できるんですよね。
でもぐぐってみると、ATTiny13Aではなく2313でやってる人が多い・・・。この時はこの理由がわかりませんでしたが、後々気付く事に。
NTSC信号って?
まずNTSC信号ってどんな仕様なんだ?というわけで、ぐぐって見つけた↓のページで勉強しました。
http://www.nahitech.com/nahitafu/mame/mame6/sync.html
http://www.nahitech.com/nahitafu/mame/mame6/sync.html
- 1ラインの長さは63.55us。
- NTSCはインターレース方式なので、1画面を2回に分けて描画する。
- 1画面のライン数は525。インターレースなので、1フィールドあたり262.5ライン。
- VSyncの長さは3ライン分。
- VSyncの前後3ラインには等価パルスが必要。
- 通常ラインのHSyncの長さは4.7us、等価パルスの場合はその半分の2.35us。
重要なのはこれくらいですかね。詳細については↑のページにお任せ。
ここで、1フィールドあたり262.5ライン?0.5ラインってなんぞ?と思ったんですが、偶数フィールド目の等価パルスが263ライン目の真ん中から始まるので262.5ということなんですね。
ここで、1フィールドあたり262.5ライン?0.5ラインってなんぞ?と思ったんですが、偶数フィールド目の等価パルスが263ライン目の真ん中から始まるので262.5ということなんですね。
回路の仕様を考えてみる
さて、NTSCがどんな感じなのか(表面上の事は)大体わかったので、どんな回路にすればいいのか考えてみます。今回はお試しなので簡単なモノクロ表示にします。
モノクロの場合、必要な信号はSync、Black、Whiteの3レベル。↓によると、NTSC信号は1Vp-pらしい。
http://www.nahitech.com/nahitafu/mame/mame6/voltage.html
本来ならBlackを0Vとするべきらしいのですが、まじめにそうしようとするとSyncが負レベルになってしまいます。ところが、ありがたいことにテレビ側でレベルを調整してくれるらしく、Syncを0Vとしても動いてくれるらしいです。
Syncを0Vとすると、各信号レベルの値は以下のようになります。
モノクロの場合、必要な信号はSync、Black、Whiteの3レベル。↓によると、NTSC信号は1Vp-pらしい。
http://www.nahitech.com/nahitafu/mame/mame6/voltage.html
本来ならBlackを0Vとするべきらしいのですが、まじめにそうしようとするとSyncが負レベルになってしまいます。ところが、ありがたいことにテレビ側でレベルを調整してくれるらしく、Syncを0Vとしても動いてくれるらしいです。
Syncを0Vとすると、各信号レベルの値は以下のようになります。
Sync | 0.0V |
Black | 0.3V |
White | 1.0V |
2本のピン(今回はPB0/1)を使って、この3つのレベルの信号を作ります。
PB0 | PB1 | 電圧 | |
Sync | L | L | 0.0V |
Black | H | L | 0.3V |
White | H | H | 1.0V |
テレビ内部の抵抗値を75Ωなので、PB0の抵抗をR1、PB1の抵抗をR2とすると、R1とR2の比率は
R2=3/7*R1
となります。さらに、電源電圧を5Vとすると抵抗値は以下のようになります。
R2=3/7*R1
となります。さらに、電源電圧を5Vとすると抵抗値は以下のようになります。
R1 | 1kΩ |
R2 | 428Ω |
428Ωの抵抗は無いので、220*2で440Ωとします。
この時ピンから出力される電流は最大で10mA弱なので、スペック的にも大丈夫そうです。
この時ピンから出力される電流は最大で10mA弱なので、スペック的にも大丈夫そうです。
したがって、回路図はこんな感じになります。
信号生成のタイミングを考えてみる
クロックは内蔵オシレータ9.6MHzを使用します。今回は表示できればいいので、考えるのは以下の4種類のライン。
- 垂直同期(Vsync)
- 等価パルス(EQP)
- ブランク(Blank)
- 通常(Normal)(今回は縦縞模様)
9.6MHzでは1ラインの処理時間は610クロックとなります。
垂直同期、等価パルス、ブランクについては、SyncとBlackの長さが違うだけで処理自体は同じです。
垂直同期、等価パルス、ブランクについては、SyncとBlackの長さが違うだけで処理自体は同じです。
と、ここで一旦1画面の構成を考えます。(「NTSC信号って?」の参照先に同じような表があります)
line | type(odd-field) | line | type(even-field) |
1 | EQP | 263.5 | EQP |
2 | EQP | 264.5 | EQP |
3 | EQP | 265.5 | EQP |
4 | Vsync | 266.5 | Vsync |
5 | Vsync | 267.5 | Vsync |
6 | Vsync | 268.5 | Vsync |
7 | EQP | 269.5 | EQP |
8 | EQP | 270.5 | EQP |
9 | EQP | 271.5 | HalfEQP |
10 | Blank | 272 | HalfEQP+HalfWait |
11 | Blank | 273 | Blank |
12 | Blank | 274 | Blank |
13 | Normal | 275 | Blank |
14 | Normal | 276 | Normal |
: | : | : | : |
262 | Blank | 524 | Blank |
263 | HalfBlank | 525 | LastBlank |
これをまとめると必要なサブルーチンは以下の8種類になります(4種類ではありませんでした。ごめんなさい)。()内はサブルーチン名。
- Vsync(S_VSYNC)
- HalfEQP1(S_EQP1)
- HalfEQP2(S_EQP2)
- HalfEQP2+HalfWait(S_EQPL)
- Blank(S_BLANK)
- HalfBlank(S_HALFBLANK)
- LastBlank(S_LASTLINE)
- Normal(S_NORMAL)
半ライン分の等価パルス(HalfEQP)がなぜ2種類あるかというと、等価パルスでのSync期間は4.7us/2=2.35usですが、9.6MHzで考えると2.35us/0.104166=22.56クロックとなるので、前半半分(HalfEQP1)では22クロック、後半半分(HalfEQP2)では23クロックとして、1ライン分のSync期間の合計が4.7usとなるようにしました。こんなことをしなくてもある程度はテレビ側で調整してくれるかもしれませんが、初めてなので真面目にやってみます。
ここで、わかりやすくするためにSyncの開始位置を各ラインの先頭として考えると以下のようなタイミングになります。(通常ラインについては後述)
S_VSYNC | S_EQP1 | S_EQP2 | S_EQPL | S_BLANK | S_HALFBLANK | S_LASTLINE | |
Sync(1回目) | 260 | 22 | 23 | 23 | 45 | 45 | 45 |
Black(1回目) | 45 | 283 | 282 | 587 | 565 | 260 | 563 |
Sync(2回目) | 260 | - | - | - | - | - | - |
Black(2回目) | 45 | - | - | - | - | - | - |
合計 | 610 | 305 | 305 | 610 | 610 | 305 | 608 |
最後のライン(S_LASTLINE)については、メインループの繰り返しでrjmp命令が必要となるため、2クロック減らしています。
ただし、実際にはアセンブラコードの実行時間があるので、それも含めて考えてみます。使う命令は下記の4種類。()内の数字は実行時間。
ただし、実際にはアセンブラコードの実行時間があるので、それも含めて考えてみます。使う命令は下記の4種類。()内の数字は実行時間。
- cbi(2)
- sbi(2)
- rcall(3)
- ret(4)
1ライン分の長さは610クロックですが、サブルーチン化するとrcallとretがあるため、この分の処理時間は最後のBlackの期間を減らす事で調整します。
ここで、制約条件として各サブルーチン終了時(つまりサブルーチン呼び出し時)の出力レベルはBlackとします。(こうすればPB1の値を気にしなくて済みます)
S_VSYNC | S_EQP1 | S_EQP2 | S_EQPL | S_BLANK | S_HALFBLANK | S_LASTLINE | ||
Sync(1回目) | cbi PORTB,PB0 | 2 | 2 | 2 | 2 | 2 | 2 | 2 |
wait | 258 | 20 | 21 | 21 | 43 | 43 | 43 | |
Black(1回目) | sbi PORTB,PB0 | 2 | 2 | 2 | 2 | 2 | 2 | 2 |
wait | 43 | 274 | 273 | 578 | 556 | 251 | 554 | |
Sync(2回目) | cbi PORTB,PB0 | 2 | - | - | - | - | - | - |
wait | 258 | - | - | - | - | - | - | |
Black(2回目) | sbi PORTB,PB0 | 2 | - | - | - | - | - | - |
wait | 36 | - | - | - | - | - | - | |
合計 | 603 | 298 | 298 | 603 | 603 | 298 | 601 | |
合計(rcall,ret含む) | 610 | 305 | 305 | 610 | 610 | 305 | 608 |
これでほぼ完成です。あとはこれらをアセンブラコードに落とすだけです。
通常ライン(S_NORMAL)についてはS_BLANKをベースにして作るだけなのでここには書きません。
通常ライン(S_NORMAL)についてはS_BLANKをベースにして作るだけなのでここには書きません。
とりあえず最初は映す事が目的なのでwaitの部分は単純なカウンタで実装します。
後でタイマー割り込みに置き換えます。
後でタイマー割り込みに置き換えます。
ソースコード
wiki_tn13_ntsc01.asm実行結果
アナログのテレビがないので、デジタルの液晶テレビに繋いでます。
何やら画面が歪んでいる・・・。
ぐぐってみると、やはりAVRで画面が乱れているという人がいました。
http://jsdiy.web.fc2.com/avr_tiny2313_vblock/
どうやらAVRの内部発振回路が原因らしい。
なにやら新しめのAVRでは不安定らしいですね。ただ、乱れ方が少し違うのが気になりますが、オシロを持ってないので、解析できません(´・ω・`)
http://elm-chan.org/docs/avr/jitter.html
師匠に聞いてみると、RC発振回路は不安定なので、タイミングが重要となる回路では水晶を使うべきとのこと。
ATTiny13Aのマニュアルを確認してみると、9.6MHz/3V/25℃の条件での精度は±10%と書いてある。10%・・・w
ぐぐってみると、やはりAVRで画面が乱れているという人がいました。
http://jsdiy.web.fc2.com/avr_tiny2313_vblock/
どうやらAVRの内部発振回路が原因らしい。
なにやら新しめのAVRでは不安定らしいですね。ただ、乱れ方が少し違うのが気になりますが、オシロを持ってないので、解析できません(´・ω・`)
http://elm-chan.org/docs/avr/jitter.html
師匠に聞いてみると、RC発振回路は不安定なので、タイミングが重要となる回路では水晶を使うべきとのこと。
ATTiny13Aのマニュアルを確認してみると、9.6MHz/3V/25℃の条件での精度は±10%と書いてある。10%・・・w
なるほど。でもとりあえず、今回のところはこれで良しとします。
外部発振器を使ってのリベンジは別ページ「AVR/ATTiny13Aでビデオ信号生成(NTSC) その2」に記載します。
外部発振器を使わずにふにょふにょの画面で遊ぶのもいいかもしれませんねw
外部発振器を使ってのリベンジは別ページ「AVR/ATTiny13Aでビデオ信号生成(NTSC) その2」に記載します。
外部発振器を使わずにふにょふにょの画面で遊ぶのもいいかもしれませんねw