目次>6章 第3ステップ(実用品)>1ピンで液晶ディスプレイを制御  >ソース

1ピンで液晶ディスプレイを制御(ソース)
(シリアル制御LCD)<PIC16F84A>PICC-Lite

更新日2008.1.14

以下のソースを取り込んで、コンパイル→PIC書き込み→実行して下さい。

// ========================== ここから ==============================
// PIC16F84A
// CLOCK : 20MHz
// 他のPICからシルアル通信風にデータを受信し、LCDに出力する
//
// 通信速度 以下をPBSで切り替える
//	・ 4800bps
//	・ 9600bps
//	・19200bps
//	・28800bps
//	・38400bps
//	・57600bps
//
// TMR0:ビット受信のサンプリング間隔
//		0.2μS×100=20μS  : 50Kbps
//		通信速度	TMR0プリスケーラ	256-TMR0
//		4800			8				130
//		9600			4				130
//		19200			2				130
//		28800			2				87
//		38400			2				65
//		57600			2				43
//
//
//          ↓スタートビット
//          __
//      ___|  |  |  |  |
//          ↑ ↑ ↑
//          |  20μS毎にサンプリング
//          立ち上がりから10μS遅らせる
//
//
//
// RA0:LCD_E
// RA1:LCD_R/W
// RA2:LCD_RS

// RA3:SW1(スピード切り替え)
// RA4:SW2

// RB0:LCD_DB4
// RB1:LCD_DB5
// RB2:LCD_DB6
// RB3:LCD_DB7

// RB4:
// RB5:LED(データ受信時点灯,バッファオーバラン時点灯)
// RB6:
// RB7:他のPICからシリアル受信(ピン変化割込み)
//
// LCD_V0 : 350ΩでGND
// LCD_DB0-3:GND
//


#include "pic.h"

#define LCD_E  RA0
#define	LCD_RW RA1
#define	LCD_RS RA2
#define	buffsize 40
#define	RS_data 0x11
#define	RS_cmd  0x12
#define	EEsave  0

// スピード表示文字列のアドレス(EEPROM)
#define	EEKbps 8		// 'Kbps' EEPROMアドレス
#define	EE48  13		// '4.8'  EEPROMアドレス
#define	EE96  17		// '9.6'  EEPROMアドレス
#define	EE192 21		// '19.2' EEPROMアドレス
#define	EE288 26		// '28.8' EEPROMアドレス
#define	EE384 31		// '38.4' EEPROMアドレス
#define	EE576 36		// '57.6' EEPROMアドレス

static void OverRun();
static void Delay_1ms();
static void Delay_ms(unsigned char);
static void pic_init();
static void LCD_send4(unsigned char);
static void LCD_Busy_wait();
static void LCD_cmd(unsigned char);
static void LCD_data(unsigned char);
static void LCD_init();
static void bufLCD();
static void sw_RA3_on();
static void EEtoLCD(unsigned char);
static void EEwrite(unsigned char, unsigned char);
static unsigned char EEread(unsigned char);
static void EEspeed();
static void speedDSP();


__CONFIG(HS & WDTDIS & PWRTEN & UNPROTECT);

__EEPROM_DATA(0x06, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF);
__EEPROM_DATA('K', 'b', 'p', 's', 0x00, '4', '.', '8');
__EEPROM_DATA(0x00, '9', '.', '6', 0x00, '1', '9', '.');
__EEPROM_DATA('2', 0x00, '2', '8', '.', '8', 0x00, '3');
__EEPROM_DATA('8', '.', '4', 0x00, '5', '7', '.', '6');
__EEPROM_DATA(0x00, ' ', ' ', ' ', ' ', ' ', ' ', ' ');
__EEPROM_DATA(' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ');
__EEPROM_DATA(' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ');


static unsigned char buff[buffsize];
static unsigned char buffR;		// バッファ読み込みポインタ
static unsigned char buffW;		// バッファ書き込みポインタ
static unsigned char SW_RS;		// LCD書き込みモード
static unsigned char comm_stat;	// 通信状態
	// 0:無通信
	// 1:スタートビット受信,半ビット時間待ち
	// 2:データビット受信中
	// 3:データビット受信完了,ストップビット待ち

static unsigned char comm_char;	// 受信データ
static unsigned char comm_bit;	// 受信データのビット数
static unsigned char T0val;		// TMR0設定値
static unsigned char sw_RA3;	// RA3 SW状態
static unsigned char speed;		// 通信速度(1:4800,2:9600,3:19200)


