/*
テストプログラム
割り込み、ペリフェラルデバイスなどの実験
*/

/*16F877用の設定（追加レジスター）*/
char PIE1@0x8c;
char PIE2@0x8d;
char TRISC@0x87;
char TRISD@0x88;
char TRISE@0x89;
char PORTC@0x07;
char PORTD@0x08;
char PORTE@0x09;
char TXSTA@0x98;
char SPREG@0x99;
char RCSTA@0x18;
char PIR1@0x0c;
char RCREG@0x1a;
char TXREG@0x19;
char ADCON0@0x1f;
char ADCON1@0x9f;
char ADRESH@0x1e;
char ADRESL@0x9e;
char _EEDATA@0x10c;
char _EEADR@0x10d;
char _EEDATAH@0x10e;
char _EEADRH@0x10f;
char _EECON1@0x18c;
char _EECON2@0x18d;

/*built in delayのためのクロック設定*/
/*20MHz*/
#pragma CLOCK_FREQ 20000000
/*delay関連の命令
void delay_s(char time);	秒
void delay_ms(char time);	ミリ秒
void delay_us(char time);	マイクロ秒
*/

/*****LED点滅ルーチン*****/
void LED_blink(void)@0x800;	/*LEDを点滅させる*/
char LED_blink_cnt;		/*点滅分周カウンタ*/
char LED_blink_state;	/*点滅ステート*/

/*****チップ初期化ルーチン*****/
void chip_init(void);

/*****LCD関連*****/
void LCD_init(void);		/*LCD初期化*/
void LCD_cmd(char data);	/*LCDコマンドwrite*/
void LCD_data(char data);	/*LCDデータwrite*/
void LCD_clear(void);		/*LCD表示クリア*/
void LCD_locate(char x,char y);	/*指定位置にカーソルをセット*/
void LCD_char(char data);	/*data値をキャラクタで表示*/
void LCD_char_low(char data);	/*dataの下位のみキャラクタで表示*/

/*****ADC関連*****/
int ADC_get(char ch);		/*ADCからデータを取り出す*/

/*****シリアル通信関連*****/
void RS_put(char data);			/*シリアルポートに１字送信する*/
void RS_printf(const char *cp);	/*シリアルポートに文字列を出力*/
void RS_scan(char echo_flag);	
			/*シリアルポートから文字列を取得してRS_bufに入れる*/
#define BUFSIZE 5
char RS_buf[BUFSIZE]@0x120;	/*シリアル通信バッファ*/
char RS_buf_point;	/*バッファ内の位置*/

#define CR 0x0d		/* \n */
#define LF 0x0a		/* \r */
#define ESC 0x1b

/*****文字列表示ルーチン*****/
void printf(const char *cp);	/*文字列を表示する*/

/*****BCD-int変換*****/
int bcd_to_int(void);			/*bcdをintに変換*/
void int_to_bcd(int value);		/*intをbcdに変換*/
char digit[3]@0xa0;	/*digit12->digit[0],digit34->digit[1]*/

/*****文字列操作*****/
int charbuf_to_int(void);	/*文字列をint型で返す（５文字以内）*/
void int_to_charbuf(int value);	/*intを文字列（十進）に直す*/
void int_to_charbuf2(int value);/*intを文字列（16進）に直す*/
char charbuf[5]@0x1a0;		/*引き渡す文字列*/
void clear_charbuf(void);	/*引き渡し文字列バッファを'0'にする*/

/*****util*****/
void RSbuf_to_charbuf(void);	/*５文字をcharbufに移す*/

/*****EEPROM制御*****/
char read_data_EEPROM(char adr);	/*EEPROMからデータを読む*/
void write_data_EEPROM(char adr,char data);
								/*EEPROMにデータを書き込む*/

/*****const data*****/
const char *title1 = "This is test.";
const char *title2 = "test.";
const char *echoinfo = "data=";

/*****メインルーチン*****/


