// Pic Perpetual Calendar Date Controller
//
// PIC16F628A
//
// Pin connections
//
// RA0 (17) out  SCL
// RA1 (18) out SDA8
// RA2 (1)  out  SDA9
// RA3 (2)  out  SDA10
// RA4 (3)  open
// RA5 (4)  out to master
// RA6 (15) out  SDA2
// RA7 (16) open

// RB0 (6)  out  SDA1
// RB1 (7)  in Serial from master
// RB2 (8)  out TX
// RB3 (9)  out  SDA3
// RB4 (10) out  SDA4
// RB5 (11) out  SDA5
// RB6 (12) out  SDA6
// RB7 (13) out  SDA7

// data direction registers  1=input 0=out
// trisa = 00110000 0x00
// trisb = 01111000 0x02

// 
// 

#include <system.h>
#include <string.h>

#pragma CLOCK_FREQ 4000000	

#pragma DATA _CONFIG , _INTOSC_OSC_NOCLKOUT & _WDT_OFF & _LVP_OFF &_MCLRE_OFF  & _BODEN_OFF
unsigned char bright[37]; // 0 for normal >0 for bright	


// i2c connection routines, have these next 5 call bit and delay routines

void sda_high(unsigned char a)
{
	switch(a)
	{
		case 1:
			set_bit( portb, 0 ); 	
			break;		
		case 2:
			set_bit( porta, 6 ); 	
			break;		
		case 3:
			set_bit( portb, 3 ); 	
			break;		
		case 4:
			set_bit( portb, 4 ); 	
			break;		
		case 5:
			set_bit( portb, 5 ); 	
			break;		
		case 6:
			set_bit( portb, 6 ); 	
			break;		
		case 7:
			set_bit( portb, 7 ); 	
			break;		
		case 8:
			set_bit( porta, 1 ); 	
			break;		
		case 9:
			set_bit( porta, 2 ); 	
			break;		
		case 10:
			set_bit( porta, 3 ); 	
			break;		
		default:
			break;
	}
     
}


void sda_low(unsigned char a)
{
	switch(a)
	{
		case 1:
			clear_bit( portb, 0 ); 	
			break;		
		case 2:
			clear_bit( porta, 6 ); 	
			break;		
		case 3:
			clear_bit( portb, 3 ); 	
			break;		
		case 4:
			clear_bit( portb, 4 ); 	
			break;		
		case 5:
			clear_bit( portb, 5 ); 	
			break;		
		case 6:
			clear_bit( portb, 6 ); 	
			break;		
		case 7:
			clear_bit( portb, 7 ); 	
			break;		
		case 8:
			clear_bit( porta, 1 ); 	
			break;		
		case 9:
			clear_bit( porta, 2 ); 	
			break;		
		case 10:
			clear_bit( porta, 3 ); 	
			break;		
		default:
			break;
	}
}

void scl_high(void)
{
    set_bit( porta, 0 );
}


void scl_low(void)
{
    clear_bit( porta, 0 );
}

void i2c_delay()
{
	delay_us(5);
}

// i2c routines

void i2c_start(void)
{
unsigned char i;
    scl_high();
    for(i=1;i<11;i++)
    {
	    sda_high(i);	
    }
    i2c_delay();  
    for(i=1;i<11;i++)
    {
	    sda_low(i);	
    }
    i2c_delay();
    scl_low();
    i2c_delay();
}

void i2c_stop(void)
{
unsigned char i;
    for(i=1;i<11;i++)
    {
	    sda_low(i);	
    }
    scl_low();  
    i2c_delay();      
    scl_high();
    i2c_delay();
    for(i=1;i<11;i++)
    {
	    sda_high(i);	
    }
    i2c_delay();
}

// bit out
// takes one bit from each byte in listof 10
void i2c_bitsout(unsigned char data[],unsigned char bitnum)
{
unsigned int d;
unsigned char i;

// extract bit from array
for (i=0;i<10;i++)
{
	
	if ((data[i]>>bitnum)&1)
		sda_high(i+1);
	else
		sda_low(i+1);
}
    // pulse clock
    i2c_delay();
    scl_high();
    i2c_delay();
    scl_low();
    i2c_delay();
}

void i2c_ack()
{
    i2c_delay();
    scl_high();
    i2c_delay();
    scl_low();
    i2c_delay();
}

