//****************************************************************
//
//	簡易送受信チップ・ファームウェア
//
//		Ver.040811	by K.I
//
//****************************************************************
#define SYSCLOCK 10000000	//10MHz or 20MHz
#define HAMMING		//Hamming decode ON

#include <16f877.h>
#fuses HS,NOWDT,NOPROTECT,PUT,NOBROWNOUT,NOLVP
#use delay(clock=SYSCLOCK)
#use RS232(BAUD=9600,XMIT=PIN_C6,RCV=PIN_C7)
#use fast_io(A)
#use fast_io(B)
#use fast_io(C)
#use fast_io(D)

#if SYSCLOCK == 10000000
#define NOISE 0xE0
#define EDGE_BIT 2
#define CLK50MS 961
#endif

#if SYSCLOCK == 20000000
#define NOISE 0xF0
#define EDGE_BIT 3
#define CLK50MS 1923
#endif

// ---- 多byteデータアクセス用共用体 --------
union buf16 {
	int16	longbuf;
	int8	buf[2];
};

union buf32 {
	int32	longbuf;
	int8	buf[4];
};

// ----  PIN設定 --------
#define TXD	pin_c5
#define RXD	pin_a3
#define XSTART	pin_e1
#define RXE	pin_a5
#define TXE	pin_e0

#define SEND	pin_a1
#define TX_RX	pin_a2
#define OPR	pin_e2
#define RANDOM	pin_c0

#define PLL_DATA	pin_c1
#define PLL_CLK	pin_c2
#define PLL_LE	pin_c3

#define XXX	pin_a4
#define STB	pin_a0
#define LD	pin_c4

#define SWIN	input_b()
#define LEDOUT	output_d
#define LEDIN	input_d()

// ---- タイマ0プリセット値(基本クロック設定) --------
#define T0PRESET	-95	//-95:(10MHz時→約52us,20MHz時→約26us)

// ---- フレームヘッダ --------
#define FRAMEHEAD1 0b11110001
#define FRAMEHEAD2 0b00110101

// ---- PLL 設定値 --------
#define TXR 0x082051	//送信
#define TXN 0x852830
//#define TXN 0x85282C	// Level down
#define RXR 0x082051	//受信
#define RXN 0x850430
#define OFFR 0x082011	//OFF(スタンバイ)
#define OFFN 0x850430

#define PLL_RX	0
#define PLL_TX	1
#define PLL_OFF	2
#define PLL_NONE	3

// ---- PN符号発生用 --------
#define PN_TAP	0x0011	//511PN用タップ
#define PN_STAGE	0x01ff   //9bitマスク

// ----  テーブル --------
#if SYSCLOCK == 10000000
	int8 const version[] = "sdtc v.040929-10M-30 by K.I NPC";
#endif
#if SYSCLOCK == 20000000
	int8 const version[] = "sdtc v.040929-20M-30 by K.I NPC";
#endif
	int8 const hamming_encode[16] = {
	0x00,0x1e,0x2d,0x33,0x4b,0x55,0x66,0x78,
	0x87,0x99,0xaa,0xb4,0xcc,0xd2,0xe1,0xff };

	int8 const hamming_decode[128] = {
	0x00,0x00,0x00,0x43,0x00,0x25,0x16,0x0F,
	0x00,0x19,0x2A,0x0F,0x4C,0x0F,0x0F,0x0F,
	0x00,0x19,0x16,0x33,0x16,0x55,0x16,0x16,
	0x19,0x19,0x5A,0x19,0x3C,0x19,0x16,0x0F,
	0x00,0x25,0x2A,0x33,0x25,0x25,0x66,0x25,
	0x2A,0x69,0x2A,0x2A,0x3C,0x25,0x2A,0x0F,
	0x70,0x33,0x33,0x33,0x3C,0x25,0x16,0x33,
	0x3C,0x19,0x2A,0x33,0x3C,0x3C,0x3C,0x7F,
	0x00,0x43,0x43,0x43,0x4C,0x55,0x66,0x43,
	0x4C,0x69,0x5A,0x43,0x4C,0x4C,0x4C,0x0F,
	0x70,0x55,0x5A,0x43,0x55,0x55,0x16,0x55,
	0x5A,0x19,0x5A,0x5A,0x4C,0x55,0x5A,0x7F,
	0x70,0x69,0x66,0x43,0x66,0x25,0x66,0x66,
	0x69,0x69,0x2A,0x69,0x4C,0x69,0x66,0x7F,
	0x70,0x70,0x70,0x33,0x70,0x55,0x66,0x7F,
	0x70,0x69,0x5A,0x7F,0x3C,0x7F,0x7F,0x7F };