/********************************************/
/*メイン関数*/
/********************************************/
main()
{
	char i,x;
	int tmp;
	
	chip_init();
	LCD_init();
	
	/*シリアル通信バッファポインタの初期化*/
	RS_buf_point = 0;
	
	
	/*eepromから表示する*/
	LCD_locate(0,0);
	for(i = 0;;i++){
		x = read_data_EEPROM(i);
		write_data_EEPROM(0x10+i,x);
		if(x == 0){
			break;
		}
		LCD_data(x);
	}
	LCD_locate(0,1);
	for(i = 0;;i++){
		x = read_data_EEPROM(0x10+i);
		if(x == 0){
			break;
		}
		LCD_data(x);
	}
	
	delay_s(1);
	LCD_clear();
	
	/*割り込みを許可する*/
	set_bit(INTCON,7);
	
mainlp:
	RS_scan(1);
	RS_put(CR);
	RS_put(LF);
	RS_printf(echoinfo);
	for(i = 0;i < RS_buf_point;i++){
		RS_put(RS_buf[i]);
	}
	RS_put(CR);
	RS_put(LF);
	
	RSbuf_to_charbuf();
	tmp = charbuf_to_int();
	int_to_charbuf2(tmp);
	for(i = 0;i < 4;i++){
		RS_put(charbuf[i]);
	}
	RS_put(CR);
	RS_put(LF);
	
	goto mainlp;
}

/********************************************/
/*割り込み関数*/
/********************************************/
void interrupt(void)
{
	clear_bit(INTCON,2);
	LED_blink();
	
	return;
}

/********************************************/
/*ユーティリティールーチン*/
/********************************************/
/*****EEPROMにデータを書き込む*****/
void write_data_EEPROM(char adr,char data)
{
	/*割り込みを禁止する*/
	clear_bit(INTCON,7);
	
	_EEADR = adr;
	_EEDATA = data;
	clear_bit(_EECON1,7);	/*Access data memory*/
	set_bit(_EECON1,2);		/*書き込みを許可する*/
	_EECON2 = 0x55;
	_EECON2 = 0xaa;
	set_bit(_EECON1,1);
	while((_EECON1 & 10b) != 0);
	clear_bit(_EECON1,3);
	
	return;
}

/*****EEPROMからデータを読む*****/
char read_data_EEPROM(char adr)
{
	_EEADR = adr;
	clear_bit(_EECON1,7);	/*Access data memory*/
	set_bit(_EECON1,0);		/*読み出し開始*/
	
	return _EEDATA;
}

/*****文字列操作*****/
/*char charbuf[5];引き渡す文字列,10進数*/
/*キャラクタコードをintに直す*/
/*****文字列をint型で返す（５文字以内）*****/
int charbuf_to_int(void)
{
	char i,tmp;
	int dest;
	dest = 0;
	
	for(i = 0;i < 5;i++){
		if((charbuf[i] < 0x30)||(charbuf[i] > 0x39)){
			return dest;
		}
	}
	
	digit[0] = charbuf[3] - 0x30;
	digit[0] = digit[0] << 4;
	tmp = charbuf[4] - 0x30;
	digit[0] = digit[0] + tmp;
	
	digit[1] = charbuf[1] - 0x30;
	digit[1] = digit[1] << 4;
	tmp = charbuf[2] - 0x30;
	digit[1] = digit[1] + tmp;
	
	digit[2] = charbuf[0] - 0x30;
	
	dest = bcd_to_int();
	
	return dest;
}

/*****intを文字列に直す*****/
void int_to_charbuf(int value)
{
	int_to_bcd(value);
	charbuf[0] = digit[2] + 0x30;
	charbuf[1] = (digit[1] >> 4) + 0x30;
	charbuf[2] = (digit[1] & 0x0f) + 0x30;
	charbuf[3] = (digit[0] >> 4) + 0x30;
	charbuf[4] = (digit[0] & 0x0f) + 0x30;
	return;
}

