PIC AVR 工作室->TopPage->工作のツボ->ビデオ表示のツボ

ビデオ表示のツボ

はじめに

マイコンでNTSCのビデオ信号を生成する方法を、サンプルプログラムを使って整理して行きたいと思います。

映像信号ってどんな風になっていれば良いのか、シンクロ信号はどんな風になっていればいいのか、各信号の電圧は どうなっていればいいのか、それらをマイコンで実現するにはどんな風にすればいいのか。

そして、そもそもビデオ信号の規格はどうなっているのか。規格からどのくらい外れるとどんな影響が起こるのか。 そういったことを、私の経験を元にしてわかったことを纏めておきたいと思います。

「ビデオ信号の規格って厳格っぽいし複雑だし…規格にきちんと沿ってないとマトモに表示されないんじゃないの?」 などと考えて自作をしり込みしている方の一助になれば幸いです。

サンプルプログラムはAVR用(tiny2313)ですが、その他のマイコンでも、考え方はそれほど変わらないでしょう。 私の場合、安価なマイコンの中でもMIPS値の大きいAVRをチョイスしましたが、お好きなマイコンに読み替えて お試し下さい。

出力イメージ

出力イメージ(ビデオキャプチャ画面)を以下に貼っておきます。下記のサンプルプログラムを実行した結果です。

画面の右と左にそれぞれ1本ずつ細い縦線が表示されます。とりあえず、完成予想図と思ってご覧ください。

ちょっとシンプルではありますが…

最初にどんな絵を描いてみても良いのですが、まずはこんなものを描いてみることで、テレビ画面では走査線上の どのタイミングからどのタイミングまでが表示可能となるのかが掴めます。それによって左側の余白期間、右側の余白期間に それぞれどの程度確保すればよいのかが判るのでお奨めです。

なお、このキャプチャー画面では左右に比較的余白が大きく出ていますが、使用したビデオキャプチャーが左右の広い範囲まで 取り込めるモノだからです。一般的なテレビに表示してみると、左右の余白がもっと削れて表示されるかと思います。

サンプルプログラム

ダウンロードファイル

AVRのTINY2313で映像信号を出力するプログラムです。出力信号はテストボードV1 に合わせて、映像信号はPB0から、シンクロ信号はPD6から出力されるようになっています。

このプログラムについて、以下では一部分ずつ取り出してその各処理内容を解説していきます。

映像信号1本分の出力について

まず、映像の走査線1本から始めたいと思います。

1画面分の映像信号というのは、二百数十本からなる走査線の映像信号と、9本の垂直同期信号から成っています。 その詳細は後で纏めて整理することとして、この映像信号から1本だけを取り出して触れてみたいと思います。

映像信号の走査線出力ロジックについて

まずはサンプルプログラムから、映像信号を含む走査線1本分の出力サブルーチンを切り出してみました。 サンプルプログラムの一番下の方にあるサブルーチンです。

このサブルーチンが呼び出されると、水平同期信号出力→左余白→左の線→真中余白→右の線→右余白、 の順で出力して呼び出し元に帰ります。とりあえずザーッと眺めてみてください。次項で各部分の解説をします。

    ;*****          *****
    ;***** <1ライン描写> *****
    ;*****          *****

LINE_DRAW:
    ;***** sync 出力 *****
    cbi    portB,0    ;ポートBの0ビット目をリセット
    cbi    portD,6    ;ポートDの6ビット目をリセット

    ;***** 4.7us *****
    ldi    l_count1,23  ;l_countに23をセット
DR_LOOP1:
    dec    l_count1    ;l_count1を1減らす 1clk
    cpi    l_count1,0   ;l_count1と0を比較 1clk
    brne    DR_LOOP1    ;0になるまで繰り返す 2clk
                     ;23回 * 0.2us(4clk) = 4.6us
    nop
    nop            ;0.05us * 2 = 0.1us


    ;***** black 出力 *****
    sbi    portD,6    ;ポートDの6ビット目をセット

    ;***** 10.0us *****
    ldi    l_count1,50  ;l_countに50をセット
DR_LOOP2:
    dec    l_count1    ;l_count1を1減らす 1clk
    cpi    l_count1,0   ;l_count1と0を比較 1clk
    brne    DR_LOOP2    ;0になるまで繰り返す 2clk
                     ;50回 * 0.2us(4clk) = 10.0us


    ;***** white 出力 *****
    sbi     portB,0    ;ポートBの0ビット目をセット

    ;***** black 出力 *****
    cbi     portB,0    ;ポートBの0ビット目をリセット


    ;***** 40.0us *****
    ldi     l_count1,200   ;l_countに200をセット
