ドット液晶 (128Wx64H) PC-PZ60D を使ってみる
お知らせ
以下の内容は作業した記録(のようなモノ)をだらだらと書きつづっただけのものです。作業が進むにつれ、それまで進めてきた内容に「ありゃりゃぁ、こりゃ失敗やったぁ〜」というのがたくさんあります(笑)。その時点では「まっ、こんなモンにしとこか?」と作ってはみたものの、少し後になって「げげげっ!」と慌てて修正した部分が・・・・・。
で、最後まで読んで初めて最初の方のアノ内容はちょっとマズイらしい…となったら、そりゃあんまりじゃん! …ということで、回路図やソフトの最新版へのリンクを作ってみました。
回路図 H8/3694版(PDF) 2007.03.17 (57KB) (別窓で開く)
回路図 H8/3664版(PDF) 2007.03.17 (57KB) (別窓で開く)
ソフト H8/3694(20MHz)版 [ PCPZ60soft.lzh ] 2006.09.21 (172KB) (内容)
ソフト H8/3664(20MHz)版 [ LCD3664a.lzh ] 2006.12.18 (36KB) 3694版からの変更部分のみ
H8/3694版(2006.09.21)には以下の BUG を確認しています。H8/3664版(2006.12.18)では修正済です。
Time/DIV を 50mS 以上へ設定した時、25mS 時と同一のサンプルレートになります。
原因:タイマWのクロックを設定している部分 (PCPZ60.C の 590行付近、SampleTimeTabl[ ] の定義内容) にミスがあります。
なるべく早く修正する予定なのですが・・・
H8/3664 の 16MHz 版 (3664 の最高周波数は 16MHz ですから・・・)
全てのソフトで AUTO Triger モード時、トリガがかかりません。(トリガロジック未実装…)
( NORMAL Triger モードはエッジドリガで動作します)
変更
2006.05.08 負電源のチャージポンプを2段から3段へ(電圧が少し足りなかった…)
2006.05.09 「ハードウェアの構成を考える」一部修正 垂直タイミングの追加など
2006.05.10 「ハードウェアの見直し」追加
2006.05.13 「ハードウェアの構成を考える」FS のタイミング変更
2006.05.20 「表示フォントを考える」FONT8Hr.lzh 変更(左右逆並びを修正)
2006.05.29 「グラフィック関数…」Cソース差替え(テスト用を UP してました)
2006.06.20 「オシロのスケール表示…」表示変更に伴いソース差替え(少し速く)
2006.06.24 「オシロの波形表示…」リングバッファのポインタ変更(ひとつ後へ)
2006.07.07 プログラムの関数名、変数名などの一部を変更(あちこちの矛盾を少し修正)
2006.08.14 「水平ライン転送プログラム」変更(変な記述部分を修正)
2006.09.16 「水平ライン転送プログラム」変更(高速サンプル時のノイズ低減)
2007.03.21 回路図変更(主にチャージポンプ部分)
作業予定の項目 1〜13 2006.05.02 (14〜以降 は後から追加)
1.ドット液晶について
2.ハードウェアの構成を考える
3.フレームレートを考える
4.表示フォントを考える
5.負電圧を考える
6.基本部分を組立てる
7.何か表示してみる
8.文字を表示してみる
9.グラフィック関数を考える
10.オシロのスケール表示を考える
12.オシロの波形表示を考える
13.A/Dサンプルを考える
14.画面書換え時間とタイミングを考える
15. 最終章?「お礼」(とプログラム)
16. 番外編1 画面のノイズを何とかしたい・・・
17. 番外編2 H8/3664(SDIP:16MHz)版
18. 気になっていたハードウェア部分 (BUG?) を修正
│←←←←←←← 1本目 →→→→→→→│← 2本目 │ dot1 dot2 dot3 dot128│ ┬───┬───┬───┬───┬──‥┬───┬───┬── SD ┴───┴───┴───┴───┴──‥┴───┴───┴── ┌─┐ ┌─┐ ┌─┐ ┌─┐ ┌─┐ ┌─┐ ┌─┐ ┌─┐ CK ┘ └─┘ └─┘ └─┘ └─┘ └‥┘ └─┘ └─┘ └ ┌─┐ ┌─┐ HS ─┘ └───────────────‥─┘ └─────── ┌─────────────‥────┐ FS ─────┘ └──────各水平ラインの信号は CK 同期(立下りエッジ)で 128 ドットぶんをシリアルに入力し、最後の( 128 ドット目の)表示ドットのデータに重ねて HS が出力されます。 FS は最初の水平ラインの間、ONになります。
┌───────────────────────────┐ │┌───┬───┬───┐ ┌───┬───┐│ ││ 1, 1│ 1, 2│ 1, 3│・・・・・│ 1,127│ 1,128││ │├───┼───┼───┘ └───┼───┤│ ││ 2, 1│ 2, 2│ │ 2,128││ │└───┴───┘ └───┘│ │ ・ ・ │ │ ・ ・ │ │┌───┐ ┌───┐│ ││64, 1│・・・・・・・・・・・・・・・・・│64,128││ │└───┘ └───┘│ └───────────────────────────┘左上のドットを [1,1] として、右下のドットを [64,128] とします。
VramTop + 0 VramTop + 1 ┌──┬──┬──┬──┬──┬──┬──┬──┐┌──┬──┬──┬… │1, 1│1, 2│1, 3│1, 4│1, 5│1, 6│1, 7│1, 8││1, 9│1,10│1,11│… └──┴──┴──┴──┴──┴──┴──┴──┘└────────┴… bit0 bit1 bit2 bit3 bit4 bit5 bit6 bit7 bit0 bit1 bit2
MOV.B #16,R2H ; 2 16byte/Line ?200: ; ----- Byte Loop -----↓↓↓↓↓ MOV.B @ER6,R0L ; 4 VRAM data INC.W #1,R6 ; 2 VRAM address MOV.B #8,R2L ; 2 8dots ?300: ; ----- dot Loop -----↓↓↓↓↓ MOV.B @LCD_Port:8,R1H ; 4 BSET #LCD_CK,R1H ; 2 CK=Hi MOV.B R1H,@LCD_Port:8 ; 4 out SHLR.B R0L ; 2 CY = dot (ON/OFF) BST #LCD_SD,R1H ; 2 set SD DEC.B R1L ; 2 Last Dot ? (of H-Line) BEQ ?400 ; 4 NO then JP MOV.B R1H,@LCD_Port:8 ; 4 out BCLR #LCD_CK,R1H ; 2 CK=Lo MOV.B R1H,@LCD_Port:8 ; 4 out DEC.B R2L ; 2 8bits (1byte) All ? BNE ?300 ; 4 ; ----- dot Loop -----↑↑↑↑↑ 36 DEC.B R2H ; 2 1Line End ? BNE ?200 ; 4 ; ----- Byte Loop -----↑↑↑↑↑ 4832 ↑ 実行ステート数これを見ると、1ドットの出力に要するステート数は 36 になっています。
□□□■■□□□□■■■■■□□ 1 □□□■□□□■■■■□□ □□■□□■□□□■□□□□■□ 2 □□■□■□□■□□□■□ □■□□□□■□□■□□□□■□ 3 □■□□□■□■□□□■□ □■□□□□■□□■■■■■□□ 4 □■□□□■□■■■■□□ □■■■■■■□□■□□□□■□ 5 □■■■■■□■□□□■□ □■□□□□■□□■□□□□■□ 6 □■□□□■□■□□□■□ □■□□□□■□□■■■■■□□ 7 □■□□□■□■■■■□□ □□□□□□□□□□□□□□□□ 8 □□□□□□□□□□□□□ 1234567812345678 1234561234561 └──────┘└──────┘ └────┘└────┘で、見栄えと 20 桁 の表示を目指して 横 6ドット のフォント にしてみます。
■■■■■■■■■■ ■■■■■■■■■■ ■■■■■■■■■■ ■■■■■■■■■■ ■■■■■■■■■■ □■□□□■ ■■□■□□□■■■ ■■■■■■■■■■ □■□□□■ ■■□■□□□■■■ ■■■■■■■■■■ □■□□□■ ■■□■□□□■■■ ■■■■■■■■■■ □■■■■■ ■■□■■■■■■■ ■■■■■■■■■■ + □■□□□■ = ■■□■□□□■■■ ■■■■■■■■■■ □■□□□■ ■■□■□□□■■■ ■■■■■■■■■■ □■□□□■ ■■□■□□□■■■ ■■■■■■■■■■ □□□□□□ ■■□□□□□□■■ ■■■■■■■■■■ ■■■■■■■■■■ ■■■■■■■■■■ ■■■■■■■■■■となって、ちょっと見づらい感じになります。
■■■■■■■■■■■ ■■■■■■■■■■■ ■■■■■■■■■■■ ■■■■■■■■■■■ ■■■■■■■■■■■ □■□□□■□ ■■□■□□□■□■■ ■■■■■■■■■■■ □■□□□■□ ■■□■□□□■□■■ ■■■■■■■■■■■ □■□□□■□ ■■□■□□□■□■■ ■■■■■■■■■■■ □■■■■■□ ■■□■■■■■□■■ ■■■■■■■■■■■ + □■□□□■□ = ■■□■□□□■□■■ ■■■■■■■■■■■ □■□□□■□ ■■□■□□□■□■■ ■■■■■■■■■■■ □■□□□■□ ■■□■□□□■□■■ ■■■■■■■■■■■ □□□□□□□ ■■□□□□□□□■■ ■■■■■■■■■■■ ■■■■■■■■■■■ ■■■■■■■■■■■ ■■■■■■■■■■■となります。
01234567 ┌────────┐ │□■□□□■□□│ → これを1バイトへパッキング(左側が bit0 ) └────────┘ このバイトデータは 0x22 となります。で、データの表現としては
□■□□□■□□ → 1バイト目 0x22 □■□□□■□□ → 2バイト目 0x22 □■□□□■□□ → 3バイト目 0x22 □■■■■■□□ → 4バイト目 0x3e □■□□□■□□ → 5バイト目 0x22 □■□□□■□□ → 6バイト目 0x22 □■□□□■□□ → 7バイト目 0x22 □□□□□□□□ → 8バイト目 0x00
struct ascii_font { : 0x22, 0x22, 0x22, 0x3e, 0x22, 0x22, 0x22, 0x00, // 'H' : } AsciiFont ;もしくは
.DATA.B H'22, H'22, H'22, H'3e, H'22, H'22, H'22, H'00 ; 'H'となります。
/* ------------------------------------------------------------------------ */ /* ANKフォントファイルを読み出してアセンブラのソースへ変更する */ /* ------------------------------------------------------------------------ */ u_char src_file[] = "ANK8H.FNT"; // アセンブラソース ファイル名 u_int AAA = 0x81 + (0x97 * 256); // 全角「@」 sjis=8197, jis=2177 u_int ZSP = 0x81 + (0x40 * 256); // 全角「 」 sjis=8140, jis=2121 #define BUF_LEN 80+2 // 行バッファ長 int main (int argc, char *argv[]) { int i, j, row_dat, row_ct; char font_file[80]; // ファイル名 char font_buf[9+1][BUF_LEN]; // フォント マップデータ union font { u_char bufc[BUF_LEN]; u_int bufi[BUF_LEN/2]; } font_wk; FILE *fp_i, *fp_o; printf("Font file (8x8) --> %s (for Assemble) 05.01.13 Jaku.\n", src_file); if (argc != 2) { /* コマンド・ライン パラメータの確認 */ printf("\x1b[32m\n"); printf("独自フォントファイルを読み出してアセンブラ用のソースファイルを出力する。\n"); printf("独自フォントファイル(8×8ドット)のフォーマットは以下の通り。\n"); printf("\n"); printf(":30 (←アスキーコード)\n"); printf(" @@@ ; 0 (表記内容は全角文字)\n"); printf(" @ @ ; 1\n"); printf(" @ @@ ; 2\n"); printf(" @ @ @ ; 3\n"); printf(" @@ @ ; 4\n"); printf(" @ @ ; 5\n"); printf(" @@@ ; 6\n"); printf(" ; 7\n"); printf("\n"); printf("使い方: A>\x1b[mFONT8H ANK5FONT.MAP\n\x1b[32m"); printf("\n"); printf(" この例だと フォントファイル「 ANK5FONT.MAP 」を読み出して、アセンブラの\n"); printf(" ソースファイルへ変更し、「 %s 」として出力する。\n", src_file); printf(" 注:出力ファイル名は %s 固定\n", src_file); printf("\x1b[m\n"); return (1); } strcpy(font_file, argv[1]); // フォントファイル名 if ( (fp_i = fopen(font_file, "r")) == NULL ) { printf("\x1b[31mfile not found %s\x1b[m\n", font_file); return (-1); } if ( (fp_o = fopen(src_file, "w")) == NULL ) { printf("\x1b[31mfile not open %s\x1b[m\n", src_file); return (-1); } row_ct = 0; while ( (fgets(font_buf[row_ct], BUF_LEN, fp_i)) != NULL ) { printf("\x1b[34m%s\x1b[m", font_buf[row_ct]); if (font_buf[row_ct][0] == '\n') continue; // RETのみの行は読飛ばし if (font_buf[row_ct][0] == ':') row_ct = 0; // ASCII コードの行 if (row_ct < 8) { // 1文字分のフォントデータ読込 row_ct++; continue; } for (row_ct=1; row_ct<=8; row_ct++) { // フォントマップ書き出し if (row_ct == 1) fprintf(fp_o, "\n"); fprintf(fp_o, " ; %s", font_buf[row_ct]); } for (row_ct=1; row_ct<=8; row_ct++) { // 上からデータ1行づつ処理 strcpy (font_wk.bufc, font_buf[row_ct]); // ---- マップ→ビット展開 if (row_ct == 1) fprintf(fp_o, " .DATA.B "); row_dat = 0x00; for (i=0; i<8; i++) { row_dat = row_dat >> 1; // ドット並び −MSBが右− if ( font_wk.bufi[i] == AAA) row_dat += 0x80; // row_dat = row_dat << 1; // ドット並び −MSBが左− // if ( font_wk.bufi[i] == AAA) row_dat += 0x01; } fprintf(fp_o, "H'%03X", row_dat); if (row_ct < 8) fprintf(fp_o, ","); // 最終桁以外 } fprintf(fp_o, " ; %s", font_buf[0]); row_ct = 0; } fprintf(fp_o, "%c", '\x1a'); fclose(fp_i); fclose(fp_o); return (0); }
#define s ((((((((0 #define X )*2+1 #define _ )*2 unsigned char ANKFONT[]={ s _ _ _ X X _ _ _ , // 00 font1 s _ _ X _ _ X _ _ , // 01 s _ _ X _ _ X _ _ , // 02 s _ _ X X X X _ _ , // 03 s _ X _ _ _ _ X _ , // 04 s _ X _ _ _ _ X _ , // 05 s _ X _ _ _ _ X _ , // 06 s _ X _ _ _ _ X _ , // 07プリプロセッサ をこういう風に使うなんて ・ ・ ・ 私もこういうセンスが欲しい・・・
s _ X X X X X _ _ , // 00 font2 s _ X _ _ _ _ X _ , // 01 s _ X _ _ _ _ X _ , // 02 s _ X X X X X _ _ , // 03 s _ X _ _ _ _ X _ , // 04
....以下すきなだけ、1文字につき8行で
}; #undef s #undef X #undef _
void main(void) { InitialIO(); // 内臓I/O初期化 P75 = 1; // 負電源出力 ON while (1) { // メインループ dword i; dword n; P75 = 1; // 念のため n = 75; // チャージポンプ駆動周波数を決める…適当に while (--n > 0) ; P76 = ~P76; // 方形波作成 } }次の図は各部の波形です。
A: CH1 黄色 チャージポンプ一段目出力 2V/div B: CH2 水色 〃 二段目出力 2V/div C: CH3 紫色 〃 三段目出力 2V/div D: CH4 緑色 最終出力 2V/div 時間軸は 25uS/div
無負荷 液晶を接続
void main(void) { InitialIO(); // 内臓I/O初期化 P75 = 1; while (1) { // メインループ dword i; dword n; for(i=10000; i != 0; i--) { // チャージポンプ動作期間 P75 = 1; // 念のため n = 75; // 駆動周波数を決める 適当に while (--n > 0) ; P76 = ~P76; // 方形波作成 } n = 75; while (--n > 0) ; // 時間待ち P75 = 0; // 負電源出力 OFF n = 3000000; while (--n > 0) ; // 負電源放電期間 P75 = 1; // 負電源出力 ON } }次の図は各部の波形です。
CH1 黄色 負電源出力制御 (P75) 5V/div CH2 水色 チャージポンプ一段目出力 2V/div CH4 緑色 最終出力 2V/div
250mS/div 500uS/div
// PC−PZ60D(ドット液晶 128W x 64H ) 2006.05.07これで、何とか「縦線」だけですが表示できました。
#include "3694Cnst.h" // 基本定数定義 #include "3694Mmap.h" // 内臓I/O
#define H_DOT_MAX 128 #define V_DOT_MAX 64
#define LCD_CK P14 #define LCD_SD P15 #define LCD_HS P16 #define LCD_FS P17
void DrvH(byte); // 水平ライン テスト駆動 void StartTMV(void); // タイマV カウント開始 void initialTMV(void); // タイマV 初期化 void VeeOn(void); // 負電源 出力 ON void InitialIO(void); // 内臓I/O初期化
void main(void) { InitialIO(); // 内臓I/O初期化 VeeOn(); // 負電源 出力 ON StartTMV(); // タイマV カウント開始 while (1) { byte b; for (b=0; b < V_DOT_MAX; b++) { DrvH(b); // 水平ライン テスト駆動 } } } // ----------------------------------- // 水平ライン テスト駆動 // ----------------------------------- // Max.Max 0.0 0.1 0.2 0.3 0.Max 1.0 1.1 // ┬───┬───┬───┬───┬──‥┬───┬───┬─ // SD ┴───┴───┴───┴───┴──‥┴───┴───┴─ // ┌─┐ ┌─┐ ┌─┐ ┌─┐ ┌─┐ ┌─┐ ┌─┐ ┌─ // CK ┘ └─┘ └─┘ └─┘ └─┘ └‥┘ └─┘ └─┘ // ┌───┐ ┌───┐ // HS ─┘ └─────────────‥─┘ └──── // ┌───┐ // FS ──┘ └────────────‥──────────
const byte Hdata[] = { 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, // 0 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 4 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 8 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 12 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00 } ;
void DrvH(byte b) { byte h; byte *p;
p = Hdata; // 表示データ
LCD_CK = 0; LCD_SD = 0; LCD_HS = 0; LCD_FS = 0; for (h=0; h < H_DOT_MAX-1; h++) { LCD_CK = 1; LCD_SD = *p++; LCD_CK = 0; } // ---- 最後のドット LCD_CK = 1; if (b==V_DOT_MAX-1) LCD_FS = 1; // 最後の水平ライン LCD_SD = *p; LCD_HS = 1; LCD_CK = 0; LCD_HS = 0; LCD_FS = 0; }
void VeeOn(void) // 負電源 出力 ON { P75 = 1; }
void StartTMV(void) // タイマV カウント開始 { initialTMV(); }
void initialTMV(void) // タイマV 初期化 { TCRV0 = 0x01; TCSRV = 0x03; TCORA = 0xff; // タイムコンスタント A TCORB = 0xff; // タイムコンスタント B TCNTV = 0; // タイマV カウンタ }
void InitialIO(void) // 内臓I/O初期化 { PMR1 = 0x00; // ポートモード (端子機能選択) PDR1 = 0x00; // ポート出力データ PCR1 = 0xf4; // ポート入出力 0:入力, 1:出力 PUCR1 = 0x0f; // 入力プルアップMOS 1:ON PDR2 = 0x00; // ポート出力データ PCR2 = 0x00; // ポート入出力 0:入力, 1:出力 PMR5 = 0x00; // ポートモード (端子機能選択) PDR5 = 0x00; // ポート出力データ PCR5 = 0x00; // ポート入出力 0:入力, 1:出力 PDR7 = 0x00; // ポート出力データ PCR7 = 0x70; // ポート入出力 0:入力, 1:出力 PDR8 = 0x20; // ポート出力データ PCR8 = 0x20; // ポート入出力 0:入力, 1:出力 }
CH1 黄色 FS (P17) 5V/div CH2 水色 HS (P16) 5V/div CH3 紫色 SD (P15) 5V/div CH4 緑色 CK (P14) 5V/div
1uS/div 100uS/div
│←←←←←←← 1本目 →→→→→→→│← 2本目 │ 1dot 2dot 3dot 128dot│ ┬───┬───┬───┬───┬──‥┬───┬───┬── SD ┴───┴───┴───┴───┴──‥┴───┴───┴── ┌─┐ ┌─┐ ┌─┐ ┌─┐ ┌─┐ ┌─┐ ┌─┐ ┌─┐ CK ┘ └─┘ └─┘ └─┘ └─┘ └‥┘ └─┘ └─┘ └ ┌─┐ ┌─┐ HS ─┘ └───────────────‥─┘ └─────── ┌─────────────‥────┐ FS ─────┘ └──────尚、 FS の ON 幅は、最初の水平ラインの表示の1ドット幅だけでもOKです。
// PCPZ-60D 水平ライン転送関数テスト 2006.05.13ちょっと見づらいのですが…、液晶の画面はこんな感じです。
void main(void) { word w; initial_io(); // 内臓I/O初期化 initial_LCD(); // LCD 初期化 for (w=0; w< 1023; w+=65) { Vram[w] = 0x55; } Vram[1023] = 0xaa; while (1) { byte b; for (b=0; b< V_DOT_MAX; b++) { Drv_PCPZ60D_H(); // LCD Hライン駆動 } } }
; ============================================================================
VRAM_TOP: _Vram: .RES.B VRAM_LEN ; VRAM _VramAdr: .RES.W 1 ; 転送中の VRAM アドレス
; -------------------------------------------------------------------- ; LCD Hライン駆動 ; -------------------------------------------------------------------- ; レジスタ使用一覧 ; R0L : VRAM data [ B7 --> B0 ] --> CY --> LCD ; R1H : Port-data to @LCD_Port:8 ; R1L : dot counter H_DOT_MAX --> 0) ; R2H : byte counter 16(bytes/line) --> 0 ; R2L : bit counter 8 --> 0 ; R6 : VRAM address ; -------------------------------------------------------------------- ; ポート割付け LCD_Port .EQU PDR1 LCD_CK .EQU 4 LCD_SD .EQU 5 LCD_HS .EQU 6 LCD_FS .EQU 7
_Drv_PCPZ60D_H: PUSH R2 PUSH R6 MOV.B #H_DOT_MAX,R1L ; Dot counter MOV.W @_Vram_ADR,R6 ; H-Line Top MOV.B @LCD_Port:8,R1H ; Port data CMP.W #VRAM_TOP,R6 BNE ?100 BSET #LCD_FS,R1H ; set FS ON ?100: MOV.B #16,R2H ; 16(bytes) x 8(bits) = 128dots / line ?200: ; ----- Byte (8dots) Loop ------------ MOV.B @ER6,R0L INC.W #1,R6 MOV.B #8,R2L ?300: ; ----- Dot Loop --------------------- BSET #LCD_CK,R1H ; set CP MOV.B R1H,@LCD_Port:8 ; out SHLR.B R0L ; CY = dot (ON/OFF) BST #LCD_SD,R1H ; set SD DEC.B R1L ; Last Dot ? (dot counter) BNE ?400 ; NO then JP BSET #LCD_HS,R1H ; Last Dot (HS) ON ?400: MOV.B R1H,@LCD_Port:8 ; out BCLR #LCD_CK,R1H ; set CK-Lo MOV.B R1H,@LCD_Port:8 ; out DEC.B R2L ; 8bits (1byte) All ? ----- Dot Loop - BNE ?300 DEC.B R2H ; 1Line End ? ------------ Byte Loop - BNE ?200 BCLR #LCD_SD,R1H ; clear SD BCLR #LCD_HS,R1H ; clear HS MOV.B R1H,@LCD_Port:8 ; out BCLR #LCD_FS,R1H ; clear FS MOV.B R1H,@LCD_Port:8 ; out CMP.W #VRAM_TOP+VRAM_LEN-1,R6 ; VRAM End ? BCS ?600 ; NO then JP MOV.W #VRAM_TOP,R6 ?600: MOV.W R6,@_VramAdr ; H-Line Top (for next transefer) ; ------------------------------------ BNOT #6,@PDR7 ; DC−DC制御(−10V:LCD用) ?900: ; ------------------------------------ POP R6 POP R2 RTS
CH1 黄色 FS (P17) 5V/div CH2 水色 HS (P16) 5V/div CH3 紫色 SD (P15) 5V/div CH4 緑色 CK (P14) 5V/div
最初の水平ライン開始付近 最終と最初の水平ライン 2.5uS/div 50uS/div
// PCPZ-60D 水平ライン転送関数テスト 2006.05.15最適化の前は 900uS 程度だったのですが、「スピードの最適化」を ON にすると、上のコードを出力し、結果は 236uS 程で動作しています。アセンブラ記述との差は 5% 余り で、これなら C で充分です。
#define VRAM_TOP 0xf800 #define VRAM_END 0xfbff #define LCD_Port PDR1 #define CK_ON 0x10 #define SD_ON 0x20 #define HS_ON 0x40 #define FS_ON 0x80 #define CK_OFF 0xef #define SD_OFF 0xdf #define HS_OFF 0xbf #define FS_OFF 0x7f
void DrvH2() { byte out; byte dotct; byte vd; byte *p;
dotct = 128; // dot counter out = LCD_Port; // out data p = VramAdr; // 転送する水平ラインの VRAM アドレス if (p == (byte *)VRAM_TOP) out |= FS_ON; // Top ラインなら FS ON LCD_Port = out; while (dotct) { // 全ドット転送ループ byte i; vd = *p++; // VRAM data for (i=0; i<8; i++) { out |= CK_ON; // CK Hi if (vd % 2) out |= SD_ON; // set SD else out &= SD_OFF; vd /= 2; if (--dotct == 0) out |= HS_ON; // HS Hi LCD_Port = out; out &= CK_OFF; // CK off LCD_Port = out; } } out &= HS_OFF; LCD_Port = out; out &= FS_OFF; LCD_Port = out; if (p > (byte *)VRAM_END) p = (byte *)VRAM_TOP; VramAdr = p; // 転送する水平ラインの VRAM アドレス // P76 = !P76; // 負電源チャージポンプ駆動 P76 = ~P76; // 負電源チャージポンプ駆動 2006.08.14 }
; ============================================================================ 以下はコンパイラの出力を整理したものです。
_DrvH2: ; function: DrvH2 PUSH.W R5 MOV.B #-128,R5H ; dotct = 128; MOV.B @65492:8,R0L ; out = LCD_Port; MOV.W @_VramAdr:16,R1 ; p = VramAdr; CMP.W #-2048,R1 ; if (p == (byte *)VRAM_TOP) BNE L48:8 BSET.B #7,R0L ; out |= FS_ON; L48: MOV.B R0L,@65492:8 ; LCD_Port = out; BRA L50:8 ; while L49: MOV.B @ER1+,R5L ; vd = *p++; MOV.B #8,R0H ; for (i=0; i< 8; i++) { L51: BSET.B #4,R0L ; out |= CK_ON; BTST.B #0,R5L ; if (vd % 2) BEQ L52:8 ; else BSET.B #5,R0L ; out |= SD_ON; BRA L53:8 L52: BCLR.B #5,R0L ; out &= SD_OFF; L53: SHLR.B R5L ; vd /= 2; DEC.B R5H ; if (--dotct == 0) BNE L54:8 BSET.B #6,R0L ; out |= HS_ON; L54: MOV.B R0L,@65492:8 ; LCD_Port = out; BCLR.B #4,R0L ; out &= CK_OFF; MOV.B R0L,@65492:8 ; LCD_Port = out; DEC.B R0H ; i++; BNE L51:8 L50: MOV.B R5H,R5H ; while (dotct) { BNE L49:8 BCLR.B #6,R0L ; out &= HS_OFF; MOV.B R0L,@65492:8 ; LCD_Port = out; BCLR.B #7,R0L ; out &= FS_OFF; MOV.B R0L,@65492:8 ; LCD_Port = out; CMP.W #-1025,R1 ; if (p > (byte *)VRAM_END) BLS L55:8 MOV.W #-2048,R1 ; p = (byte *)VRAM_TOP; L55: MOV.W R1,@_VramAdr:16 ; VramAdr = p; BNOT.B #6,@65498:8 ; P76 = ~P76;
POP.W R5 ; } RTS
struct bit { unsigned char b7 : 1; unsigned char b6 : 1; unsigned char b5 : 1; unsigned char b4 : 1; unsigned char b3 : 1; unsigned char b2 : 1; unsigned char b1 : 1; unsigned char b0 : 1; } ; #define PDR7_BIT (*(volatile struct bit *)0xFFDA) #define P76 PDR7_BIT.b6でも、コンパイラの最適化スイッチには賛否両論あって簡単には決められないかも知れませんね。私はこの結果を見て小さいプログラムならコンパイラのスイッチに頼ろうか?とよろめいてしまいました。(最適化スイッチの中には「安全な最適化」なんてのもあったりして… ちょっと考えてしまうのも確かですが:笑)
ここから表示 1 ↓ 2 3 Dot位置 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 ┌┬┬┬┬┬┬┬┬┬┬┬┬┬┬┬┬┬┬┬┬┬┬┬┬┬┬┬┬┬┬ VRAM │ +0 │ +1 │ +2 │ +3 └┴┴┴┴┴┴┴┴┴┴┴┴┴┴┴┴┴┴┴┴┴┴┴┴┴┴┴┴┴┴ 0 1 2 3 4 5 6 ┌┬┬┬┬┬┬┐ 一文字目 │ ■■■■■ │ ■ は字体の実体 └┴┴┴┴┴┴┘ 0 1 2 3 4 5 6 ┌┬┬┬┬┬┬┐ 二文字目(連続表示の場合) │ ■■■■■ │ └┴┴┴┴┴┴┘元のフォントデータの横方向は8ビットですが、展開は7ビットで行います。
; -------------------------------------------------------------------- ; ドット位置指定 6ドットピッチ 1文字表示 2006.05.20 ; -------------------------------------------------------------------- ; CALL R0 : 表示キャラクタコード ASCII (0x20 - 0x7F) ; R1L: 表示位置 X (ドット位置: 0 - H_DOT_MAX-1 ) ; R1H: Y (ドット位置: 0 - V_DOT_MAX-1 ) ; ; 文字の表示位置は「文字の左上」の点(座標)で指定する ; ; ■□□□□□□ ; □□□□□□□ ; □□□□□□□ ; □□□□□□□ ; □□□□□□□ ; □□□□□□□ ; □□□□□□□ ; □□□□□□□ ; ; void disp_ank6c(unsigned int ascii, unsigned char x, unsigned char y)これで何とか表示できるようです。適当に文字を表示してみました。表示位置がドット単位なので「一部を重ねて表示」とかもできます。
_disp_ank6c: PUSH R2 PUSH R3 PUSH R4 PUSH.L ER5 PUSH.L ER6 MOV.W #VRAM_END,E5 ; E5 : #VRAM_END MOV.W #H_DOT_MAX/8,E6 ; E6 : #16 ; ----- check Range X,Y -------------- CMP.B #H_DOT_MAX-7,R1L ; x BCC ?900:16 CMP.B #V_DOT_MAX,R1H ; y BCC ?900:16 ; ----- check disp-char ASCII ? ------ CMP.B #H'80,R0L ; Ascii ? BCC ?900:16 ; NO then JP SUB.W #H'0020,R0 ; Offset of Font-Tabel BCS ?900:16 ; NO then JP ; ----- get Font-table address ------- SHLL.W R0 ; R0 x 8 SHLL.W R0 SHLL.W R0 ADD.W #_FontTbl8ankL,R0 ; R0 : Font-table address ; ----- get VRAM Address ------------- MOV.B R1H,R6L ; [Y]: Line offset MOV.B #0,R6H SHLL.W R6 ; R6 x 16 (16bytes / H-line) SHLL.W R6 SHLL.W R6 SHLL.W R6 MOV.B R1L,R2L ; [X]: dot position SHLR.B R2L ; 1/8 (dot --> byte) SHLR.B R2L SHLR.B R2L ; R2L: byte(s) offset from Line-top MOV.B #0,R2H ADD.W R2,R6 ; R2 : byte(s) offset from Line-top ADD.W #VRAM_TOP,R6 ; R6 : VRAM address (LSB byte) AND.B #B'00000111,R1L ; R1L: dot(s) offset from byte-boundary ; ----- make VRAM Mask --------------- MOV.W #H'007F,R5 ; expansion is 7bits ('1'=mask) MOV.B R1L,R1H ; dot(s) offset from byte-boundary BEQ ?250 ?200: SHLL.W R5 DEC.B R1H BNE ?200 ?250: NOT.W R5 ; R5 : VRAM Mask data (new Font field) ; ===== Copy Font to VRAM ============ MOV.B #8,R2L ; R2L: V-dots ( Font data = 8bytes ) ?300: ; ===== copy H-Line Loop ============= MOV.B @ER0+,R3L ; Font data MOV.B #0,R3H ; R3 : Font data MOV.B R1L,R1H ; dot(s) offset from byte-boundary BEQ ?400 ?350: SHLL.W R3 ; R3 : Font data (add offset) DEC.B R1H BNE ?350 ?400: MOV.B @ER6,R4L ; VRAM (LSB) (don't read Word !) MOV.B @(1,ER6),R4H ; R4 : VRAM data AND.W R5,R4 ; Mask OR.W R3,R4 ; set New Font MOV.B R4L,@ER6 ; to VRAM (LSB) (don't write Word !) MOV.B R4H,@(1,ER6) ADD.W E6,R6 ; R6 : VRAM next Line (pointer) CMP.W E5,R6 ; VRAM End ? BCC ?900 ; YES then JP DEC.B R2L ; R2L: V-dots ( Loop count ) BNE ?300 ; ============= copy H-Line Loop ===== ?900: POP.L ER6 POP.L ER5 POP R4 POP R3 POP R2 RTS
// ----------------------------------- // フォント展開 2006.05.20 // ----------------------------------- // 表示位置は画面左上を 0, 0 としてドット単位で指定するこんな感じで何とか文字の展開はできているようです(アセンブラ版と同じ画面になります)。最適化スイッチONで、実行時間は 33〜71μSでした。これなら余ほどのことが無い限りCで十分な気がします。(やっぱりアセンブラはもういらん・・・)
extern byte FontTbl8ankL[]; // Font-table address
union vd { word w ; struct { unsigned char h :8 ; // ビット割付けに順注意! unsigned char l :8 ; } b ; } ;
void disp_ank(word c, byte x, byte y) { byte i; byte *vramp, *fontp; union vd vram_data; byte dot_offset; word font_data, vram_mask;
if (x > H_DOT_MAX-7) return; // 範囲チェック if (y > V_DOT_MAX-1) return; if (c < 0x20) return; // Font は 0x20 から if (c > 0x7f) return; // 0x7f まで c -= 0x20;
fontp = FontTbl8ankL; // Font data table fontp += c * 8; // 8bytes / character vramp = Vram; vramp += (word)y * 16 + (word)x / 8;// 16bytes/H-line, 8dots/byte dot_offset = x & 0x07; // x % 8
vram_mask = 0x007f << dot_offset; // expansion is 7bits ('1'=mask) vram_mask = ~vram_mask;
for (i=0; i<8; i++) { // 8 H-line / Font font_data = (word)*fontp++ << dot_offset; vram_data.b.l = *vramp; // VRAM data LSB vram_data.b.h = *(vramp+1); // VRAM data MSB vram_data.w &= vram_mask; vram_data.w |= font_data; *vramp = vram_data.b.l; *(vramp+1) = vram_data.b.h; vramp += 16; // 16bytes / H-line if ((word)vramp > VRAM_END) break; } }
┌───────────────────────────┐ │┌───┬───┬───┐ ┌───┬───┐│ ││ 0, 0│ 0, 1│ 0, 2│・・・・・│ 0,126│ 0,127││ │├───┼───┼───┘ └───┼───┤│ ││ 1, 0│ 1, 1│ │ 1,127││ │└───┴───┘ └───┘│ │ ・ ・ │ │ ・ ・ │ │┌───┐ ┌───┐│ ││63, 0│・・・・・・・・・・・・・・・・・│63,127││ │└───┘ └───┘│ └───────────────────────────┘左上の座標を [0,0] として、右下の座標を [63,127] とします。
; CALL R0L: x 0-127 ; R0H: y 0-63 ; void pset(unsigned char x, unsigned char y);Cで書くともっと簡単で
_pset: ; ----- check Range X,Y -------------- CMP.B #H_DOT_MAX,R0L ; X BCC ?900 CMP.B #V_DOT_MAX,R0H ; Y BCC ?900
MOV.B R0L,R1L ; X dot(s) AND.B #B'00000111,R1L ; R1L: X dot(s) offset MOV.B R0L,R1H ; X dot(s) SHLR.B R1H ; X/8 dot --> byte SHLR.B R1H SHLR.B R1H ; R1H: H offset (byte(s)) MOV.B R0H,R0L ; Y Line(s) MOV.B #H_DOT_MAX/8,R0H ; byte(s) / Line MULXU.B R0H,R0 ; R0 : V offset (byte(s)) ADD.B R1H,R0L ; add H offset ADDX #0,R0H ; R0 : VRAM offset (byte(s)) ADD.W #VRAM_TOP,R0 ; R0 : VRAM address of Cursor BSET R1L,@ER0 ; ----- Bit SET ---------------------- ?900: RTS
void p_set(byte x, byte y) { byte dot_offset; byte *vramp;とまぁ特に問題は無いと思います。ビットを落とせばクリアですし、反転すれば画像の白黒が反転します。以後、グラフィック関連の各関数は基本的にこの「点描画」の関数を呼ぶことにします。
if (x > H_DOT_MAX-1) return; // 範囲チェック if (y > V_DOT_MAX-1) return;
vramp = Vram; vramp += (word)y * 16 + (word)x / 8; // 16bytes/H-line, 8dots/byte dot_offset = x & 0x07; // x % 8 *vramp |= 0x01 << dot_offset; }
; LINE ; (xs, ys) から (xs, ys) まで直線を描く ; CALL R0L: x1 ; R0H: y1 ; R1L: x2 ; R1H: y2 ; void line(unsigned char x1, unsigned char y1, ; unsigned char x2, unsigned char y2); _line: PUSH.L ER2 PUSH.L ER3 PUSH R4 PUSH R5 PUSH.L ER6 ; ----- 範囲チェック CMP.B #H_DOT_MAX,R0L ; x1 BCC ?900:16 CMP.B #V_DOT_MAX,R0H ; y1 BCC ?900:16 CMP.B #H_DOT_MAX,R1L ; x2 BCC ?900:16 CMP.B #V_DOT_MAX,R1H ; y2 BCC ?900:16で、またまたCでも書いてみました。
MOV.W R0,E2 ; E2 : start CMP.B R0L,R1L ; x1 - x2 = 0 なら例外処理(縦線) BEQ ?600:16 CMP.B R0H,R1H ; y1 - y2 = 0 なら例外処理(横線) BEQ ?700:16 ; ------------------------------------ ; 始点終点間のX幅とY幅の大小を調べる ; ------------------------------------ SUB.B R1L,R0L ; R0L: X (signed) BPL ?100 MOV.B R0L,R2L ; R0L: 負数→絶対値 MOV.B #0,R0L SUB.B R2L,R0L ?100: SUB.B R1H,R0H ; R0H: Y (signed) BPL ?150 MOV.B R0H,R2H ; R0H: 負数→絶対値 MOV.B #0,R0H SUB.B R2H,R0H ?150: CMP.B R0L,R0H ; X幅 と Y幅 の比較 BCC ?300:16 ; X < Y ?200: ; ===== X > Y ======================== MOV.W E2,R0 ; E2 : start MOV.W R1,R3 ; R3 : end CMP.B R0L,R1L BCC ?220 MOV.W R0,R3 ; exchange start <--> end MOV.W R1,E2 ?220: ; xs < xe BSR _pset:16 ; Draw Point (xs, ys) Start point MOV.W E2,R0 ; E2 : xs,ys(start) SUB.B R0H,R3H ; R3H: ye - ys (signed) ADD.B #-1,R3H ; 始点を含む ys-->ye 間のドット数 BMI ?230 ADD.B #2,R3H ; R3H: Y (signed : ±1〜±64) ?230: MOV.B R3H,R5L EXTS.W R5 ; R5 : Y (signed : ±1〜±64) MOV.B R3H,R4L ; Y (signed : ±1〜±64) ADD.B #1,R4L ; (四捨五入…零捨一入?) (half adjust) SHAR.B R4L ; ÷2 EXTS.W R4 ; R4 : Y/2 (signed) SUB.B R0L,R3L ; R3L: xe - xs (0〜127) INC.B R3L ; 始点を含む xs-->xe 間のドット数 EXTU.W R3 ; R3 : X (dot count : 1〜128)
MOV.B R3L,R2L ; X (dot count) DEC.B R2L ; R2L: Loop counter (X-1 → 0) MOV.W #1,E3 ; E3 : N (1 → X) ?250: MOV.W R5,R6 ; R6 : Y (signed) MULXS.W E3,ER6 ; ER6: Y * N (signed) ADD.W R4,R6 ; add Y/2 (四捨五入…零捨一入?) DIVXS.W R3,ER6 ; R6L: dY (Y*N + Y/2) / X MOV.W E2,R0 ; xs, ys ADD.B R6L,R0H ; R0H: yn = ys + dY MOV.W E3,R6 ; N ADD.B R6L,R0L ; R0L: xn = xs + N BSR _pset:16 ; Draw Point (xn, yn) !xxR0,R1 INC.W #1,E3 ; E3 : N DEC.B R2L ; R2L: Loop counter BNE ?250 BRA ?900:16 ?300: ; ===== X < Y =============== or X = Y MOV.W E2,R0 ; E2 : start MOV.W R1,R3 ; R3 : end CMP.B R0H,R1H BCC ?320 MOV.W R0,R3 ; exchange start <--> end MOV.W R1,E2 ?320: ; ys < ye BSR _pset:16 ; Draw Point (x, y) !xxR0,R1 MOV.W E2,R0 ; E2 : xs,ys(start) SUB.B R0L,R3L ; R3L: xe - xs (signed : ±1〜±127) ADD.B #-1,R3L ; 始点を含む xs-->xe 間のドット数 BMI ?330 ADD.B #2,R3L ?330: MOV.B R3L,R5L EXTS.W R5 ; R5 : X (signed : ±1〜±128) MOV.B R3L,R4L SHAR.B R4L EXTS.W R4 ; R4 : X/2 (signed) SUB.B R0H,R3H ; R3H: ye - ys (0〜63) INC.B R3H ; 始点を含む ys-->ye 間のドット数 MOV.B R3H,R3L EXTU.W R3 ; R3 : Y MOV.B R3L,R2L ; Y DEC.B R2L ; R2L: Loop counter (Y-1 → 0) MOV.W #1,E3 ; E3 : N (1 → Y) ?350: MOV.W R5,R6 ; R6 : X (signed) MULXS.W E3,ER6 ; ER6: X * N (signed) ADD.W R4,R6 ; add X/2 (四捨五入…零捨一入?) DIVXS.W R3,ER6 ; R6L: dX (X*N + X/2) / Y MOV.W E2,R0 ; xs, ys ADD.B R6L,R0L ; R0L: xn = xs + dX MOV.W E3,R6 ADD.B R6L,R0H ; R0H: yn = ys + N BSR _pset:16 ; Draw Point (xn, yn) !xxR0,R1 INC.W #1,E3 ; E3 : N DEC.B R2L ; R2L: Loop counter BNE ?350 BRA ?900
?600: ; ----- X=0(縦線) --------------- ; E2 : start MOV.W R1,R3 ; R3 : end BSR _pset:16 ; Draw Point (xn, yn) !xxR0,R1 MOV.W E2,R0 ; start CMP.B R0H,R3H ; Y=0 なら終了 BEQ ?900 BCC ?620 MOV.B R0H,R2L ; exchange R0H <--> R3H MOV.B R3H,R0H MOV.B R2L,R3H MOV.W R0,E2 ?620: BSR _pset:16 ; Draw Point (xn, yn) !xxR0,R1 MOV.W E2,R0 INC.B R0H MOV.W R0,E2 CMP.B R0H,R3H BCC ?620 BRA ?900
?700: ; ----- Y=0(横線) --------------- MOV.W R0,E2 ; E2 : start MOV.W R1,R3 ; R3 : end BSR _pset:16 ; Draw Point (xn, yn) !xxR0,R1 MOV.W E2,R0 CMP.B R0L,R3L BCC ?700 MOV.B R0L,R2L ; exchange R0L <--> R3L MOV.B R3L,R0L MOV.B R2L,R3L MOV.W R0,E2 ?720: BSR _pset:16 ; Draw Point (xn, yn) !xxR0,R1 MOV.W E2,R0 INC.B R0L MOV.W R0,E2 CMP.B R0L,R3L BCC ?720 ?900: POP.L ER6 POP R5 POP R4 POP.L ER3 POP.L ER2 RTS
// 線描画 // (x1,y1) - (x2,y2) の間を直線で結ぶここで、恒例?の実行時間を比べてみます。描画は液晶画面の左上から右下です。
void line2(byte x1, byte y1, byte x2, byte y2) { int X, Y, hX, hY, dX, dY, n; byte temp;
if (x1 > H_DOT_MAX-1) return; // 範囲チェック if (x2 > H_DOT_MAX-1) return; if (y1 > V_DOT_MAX-1) return; if (y2 > V_DOT_MAX-1) return;
if (x1 == x2) { // 「縦線」 (例外処理) lineV(x1, y1,y2); return; } if (y1 == y2) { // 「横線」 (例外処理) lineH(y1, x1,x2); return; }
X = (int)x2 - (int)x1; Y = (int)y2 - (int)y1; if (X < 0) X = 0 - X; else X = X; if (Y < 0) Y = 0 - Y; else Y = Y; // ----------------------------------- if (X > Y) { // X > Y (Xで刻んで行く) // ----------------------------------- if (x1 > x2) { // 始点の X座標が大きければ temp = x1; // 始点と終点の座標を入れ替える x1 = x2; x2 = temp; temp = y1; y1 = y2; y2 = temp; } // 始点X < 終点X pset(x1, y1); // 始点
X = (int)x2 - (int)x1; // X : +1〜+127 X += 1; // 始点を含む x1 --> x2 間のドット数 Y = (int)y2 - (int)y1; // Y : ±1〜±63 if (Y < 0) Y -= 1; // 始点を含む y1 --> y2 間のドット数 else Y += 1; hY = Y / 2; for (n = 1; n < X; n++) { dY = (Y * n + hY) / X; // (dY : signed) pset(x1 + n, y1 + dY); } // ----------------------------------- } else { // X < Y (Yで刻んで行く) // ----------------------------------- if (y1 > y2) { // 始点の X座標が大きければ temp = x1; // 始点と終点の座標を入れ替える x1 = x2; x2 = temp; temp = y1; y1 = y2; y2 = temp; } // 始点Y < 終点Y pset(x1, y1); // 始点
X = (int)x2 - (int)x1; // X : ±1〜±127 if (X < 0) X -= 1; // 始点を含む x1 --> x2 間のドット数 else X += 1; Y = (int)y2 - (int)y1; // Y : +1〜+63 Y += 1; // 始点を含む y1 --> y2 間のドット数 hX = X / 2; for (n = 1; n < Y; n++) { dX = (X * n + hX) / Y; pset(x1 + dX, y1 + n); } } }
// ----------------------------------- // 縦線描画 // -----------------------------------
void lineV(byte x, byte y1, byte y2) { byte n, y; pset(x, y1); if (y1 == y2) return; if (y1 > y2) { n = y1; y1 = y2; y2 = n; } y = y2 - y1; for (n=1; n<=y; n++) pset(x, y1 + n); }
// ----------------------------------- // 横線描画 // -----------------------------------
void lineH(byte y, byte x1, byte x2) { byte n, x; pset(x1, y); if (x1 == x2) return; if (x1 > x2) { n = x1; x1 = x2; x2 = n; } x = x2 - x1; for (n=1; n<=x; n++) pset(x1 + n, y); }
実行時間 | 点描画関数は アセンブラ版 |
点描画関数は C版 |
アセンブラ版 | 920μS | 1400μS |
C版 | 830μS | 1250μS |
分割Div数 | Divあたり のドット数 |
Div細分割 (サブスケール) |
Div細分割 ドット数 |
4 Div | 16ドット | 4分割 | 4ドット |
5 Div | 12ドット | 4分割 | 3ドット |
6 Div | 10ドット | 2分割 | 5ドット |
void drawSubScaleH(byte y) // 横スケール描画 { byte x; for (x=4; x< H_DOT_MAX; x+=4) pset(x, y); }実行時間は、約 1.3mS 程度のようです。(画面クリアは除く)
void drawSubScaleV(byte x) // 縦スケール描画 { byte y; for (y=4; y< V_DOT_MAX; y+=4) pset(x, y); }
void drawScale(void) // 横 8div 縦 4div スケール描画 { byte *p; byte i;
p = (byte *)VRAM_TOP; // 外枠描画 上 for (i=0; i< 16; i++) { *p = 0xff; p += 1; } p = (byte *)VRAM_TOP + 1008; // 下 for (i=0; i< 16; i++) { *p = 0xff; p += 1; } p = (byte *)VRAM_TOP + 16; // 左 for (i=1; i< 63; i++) { *p = 0x01; p += 16; } p = (byte *)VRAM_TOP + 31; // 右 for (i=1; i< 63; i++) { *p = 0x80; p += 16; }
drawSubScaleH( 1); drawSubScaleH( 16); drawSubScaleH( 32); drawSubScaleH( 48); drawSubScaleH( 62); drawSubScaleV( 1); drawSubScaleV(126); for (i=16; i< H_DOT_MAX; i+=16) { drawSubScaleV(i); } }
; 横 8 div 縦 4 div drawScale1: ; ----- 外枠描画 --------------------- XOR.L ER0,ER0 NOT.L ER0 ; ER0: H'FFFF MOV.W #VRAM_TOP,R1 ; 上 BSR drawScaleH1:16 MOV.W #VRAM_TOP+1008,R1 ; 下 BSR drawScaleH1:16 MOV.W #VRAM_TOP+16,R1 ; 左 MOV.B #V_DOT_MAX-2,R0H ; 長さ MOV.B #H'01,R0L BSR drawScaleV1:16 MOV.W #VRAM_TOP+31,R1 ; 右 MOV.B #V_DOT_MAX-2,R0H ; 長さ MOV.B #H'80,R0L BSR drawScaleV1:16 ; ----- 垂直スケール描画 MOV.L #H'01000100,ER0 MOV.W #VRAM_TOP+64,R1 BSR drawScaleH2:16 MOV.W #VRAM_TOP+128,R1 BSR drawScaleH2 MOV.W #VRAM_TOP+192,R1 BSR drawScaleH2 MOV.W #VRAM_TOP+320,R1 BSR drawScaleH2 MOV.W #VRAM_TOP+384,R1 BSR drawScaleH2 MOV.W #VRAM_TOP+448,R1 BSR drawScaleH2 MOV.W #VRAM_TOP+576,R1 BSR drawScaleH2 MOV.W #VRAM_TOP+640,R1 BSR drawScaleH2 MOV.W #VRAM_TOP+704,R1 BSR drawScaleH2 MOV.W #VRAM_TOP+832,R1 BSR drawScaleH2 MOV.W #VRAM_TOP+896,R1 BSR drawScaleH2 MOV.W #VRAM_TOP+960,R1 BSR drawScaleH2 ; ----- 水平スケール描画 MOV.L #H'11111111,ER0 MOV.W #VRAM_TOP+256,R1 BSR drawScaleH2 MOV.W #VRAM_TOP+512,R1 BSR drawScaleH2 MOV.W #VRAM_TOP+768,R1 BSR drawScaleH2これでやっと 140μS といったところです。
BRA drawScalee:16
drawScaleH1: ; ----- 水平連続ライン描画 MOV.L ER0,@ER1 ADDS.L #4,ER1 MOV.L ER0,@ER1 ADDS.L #4,ER1 MOV.L ER0,@ER1 ADDS.L #4,ER1 MOV.L ER0,@ER1 RTS
drawScaleV1: ; ----- 垂直連続ライン描画 ; R0L: 横方向ドット位置 ; R0H: 縦方向の長さ(ドット数) ?100: MOV.B R0L,@ER1 ADD.W #16,R1 DEC.B R0H BNE ?100 RTS
drawScaleH2: ; ----- 水平ラインサブスケール描画 MOV.L ER0,@ER1 AND.W #H'FDFF,E0 ADDS.L #4,ER1 MOV.L ER0,@ER1 ADDS.L #4,ER1 MOV.L ER0,@ER1 ADDS.L #4,ER1 OR.B #H'80,R0L ; 例外処理:外枠(右) MOV.L ER0,@ER1 AND.B #H'3F,R0L RTS
![]() |
普通のメモリから仮想的に作成したリングバッファと、書込み位置を記録する書込ポインタ WP を用意します。
WP が指す場所には前回(最終サンプル)のデータが入っています。新しいサンプルデータは WP を +1 して、その WP が指す場所へ格納されます。データサンプルが続く限り、WP はこのリング上をグルグル回るわけです。 |
![]() |
サンプリング終了時、左図のようになっていたならば、「A」が一番古いデータで画面の左端に表示され、「B」のデータがトリガ点、「C」のデータが一番新しいもので画面の右端に表示される事になります。 |
□ □■□ □■■■□ □■□ □
![]() |
イメージはこんな感じでしょうか。 |
![]() |
上の例だと、「外枠のサブスケール」がちょっと重い感じですので、その部分(サブスケール)を削ってみました。
何だかちょっとマヌケな感じもしますが、画面が小さいのであまりゴチャゴチャしない方がいいかな?ということで、このイメージで行くことにします。 |
![]() |
とりあえずCで書いてみましたが、その表示を見た結果、マーカで外枠が消えるのは、やはり見栄えが悪い…ということで、外枠と重なる部分のスペースは削除しました。
予想通り、かなり見にくいマーカですが、とりあえずコレで進めることにします。画面いっぱいに表示されているノコギリ波はテスト用にサンプルバッファへセットした擬似波形です。 波形は2チャネルの表示を予定しています。(今回のテストでは1チャネルのみの表示です) |
extern byte *AdWp; // A/Dサンプルデータの書込みポインタ extern byte AdBuf0[]; // A/D CH0 sample data buffer (Length=128) extern byte AdBuf1[]; // CH1 extern byte DotPY; // ドット描画位置 (DP) Y (起点) extern byte DotPX; // X
extern void pset(byte, byte); // ドット表示 extern void psetDp(byte, byte); // ドット表示(位置記憶:起点) extern void pclr(byte, byte); // ドット消去
// ----------------------------------- // 線描画 (相対) // ----------------------------------- // 現在位置 - (x2,y2) の間を直線で結ぶ // 現在位置は DotPX, DotPY を参照する
void lineto(byte x2, byte y2) { byte x1, y1; x1 = DotPX; // ドット描画位置 (DP) X (今回起点) y1 = DotPY; // Y DotPX = x2; // ドット描画位置 (DP) X (次回起点) DotPY = y2; // Y line(x1, y1, x2, y2); } // ----------------------------------- // トリガポジション表示 // -----------------------------------
void drawTrigPosi(byte x) { pclr(x-1,1); pset(x,1); pclr(x+1,1); pclr(x-2,2); pset(x-1,2); pset(x,2); pset(x+1,2); pclr(x+2,2); pclr(x-1,3); pset(x,3); pclr(x+1,3); pclr(x,4); } // ----------------------------------- // トリガレベル表示 // -----------------------------------
void drawTrigLevel(byte y) { pclr(2,y-2); pclr(1,y-1); pset(2,y-1); pclr(3,y-1); pset(1,y ); pset(2,y ); pset(3,y ); pclr(4,y); pclr(1,y+1); pset(2,y+1); pclr(3,y+1); pclr(2,y+2); } // ----------------------------------- // 波形描画 // ----------------------------------- // ch 入力チャネル番号 0 or 1
void drawWave(byte ch) { byte *dp; byte b; word buf_end;
buf_end = (word)AdBuf0 + 128; dp = AdWp +1; // A/Dサンプルデータの書込みポインタ if (dp > (byte *)buf_end) dp -= 128; if (ch != 0) { // CH1 dp += 128; buf_end += 128; } psetDp(0, V_DOT_MAX - (*dp++ / 4) - 1); // 始点描画 & 起点記録 if (dp > (byte *)buf_end) dp -= 128; for (b=1; b < 128; b++) { if (dp > (byte *)buf_end) dp -= 128; // リングバッファ lineto(b, V_DOT_MAX - (*dp++ / 4) - 1); } }
// --------------------------------------------------------------------------- // Main // --------------------------------------------------------------------------- void main(void) { byte b; byte *dp; word w;
InitialIo(); // 内臓I/O初期化 VeeON(); // 負電源 出力 ON InitialLCD(); // LCD 初期化 ClrScreen(); // 画面クリア drawScale(0); // スケール描画
b = 0; dp = AdBuf0; for (w=0; w<256; w++) { // サンプルバッファへ擬似波形をセット *dp++ = b; b += 7; }
AdWp = AdBuf0 + 127; // ポインタをバッファの最後へ drawWave(0); drawTrigLevel(40); // トリガレベル表示 drawTrigPosi(16); // トリガポジション表示
while (1) { for (b=0; b < V_DOT_MAX; b++) { Drv_PCPZ60D_H(); // LCD Hライン駆動 } } }
|
|
|
.SECTION ADBUFF, DATA, LOCATE=H'FC00 ; FC00-FCFF _AdBuf0: .RES.B H_DOT_MAX ; A/D CH0 バッファ _AdBuf1: .RES.B H_DOT_MAX ; A/D CH1 バッファこんな感じでしょうか?
_AdWp: .RES.W 1 ; A/Dサンプルデータの書込みポインタ _AdTrigP: .RES.B 1 ; ADトリガ位置(ポインタ値) _AdDispP: .RES.B 1 ; AD(トリガ点の)表示位置 _AdCt: .RES.B 1 ; AD変換停止までのサンプル数 _AdTrigLv: .RES.B 1 ; ADトリガレベル
_AdFlag: .RES.B 1 ; AD制御フラグ
AD_RUN .EQU 0 ; ADサンプル中 0:no 1:yes AD_TRG_RDY .EQU 1 ; トリガ前サンプル数OK 0:yet 1:OK AD_TRG_PASS .EQU 2 ; トリガ条件検出 0:yet 1:OK AD_TRG_POL .EQU 3 ; ADトリガ極性 0:+ 1:- SING_TRIG .EQU 5 ; シングルトリガ 0:Normal 1:Single AUTO_TRIG .EQU 6 ; オートトリガ 0:Normal 1:Auto H_DISP .EQU 7 ; 電圧軸表示 0:4div 1:5div
; -------------------------------------------------------------------- ; V25 0032 A/D変換終了 【割込処理】 ; -------------------------------------------------------------------- ; 【注意】 ; トリガは CH0 固定 ; _AdWp は 最終データの位置を保持 ; _AdFlag をレジスタへコピーして操作し、最後に書き戻している _irqAD: PUSH R0 ;6 PUSH R1 ;6 PUSH R2 ;6 MOV.B @_AdFlag:8,R1H ;4 A/D関連フラグ ; ----- A/D結果の読出 ------------- MOV.B @ADDRAH:8,R0L ;4 CH0 A/D結果(8ビットのみ読出) MOV.B @ADDRBH:8,R0H ;4 CH1 A/D結果 ; ----- A/D格納バッファポインタ --- MOV.B @_AdDispP:8,R2H ;4 R2H: トリガ点前表示サンプル数 CMP.B R6L,R2H ;2 トリガ前サンプル数OK? BCC ?100 ;4 まだ ならJP BSET #AD_TRG_RDY,R1H ;2 トリガ前サンプルデータOK ?100: MOV.W @_AdWp,R2 ;6 A/Dサンプルデータの書込みポインタ ; ----- 前回データ読出(トリガ用) --- MOV.B @ER2,R1L ;4 CH0 前回データ ; ----- 格納ポインタ 更新 ----------- INC.B R2L ;2 ADDS #1,ER6 BCLR #7,R2L ;2 リングバッファ処理 EF00-EF7F MOV.W R2,@_AdWp ;6 ADバッファのポインタ 更新 ; ----- A/D結果 格納 ------------- MOV.B R0L,@ER6 ;4 CH0 今回データ MOV.B R0H,@(H_DOT_MAX,ER6) ;6 CH1 〃 BTST #AD_TRG_PASS,R1H ;2 トリガ条件検出? 0:まだ 1:済み BNE ?300 ;4 トリガ検出済みならJP ; ----- トリガ前データ数確認 --------- BTST #AD_TRG_RDY,R1H ;2 トリガ前サンプルデータOK? BEQ ?400 ;4 でなければJP ?200: ; ----- トリガ条件 確認 ------------- MOV.B @AdTrigLv:8,R2H ;4 R2H: ADトリガレベル BTST #AD_TRG_POL,R1H ;2 ADトリガ極性? 0:+, 1:- BNE ?250 ;4 ; ----- ↑エッジ トリガ CMP.B R2H,R1L ;2 前回データ R1L はトリガレベル以上? BCC ?400 ;4 ならJP CMP.B R2H,R0L ;2 今回データ R0L はトリガレベル以上? BCC ?260 ;4 ならJP BRA ?400 ;4 ?250: ; ----- ↓エッジ トリガ CMP.B R1L,R2H ;2 前回データ R1L はトリガレベル以下? BCC ?400 ;4 ならJP CMP.B R0L,R2H ;2 今回データ R0L はトリガレベル以下? BCS ?400 ;4 でなければJP ?260: ; ----- トリガ!!! BSET #AD_TRG_PASS,R1H ;2 トリガ条件検出 フラグ ON MOV.B R2L,@_AdTrigP:8 ;4 ADトリガ位置(データ位置 0-127) MOV.B @_AdDispP:8,R2H ;4 R2H: トリガ点前表示サンプル数 MOV.B #H_DOT_MAX-1,R2L ;2 R2L: 表示総ドット数 SUB.B R2H,R2L ;2 トリガ点後(右側)表示サンプル数 MOV.B R2L,@_AdCt:8 ;4 AD変換停止までのサンプル数 BRA ?400 ;4 ?300: ; ----- 既にトリガ検出済み MOV.B @_AdCt:8,R2L ;4 R2L: AD変換停止までのサンプル数 DEC.B R2L ;2 MOV.B R2L,@_AdCt:8 ;4 AD変換停止までのサンプル数 更新 BNE ?400 ;4 ; ----- 全サンプル取得済み BCLR #ADCSR_ADIE,@ADCSR:8 ;8 AD変換終了割込 禁止(サンプル終了) BCLR #AD_RUN,R1H ;2 ADサンプル停止 BRA ?500 ;4 ?400: ; 次のADデータ採取へ ?500: ?900: MOV.B R1H,@_AdFlag:8 ;4 A/D関連フラグ 書き戻し POP R2 ;6 POP R1 ;6 POP R0 ;6 RTE ;10
┌──────────┐ │H/3694 │30pin │ FTIOD(P84)│→→→┐ │ │ │ │ ADTRG(P55)│←←←┘ │ │16pin └──────────┘ADTRIG 入力の最小パルス幅は 2クロック となっていますので、出力パルスはその倍(4クロック:0.2μS)以上とすれば安心です。( GRD へは GRA - 4 を設定します)
|
// タイマW 初期化 // クリア コンペアマッチAでクリア // 出力 FTIOD // 割込 無し
void initialTmw(void) { TMRW.BYTE.TMR = 0x04; // TMRW タイマモード TMRW.BYTE.TCR = 0xB0; // TCRW 制御 TMRW.BYTE.TSR &= 0x00; // TSRW ステータス TMRW.BYTE.TIOR0 = 0x03; // TIOR0 入出力制御 TMRW.BYTE.TIOR1 = 0x20; // TIOR1 入出力制御 TMRW.BYTE.TIER = 0x00; // TIERW 割込制御 TMRW.TCNT = 0x0000; // カウント開始時の初期値 TMRW.GRA = 1250; // 1mS/div (とりあえず) TMRW.GRD = 1246; // GRA - 4 TMRW.TMR.BIT.CTS = 1; // タイマW カウント開始 }
_irqAD: PUSH R0 PUSH R1 PUSH R2 MOV.B @ADCSR:8,R0L ; AD制御/ステータスレジスタ AND.B #B'01011111,R0L ; 変換終了フラグクリア、変換開始クリア MOV.B R0L,@ADCSR:8 ; ----- A/D結果の読出 ------------- MOV.B @ADDRA:8,R0L ; CH0 A/D結果(上位8ビットのみ読出) MOV.B @ADDRB:8,R0H ; CH1 A/D結果 MOV.B @_AdFlag:8,R1H ; A/D関連フラグ MOV.W @_AdWp,R2 ; A/Dサンプルデータの書込みポインタ ; ----- A/D格納バッファポインタ --- MOV.B @_AdDispP:8,R1L ; R1L: トリガ点前表示サンプル数 CMP.B R2L,R1L ; トリガ前サンプル数OK? BCC ?100 ; まだ ならJP BSET #AD_TRG_RDY,R1H ; トリガ前サンプルデータOK ?100: ; ----- 前回データ読出(トリガ用) --- MOV.B @ER2,R1L ; CH0 前回データ ; ----- 格納ポインタ 更新 ----------- INC.B R2L ; ADDS #1,ER2 BCLR #7,R2L ; リングバッファ処理 (BufferLen=128) MOV.W R2,@_AdWp ; ADバッファのポインタ 更新 ; ----- A/D結果 格納 ------------- MOV.B R0L,@ER2 ; CH0 今回データ MOV.B R0H,@(H_DOT_MAX,ER2) ; CH1 〃 BTST #AD_TRG_PASS,R1H ; トリガ条件検出? 0:まだ 1:済み BNE ?300 ; トリガ検出済みならJP ; ----- トリガ前データ数確認 --------- BTST #AD_TRG_RDY,R1H ; トリガ前サンプルデータOK? BEQ ?400 ; でなければJP ?200: ; ----- トリガ条件 確認 ------------- MOV.B @_AdTrigLv:8,R2H ; R2H: ADトリガレベル BTST #AD_TRG_POL,R1H ; ADトリガ極性? 0:+, 1:- BNE ?250 ; ----- ↑エッジ トリガ CMP.B R2H,R1L ; 前回データ R1L はトリガレベル以上? BCC ?400 ; ならJP CMP.B R2H,R0L ; 今回データ R0L はトリガレベル以上? BCC ?260 ; ならJP BRA ?400 ?250: ; ----- ↓エッジ トリガ CMP.B R1L,R2H ; 前回データ R1L はトリガレベル以下? BCC ?400 ; ならJP CMP.B R0L,R2H ; 今回データ R0L はトリガレベル以下? BCS ?400 ; でなければJP ?260: ; ----- トリガ!!! BSET #AD_TRG_PASS,R1H ; トリガ条件検出 フラグ ON MOV.B R2L,@_AdTrigP:8 ; ADトリガ位置(データ位置 0-127) MOV.B @_AdDispP:8,R2H ; R2H: トリガ点前表示サンプル数 MOV.B #H_DOT_MAX-1,R2L ; R2L: 表示総ドット数 SUB.B R2H,R2L ; トリガ点後(右側)表示サンプル数 MOV.B R2L,@_AdCt:8 ; AD変換停止までのサンプル数 BRA ?400 ?300: ; ----- 既にトリガ検出済み MOV.B @_AdCt:8,R2L ; R2L: AD変換停止までのサンプル数 DEC.B R2L MOV.B R2L,@_AdCt:8 ; AD変換停止までのサンプル数 更新 BNE ?400 ; ----- 全サンプル取得済み BCLR #TMRW_CTS,@TMRW:8 ; TimerW stop BCLR #ADCSR_ADIE,@ADCSR:8 ; AD変換終了割込 禁止 BCLR #AD_RUN,R1H ; ADサンプル停止 BCLR #TMRW_CTS,@TMRW:8 ; TimerW stop BCLR #ADCSR_ADIE,@ADCSR:8 ; AD変換終了割込 禁止 ?400: MOV.B R1H,@_AdFlag:8 ; A/D関連フラグ POP R2 POP R1 POP R0 RTE心配だった動作時間は以下のようになりました。
画面書換え ├──・・・・・・・───┤ 水平ライン転送 ├─────┤ ├─────┤という感じになり、画面を短い周期でひんぱんに書き換える場合、書換え中に転送される水平ラインデータの「書換え状態」が問題になります。というのは、オシロのようなビットマップ画面の書換えは、まず画面クリアをした後、スケール描画に続いて波形描画(あるいはその逆)などという順序で行いますので、ちょうど画面をクリアした直後に「水平ラインの転送」が起動された場合、その水平ラインは何も表示されないことになります。
書換え処理 画面クリア スケール描画 波形描画 ├───┤ ├───┤├──────┤ 画面書換え ├────・・・・・・・・・────────────┤ 水平ライン転送 ├───────┤仮に上のようなタイミングで動作した場合、このとき転送される水平ラインは「空白データ」(クリアされた画面データ)となりますので、その水平ラインは何も表示されない(白く抜ける)事になります。これが「たまに起きる」だけならほとんど問題は無さそうですが、ひんぱんに画面を書き換える(オシロのような)場合には画面のチラツキが目立つようになります。最悪のタイミングを考えると画面に白抜けの水平ラインが目立つ事になるかも知れません。
水平ライン転送 ├─────┤ ├─────┤ 画面書換え ├───────┤
旧画面 新画面 画面表示 …──────────────┤├─────── 最終の水平ライン転送 ├───┤ 画面書換え ├──────┤ 最初の水平ライン転送 ├───┤画面の書換えが充分に短い時間(水平ラインの転送周期から、水平ラインの転送時間を引いた残りより短い時間)であれば、これで問題は無さそうですが、H8/3692 の実際の画面書換えはそんなに速くありません。次の項でそのあたりを考えてみます。
|
画面クリア 0.15mS スケール描画 0.4mS チャネル1波形描画 5〜30mS チャネル2波形描画 5〜30mS マーカ、タイムDiv描画 0.5mS波形の描画時間は波形の複雑さによって大きく変化します。
224uS │←←←← 520uS→→→│← →│ 64本目 1本目 2本目 水平ライン転送 ├───┤ ├───┤ ├───┤ 画面消去 ├──┤ スケール描画 ├───・・・・・───┤ CH0 波形描画 ├──・・・・・─‐ │←→│←← 400uS + 224uS→→│ 150uS64本目(前の画面の最後)の水平ライン転送終了後、直ちに画面の書換えを開始したとして、次の画面の最初(1本目)の水平ラインの転送が始まるまでに終わるのは画面消去だけです。次のスケール描画は、約半分ほど描画が進んだ状態で1本目の水平ラインの転送が始まってしまいます。もしここで「1本目の水平ラインに描画されるスケール」がまだ描けていない場合、1本目の水平ラインは空白が表示され、白く抜けたラインになってしまいます。
│← 1/30秒 →│ 画面表示タイミング ┼──────┼──────┼──────┼─────… 画面書換え1 ├──┤ 大部分の書換え1画面表示 ├──────┼──────┤ 最後に書換えた「時間軸」の表示 ├──────┤ 画面書換え2 ├──┤ 書換え2画面表示 ├─────…これだと、仮に 1/15秒毎に画面を書換えた場合、時間軸の表示はその他の表示に比べて 1/2 の期間(デューティー50%)しか表示されませんので「半分の濃度」となり、コントラストが薄い状態で表示されます。実際には「薄い上にちらついて見える」事になります。
Dot 124 125 126 127 0 1 2 3 126 127 0 1 ┬─┬─┬─┬─‥‥───┬─┬─┬─┬─┬‥─┬─┬─‥───┬─┬ SD ┴─┴─┴─┴─‥‥───┴─┴─┴─┴─┴‥─┴─┴─‥───┴─┴ ┌┐┌┐┌┐┌─‥‥──┐┌┐┌┐┌┐┌┐┌‥┐┌┐┌─‥──┐┌┐┌ CK ┘└┘└┘└┘ └┘└┘└┘└┘└┘ └┘└┘ └┘└┘ ┌─┐ ┌─┐ HS ────────‥‥─┘ └────────‥─────‥─┘ └── 割込処理 ────┤‥‥├────────────────┤‥├───── タイマ割込 Tmv ↑ ↑今まで割込処理では「水平ラインの最初から転送」していたのですが、これを「前の水平ラインの転送の最後」から処理するようにしようというわけです。具体的には、上の図のように、最後のドットのデータ転送の途中で割込処理を終了し、次の割込処理ではその続き( HS の出力)からスタートするわけです。
_drv_PCPZ60D_H: PUSH R6 MOV.B @LCD_Port:8,R1H ; R1H: Port data ; ------------------------------------ ; 水平ライン 最終ドットの後始末 ; ------------------------------------ BSET #LCD_HS,R1H ; Last Dot (HS) ON MOV.B R1H,@LCD_Port:8 ; out BCLR #LCD_CK,R1H ; set CP-Lo MOV.B R1H,@LCD_Port:8 ; out BCLR #LCD_SD,R1H ; clear SD BCLR #LCD_HS,R1H ; clear HS MOV.B R1H,@LCD_Port:8 ; out BCLR #LCD_FS,R1H ; clear FS (条件判定の時間をカット) MOV.B R1H,@LCD_Port:8 ; out ; ------------------------------------ MOV.B #H_DOT_MAX,R1L ; R1L: Dot counter MOV.W @_VramAdr,R6 ; R6 : H-Line Top address CMP.W #VRAM_TOP,R6 BNE ?100 BSET #LCD_FS,R1H ; set FS ON (Top Line !) ?100: ?200: ; ----- Byte (8dots) Loop ------------ MOV.B @ER6,R0L INC.W #1,R6 MOV.B #8,R0H ?300: ; ----- Dot Loop --------------------- BSET #LCD_CK,R1H ; set CP-Hi MOV.B R1H,@LCD_Port:8 ; out SHLR.B R0L ; CY = dot (ON/OFF) BST #LCD_SD,R1H ; set SD DEC.B R1L ; Last Dot ? (dot counter) BEQ ?400 ; YES then JP (goto END) MOV.B R1H,@LCD_Port:8 ; out BCLR #LCD_CK,R1H ; set CP-Lo MOV.B R1H,@LCD_Port:8 ; out DEC.B R0H ; 8bits (1byte) All ? ----- Dot Loop - BNE ?300 BRA ?200 ?400: ; ----- Last Dot --------------------- MOV.B R1H,@LCD_Port:8 ; out CMP.W #VRAM_TOP+VRAM_LEN-1,R6 ; VRAM End ? (フレーム転送終了?) BCS ?600 ; NO then JP ; ----- フレーム(1画面)転送終了 --- MOV.B @_DispFlameN:8,R0L ; フレーム表示回数(表示毎に+1) BMI ?500 ; (オーバフロー対策 128以上は無変化) INC.B R0L MOV.B R0L,@_DispFlameN:8 ?500: BSET #FLM_END,@_LcdFlag:8 ; フレームエンド(最終H走査線転送) MOV.W #VRAM_TOP,R6 ?600: MOV.W R6,@_VramAdr ; H-Line Top (for next transefer) ; ------------------------------------ BNOT #6,@PDR7 ; DC−DC制御(−10V:LCD用) ; ------------------------------------ ?900: POP R6 RTSで、修正後の動作を調べてみます。
番外編2 H8/3664(SDIP:16MHz)版 2006.11.09
気になっていたハードウェア部分 (BUG?) を修正 2007.03.20 - 2007.03.29
修正前の回路ではコンデンサ(10μF)を74HCのゲート出力で直接ドライブしていました。直列に入っているのはダイオードのみで、電源ON時のコンデンサへの突入電流(左図の水色の経路を流れる電流)を制限する「抵抗」は入っていません。
じゃ、突入電流は無限大???(誰がその値を決めている?) |
単に 74HC04 の最大定格出力電流 25mA を超えないように抵抗を入れただけです。
抵抗値は (5V - 0.6V) ÷ 25mA ≒ 180Ω です |
普通のシリコンダイオードからショットキーへ変更して、電圧降下はどうなっているのか?調べてみました。
電圧軸は 200mV/Div 時間軸は 250uS/Div で 計測条件などは前のものと全く同一です。 おおよそ 0.32V 程度の電圧降下になっているようで、これなら全体で1V程度の電圧UPが望めそうです。 |
|
これなら充分なコントラストが得られそうだ・・・と写真に撮ったものの、なんと、ピンボケのボケボケでした。 m(_ _)m でも、必要以上に濃い表示になっている雰囲気が少しは出ているでしょうか? これで何とかコントラストは問題ないレベルになったようです。(ホッ…) |
![]() |
|
電源投入時の部分を少し拡大してみます。
計測条件は 2.5mS/div, 1V/div で、スケール上側の灰色矢印が「電源ON」のタイミングです。 問題は無さそうです。 |
|
時間軸は 50mS/div です。
水色が電源電圧で、5V/div 。 黄色が -10V の立ち上がり波形で 2V/div です。 これで見ると、-10V の立ち上がりには 0.4秒 余りかかっているようです。 |