/*****intを文字列（16進）に直す*****/
void int_to_charbuf2(int value)
{
	char tmp;
	tmp = value >> 12;
	if(tmp > 9){
		charbuf[0] = tmp + 0x37;
	}else{
		charbuf[0] = tmp + 0x30;
	}
	tmp = value >> 8;
	tmp = tmp & 0x0f;
	if(tmp > 9){
		charbuf[1] = tmp + 0x37;
	}else{
		charbuf[1] = tmp + 0x30;
	}
	tmp = value >> 4;
	tmp = tmp & 0x0f;
	if(tmp > 9){
		charbuf[2] = tmp + 0x37;
	}else{
		charbuf[2] = tmp + 0x30;
	}
	tmp = value;
	tmp = tmp & 0x0f;
	if(tmp > 9){
		charbuf[3] = tmp + 0x37;
	}else{
		charbuf[3] = tmp + 0x30;
	}
	return;
}

/*****引き渡し文字列バッファを'0'にする*****/
void clear_charbuf(void)
{
	char i;
	for(i = 0;i < 5;i++){
		charbuf[i] = '0';
	}
	return;
}

/*****５文字をcharbufに移す*****/
void RSbuf_to_charbuf(void)
{
	int i;
	clear_charbuf();
	for(i = 0;i < RS_buf_point;i++){
		charbuf[5 - RS_buf_point + i] = RS_buf[i];
	}
}

/*****BCD-int変換**************************************/
/*データの受け渡しはchar digit[0],digit[1],digit[2]で行う*/
/*下の位がdigit[0],上下4bitに分けたものであらわす*/
/******************************************************/
/*****bcdをintに変換*****/
int bcd_to_int(void)
{
	int dest;
	dest = 0xffff;
	char d12,d34,d56;
	d12 = bcd_to_char(digit[0]);
	d34 = bcd_to_char(digit[1]);
	d56 = bcd_to_char(digit[2]);
	if(d56 > 6){
		return dest;
	}
	if(d56 == 6){
		if(d34 > 55){
			return dest;
		}
		if(d34 == 55){
			if(d12 > 35){
				return dest;
			}
		}
	}
	dest = d56 * 10000;
	dest = dest + d34 * 100;
	dest = dest + d12;
	return dest;
}

/*****intをbcdに変換*****/
void int_to_bcd(int value)
{
	int tmp;
	
	digit[2] = 0;
	digit[1] = 0;
	digit[0] = 0;
	
	tmp = value / 10000;
	digit[2] = char_to_bcd(tmp);
	tmp = value % 10000;
	tmp = tmp / 100;
	digit[1] = char_to_bcd(tmp);
	tmp = value % 100;
	digit[0] = char_to_bcd(tmp);
	
	return;
}

/*****シリアルポートに１字送信する*****/
void RS_put(char data)
{
	while((TXSTA & 10b) == 0);	/*busyまち*/
	TXREG = data;
}

/*****シリアルポートに文字列を出力*****/
void RS_printf(const char *cp)
{
	char i;
	for(i = 0;cp[i] != 0;i++){
		RS_put(cp[i]);
	}
	return;
}

/*****シリアルポートから文字列を取得してRS_bufに入れる*****/
void RS_scan(char echo_flag)
{
	char recv_data;
	RS_buf_point = 0;
loop:
	if((PIR1 & 100000b) != 0){
		if((RCSTA & 110b) == 0){
			/*正常受信*/
			recv_data = RCREG;
		}else{
			/*エラー処理*/
			if((RCSTA & 100b) != 0){	/*framing error*/
				recv_data = RCREG;
				if(echo_flag != 0){
					RS_put('!');
				}
			}
			if((RCSTA & 10b) != 0){		/*overrun error*/
				clear_bit(RCSTA,4);	/*for OERR clear*/
				set_bit(RCSTA,4);	/*continuous receive*/
				if(echo_flag != 0){
					RS_put('@');
				}
			}
			goto loop;
		}
		/*受信データ処理*/
		if(recv_data == CR){
			return;
		}
		if(recv_data < 0x21){
			goto loop;
		}
		if(RS_buf_point < BUFSIZE){
			RS_buf[RS_buf_point] = recv_data;
			RS_buf_point++;
		}
		if(echo_flag != 0){
			RS_put(recv_data);
		}
	}
	goto loop;
}