DR_LOOP3:
    dec     l_count1     ;l_count1を1減らす 1clk
    cpi     l_count1,0    ;l_count1と0を比較 1clk
    brne     DR_LOOP3     ;0になるまで繰り返す 2clk
                      ;200回 * 0.2us(4clk) = 40.0us


    ;***** white 出力 *****
    sbi     portB,0     ;ポートBの0ビット目をセット

    ;***** black 出力 *****
    cbi     portB,0     ;ポートBの0ビット目をリセット


    ;***** 8.7us *****
    ldi l_count1,43 ;l_countに43をセット
DR_LOOP4:
    dec     l_count1     ;l_count1を1減らす 1clk
    cpi     l_count1,0    ;l_count1と0を比較 1clk
    brne     DR_LOOP4     ;0になるまで繰り返す 2clk
                      ;43回 * 0.2us(4clk) = 8.6us
    nop
    nop                ;0.05us * 2 = 0.1us


    ret                ;戻る

以下、各部分について説明します。

sync出力 … 水平同期信号出力について

まずはコメント文が「sync出力」となっている所です。水平同期信号を出力しているところです。

走査線の左端ではまずsync信号(水平同期信号)の出力を行います。PB0、PD6の各端子にLOWを 出力していますが、この2端子のLOW出力がsync信号となります。

そのすぐ後にある「4.7μs」というのが水平同期信号の長さに相当します。PB0、PD6の出力を 保ったまま4.7μsの時間稼ぎをしています。

20MHzで動作するAVRは20MIPSなので1命令は0.05μs。4.7μsなら94クロック分の空回しをすればよい計算です。

…正確には、sync信号の手前には”フロントポーチ”という1.5μsのblackレベル信号が必要なのですが、 便宜上このフロントポーチは1本手前の走査線の右端部分で出力済みと考えてしまうことによって、走査線はシンクロ信号から開始する という理解です。別の表現をすると、「手抜き」です。(^v^)

この方が考えるのが楽だし、プログラムが短くて済みますからねっ。(NTSCカラーコンポジットの場合はこの辺のタイミングを もう少し厳密に考慮する必要がありますが…)

black出力 … バックポーチと左側余白について

コメント部分が「black出力」と「10.0μs」となっているところです。

水平同期信号を4.7μs間出力した後で、PD6にHIGHを出力しています(PD6がHIGHに、PB0はLOWのままとなります)。 これにより、出力電圧はblack信号レベルになります。実はこの10.0μsの期間には2つの信号が入ってます。 一つはバックポーチ。もう一つは画面右側の余白(余黒?)です。

水平同期信号の後ろ側4.7μsのところをバックポーチといい、上述のフロントポーチ、水平シンクロとこのバックポーチの 3つを合わせて水平ブランキングといいます。走査線1本はこの水平ブランキング期間と映像信号で構成されています。 白黒映像の場合はこのバックポーチの期間、black信号を出力しておけばOKです。

10μsのうち、のこった部分はというと、これが画面左側の余白(余黒?)を加えたものです。

なぜ左側にわざわざ何も表示しない部分を作るのかというと、テレビ画面にはバックポーチ~次ラインのシンクロ信号の間の映像信号が すべて画面に表示されるというわけではなく、左右両端の一部信号が画面からはみ出してしまうからです。

特に古いテレビ(ブラウン管が出っ張ってる昔ながらのテレビ)だと、はみ出る量が多くなるようです。 その表示されない部分の余裕をを含めて10μsとしています。

HAMジャーナルNo103によると、どんなディスプレイでもはみ出さずに表示が可能とするためには、 映像データの幅をある程度狭めておく必要があり、具体的には左側の余白(バックポーチ含む)を約9μs、 表示領域を40μs程度に留めて置いて、残りは右余白とすると良いと書いてあります。今回のサンプルプログラムも ひとまずこれに習っています。

なお、カラー信号の場合はこのバックポーチに”カラーバースト信号”というモノが乗ります。カラーバースト信号 についてはここでは詳説しません。ご興味のある方はカラー化の野望 をご覧ください。

残りの部分について

バックポーチと左余白の出力が終ったら、プログラムでは次に「white出力」を行っています。PB0にHIGHを 出力(結果PD6、PB0ともにHIGHとなります)することでwhite出力を実現しています。

1クロック分の白を出力したらまた黒に戻し、40μsの間黒を表示します。そしてまた1クロック分の白。 残りは右余白の黒を表示します。

これで、走査線1本分の出力が完了します。(なお、この右余白の黒には上記で触れたとおり1.5μsのフロントポーチを 含めてしまっています。念のため。)

この走査線を縦に延々と繰り返していく(積み重ねていく)ことで、縦に2列の白い線が描かれることになります。

インターレースとノンインターレースについて

テレビ画面1画面分の信号構成について説明する前に、インターレースとノンインターレースのことについて 触れておきます。