// 
// send ten bytes to displays
//
void i2c_byteout(unsigned char data[])
{
unsigned char i;

	for(i=0;i<8;i++)
		i2c_bitsout(data,7-i);  // send bit n of each int
    i2c_ack();			// ack cycle
}
 
 
//
// send 10 pairs of bytes (segment data) to an address
// add = 0-3
// 
void show_array(unsigned int data[],unsigned char add,unsigned char bb[])
{
unsigned char line[10];
unsigned char i;

	switch(add)
	{
	case 0:
		add=0x70;
		break;
	case 1:
		add=0x72;
		break;
	case 2:
		add=0x74;
		break;
	default:
		add=0x76;
		break;
	}

	i2c_start();
//
	for(i=0;i<10;i++)
		line[i]=add; // address
	i2c_byteout(line);
//	for(i=0;i<10;i++)	
		i2c_delay();	
//		
	for(i=0;i<10;i++)
		line[i]=0x00; // instruction	
	i2c_byteout(line);
//	for(i=0;i<10;i++)	
		i2c_delay();	
//
	for(i=0;i<10;i++)
	{


#define GREEN_LEDS 1
//#define RED_LEDS 1

#ifdef RED_LEDS
		if(bb[i]>0)
			line[i]=0x46; // control 	!bright!
		else
			line[i]=0x16; // control  normal 
#endif
#ifdef GREEN_LEDS
		if(bb[i]>0)
			line[i]=0x76; // control 	!bright!
		else
			line[i]=0x26; // control  normal 
#endif
	}
	i2c_byteout(line);		
//	for(i=0;i<10;i++)	
		i2c_delay();	
//		
	for(i=0;i<10;i++)
		line[i]=data[i]&0xff; // low bytes	
	i2c_byteout(line);				
//	for(i=0;i<10;i++)	
		i2c_delay();	
//
	for(i=0;i<10;i++)
		line[i]=data[i]>>8; // high bytes	
	i2c_byteout(line);			
// 
	i2c_stop();
}



//
// high and low bytes for 7 segment displays 
//
rom unsigned char *segments ={

189	,	175	,
17	,	33	,
229	,	230	,
241	,	227	,
89	,	105	,
248	,	203	,
252	,	207	,
49	,	35	,
253	,	239	,
249	,	235	,
0	,	0	


};


unsigned int getsegs(unsigned char left,unsigned char right)
{
unsigned int segs,l,r;
unsigned char i;

// get left segs
	i=left;
	if(i>9)
		i=10;
	i=i*2;
	l=segments[i];
// get right segs
	i=right;
	if(i>9)
		i=10;
	i=i*2;
	r=segments[i+1];

segs= ((l&0xf0)<<8)|((r&0xf0)<<4)|((l&0xf)<<4)|((r&0xf));
return(segs);

}


//
// position mapping 
//
rom unsigned char *mappings ={
// mapping SAA1064 address to cal address
14,7,0,6,21,34,30,5,4,28,     // address 0
15,8,1,13,22,99,31,12,11,29,  // address 1
16,9,2,20,23,99,32,19,18,35,  // address 2
17,10,3,27,24,99,33,26,25,36, // address 3

};



//
//
//
void showall(unsigned char d[])
{
unsigned char i;
unsigned char p; // pointer
unsigned char n; // number to display
unsigned char z; // upper digit
unsigned int dispbits[10];
unsigned char hl[10];

// show first 10 at address 0
for(i=0;i<10;i++)
{
	p=mappings[i];
	if(p<37)
	{
		n=d[p];	
		hl[i]=bright[p];
	}
	else
	{
		n=100;
		hl[i]=0;
	}
	if (n<100)
	{
		z=n/10;
		dispbits[i]=getsegs(z,n%10);
		if((n!=0)&&(z==0))
		dispbits[i]=dispbits[i]&0x0f0f;
	}
	else
		dispbits[i]=0;	
}
show_array(dispbits,0,hl);

// show 10 at address 1
for(i=0;i<10;i++)
{
	p=mappings[i+10];
	if(p<37)
	{
		n=d[p];	
		hl[i]=bright[p];
	}
	else
	{
		n=100;
		hl[i]=0;
	}
	if (n<100)
	{
		z=n/10;
		dispbits[i]=getsegs(z,n%10);
		if((n!=0)&&(z==0))
		dispbits[i]=dispbits[i]&0x0f0f;
	}
	else
		dispbits[i]=0;	
}
show_array(dispbits,1,hl);

// show 10 at address 2
for(i=0;i<10;i++)
{
	p=mappings[i+20];
	if(p<37)
	{
		n=d[p];	
		hl[i]=bright[p];
	}
	else
	{
		n=100;
		hl[i]=0;
	}	if (n<100)
	{
		z=n/10;
		dispbits[i]=getsegs(z,n%10);
		if((n!=0)&&(z==0))
		dispbits[i]=dispbits[i]&0x0f0f;
	}
	else
		dispbits[i]=0;	
}
show_array(dispbits,2,hl);

// show 10 at address 3
for(i=0;i<10;i++)
{
	p=mappings[i+30];
	if(p<37)
	{
		n=d[p];	
		hl[i]=bright[p];
	}
	else
	{
		n=100;
		hl[i]=0;
	}	if (n<100)
	{
		z=n/10;
		dispbits[i]=getsegs(z,n%10);
		if((n!=0)&&(z==0))
		dispbits[i]=dispbits[i]&0x0f0f;
	}
	else
		dispbits[i]=0;	
}
show_array(dispbits,3,hl);

}