/*****LCD初期化ルーチン*****/
void LCD_init(void)
{
	char tmp;
	
	delay_ms(20);
	
	tmp = PORTB;
	tmp = tmp & 1b;
	tmp = tmp | 110000b;	/*function set 8bits*/
	PORTB = tmp;
	clear_bit(PORTB,2);	/* R/W 0 */
	clear_bit(PORTB,1);	/* RS 0 */
	set_bit(PORTB,3);	/* E high */
	nop();
	clear_bit(PORTB,3); /* E low */
	
	delay_ms(5);
	
	tmp = PORTB;
	tmp = tmp & 1b;
	tmp = tmp | 110000b;	/*function set 8bits*/
	PORTB = tmp;
	clear_bit(PORTB,2);	/* R/W 0 */
	clear_bit(PORTB,1);	/* RS 0 */
	set_bit(PORTB,3);	/* E high */
	nop();
	clear_bit(PORTB,3); /* E low */
	
	delay_us(100);
	
	tmp = PORTB;
	tmp = tmp & 1b;
	tmp = tmp | 110000b;	/*function set 8bits*/
	PORTB = tmp;
	clear_bit(PORTB,2);	/* R/W 0 */
	clear_bit(PORTB,1);	/* RS 0 */
	set_bit(PORTB,3);	/* E high */
	nop();
	clear_bit(PORTB,3); /* E low */
	
	delay_us(100);
	
	tmp = PORTB;
	tmp = tmp & 1b;
	tmp = tmp | 100000b;	/*function set 4bits*/
	PORTB = tmp;
	clear_bit(PORTB,2);	/* R/W 0 */
	clear_bit(PORTB,1);	/* RS 0 */
	set_bit(PORTB,3);	/* E high */
	nop();
	clear_bit(PORTB,3); /* E low */
	
	delay_us(100);
	
	/*from here 4bits mode*/
	LCD_cmd(0x2c);		/*function DL=0 4bit mode*/
	LCD_cmd(0x08);		/*display off D=C=B=0*/
	LCD_cmd(0x0c);		/*display on D=1 C=B=0*/
	LCD_cmd(0x06);		/*entry I/D=1 S=0*/
	
	return;
}

/*****LCDコマンドライト*****/
void LCD_cmd(char data)
{
	char tmp;
	
	tmp = PORTB;
	tmp = tmp & 1b;
	tmp = (data & 0xf0) | tmp;
	PORTB = tmp;
	clear_bit(PORTB,2);	/* R/W 0 */
	clear_bit(PORTB,1);	/* RS 0(command mode) */
	set_bit(PORTB,3);	/* E high */
	nop();
	clear_bit(PORTB,3); /* E low */
	
	tmp = PORTB;
	tmp = tmp & 1b;
	tmp = ((data & 0x0f) << 4) | tmp;
	PORTB = tmp;
	clear_bit(PORTB,2);	/* R/W 0 */
	clear_bit(PORTB,1);	/* RS 0(command mode) */
	set_bit(PORTB,3);	/* E high */
	nop();
	clear_bit(PORTB,3); /* E low */
	
	delay_us(40);
	
	return;
}

/*****LCDデータライト*****/
void LCD_data(char data)
{
	char tmp;
	
	tmp = PORTB;
	tmp = tmp & 1b;
	tmp = (data & 0xf0) | tmp;
	PORTB = tmp;
	clear_bit(PORTB,2);	/* R/W 0 */
	set_bit(PORTB,1);	/* RS 1(data mode) */
	set_bit(PORTB,3);	/* E high */
	nop();
	clear_bit(PORTB,3); /* E low */
	
	tmp = PORTB;
	tmp = tmp & 1b;
	tmp = ((data & 0x0f) << 4) | tmp;
	PORTB = tmp;
	clear_bit(PORTB,2);	/* R/W 0 */
	set_bit(PORTB,1);	/* RS 1(data mode) */
	set_bit(PORTB,3);	/* E high */
	nop();
	clear_bit(PORTB,3); /* E low */
	
	delay_us(40);
	
	return;
}