// ----  Global変数 ---------
	int16 tx_clk;
	int16 rx_clk;
	int8 rx_edge;
	int8 rx_count;
	int8 tx_count;
	union buf16 rx_buf;
	int8 rx_bufx;
	int8 rx_data,rx_last;
	union buf32 rx_data0,rx_data1;
	int1 test_mode=0;
	int8 pll_mode=PLL_NONE;
	int16 pn_data=0xffff;

int8 rcv_hamming(hdata,ldata)
{
	int8 rdata;

#ifdef HAMMING
	rdata =  hamming_decode[hdata>>1]>>3<<4;  //上位4bit
	rdata += hamming_decode[ldata>>1]>>3;     //下位4bit
#else
	rdata =  hdata>>4<<4;  //上位4bit
	rdata += ldata>>4;     //下位4bit
#endif

	return(rdata);
}

// ---- タイマ0割込みルーチン --------
#INT_RTCC
void rtcc_isr(){

//output_high(STB);
	set_timer0(T0PRESET);	//タイマプリセット
	tx_clk++;
	rx_clk++;

// ---- データ→受信バッファ --------
	rx_buf.longbuf >>= 1;
	if (input(RXD))
	  bit_set(rx_buf.longbuf,15);
	else
	  bit_clear(rx_buf.longbuf,15);

//output_low(STB);

// ---- ノイズ除去回路 --------
	rx_bufx = rx_buf.buf[1]&NOISE;
	if (!rx_bufx)			//3-4bit連続0→0
	  bit_clear(rx_buf.buf[1],1);
	else if (rx_bufx==NOISE)		//3-4bit連続1→1
	  bit_set(rx_buf.buf[1],1);
	else
	 if (bit_test(rx_buf.buf[1],0))	//それ以外は前回の状態を保つ
	  bit_set(rx_buf.buf[1],1);
	 else
	  bit_clear(rx_buf.buf[1],1);

// ---- 同期検出回路 --------

#if SYSCLOCK == 20000000
	if (rx_buf.buf[0]==0xf0) {		//0→1
	  rx_edge = rx_clk&0x07;
	  goto rx_0;
	}
	else if (rx_buf.buf[0]==0x0f) {	//1→0
	  rx_edge = rx_clk&0x07;
	  goto rx_1;
	}
	else if (rx_buf.buf[0]==0x00) {	//0→0
	  if ((rx_clk&0x07)==rx_edge) {
	    goto rx_0;
	  }
	}
	else if (rx_buf.buf[0]==0xff) {	//1→1
	  if ((rx_clk&0x07)==rx_edge) {
	    goto rx_1;
	  }
	}
	goto rx_end;
#endif

#if SYSCLOCK == 10000000
	rx_bufx = rx_buf.buf[0] & 0x7E;
	if (rx_bufx==0x70) {		//0→1
	  rx_edge = rx_clk&0x03;
	  goto rx_0;
	}
	else if (rx_bufx==0x0E) {		//1→0
	  rx_edge = rx_clk&0x03;
	  goto rx_1;
	}
	else if (rx_bufx==0x00) {		//0→0
	  if ((rx_clk&0x03)==rx_edge) {
	    goto rx_0;
	  }
	}
	else if (rx_bufx==0x7E) {		//1→1
	  if ((rx_clk&0x03)==rx_edge) {
	    goto rx_1;
	  }
	}
	goto rx_end;
#endif

rx_0:
//output_high(STB);
	if (bit_test(rx_count++,0)) {
	    rx_data0.longbuf <<= 1;	//rx_data0をシフト
	    bit_clear(rx_data0.longbuf,0);	//データ0
	    goto data0_check;
	}
	else {
	    rx_data1.longbuf <<= 1;	//rx_data1をシフト
	    bit_clear(rx_data1.longbuf,0);	//データ0
	    goto data1_check;
	}
	goto rx_end;
rx_1:
//output_high(STB);
	if (bit_test(rx_count++,0)) {
	    rx_data0.longbuf <<= 1;	//rx_data0をシフト
	    bit_set(rx_data0.longbuf,0);	//データ1
	    goto data0_check;
	}
	else {
	    rx_data1.longbuf <<= 1;	//rx_data1をシフト
	    bit_set(rx_data1.longbuf,0);	//データ1
	    goto data1_check;
	}
	goto rx_end;

data0_check:
	if (rx_data0.buf[3]==FRAMEHEAD1) {	//ヘッダチェック
	  if (rx_data0.buf[0]==FRAMEHEAD2) {
	    rx_data = rcv_hamming(rx_data0.buf[2],rx_data0.buf[1]);
	    if (rx_data==rx_last) {
	      LEDOUT(rx_data);
	      rx_clk=0;
	    }
	    rx_last = rx_data;
	  }
	}
	goto rx_end;

data1_check:
	if (rx_data1.buf[3]==FRAMEHEAD1) {	//ヘッダチェック
	  if (rx_data1.buf[0]==FRAMEHEAD2) {
	    rx_data = rcv_hamming(rx_data1.buf[2],rx_data1.buf[1]);
	    if (rx_data==rx_last) {
	      LEDOUT(rx_data);
	      rx_clk=0;
	    }
	    rx_last = rx_data;
	  }
	}
	goto rx_end;

rx_end:
	;
//output_low(STB);
}