テレビ放送などで使用されているNTSC信号では、インターレース表示(飛び越し走査)というものが使われています。 インターレース表示に関する細かいことはひとまず専門書に譲りますが、 簡単に言うと2枚の映像を縦に少しだけずらして交合に表示することで、見た目の縦解像度を2倍にしてしまう という方式です。(正確にはぼやけるので2倍以下になるようです)

この半ラインだけずらすという方法をどうやって実現するかというと、1枚目の画面の一番下のラインを半分でぶった切って 強制的に終らせてしまって垂直同期信号(後述)を出力し、2枚目の画面最上部は半分だけのラインからスタートする、という ことを行います。NTSCの場合、この各画面(フィールドと呼びます)の走査線は262.5本。2フィールドで525本。 これで1枚の画面(これをフレームと呼びます)が構成されるという具合です。

このインターレースをマイコンで処理しようとすると面倒くさいし、525本(実際は、これから垂直同期や 文字放送信号などを除いた約480本)の解像度を必要とはしていないので、ノンインターレース(プログレッシブ)という 方法で表示をしているものが多いです。サンプルプログラムもノンインターレースですし、スプライト表示器や 背景表示器も同様です。

ノンインターレースというのは名前から想像がつくとおり、飛び越し走査を行わない方式で、画面下部が走査線半分だけで 終ったりしません。その分処理が簡単になりますが、1回の垂直同期で1フィールド=1フレームとなります。 垂直同期1回あたりの走査線数を同じ数(262本)とすると、インターレースされない分縦方向の解像度が粗くなります。 それでも240本程度あるのでマイコン用途なら充分でしょう。以降は、ノンインターレースで処理するという前提でお話します。 垂直同期に関してこの辺が整理できてないと混乱すると思うので、念のために書いておきました。

1画面分の映像の構成

1画面を構成する走査線の種類と数

サンプルプログラムはノンインターレース表示を行っています。このノンインターレース表示の場合、1画面を構成する 走査線は以下のような構成になります。

の合計262本。1フィールド=1フレーム=262本です。垂直同期の9本と非表示映像信号を合わせて 垂直ブランキング期間と呼びます。ではサンプルプログラムのメインループを見てみましょう。

MAIN_LOOP:

    ;*************************
    ;***** メインループ *****
    ;*************************

    rcall  EQ_PULSE    ;等価パルス 呼び出し
    rcall  EQ_PULSE    ;等価パルス 呼び出し
    rcall  EQ_PULSE    ;等価パルス 呼び出し
    rcall  V_SYNC     ;垂直シンクロ 呼び出し
    rcall  V_SYNC     ;垂直シンクロ 呼び出し
    rcall  V_SYNC     ;垂直シンクロ 呼び出し
    rcall  EQ_PULSE    ;等価パルス 呼び出し
    rcall  EQ_PULSE    ;等価パルス 呼び出し
    rcall  EQ_PULSE    ;等価パルス 呼び出し   ここまでで9H

LOOP1:
    rcall  LINE_DRAW    ;1ライン描写 呼び出し

    inc   line      ;1行カウントアップ
    cpi   line,253    ;lineと253を比較 (262Hから垂直同期9Hを引いた値)
    brne   LOOP1     ;0になるまで繰り返す


    ldi   line,0
    rjmp   MAIN_LOOP   ;LOOPに相対ジャンプして無限ループ

最初の9行で、等価パルス出力サブを3回、垂直シンクロ出力サブを3回、等価パルス出力サブを3回 呼び出しています。これが上記の垂直同期9本の出力に相当します。そしてその後ろで、253回の 映像出力サブルーチン(LINE_DRAW)を呼び出しています。これは既に説明済みの”2本の線を描く映像出力”の サブルーチンです。

さて、文字放送やデータ放送用に確保されている非表示映像の部分はどうなっているかというと、 ひとまずこの”映像出力”ルーチンで代用しています。はい、手抜きです。もしかしたら、文字放送やデータ放送に 対応しているテレビでは支障があるかもしれません。でも大抵のテレビなら問題は起こらないでしょう。 スプライト表示器や背景表示器ではこの部分はブランクライン(黒レベルの線)を出力しているので、 悪さをすることは無いと思われます。

この9本の垂直シンクロ信号と残りの映像信号を合わせて1フレーム(1フィールド)の映像が完成します。

262本一通りの走査線を出力し終わると、また頭に戻って垂直同期信号出力になります。この垂直同期信号で 走査線がまた画面の上に戻ります。あとはその繰り返しです。

等価パルス信号と垂直シンクロ信号の詳細

次に、等価パルスと垂直同期信号の詳細を整理したいと思います。