/*****LCD表示クリア*****/
void LCD_clear(void)
{
	LCD_cmd(0x01);
	delay_ms(2);
	return;
}

/*****指定位置にカーソルをセット*****/
void LCD_locate(char x,char y)
{
	char tmp;
	tmp = y * 0x40 + x;
	tmp = tmp | 0x80;
	LCD_cmd(tmp);
	return;
}

/*****data値をキャラクタで表示*****/
void LCD_char(char data)
{
	char tmp;
	
	tmp = (data >> 4) & 0x0f;
	if(tmp >= 10){
		tmp = tmp + 0x37;
		LCD_data(tmp);
	}else{
		tmp = tmp + 0x30;
		LCD_data(tmp);
	}
	
	tmp = data & 0x0f;
	if(tmp >= 10){
		tmp = tmp + 0x37;
		LCD_data(tmp);
	}else{
		tmp = tmp + 0x30;
		LCD_data(tmp);
	}
	
	return;
}

/*****data値をキャラクタで表示（下位のみVer）*****/
void LCD_char_low(char data)
{
	char tmp;
	
	tmp = data & 0x0f;
	if(tmp >= 10){
		tmp = tmp + 0x37;
		LCD_data(tmp);
	}else{
		tmp = tmp + 0x30;
		LCD_data(tmp);
	}
	
	return;
}

/*****ADCからデータを取り出す*****/
int ADC_get(char ch)
{
	char tmp;
	tmp = 10000001b;
	tmp = tmp | ((ch << 3) & 111000b);
	ADCON0 = tmp;	/*読み込むチャンネルを設定する*/
	delay_us(20);	/*アナログスイッチの値が安定するまで待つ*/
	set_bit(ADCON0,2);	/*convert start(set GO bit)*/
	for(;;){
		tmp = ADCON0;
		tmp = tmp & 100b;
		tmp = tmp >> 2;
		if(tmp == 0){
			break;
		}
	}
	int result;
	result = ADRESH;
	result = result << 8;
	result = result + ADRESL;
	return result;
}

/*****文字列を表示する*****/
void printf(const char *cp)
{
	char i;
	for(i = 0;cp[i] != 0;i++){
		LCD_data(cp[i]);
	}
	return;
}

/*****LEDを点滅させる*****/
void LED_blink(void)
{
	LED_blink_cnt++;
	if(LED_blink_cnt > 2){
		LED_blink_cnt = 0;
		if(LED_blink_state == 0){
			LED_blink_state = 1;
		}else{
			LED_blink_state = 0;
		}
	}
	if(LED_blink_state == 0){
		set_bit(PORTB,0);
	}else{
		clear_bit(PORTB,0);
	}
}

/*****チップ初期化ルーチン*****/
void chip_init(void)
{
	/*****OPTIONレジスタの設定*****/
	OPTION_REG = 11000111b;	/*pre scalar 1/2*/
	
	/*****割り込み関連レジスタの設定*****/
	INTCON = 100000b;
	
	/*****ペリフェラル割り込み許可関連の設定*****/
	PIE1 = 0;
	PIE2 = 0;
	
	/*****IOポートの入出力設定*****/
	/*ADC用RA4はアナログポートではない*/
	TRISA = 110111b;
	/*LCDインターフェース*/
	TRISB = 0;
	/*RC7はUSART RXなので入力に設定*/
	TRISC = 10000000b;
	/*AC Servo用のパルス出力*/
	TRISD = 0;
	/*ADC用*/
	TRISE = 111b;
	
	/*****シリアル通信ポートの設定*****/
	TXSTA = 100000b;
	SPREG = 32;
	RCSTA = 10010000b;
	
	/*****ADCの設定*****/
	ADCON1 = 10000000b;
	ADCON0 = 10000001b;
	
	return;
}