void pll_wait_clk()			//クロック待ち
{
	while(!bit_test(tx_clk,0)) ;
	while(bit_test(tx_clk,0)) ;
}

void pll_clk_one()			//PLL設定用CLKを1回出力
{
	while(!bit_test(tx_clk,0)) ;
	output_bit(PLL_CLK,1);
	while(bit_test(tx_clk,0)) ;
	output_bit(PLL_CLK,0);
}

void pll_le_one()				//PLL設定用LEを1回出力
{
	while(!bit_test(tx_clk,0)) ;
	output_bit(PLL_LE,1);
	while(bit_test(tx_clk,0)) ;
	output_bit(PLL_LE,0);
}

void pll_set_data(int32 data)		//PLL設定
{
	int8 i;

	pll_wait_clk();
	for (i=0; i<24 ;i++) {		//24bit設定
	  if (bit_test(data,23))
	    output_bit(PLL_DATA,1);	//send '1'
	  else	
	    output_bit(PLL_DATA,0);	//send '0'
	  pll_clk_one();
	  data <<= 1;	}
	pll_le_one();
}

int8 setup_pll(int8 mode)
{
	if (mode == pll_mode) return(0);	//モード変更なし→戻る

	pll_mode = mode;
	if (pll_mode==PLL_RX) {		//RX mode
	  output_bit(TXE,0);
	  output_bit(RXE,1);
	  pll_set_data(RXR);
	  pll_set_data(RXN);
	}
	else if (pll_mode==PLL_TX) {	//TX mode
	  output_bit(RXE,0);
	  output_bit(TXE,1);
	  pll_set_data(TXR);
	  pll_set_data(TXN);
	}
	else {
	  output_bit(TXE,0);
	  output_bit(RXE,0);
	  pll_set_data(OFFR);		//PLL off
	  pll_set_data(OFFN);
	}
}
	    