垂直同期信号自体は3本の走査線で構成されます。その前後にそれぞれ3本ずつ等価パルスという走査線が 出力されます。

3本を出力している間ずっとシンクロレベルだと表示器側が水平同期のタイミングを見失ってしまうため、 然るべきタイミングで切れ込み(blackレベル出力)を挟み入れて、同期するタイミングを設けています。 これを切れ込みパルスといいます。

    ;*****             *****
    ;***** <垂直シンクロ サブ> *****
    ;*****             *****

V_SYNC:

    ;***** sync 出力 *****
    cbi   portB,0 ;ポートBの0ビット目をリセット
    cbi   portD,6 ;ポートDの6ビット目をリセット

    ;***** 27.0us *****
    ldi   l_count1,135 ;l_countに135をセット
VS_LOOP1:
    dec   l_count1 ;l_count1を1減らす 1clk
    cpi   l_count1,0 ;l_count1と0を比較 1clk
    brne   VS_LOOP1 ;0になるまで繰り返す 2clk
;135回 * 0.2us(4clk) = 27.0us


    ;***** black 出力 *****
    sbi   portD,6 ;ポートDの6ビット目をセット

    ;***** 4.7us *****
    ldi   l_count1,23 ;l_countに23をセット
VS_LOOP2:
    dec   l_count1 ;l_count1を1減らす 1clk
    cpi   l_count1,0 ;l_count1と0を比較 1clk
    brne   VS_LOOP2 ;0になるまで繰り返す 2clk
              ;23回 * 0.2us(4clk) = 4.6us
    nop
    nop ;0.05us * 2 = 0.1us


    ;***** sync 出力 *****
    cbi   portD,6 ;ポートDの6ビット目をリセット

    ;***** 27.0us *****
    ldi   l_count1,135 ;l_countに135をセット
VS_LOOP3:
    dec   l_count1 ;l_count1を1減らす 1clk
    cpi   l_count1,0 ;l_count1と0を比較 1clk
    brne   VS_LOOP3 ;0になるまで繰り返す 2clk
              ;135回 * 0.2us(4clk) = 27.0us


    ;***** black 出力 *****
    sbi   portD,6 ;ポートDの6ビット目をセット

    ;***** 4.7us *****
    ldi   l_count1,23 ;l_countに23をセット
VS_LOOP4:
    dec   l_count1 ;l_count1を1減らす 1clk
    cpi   l_count1,0 ;l_count1と0を比較 1clk
    brne   VS_LOOP4 ;0になるまで繰り返す 2clk
              ;23回 * 0.2us(4clk) = 4.6us
    nop
    nop ;0.05us * 2 = 0.1us

    ret ;戻る

プログラムを見ていただくと判ると思いますが、1本の垂直同期信号はそれぞれ真中で2つに別れていて、 それぞれシンクロ信号出力の後ろに4.7μsの切れ込み(black信号出力)が出力されています。 black-シンクロ-black-シンクロの連なりで走査線1本分の垂直同期を構成します。これが3本連なります。

続けて等価パルスについてお話をします。等価パルスは垂直同期信号の前後にそれぞれ3本ずつ出力 されています。垂直同期と同じように、1本の走査線が真中で2つに別れていて、短いシンクロ信号とその後ろに blackレベルの信号が続きます。このシンクロ-black-シンクロ-blackの連なりで1本の等価パルスの走査線が 構成されています。この短いシンクロ信号の長さは2.3μsで、映像信号走査線の水平同期信号の半分に なっています。垂直同期の前後両方に3本ずつ連なります。

    ;*****             *****
    ;***** <等価パルス サブ> *****
    ;*****             *****

EQ_PULSE:

    ;***** sync 出力 *****
    cbi   portB,0 ;ポートBの0ビット目をリセット
    cbi   portD,6 ;ポートDの6ビット目をリセット

    ;***** 2.3us *****
    ldi   l_count1,11 ;l_countに11をセット
EQ_LOOP1:
    dec   l_count1 ;l_count1を1減らす 1clk
    cpi   l_count1,0 ;l_count1と0を比較 1clk
    brne   EQ_LOOP1 ;0になるまで繰り返す 2clk
                 ;11回 * 0.2us(4clk) = 2.2us
    nop
    nop ;0.05us * 2 = 0.1us


    ;***** black 出力 *****
    sbi   portD,6 ;ポートDの6ビット目をセット

    ;***** 29.4us *****
    ldi   l_count1,147 ;l_countに147をセット
EQ_LOOP2:
    dec   l_count1 ;l_count1を1減らす 1clk
    cpi   l_count1,0 ;l_count1と0を比較 1clk
    brne   EQ_LOOP2 ;0になるまで繰り返す 2clk
                 ;147回 * 0.2us(4clk) = 29.4us


    ;***** sync 出力 *****
    cbi   portD,6 ;ポートDの6ビット目をリセット

    ;***** 2.3us *****
    ldi   l_count1,11 ;l_countに11をセット
