日曜技術者のメモ

趣味でやった事のメモ書きです。

はじめてのPICマイコン - LCD表示 2 -

前回のコードはデータ書き込み毎に一定時間
待つ様にしていたけどBusyフラグがあるので
処理完了をポーリングする方式に変更。

前はWriteのみだったのでR/W端子をGNDに落としていた。
これをRA2に接続。

f:id:ginnyu-tei:20120908184110g:plain

プログラムは以下の様に変更
9/9追記::未使用ポートの処理を修正

// ファイル名 LCD_test2.c
// 作成日   2012/09/08
// 機種    PIC16F84A
// CLOCK    4MHz
// コンパイラ MPLAB XC8
//
//PortA bit0 ----- LCD RS pin
//PortA bit1 ----- LCD E pin
//PortA bit2 ----- LCD R/W pin
//
//PortB bit4 ----- LCD 11pin
//PortB bit5 ----- LCD 12pin
//PortB bit6 ----- LCD 13pin
//PortB bit7 ----- LCD 14pin

#include "pic.h"
#include "string.h"

__CONFIG(FOSC_HS & WDTE_OFF & PWRTE_ON & CP_OFF );

//LCD制御ポート
#define RS_P(x) PORTA = (PORTA & (0xFE)) | (x)
#define E_P(x)  PORTA = (PORTA & (0xFD)) | ((x) << 1)
#define RW_P(x) PORTA =  (PORTA & (0xFB)) | ((x) << 2)

// ---------------- 1ms Wait ----------------
static void Delay_1ms() {
	unsigned int cnt;
	unsigned int i;
	cnt = 51;

	for (i=0 ; i<cnt ; i++) {
		NOP();
	}
}

// ---------------- 100us Wait ----------------
static void Delay_100us() {
	unsigned int cnt;
	unsigned int i;
	cnt = 4;

	for	(i=0 ; i<cnt ; i++) {
		NOP();
	}
}


// ---------------- n ms Wait ----------------
static void Delay_ms(unsigned char ms) {
	unsigned char c;
	for (c=ms ; c>0 ; c--) {
		Delay_1ms();
	}
}

// ---------------- LCD Busy polling ----------------
static void lcd_polling(){
    unsigned char flag;

    RW_P(0x1);
    TRISB = 0xF0;
    do {
        E_P(0x1);
        NOP(); //このNOPがないとLCDの更新が間に合わない。(750usほど)
        flag = (PORTB >> 7) & 0x01;
        E_P(0x0);

        E_P(0x1);//10MHz以上で間にNOPを入れる。
        E_P(0x0);

    }while(flag != 0x00);

    RW_P(0x0);
    TRISB = 0x0;
}

static void write4(unsigned char dat) {

    //PORTB =  ( dat << 4 );
    PORTB =  (PORTB & 0x0F) | (( dat << 4 ) & 0xF0);
    E_P(0x1);
    E_P(0x0); //10MHz以上で間にNOPを入れる。
}

static void write_LCD_IR(unsigned char dat1){

    lcd_polling();

    write4((dat1 >> 4) & 0x0F);
    write4(dat1 & 0x0F);
}

static void write_LCD_data(unsigned char dat1){

    lcd_polling();

    RS_P(0x1);
    write4((dat1 >> 4) & 0x0F);
    write4(dat1 & 0x0F);
    RS_P(0x0);

}

static void print_str(unsigned char *str){

    unsigned char n;

    for(n = 0; n < strlen(str); n++){
        write_LCD_data(str[n]);
    }

}

// ---------------- main処理 ----------------
main()	{
    unsigned char c;
    TRISA = 0x0;
    TRISB = 0x0;	// RB3:output
    PORTA = 0x00;
    PORTB = 0x00;

    //本処理
    Delay_ms(15);
    write4(0x3);
    Delay_ms(10);
    write4(0x3);
    Delay_ms(5);
    write4(0x3);
    Delay_100us();
    write4(0x2); //4bit mode
    Delay_100us();
    write_LCD_IR(0x28); //Function Set (2行表示)
    write_LCD_IR(0x0C); //LCD表示ON
    write_LCD_IR(0x01); //ディスプレイクリア
    write_LCD_IR(0x06); //Entry Mode Set(カーソル右移動、文字シフトなし)
    write_LCD_IR(0x01); //ディスプレイクリア

    print_str("Hello World !!");

    while(1) {
        NOP();
    }

}

なかなか動作しなかったのでロジアナ使って
原因を探ったのが以下波形
f:id:ginnyu-tei:20120908201103g:plain

Read時にEnableを1にしてから(A)データが変化するまで(B)
LCDを買った時についていた仕様書には160nsって書いてあったけど
ここは500ns位かかっている。
場所によってバラツキがあり、データが変化する前にReadしていたっぽい。

初めはEnableを1にしてすぐにデータ取得していたが

    do {
        E_P(0x1); //Enable=1
        flag = (PORTB >> 7) & 0x01; //すぐに取得
        E_P(0x0);

NOPを入れて遅らせる事でうまく動作した。

    do {
        E_P(0x1);
        NOP(); //このNOPがないとLCDの更新が間に合わない。(500usほど)
        flag = (PORTB >> 7) & 0x01;
        E_P(0x0);

f:id:ginnyu-tei:20120908202114j:plain