// ----	I/Oポート初期設定 --------
void setup_port() {

	byte	data;

	set_tris_A(0b00001110);
	set_tris_B(0b11111111);	//swin
	set_tris_C(0b10010001);
	set_tris_D(0b00000000);	//ledout

	output_a(0);
	output_c(0);
	output_d(0);

	if(!input(RANDOM)) {	//test mode
	  test_mode = 1;
	  data = 1;
	  while(data) {
	    LEDOUT(data);
	    delay_ms(100);
	    data <<= 1;
	  }
	  LEDOUT(data);
	}
	else {			//normal mode
	  LEDOUT(0x55);
	  delay_ms(100);
	  LEDOUT(0xaa);
	  delay_ms(100);
	  LEDOUT(0x55);
	  delay_ms(100);
	  LEDOUT(0xaa);
	  delay_ms(100);
	  LEDOUT(0x00);
	}

	output_bit(XSTART,1);	//X'tal起動設定ON

	printf("%s\n",version);
}

void setup_interrupt()		//割込み初期設定
{
	setup_timer_0(RTCC_INTERNAL|RTCC_DIV_1);
	set_timer0(T0PRESET);
	enable_interrupts(INT_TIMER0);
	enable_interrupts(GLOBAL);
}

// ---- PN符号ジェネレータ --------
int16 pn9(int16 data, int16 tap)
{
	int mask,line,i;

	data = data & PN_STAGE;
	mask = data & tap;

	line = 0;
	for (i=0; i<9 ;i++) {
	  line += mask & 0x01;
	  mask = mask>>1;
	}

	data = data<<1;
	if (line & 0x01) {
	  data = data|1;
	}

	return(data&PN_STAGE);
}

void send_1()		//1出力(TXDを0に戻すのに使用)
{
	  while(!bit_test(tx_clk,EDGE_BIT)) ;
	  output_bit(TXD,1);
	  while(bit_test(tx_clk,EDGE_BIT)) ;
	  output_bit(TXD,0);
}

void send_bit7(byte data)		//dataのbit7のみ出力
{
	if (bit_test(data,7)) {		//send '1'
	  while(!bit_test(tx_clk,EDGE_BIT)) ;
	  output_bit(TXD,1);
	  while(bit_test(tx_clk,EDGE_BIT)) ;
	  output_bit(TXD,0);
	}
	else {				//send '0'
	  while(!bit_test(tx_clk,EDGE_BIT)) ;
	  output_bit(TXD,0);
	  while(bit_test(tx_clk,EDGE_BIT)) ;
	  output_bit(TXD,1);
	}
}

void send_data(byte data)		//8ビットデータ送信
{
	byte	i;

	for (i=0; i<8 ;i++) {	//1ビット×8送信
	  send_bit7(data);
	  data <<= 1;
	}
}

void send_hamming(byte data)
{
	swap(data);
	send_data(hamming_encode[data&0x0f]); //上位4bit送信
	swap(data);
	send_data(hamming_encode[data&0x0f]); //下位4bit送信
}

void trig_stb()
{
	while(!bit_test(tx_clk,EDGE_BIT)) ;	  //トリガ用パルス生成
	output_high(STB);
	while(bit_test(tx_clk,EDGE_BIT)) ;
	output_low(STB);
}

void out_stb()
{
	if (bit_test(rx_buf.buf[0],0))	//rx_bufを出力
//	if (bit_test(rx_data0.longbuf,0))	//rx_data0を出力
	  output_high(STB);		//STB端子に出力
	else
	  output_low(STB);
}


//void pn_mode()			//PN符号出力モード
//{
//	int8 data;
//
//	while(!input(RANDOM)) {	//RANDOMキーが押されていたら
//	  setup_pll(PLL_TX);	//PLL TX設定
//	  pn_data = pn9(pn_data,PN_TAP);
//	  send_bit7(pn_data);
//	}
//}