// =================== 割り込み処理 ====================
static void interrupt intr(void) {
	if	(T0IF) {			// TMR0割り込み?
		TMR0 = 256 - T0val;	// TMR0
		T0IF = 0;			// TMR0割り込みクリア
		RB4 ^= 1;	// DEBUG
		switch (comm_stat) {
			case 1:		// ■スタートビット受信開始,半ビット待ち
//				if	(RB7) {			// スタートビット?
					comm_stat = 2;	// データビット受信中
					comm_bit = 8;	// 8ビット受信開始
//				} else {
//					comm_stat = 0;	// 無通信
//				}
				break;
			case 2:		// ■データビット受信中
				comm_char <<= 1;
				if	(RB7) {
					comm_char |= 0x01;
				}
				comm_bit--;
				if	(!(comm_bit)) {
					buff[buffW] = comm_char;
					comm_stat = 3;	// データビット受信完了,ストップビット待ち

					// バッファ書き込みポインタの更新
					buffW++;
//					if	(buffW < buffsize) {	// バッファポインタは最終位置でない
//						if	(buffW == buffR) {
//							OverRun();	// オーバーラン(LED点灯し停止する)
//						}
//					} else
					if	(buffW == buffsize) {	// バッファポインタは最終位置?
//						if	(buffR != 0) {
							buffW = 0;
//						} else {
//							OverRun();	// オーバーラン(LED点灯し停止する)
//						}
					}

				}
				break;
			case 3:		// ■ストップビット待ち
				comm_stat = 0;	// 無通信状態
				if	(!RB7) {
					RB5 = 0;	// LED消灯
				}
				RB4 = 0;	// DEBUG
				T0IE = 0;
				RBIE = 1;
				RBIF = 0;
				break;
		}
	} else
	if	(RBIF) {	// PORTBピン変化割り込み?
		RB5 ^= 1;
		if	(RB7) {				// スタートビット?
			if	(comm_stat == 0) {	// ■無通信状態
				comm_stat = 2;	// データビット受信中
				comm_bit = 8;	// 8ビット受信開始
//				comm_stat = 1;		// スタートビット受信開始,半ビット待ち
//				RB5 = 1;			// LED点灯
				RB4 = 0;	// DEBUG
//				TMR0 = 256 - (T0val >>1);		// TMR0
//				TMR0 = 256 - T0val + 20;		// TMR0
//				TMR0 = 256 - T0val - 10;		// TMR0
				TMR0 = 256 - T0val;		// TMR0
				T0IF = 0;
				T0IE = 1;
//				RBIE = 0;
			}
		}
		RBIF = 0;	// PORTBピン変化割り込みクリア
	}
}

// ==================== メイン処理 ===========================
main()	{
	unsigned char i;
	unsigned char c;

	pic_init();			// PICの初期化

	LCD_init();			// LCDの初期化
	EEspeed();

	buffW = 0;
	buffR = 0;
	SW_RS = 1;			// LCD Data mode

	Delay_ms(250);
	Delay_ms(250);
	comm_stat = 0;
	RB7 = 0;
	INTCON = 0x88;		// GIE=1, RBIE=1

	while(1) {
		if	(buffW != buffR) {
			bufLCD();
		}
		if	(RA3) {			// RA3:1 (SW off)
			sw_RA3 = 0;
		} else {			// RA3:0 (SW on)
			sw_RA3_on();
		}
	}
}


// ==================== バッファオーバラン発生 ================
static void OverRun() {
	RB5 = 1;
	while(1) {}
}