EQ_LOOP3:
    dec   l_count1 ;l_count1を1減らす 1clk
    cpi   l_count1,0 ;l_count1と0を比較 1clk
    brne   EQ_LOOP3 ;0になるまで繰り返す 2clk
                 ;11回 * 0.2us(4clk) = 2.2us
    nop
    nop ;0.05us * 2 = 0.1us


    ;***** black 出力 *****
    sbi   portD,6 ;ポートDの6ビット目をセット

    ;***** 29.4us *****
    ldi   l_count1,147 ;l_countに147をセット
EQ_LOOP4:
    dec   l_count1 ;l_count1を1減らす 1clk
    cpi   l_count1,0 ;l_count1と0を比較 1clk
    brne   EQ_LOOP4 ;0になるまで繰り返す 2clk
                 ;147回 * 0.2us(4clk) = 29.4us

    ret ;戻る

垂直同期や等価パルスの走査線がなぜ真中で2つに別れているかというと、インターレース処理と大きく 関係しています。

インターレースについては上記でも説明していますが、262本目の映像処理出力が終って、次の263本目の 映像出力の最中、丁度半分だけ出力し終わったところで強制的に映像信号を中断し、そこから垂直同期信号出力 を行うことで、2フィールドの映像を走査線半本分ずらし、見た目の垂直解像度を向上させる仕組みです。

このため、2つ目のフィールドは通常の水平同期信号とは丁度半分ずれたところから垂直同期処理が始まる ことになります。表示器側がこのズレを検知、吸収できるように、あらかじめ走査線0.5本分のタイミングで 切れ込みを入れておくことで、この後に続く映像信号の垂直同期がいきなり0.5本ずれるということにならず、 0.5本のズレをこの垂直同期期間内で処理できるようにしているという仕組みです。

なので、スプライト表示器や背景表示器のように「ノンインターレース」で表示している場合は、 この走査線0.5本分のところで切れ込みを入れておく必要は、本来なら有りません。(実際、切れ込みを入れずに 表示を行っているプログラムを作っている人もいるようです。ご興味のある方は色々検索かけてみてください)

なお、等価パルスというのは本来垂直同期信号本体とは別物で、映像信号の変形と考えるのが良いのかもしれません。 垂直同期信号と同様に0.5本分の長さのところで切れ込みのパルスを内包しているのですが、走査線1本に 含まれる等価パルス2個分を加えると通常の水平同期信号と同じ長さになります。

同期信号は、表示器側内で積分されて、走査線の復帰に使われるらしいのですが、積分した長さが同じということは 等価パルスと通常の水平同期信号は表示器側で同じ扱い(水平方向の復帰…右端から左端へ)に使われます。

それに対して、3本の垂直同期信号の走査線はシンクロ信号の積分値が大きく違っているため、表示器側では 水平同期信号とは区別されて処理されます。表示器側では垂直同期信号を受け取ると、この3本の垂直同期信号の 期間に走査線が画面の下端から上端まで一気に戻ります。

まとめ

以上のように、等価パルス+垂直同期信号+等価パルスの9本、非表示映像信号の11~12本(ここまでを 垂直ブランキング期間と呼びます)、及び映像信号240~241本の計262本を連続して出力していくことで ビデオ表示が実現されます。ひとまずノンインターレスの表示についてお話しましたが、インターレス表示の場合は 垂直同期期間のところの信号がちょっと変わるだけですし、マイコンではそもそもインターレス表示を行う要件は 無さそうなので、別途検索するとか専門書を ご覧頂くとかしてください。

誤差とその影響

信号のタイミングや電圧が、厳密な信号の規格からどの程度ずれたらどの様な影響が生じるのかについて、 私がいろいろ実験してみてわかった範囲でまとめておきたいと思います。

(1)垂直同期関係

マイコンで映像信号を生成した時にしばしば発生して困る事象といえば、「像が揺れる」「像がズレる」「像が流れる」 ということが多いかと思われます。それらのうち、この(1)では垂直同期信号に関することを記したいと思います。

先述のとおり、垂直ブランキング期間のうち先頭9本はその他の走査線と大きく異なった構造をしています。3本の等価パルス+ 3本の垂直同期信号+3本の等価パルスの9本で構成されていることは先述したとおりです。この垂直同期信号が ある程度崩れた時に、どんなことが起こるかということを記します。

上述のサンプルプログラムを作る前に、実は9本全部を「垂直同期信号」として出力するプログラムを組んだことが あります(等価パルス6本を出力する代わりに垂直同期信号を出力して、垂直同期信号を9本にしたという意味です)。