//--PN符号ジェネレータによる511PN出力が何故かうまくいかないため、
//--データをすべて列挙する方法にした。(但し最初に1が多いので512bitになっている)
void pn_mode()			//PN符号出力モード
{
	while(!input(RANDOM)) {	//RANDOMキーが押されていたら
	  setup_pll(PLL_TX);	//PLL TX設定
		send_data(0b10000011);	//(最初の1は本当は不要)
		send_data(0b11011111);
		send_data(0b00010111);
		send_data(0b00110010);
		send_data(0b00001001);
		send_data(0b01001110);
		send_data(0b11010001);
		send_data(0b11100111);
		send_data(0b11001101);
		send_data(0b10001010);
		send_data(0b10010001);
		send_data(0b11000110);
		send_data(0b11010101);
		send_data(0b11000100);
		send_data(0b11000100);
		send_data(0b01000000);
		send_data(0b00100001);
		send_data(0b00011000);
		send_data(0b01001110);
		send_data(0b01010101);
		send_data(0b10000110);
		send_data(0b11110100);
		send_data(0b11011100);
		send_data(0b10001010);
		send_data(0b00010101);
		send_data(0b10100111);
		send_data(0b11101100);
		send_data(0b10010010);
		send_data(0b11011111);
		send_data(0b10010011);
		send_data(0b01010011);
		send_data(0b00110000);
		send_data(0b00011000);
		send_data(0b11001010);
		send_data(0b00110100);
		send_data(0b10111111);
		send_data(0b10100010);
		send_data(0b11000111);
		send_data(0b01011001);
		send_data(0b01100111);
		send_data(0b10001111);
		send_data(0b10111010);
		send_data(0b00001101);
		send_data(0b01101101);
		send_data(0b11011000);
		send_data(0b00101101);
		send_data(0b01111101);
		send_data(0b01010100);
		send_data(0b00001010);
		send_data(0b01010111);
		send_data(0b10010111);
		send_data(0b01110000);
		send_data(0b00111001);
		send_data(0b11010010);
		send_data(0b01111010);
		send_data(0b11101010);
		send_data(0b00100100);
		send_data(0b00110011);
		send_data(0b10000101);
		send_data(0b11101101);
		send_data(0b10011010);
		send_data(0b00011101);
		send_data(0b11100001);
		send_data(0b11111111);
		if (rx_clk>CLK50MS) LEDOUT(0);	//LED消灯
	}
}

void tx_mode()			//送信モード
{
	if (!input(SEND)) {
	  setup_pll(PLL_TX);	//PLL TX設定
	  tx_count=0;
	  while(!input(SEND)) {	//送信キーが押されていたら
	    tx_clk = 0;
	    trig_stb();		//STBにトリガ出力
	    send_data(0xff);	//ヘッダFF出力
	    send_data(FRAMEHEAD1);	//ヘッダ1出力
	    send_hamming(SWIN);	//データ出力
	    send_data(FRAMEHEAD2);	//ヘッダ2出力
	    send_1();		//出力を0に戻す
	    while(tx_clk<CLK50MS);	//txクロックで50ms待ち
	    if (!test_mode) {
	      if (++tx_count>=90) break;	///4.5秒間データ送信
	    }
	    if (rx_clk>CLK50MS) LEDOUT(0);	//LED消灯
	  }

	  if (!test_mode) {
	    setup_pll(PLL_RX);	//PLL OFF設定
	    tx_count=0;
	    while(TRUE) {
	      tx_clk = 0;
	      while(tx_clk<CLK50MS);
	      if (++tx_count>=50) break;		//2.5秒間無送信
	      if (rx_clk>CLK50MS) LEDOUT(0);	//LED消灯
	    }
	  }
	}
}

void rx_mode()			//受信モード
//data0、data1バッファのいずれかのデータを受信データとする
{
	out_stb();		//STB出力
	setup_pll(PLL_RX);	//PLL RX設定
}

void main()
{
	setup_port();		//ポート初期設定
	setup_interrupt();	//割込み初期設定

	while(TRUE) {
	  while (!input(OPR)) {
	    setup_pll(PLL_OFF);	//OPRが0なら強制的にOFF
	    output_bit(XSTART,0);	//X'tal起動設定OFF
	  }
	  output_bit(XSTART,1);	//X'tal起動設定ON

	  if (rx_clk>CLK50MS) LEDOUT(0);	//LED消灯

	  if (input(TX_RX)) {		//TX_RXが1で
	    if (test_mode) {		//テストモードなら
	      setup_pll(PLL_TX);		//キャリアを出し続ける
	    }
	  }
	  else if (!input(TX_RX)) {	//TX_RXが0なら受信モードに
	    rx_mode();			//(キャリア停止)
	  }

	  tx_mode();		//送信モード
	  pn_mode();		//PNモード

	}	
}