// ==================== 1mSウェイトルーチン ================
static void Delay_1ms() {
	unsigned int cnt;
	unsigned int i;
	cnt = 384;

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

// ==================== 1mS×ms ウェイトルーチン ===========
static void Delay_ms(unsigned char ms) {
	unsigned char c;
	for (c=ms ; c>0 ; c--) {
		Delay_1ms();
	}
}

// ==================== PICの初期化 ===========================
static void pic_init() {
	OPTION = 0x00;		// TMR0プリスケーラ:2
	TRISA = 0xF8;		// RA0-2:out
	TRISB = 0x80;		// RB0-6:out
	PORTA = 0x00;
	PORTB = 0x00;
	INTCON = 0x00;		// GIE:0 ,T0IE:0 ,RBIE:0
}

// ==================== LCD 4ビット送信 =========================
static void LCD_send4(unsigned char c) {
	PORTB = (PORTB & 0xF0) | c;
	LCD_E = 1;
	NOP();
	LCD_E = 0;
}


// ==================== LCD Busy解除を待つ ====================
static void LCD_Busy_wait() {
	LCD_E = 0;
	LCD_RW = 1;
	LCD_RS = 0;
	TRISB = TRISB | 0x0F;	// PORTB(3-0):input

	while(1) {
		LCD_E = 1;			// 上位4ビット読み込み
		NOP();
		if	(RB3 == 0) {	// Busy = 0を待つ
			LCD_E = 0;
			break;
		}
		LCD_E = 0;
		NOP();
		LCD_E = 1;			// 下位4ビット読み込み
		NOP();
		LCD_E = 0;
		NOP();
	}
	LCD_E = 1;				// 下位4ビット読み込み
	NOP();
	LCD_E = 0;

	TRISB = TRISB & 0xF0;	// PORTB(3-0):output
}


// ==================== LCDコマンドWRITE =================
static void LCD_cmd(unsigned char cmd) {
	LCD_Busy_wait();	// Busy解除待ち

	LCD_RS = 0;
	LCD_RW = 0;
	PORTB = (PORTB & 0xF0) | ((cmd >>4) & 0x0F);
	LCD_E = 1;			// 上位4ビット書き込み
	NOP();
	LCD_E = 0;

	PORTB = (PORTB & 0xF0) | (cmd & 0x0F);
	LCD_E = 1;			// 下位4ビット書き込み
	NOP();
	LCD_E = 0;
}


// ==================== LCDデータWRITE =================
static void LCD_data(unsigned char cmd) {
	LCD_Busy_wait();	// Busy解除待ち

	LCD_RS = 1;
	LCD_RW = 0;
	PORTB = (PORTB & 0xF0) | ((cmd >>4) & 0x0F);
	LCD_E = 1;			// 上位4ビット書き込み
	NOP();
	LCD_E = 0;

	PORTB = (PORTB & 0xF0) | (cmd & 0x0F);
	LCD_E = 1;			// 下位4ビット書き込み
	NOP();
	LCD_E = 0;
}


// ==================== LCDの初期化 ===========================
static void LCD_init() {
	Delay_ms(15);
	LCD_E = 0;
	LCD_RW = 0;
	LCD_RS = 0;

	LCD_send4(0x03);	// DB4-7に'3'を出力(8bitモードに)@
	Delay_ms(5);
	LCD_send4(0x03);	// DB4-7に'3'を出力(8bitモードに)@
	Delay_ms(1);
	LCD_send4(0x03);	// DB4-7に'3'を出力(8bitモードに)@
	Delay_ms(1);
	LCD_send4(0x02);	// DB4-7に'2'を出力(4bitモードに)
	Delay_ms(1);

	// ここからは4bitモードで、Busyフラグが使える
	LCD_cmd(0x28);	// DL=0:4bit ,N=1:2lines ,F=0:5*7dot
	LCD_cmd(0x08);	// Display off
	LCD_cmd(0x0C);	// Display on ,cursor on ,no blink
	LCD_cmd(0x06);	// Entry mode (Inc ,No Shift)
	LCD_cmd(0x01);	// Clear Display
}


// =============== bufferの内容をLCDに出力 ===================
static void bufLCD() {
	unsigned char c;
	c = buff[buffR];

	switch (c) {
		case RS_cmd:
			SW_RS = 0;		// Command mode set
			break;
		case RS_data:
			SW_RS = 1;		// Data mode set
			break;
		default:
			if	(SW_RS) {LCD_data(c);}
			else		{LCD_cmd(c); }
	}
	buffR++;
	if	(buffR == buffsize) {	// バッファポインタは最終位置?
		buffR = 0;
	}
}


// ==================== RA3スイッチON処理 ==============
//				スピード切り替え
static void sw_RA3_on() {
	Delay_ms(20);		// チャタリングキャンセル
	if	(!RA3) {		// RA3 on 確定
		if	(!(sw_RA3)) {
			sw_RA3 = 1;
			if	(!(speed)) {speed = 6;}
			LCD_cmd(0x01);		// LCDクリア
			speedDSP();			// スピードをLCDに表示
			comm_stat = 0;		// 無通信状態
			EEwrite(EEsave,speed);
			speed--;
		}
	}
}

// ============= EEPROMに保存したスピードをLCDに表示 ==========
static void EEspeed() {
	speed = EEread(EEsave);
	LCD_cmd(0x01);		// LCDクリア
	speedDSP();			// スピードをLCDに表示
}

// ==================== スピードをLCDに表示 =================
static void speedDSP() {
	OPTION = 0x00;	// TMR0プリスケーラ:2
	switch(speed) {
		case 1:
			OPTION = 0x02;	// TMR0プリスケーラ:8
			T0val = 127;	// 4.8Kbps
			EEtoLCD(EE48);
			break;
		case 2:
			OPTION = 0x01;	// TMR0プリスケーラ:4
			T0val = 124;	// 9.6Kbps
			EEtoLCD(EE96);
			break;
		case 3:
			T0val = 119;	// 19.2Kbps
			EEtoLCD(EE192);
			break;
		case 5:
			T0val = 54;		// 38.4Kbps
			EEtoLCD(EE384);
			break;
		case 6:
			T0val = 32;		// 57.6Kbps
			EEtoLCD(EE576);
			break;
		default:
			T0val = 76;		// 28.8Kbps
			speed = 4;
			EEtoLCD(EE288);
			break;
	}
	EEtoLCD(EEKbps);
}

// ============= EEPROM[addr]の内容を0x00までLCDに出力 ==========
static void EEtoLCD(unsigned char addr) {
	unsigned char c;
	unsigned char i;

	i = addr;
	while(1) {
		c = EEread(i);	// EEPROMの内容を読み込み
		if	(c == 0x00) {break;}	// EEPROMの内容が0x00なら終了
		LCD_data(c);	// EEPROMから読み込んだ文字をLCDに表示
		i++;
	}
}

// ============= EEPROM 書き込み ================================
static void EEwrite(unsigned char addr, unsigned char dat) {
		EEPROM_WRITE(addr , dat);	// EEPROMに書き込み
}

// ============= EEPROM 読み込み ================================
static unsigned char EEread(unsigned char addr) {
	return(EEPROM_READ(addr));
}
// =========================== ここまで ===============================