その結果どうなるかというと… ちゃんと表示されました。(^_^)

垂直同期については、ある程度いい加減であっても許容範囲に入るみたいです。色んなサイトを見てみましたが、 やはり垂直同期が少しぐらいずれてても影響はほとんど無いという意見が多かったです。

本来の480iのNTSC信号では、垂直同期~垂直同期の時間は厳密に定義されているはずなのですが、 少しくらいずれていても、映像が揺れたり、ズレたり、流れたりすることはほとんど起こらないようです。 垂直同期信号が規格からちょっとくらいズレていても、そのズレはある程度の範囲なら受像機側で補正してしまうようです。 何しろ、垂直同期信号の役割は「走査線の本数の初期化」が出来れば良い訳で、走査線の数を数える動作に 間違えが発生しなければ、ズレや揺れなどが発生することは無いということなのでしょう。多分。

実験はしていないのですが、そういう経緯からインターリーブの為に設けられている等価パルスでさえ(スプライト表示器の様に) プログレッシブ出力を行う範囲であれば、走査線の半分に切り込みパルスを入れておく必要すらないのかも知れません。

そのうち時間が出来たら、切り込みパルスってホントに必要なのかためしてみたいと思っています。

(2)水平同期関係

映像が乱れる一番大きな原因はこの水平同期関係の誤差によるものでしょう。ここが厳密に守られていないと 映像が乱れることが非常に多くなります。自前でビデオ表示のプログラム組んだ時に、思い通りに行かなくて 困るのは多分これが一番の原因かと思われます。

走査線1本の表示に使う時間はビデオ信号の基礎とその操作法(CQ出版)48ページ によると、1秒 ÷ 59.94フィールド ÷ 262.5ライン = 63.56…μ秒/ライン(カラー信号の場合)、 1秒 ÷ 60フィールド ÷ 262.5ライン = 63.49…μ秒/ライン(白黒の場合、31ページより)となります。

さて、ここからどの程度どんな風にズレると映像が乱れるのでしょうか? 結論から言うと、 走査線1本1本の表示時間に”ばらつきがある”ときに表示が乱れることになります。逆にいうと、上記の時間から少しくらいなら ずれても問題なく表示されます。実際、スプライト表示器のプログラムでは 水平同期期間は63.4μ秒にしていますが、ちゃんと表示されています。

1本1本の表示時間にばらつきがあるとはどういうことかというと、例えば1ライン目が63.49μ秒、2本目が63.30μ秒、 3本目が63.45μ秒…みたいな感じに1本1本の走査線の表示時間が異なる映像信号を出力した場合です。

もう少し現実的な話をします。例えば、1フィールド252.5本のうち、途中に1本だけ時間が長い走査線を描いたとしましょう。 すると、その走査線1本の映像自体は乱れないのですが、その走査線の1本下側、もしくは長いときは数本に渡って、映像信号が 左右方向のズレを生じます。水平同期信号だけきちんと4.7μ秒になっていてもダメなんです。水平同期信号も映像部分の長さも 各走査線毎に同じ長さになっていないと映像が左右にズレてしまいます。

さて、映像がちらちらと不安定に左右に揺れる状態はなぜ起こるのでしょうか?

答え … 走査線の表示時間が1本1本まちまちで安定していない状態です。

これらを踏まえると、「左右のどちらかに映像が片寄って変形しているのは、そのすぐ上側の走査線の表示時間が狂っている」状態 ですが、「映像がゆらゆらと安定しない状態なのは、1本1本の走査線の処理時間がまちまちになっている」ということが いえるかと思います。何かプログラムを作ったときに映像が思い通りに表示されない場合に、その原因を突き止める 一つの参考にしていただければ幸いです。

これらを踏まえて、マイコンでビデオ表示のプログラムを出力するにあたって、どんな対策を行えば映像が安定して表示されるかについても 考えておきたいと思います。

いちばん簡単な対策は、水平同期を出力するトリガを、63.4μ秒程度のタイマー割り込みで発生させることです。

これなら、プログラムを作る上で気にしないとならないのは「1本の走査線を表示する処理を、63.4μ秒未満に抑える」 ということだけ。プログラムを組む容易さではこれが一番でしょう。特に、C言語など高級言語でビデオ表示をするためには これ以外に選択肢はないかと思います。

割り込みを使うと上記のとおりシンプルな考えで済むので便利なのですが、いくつかのデメリットもあることはあります。その一つは、 1フィールドの走査線は262.5本(もしくはノンインターレスなら262本)のため、走査線の数を数えるためのカウンタの値が 255(16進数で0xff)を超えます。なのでこの走査線数をカウントするには2バイトの変数が必要になるのですが、 機械語で行おうとすると1命令ですまなくなるのでちょっと面倒なのが1点。

