// lcd_task.c // // --- public domain, no warranty. #include #include // tm_putstring() #include // memset() #include "lcd_task.h" #define PBDDR ((_UB *)0x00ffffd4) #define LCD_GPIO ((_UB *)0x00ffffd6) // PBDR #define LCD_DB(x) ((x) & 0x0f) // DB4-7 <- PB0-3 #define LCD_RS (1 << 4) // RS <- PB4 #define LCD_E (1 << 7) // E <- PB7 #define LCD_E_DELAY 100 // usec #define LCD_CMD_DELAY 500 // usec EXPORT UB LCDBuf[LCDBUF_SIZE]; EXPORT UB LEDFlg = 0; EXPORT ID LCDTsk; /* μsec単位の待ち */ LOCAL void WaitUsec(UW usec) { #define CPUkHz (CPU_CLOCK / 1000) #define WaitFactor (20 * 1000) usec = (CPUkHz * usec + WaitFactor - 1) / WaitFactor; /* * ステート数は、内蔵ROM上で実行した場合の値。 * 外部バスに接続したRAM等では、より多くのステート数となる。 * * ※H8/3052Fのバスステートコントローラの設定値を * デフォルトのままとし、AKI-H8-LANボードのSRAM上に * コードを配置した場合、内蔵ROM上に配置した場合と * 比べて6倍程度の実行時間となる。 */ Asm( "0: mov.l %0, %0 \n" // 2 " beq 1f \n" // 4 " nop \n" // 2 " nop \n" // 2 " nop \n" // 2 " nop \n" // 2 " dec.l #1, %0 \n" // 2 " bra 0b \n" // 4 (total 20clk) "1: \n" :: "r"(usec)); return; } /* 4bit単位のデータ送信 */ LOCAL void lcd_writenibble(UB data) { *LCD_GPIO = data | LCD_E; WaitUsec(LCD_E_DELAY); *LCD_GPIO = data & ~LCD_E; WaitUsec(LCD_E_DELAY); return; } /* LCDコマンド書き込み */ LOCAL void lcd_writecmd(UB data) { lcd_writenibble(LCD_DB(data >> 4)); lcd_writenibble(LCD_DB(data >> 0)); WaitUsec(LCD_CMD_DELAY); /* * 一部のコマンド(Clear Display, Cursor At Home)は、 * さらに待つ必要がある */ return; } /* LCDデータ書き込み */ LOCAL void lcd_writedat(UB data) { lcd_writenibble(LCD_DB(data >> 4) | LCD_RS); lcd_writenibble(LCD_DB(data >> 0) | LCD_RS); WaitUsec(LCD_CMD_DELAY); return; } /* LCD初期化 */ LOCAL void lcd_reset(void) { *LCD_GPIO = 0x00; tk_dly_tsk(20); lcd_writenibble(0x03); // Function Set WaitUsec(5000); lcd_writenibble(0x03); // Function Set WaitUsec(125); lcd_writenibble(0x03); // Function Set WaitUsec(LCD_CMD_DELAY); lcd_writenibble(0x02); // Function Set: 4bit WaitUsec(LCD_CMD_DELAY); lcd_writecmd(0x28); // Function Set: 4bit, 2lines, 5x7dots lcd_writecmd(0x08); // Display On/Off Control: all off lcd_writecmd(0x01); // Clear Display WaitUsec(2000); // *** ここは追加の待ちが必要 lcd_writecmd(0x06); // Entry Mode Set: increment, no shift lcd_writecmd(0x0c); // Display On/Off Control: display on return; } /* 文字表示位置の設定 */ LOCAL void lcd_setpos(UINT position) { /* * positionと表示位置との関係は、以下の通りとする。 * 00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f * 10 11 12 13 14 15 16 17 18 19 1a 1b 1c 1d 1e 1f */ if (position & 0x10) position |= 0x40; position &= 0x4f; lcd_writecmd(0x80 | position); return; } /* LCDへの文字列表示タスク・本体 */ EXPORT void lcd_task(void) { ER er; INT i; /* * wake-upする度に、LCDBufの内容を全てLCDに転送 * (本当は描画した部分だけLCDに転送すべきだが) * * tk_rel_wai()による待ち解除を受けた場合、 * LEDFlgの内容に応じてLEDの点灯/消灯とする * (LCDとLEDを同一のGPIOで制御しているため) * * …という予定だったが、LEDの点灯制御を行うと * LCDが誤動作する確率が高いように思われるため、 * LEDの点灯制御は行わないことにする。 */ while (1) { er = tk_slp_tsk(TMO_FEVR); tk_can_wup(TSK_SELF); // たまっている起床要求は破棄 if (er >= E_OK) { for (i = 0; i < sizeof(LCDBuf); i++) { lcd_setpos(i); lcd_writedat(LCDBuf[i]); WaitUsec(LCD_E_DELAY); } } // *LCD_GPIO = LEDFlg ? 0x00 : 0x0f; } /* ここへは来ない */ tk_exd_tsk(); } /* LCDへの文字列表示タスク・起動部分 */ EXPORT ER lcd_task_init(W start) { W er; /* 終了処理(何もしない) */ if (start < 0) { er = E_OK; goto fin0; } /* * 初期起動タスクを流用するため、最低限の初期化を行ったら * タスク本体を直接呼び出す */ memset(LCDBuf, ' ', sizeof(LCDBuf)); // '\0'で埋めるのはダメ *PBDDR = LCD_E | LCD_RS | LCD_DB(0x0f); lcd_reset(); LCDTsk = tk_get_tid(); tk_wup_tsk(TSK_SELF); // 初期画面の描画を予約 er = E_OK; fin0: return er; }