//
// receive from serial, returns 0 if no data, 0x01nn if nn has been received
//
unsigned int getbyte()
{
unsigned int rv;

	// check and clear error flag if set;
	if(rcsta.1)
	{
		rcsta = 0x80;			
		rcsta = 0x90;	
		rv=rcreg;
		rv=rcreg;
		return(0);
	}

	if(pir1.5)
	{
		rv=rcreg|0x0100;
		return(rv);
	}
	else
	{
		return(0);
	}

}


unsigned char firstday; // 1-37
unsigned char monthsize;
unsigned char entry;
unsigned char highlite; // 0=no highlight
unsigned char opcommand; // 0 = no test  1=count 2=alt all segs


// 0n decode:
// 
// 0	low byte
// 1	high byte
// 2	set first day
// 3	set month size
// 4	set highlight
// 5	do op  0=stop 1=blank 2=fill 3=count 4=seg test

void interrupt()
{
unsigned char command;
unsigned char d;
unsigned int rec;

rec=rcreg;
command=(rec>>4)&0xf;
d=rec&0x0f;

switch(command)
{
case 0:
	entry=(entry&0xf0)|d;
	break;
case 1:
	entry=(entry&0x0f)|(d<<4);
	break;
case 2:
	firstday=entry;
	break;
case 3:
	monthsize=entry;
	break;
case 4:
	highlite=entry;
	break;
case 5:
	opcommand=d;
	break;
}

	// check and clear error flag if set;
	if(rcsta.1)
	{
		rcsta = 0x80;			
		rcsta = 0x90;	
		d=rcreg;
		d=rcreg;
	}


}

//
// segment test   alternate segs
//
// show one segment
//
void segtest(unsigned char p)
{
unsigned int segs[10];
unsigned int v;
unsigned char i;
unsigned char b[10];

switch(p)
{
case 0:
		v=8194;
		break;
case 1:
		v=528;
		break;
case 2:
		v=4097;
		break;
case 3:
		v=34816;
		break;
case 4:
		v=68;
		break;
case 5:
		v=136;
		break;
case 6:
		v=17408;
		break;
case 7:
		v=288;
		break;
}


	
for(i=0;i<10;i++)
{
	segs[i]=v;
}
	
//no bright segs
for(i=0;i<10;i++)
	b[i]=0;

for(i=0;i<4;i++)
{
	show_array(segs,i,b);
}


}




//
// mainline loop
//
void main()
{
unsigned char i;
unsigned char c;
unsigned int dispbits[10];
unsigned int b;

unsigned char display[37]; // 0-99  blank if 0




// set port directions
trisa = 0x00;	// porta direction
trisb = 0x06;	// portb direction 
cmcon = 0x07;	// turn off comparitors
//clear_bit( option_reg, 7 ); // turn on portb pullups

// enable interrupts
//set_bit( intcon, T0IE ); //enable TMR0 overflow bit
set_bit( pie1, RCIE ); //enable TMR0 overflow bit
set_bit( intcon, PEIE ); // start interrupts
set_bit( intcon, GIE ); // start interrupts

// turn on serial receiver
spbrg = 25;		// 2400 BAUD
txsta = 0x20; 	// TXEN = 1, 8 bit, async
rcsta = 0x90;	// SPEN = 1 , CREN = 1


b=0;
c=0;
firstday=0;
monthsize=0;
entry=0;
highlite=0; 
opcommand=0; 

// clear dates
for(i=0;i<37;i++)
	display[i]=100;
showall(display);	

while( 1 )
{


switch(opcommand)
{
case 1: // blank
	for(i=0;i<37;i++)
		display[i]=100;
	showall(display);
	opcommand=0;	
	break;
case 3: // show address
	for(i=0;i<37;i++)
		display[i]=i+1;
	showall(display);	
	opcommand=0;
	break;
case 4: // seg	
	segtest(c);
	c++;
	if(c>7) c=0;
	 delay_ms(200);
	break;	
case 2: // fill dates
	for(i=0;i<37;i++)
		display[i]=100;
	for(i=0;i<monthsize;i++)
	{
		c=i+firstday;
		if(c<37)
			display[c]=i+1;
	}
	// handle highlight
	for(i=0;i<37;i++)
		bright[i]=0;
	i=highlite+firstday-1;
	if(i<37)
		bright[i]=1;
	// show month
	showall(display);
	opcommand=0;
	break;
default:
	break;
}


if(0)
{
display[0]=monthsize;
display[1]=firstday;
display[2]=highlite;
display[3]=opcommand;
showall(display);
}

// inc count


 	delay_ms(200);
   
} //endless while loop
} // program end