(下手な処理を行うと、水平同期の出力タイミングがずれちゃったりするんです… ちなみに割り込みを使用しない場合、 垂直同期期間の9本を別腹と考えてしまえば、262-9=253本となり、1バイトのレジスタでカウントが可能となります。)

もう一つは、割り込み処理(レジスタのpush/popの作業含む)にそれなりに処理時間とメモリリソースを食ってしまうので、 無駄が多いということが挙げられると思います。特に、スプライト表示器テキスト表示器の場合は、デコード処理が重かったため少しでも処理時間を 稼ぎたいという要件があったので、割り込みの利用を見送ってプログラムの1ステップ1ステップの時間を合計して きっかり63.4μ秒になるようなロジックを組んでいます。

さらっと書きましたが、実はこれって結構大変なんです。プログラムの1箇所でも直すと、その都度処理時間(クロック数)を 厳密に計算しなおさないとならないんです。まぁ、シミュレーターを使いこなせれば何とでもなりますけどね。 ちなみに、このページでサンプルプログラムとして使用しているプログラムも、ご覧のとおり割り込みを使用していません。

割り込みを使わないプログラムは確かに面倒なのですが、割り込みを使うプログラムに比べると可読性という観点では 少しマシになる気がします。特に、メインルーチンを極めてシンプルな記述にすることが出来、全体の処理概要が掴みやすく なります。まぁ、どちらを選ぶかはお好みで…。

(3)電圧関係

電圧についてはあまり色んなバリエーションを試せたわけではないので、私レベルでは語れることはほとんどないと思います。

ただ、結構適当な抵抗値を選んで回路を組んでみたんですけど、それでもきちんと映像信号として出力されているようなので、 「白黒信号生成のレベルならシビアに考える必要はない」と踏んでよいと思います。

特に垂直シンクロなんかは、電圧にしても波形形状にしても結構適当で充分シンクロの役を果たしてくれているようです。 垂直帰還には垂直シンクロ信号を積分することで走査線を最上部に戻しているはずなんですが、少しくらい信号が狂っても 全然問題なく動いてくれています。

一方、カラーのコンポーネント信号の場合は、出力電圧についてはかなりシビアに考える必要があるかと思います (まだ未知の領域ではあるのですが…)。このページでは省略しますがご興味のある方は カラー化の野望にまとめていきたいと思うので適宜ご覧ください。

抵抗値の計算について

電圧の計算についてもう少し触れたいと思います。正確には電圧を決定する抵抗値のことです。

