AVR本のLCD表示対策

「AVR活用ブック」のサンプルプログラムで「LCDがうまく表示できない」というご指摘を頂いたので、対策してみました。

▲ストップウォッチ(3章-1)を実行させて、うまく表示できない場合の例です。LCDに対して信号がうまく出てないようです。


▲原因は、最適化オプションを「-O0」以外に設定してるからです(信号が早く出すぎる)。今のところだと、スイッチを「-O0」にすると大丈夫みたいです。


▲問題はlcd_waitにあります。
ループ回数で時間を作ってます。できるだけシンプルに載せたいので、あえてローテクに作ってたのですが、動作の安定性がよくありません。

なので、タイマ2を使って、時間を待つように改造してみました。
こうすると、どんな最適化をしてもちゃんと表示されます。

//-----------------------------------------------------------
//    LCD出力関数 lcd.c                                      
//最適化オプションを入れても動くように対応しました。
//-----------------------------------------------------------
#include <avr/io.h>
#include "lcd.h"

#define LCD_PORT   PORTD // ポート
#define LCD_PORTO  DDRD  // ポート入出力設定

#define LCD_E  (1 << 3)  // ENABLE           
#define LCD_RS (1 << 2)  // REGISTER SELECT  
#define LCD_D4      4    // Data Bus D4      
#define LCD_DATMASK 0xf  // Data Bus用マスク(4ビット送信) 

void lcd_put_4bit(char bitdata);
void lcd_wait( long m );

//-----------------------------------------------------------
//    ウエイト                                               
// 引数 m: 単位msec
// timer2を使ってます。注意
//-----------------------------------------------------------
void lcd_wait( long m )
{
#define F_CPU    8000000     // CPUクロック周波数 [Hz]

#define PRESCALE   1024      // プリスケーラ値
#define PRESCALER  7         // プリスケーラレジスタ値

    long cntmax;

    cntmax = (F_CPU/PRESCALE)* m/1000;

    TCCR2A = 0;               // タイマ モード 
    TCCR2B = PRESCALER;       // タイマ プリスケーラ設定

    TCNT2 = 0x100- cntmax;
    TIFR2 |= (1<<TOV2);
    while((TIFR2 & (1<<TOV2))==0);
}


//-----------------------------------------------------------
//    LCD初期化                                              
//-----------------------------------------------------------
void lcd_init(void)
{
    lcd_wait(30);
	LCD_PORTO = LCD_E | LCD_RS | (LCD_DATMASK<<LCD_D4);

    LCD_PORT &= ~LCD_RS; // RS = L 
    lcd_put_4bit( 0x3 ); // Function Set Mode 
    lcd_wait(4);
    lcd_put_4bit( 0x3 );
    lcd_wait(1);
    lcd_put_4bit( 0x3 );
    lcd_put_4bit( 0x2 ); // Function Set Mode=4bit 

    lcd_put_ch( 0x28 );  // Mode=4bit / Line=2 / Font=5x7dot
    lcd_put_ch( 0x0E );  // Display=ON / Cursor=ON / Blink=ON 
    lcd_put_ch( 0x06 );  // Cursor=Increment / Display_shift=OFF 
    lcd_put_ch( 0x01 );  // Display Clear
    lcd_wait(2);
    LCD_PORT |= LCD_RS;    // RS = H 
}


//-----------------------------------------------------------
//    LCD表示位置の設定                                      
// 引数:char tx   :x座標                                    
// 引数:char ty   :y座標                                    
//-----------------------------------------------------------
void lcd_position(char tx ,char ty)
{
    LCD_PORT &= ~LCD_RS;    // RS = L 
    lcd_put_ch(0x80 | tx | (0x40 * ty)); // Address = 0 
    LCD_PORT |= LCD_RS;     // RS = H 
}

//-----------------------------------------------------------
//        LCD 4ビット送信                                    
// 引数: char bitdata : DB7-4に与えるデータ                 
//-----------------------------------------------------------
void lcd_put_4bit(char bitdata)
{
    LCD_PORT &= ~(LCD_DATMASK << LCD_D4);
    LCD_PORT |= (LCD_DATMASK & bitdata)<<LCD_D4;  //データを格納 

    lcd_wait(1);
    LCD_PORT ^= LCD_E;     // ENABLE = L
    lcd_wait(1);

    LCD_PORT ^= LCD_E;     // ENABLE = H 
}


//-----------------------------------------------------------
//        LCD 1バイト送信                                   
// 引数:  char ch : LCDモジュールへの送信データ             
//-----------------------------------------------------------
int lcd_put_ch(char ch)
{
    lcd_put_4bit(ch >> 4);            // 上位4ビット送信 
    lcd_put_4bit(LCD_DATMASK & ch);   // 下位4ビット送信 

    lcd_wait(1);
	return(0);
}

Hello world」2-1
http://nicotak.com/avr/prog2_1.lzh

「ストップウォッチ」3-1
http://nicotak.com/avr/prog3_1.lzh