詳しいことは、 こちらのページ(Rickard's electronic projects page)にその原理がかかれていますが、 合成抵抗値の計算と抵抗による分圧の考え方を整理しておきたいと思います。計算方法がわかってしまえば マイコンの電源が5V以外の場合でも応用が利きますので、他の電圧で使いたい方は計算してみてください。

その前に、大前提としてテレビ側の入力回路を簡単に整理しておきたいと思います。

まずはテレビ側の回路(概略)について

テレビのビデオ入力端子はご存知のとおり黄色のRCAジャック入力になっています。内側が信号で 外側がグランドになっています。この内側の信号線に電圧を掛けることで、映像信号やシンクロ信号を テレビに伝えることが出来ます。その中身を単純に描くと、おおよそ以下のようになります。

左側の2本の線がビデオ入力信号です。上が信号、下がグランド線です。

このように内部に75Ωの抵抗が入っており、この抵抗の両端にかかる電圧を変化させることで信号を 伝えます。0V(グランド電位)がシンクロレベル、0.3Vが黒レベル(≒ペデスタルレベル)、 1Vが白レベルになります。シンクロレベルから白レベルまでが1Vppとなる信号です。

本当は、シンクロレベルは-0.3V、黒レベル(≒ペデスタルレベル)は0V、白レベルは0.7V の1Vppなのですが、5V単電源のマイコンで負電圧を作るのが大変なのと、テレビの内側では カップリングコンデンサとペデスタルクランプという仕組みを使って黒レベル=0Vとなるように 定義し直しています。

その詳しい仕組みはひとまず省きますが、普通のテレビならシンクロレベルから白レベルまでを1Vppで 出力しておけば、大抵のテレビでは内部で勝手に正常なビデオ信号に変換して入力してくれると理解してください。

最近のテレビは、大抵ペデスタルクランプ回路とカップリングコンデンサが内蔵されていて、DC(直流)を 直接信号として入力しないようになっているのですが、モノによってはクランプ方式が異なっているかも知れません。 特に古ーいテレビとか。そういう機器と繋ぐ場合には、マイコンボード側にカップリングコンデンサを 付けておかないとマトモなビデオ信号として扱われなかったり、ひどい場合はテレビやマイコンを 壊すことになるかもしれません。出来るだけ、出力部分のところにカップリングコンデンサを 挟むようにしてください。その方が安全です。(今回の説明文中では省略していますのでご注意を)

カップリングコンデンサには100uF程度の電解コンを使えばよいかと思います。 挿入場所や向きはテストボードV1の回路図を ご参照ください。

ご興味のある方は、「ペデスタルクランプ」などと検索を掛けてみてください。

テレビとマイコンの接続について

上記のようなことを念頭に、マイコンとテレビの接続について考えてみます。「白黒の映像」とそれに伴う 「シンクロ信号」の2つの信号をマイコン側で生成する必要があるのはすぐに想像できるかと思います。とすると、 接続概要はこんな感じでしょう。

この映像信号とシンクロ信号からデータを出力し(正確には電圧を出力し)、それを混合してから テレビ側の入力につなぎます。とはいっても、単に出力を直結してテレビに繋げば良いわけでは ありません。(っていうか、直結すると映像信号とシンクロ信号の出力が5Vと0Vで衝突すると ショートしてマイコンが火を噴きます)

特にカラー映像を出力する場合は、マイコンからDAコンバーターで電圧に変換したり、 オペアンプに繋いで演算結果を出力電圧にするなど、方法はいくつかあると思いますが、 白黒信号、しかもグレーが無くて白か黒のどちらかだけが出せればよいのであれば、抵抗による分圧を使うのが 一番安上がりでしょう。テストボードV1PLAYERも抵抗の分圧を使ってます。

抵抗の分圧を使った場合はこんな感じになります。

各出力ピンからR1、R2を介して電圧(5Vか0V)を出力することでR3(75Ω)の抵抗に 望みの電圧を生じさせます。

回路と抵抗値について

↓これが回路部分だけを抜き出した図です。

このうち、R3はどのテレビでも75Ωなので、残りのR1とR2の抵抗値を適当に設定して、 白レベルなら1V、黒レベルなら0.3V、シンクロレベルなら0Vとなるようにしてあげればいいわけです。

以下、これらの抵抗値を求める計算式を考えてみます。

白レベルについて

白レベルは一番電圧が高い状態なので、マイコンからの2つの出力はともに5Vとします。 すると↓こんなかんじになります。

R4(=合成抵抗値)とR3(75Ω固定)で5Vを分圧した結果が映像信号(この場合1V)になるわけです。

この場合の電圧と抵抗値を計算式にするとこんな感じです。

これらの式からR4を消去すると、R1とR2の関係の式に変形することができます。(R3は75Ω固定です)

黒レベルについて

同様に、黒レベルについて考えます。黒レベルは映像信号が無い(=0V)状態だと 考えてください。シンクロ信号側は5Vのままです。マンガにするとこんな感じ↓。

さらにそれを計算式にしてみるとこんな感じです。分圧した結果、0.3Vになればよいわけです。

これらの式からR5を消去すると、R1とR2の関係の式に変形することができます。(R3は75Ω固定です)

シンクロレベルについて

計算も何もありません。↓こんな感じなので当然0Vです。

で、これらの計算式を使って抵抗値を求めます

上記のR4、R5を消去した2つの式から、R1とR2の連立方程式になるのが判るかと思います。 あとは算数のお勉強になっちゃうので省きます。ただ、計算結果で求まった抵抗値が店頭に並んで いるかは別問題なので、実際はそれに近い抵抗値を選んで使って下さい。

ちなみに、マイコンの電源が5Vの場合はR1=470Ω、R2=1.0KΩにすると だいたいいい感じになります。

このようにマイコンボードから白を1V、黒を0.3V、シンクロを0Vで出力しておけば、テレビ側の ペデスタルクランプ回路によってそれぞれ0.7V、0V、-0.3Vに読み替えられて処理されます。

補足:黒レベルとペデスタルレベルについて

黒レベルとペデスタルレベルは、上記でほぼ同じ(≒)と述べましたが、厳密には区別されています。

詳しいことはビデオ信号の基礎とその操作法のP40 「コラム3」で解説されていますが、アメリカで使われている規格ではペデスタルレベルより黒レベルの方がほんの少しだけ 電圧が高くなっています。日本ではペデスタルレベルと黒レベルは同じ電圧です。日本のビデオ信号をアメリカのテレビで 表示すると、黒部分の映像は黒以上に黒い信号となって(?)扱われます。

小さい差なので、あまり取りざたされることは無いみたいですが…。なお、黒レベルは「セットアップレベル」 とも呼ばれます。

この本のコラム3ではIREという単位が使われていますが、通常、ビデオ信号のレベルは電圧(V)ではなく このIREという単位を使うことになっています。今回はこのIREの説明は省略します。

(4)走査線関係

(5)非表示映像信号

(6)