/*
	Пример на управление на 3 1/2 разреден LCD дисплей OD-358
 */
#include <18f2525.h>	// 24576 flash, 3986 SRAM, 1024 EEPROM, Rev 0xA4
#DEVICE ICD=TRUE
#DEVICE HIGH_INTS=TRUE
#FUSES HS, NOWDT, NOPROTECT, NOBROWNOUT, PUT, NOLVP, NOWRT
#CASE
#define OSC_CLOCK 8000000
#USE Delay(Clock=OSC_CLOCK)
#USE FAST_IO(A)
#USE FAST_IO(B)
#USE FAST_IO(C)


typedef struct {
	char	fflashDigits;		// bit0 - Flashing Digit 0 are enabled
								// bit1 - Flashing Digit 1 are enabled
								// bit2 - Flashing Digit 2 are enabled
								// bit3 - Flashing Digit 3 are enabled
								// bit4 -
								// bit5 -
								// bit6 -
								// bit7 -
	char	cflashDigits;
	char	lcd[4];				// 4 байта видеопамет, lcd[3]=MSB, lcd[0]=LSB
	char	cflashLowBatt;		// брояч за мигане на символа _Triangle
	int		semicolon:1;		// флаг показване/скриване на двуеточието
	int		dp1:1;				// флаг показване/скриване на десетична точка 1
	int		dp2:1;				// флаг показване/скриване на десетична точка 2
	int		dp3:1;				// флаг показване/скриване на десетична точка 3
	int		lowbatt:1;			// флаг за показване на символа _Triangle
	int		fflashLowBatt:1; 	// флаг за мигане на символа _Triangle
	int		fflashTempSign:1;	// флаг за мигане на показвания аналогов канал
	int		fCelsius:1;			// флаг за показване в Целзий или Фаренхайт.
} Display;


Display display;
char	lcd[4];	// lcd[0] is LSB, lcd[3] is MSB

// Подредба на дисплейните символи в CharTable
enum Characters {
	char0 = 0, char1, char2, char3,
	char4, char5, char6, char7,
	char8, char9, charA, charB,
	charC, charD, charE, charF,
	charT, charH, charP, charL,
	UpLine, Minus, BotLine, Space,
	charU, charN, charO, charR,
	HiDegree, LowPulse, HiPulse, LoDegree = charO,
	charY, charF180 = 32
};

/* Подредба на сегментите на един разред от LCD дисплея,
   по битове в преместващите регистри:
	segB = Q1
	segA = Q2
	segC = Q2
	segD = Q4
	segE = Q5
	segF = Q6
	segG = Q7
	segH = Q8
*/

char const CharTable[33] = {
	0x3F, 0x05, 0x5B, 0x4F,	// characters 0,1,2 & 3
	0x65, 0x6E, 0x7E, 0x07,	// characters 4,5,6 & 7
	0x7F, 0x6F, 0x77, 0x7C,	// characters 8,9,A,B
	0x3A, 0x5D, 0x7A, 0x72,	// characters C,D,E,F
	0x78, 0x75, 0x73, 0x38,	// characters T,H,P,L
	0x02, 0x40, 0x08, 0x00,	// Upper line,Minus, Bottom line, Space
	0x1C, 0x54, 0x5C, 0x50,	// 'u', 'n', 'o', 'r'
	0x63, 0x1C, 0x23, 0x6D,	// HiDegree, LowPulse, HiPulse, charY
	0x4D					// charF180
};


#bit _Clock=PORTB.0
#bit _Data=PORTB.1
#bit _Strobe=PORTB.2
#bit _LowBatt=PORTB.3	// Low batt segment
#bit _BP=PORTB.4	// Back Plane


void Load4094(void) {
int8 i = 4 * 8; 	// Load 4 registers 4094
	do {
		_Data = shift_left( lcd, 4, 0 );
		_Clock = 1; _Clock = 0;
	} while( --i );
	_Strobe = 1; _Strobe = 0;
}


void ClearDisplay(void) {
	display.lcd[3] = display.lcd[2] = display.lcd[1] = display.lcd[0] = Space;
}

void UpdateDisplay(void)	{
int8 i;
	i = 0;
	do {
		lcd[i] = display.lcd[i] > 31 ? charE : CharTable[display.lcd[i]];
	} while( ++i != 4 );

	if( display.dp1 )
		lcd[0] |= 0x80;
	if( display.dp2 )
		lcd[1] |= 0x80;
	if( display.dp3 )
		lcd[2] |= 0x80;
	if( display.semicolon )
		lcd[3] |= 0x80;

	if( ! _BP ) {
		lcd[0] = ~lcd[0];
		if( display.fflashDigits & 0x10 )
			lcd[0] = 0xFF;

		lcd[1] = ~lcd[1];
		if( display.fflashDigits & 0x20 )
			lcd[1] = 0xFF;

		lcd[2] = ~lcd[2];
		if( display.fflashDigits & 0x40 )
			lcd[2] = 0xFF;

		lcd[3] = ~lcd[3];
		if( display.fflashDigits & 0x80 )
			lcd[3] = 0xFF;

		if( display.lowbatt )
			_LowBatt = 0;
		else
			_LowBatt = 1;
  	} else {
		if( display.fflashDigits & 0x10 )
			lcd[0] = 0x00;
		if( display.fflashDigits & 0x20  )
			lcd[1] = 0x00;
		if( display.fflashDigits & 0x40  )
			lcd[2] = 0x00;
		if( display.fflashDigits & 0x80  )
			lcd[3] = 0x00;

		if( display.lowbatt )
			_LowBatt = 1;
		else
			_LowBatt = 0;
	}
	Load4094();
	_BP = ! _BP;		// Toggle _BP
}

void	InitHardware(void)	{
	INTCON = 0; INTCON2 = 0; INTCON3 = 0;
	PIE1 = 0; PIE2 = 0;
	T0CON = 0; T1CON = 0; T2CON = 0; T3CON = 0;
	PORTA = 0; PORTB = 0; PORTC = 0;
	TRISA = 0b11011111; TRISB = 0b11100000; TRISC = 0b11011011;
	CMCON = 0x07;	// Comparators OFF
	CVRCON = 0x00;
	display.fCelsius = 1;
	ClearDisplay();
	_Clock = 0; _Strobe = 0;  _BP = 0;
	IPEN = 1;	// Set interrupt priority scheme
	INTCON = 0b11000000;				// Enable all unmasked interrupts
}

void main(void)	{
int i,j;
short time7ms8125;

	InitHardware();		// Инициализация на периферията на процесора

	time7ms8125 = 0;
	i=0;
	j=128;

	while( 1 ) {
	
		/* Прави се цикъл с някой от таймерите на 7,8125ms, което е 128Hz,
			но реално LCD-то ще работи на 64Hz. При достигане на цикъла на таймера
			се вдига time7ms8125 и се извиква UpdateDisplay(). В примера не правя
			такъв цикъл, така че по този начин програмата няма да заработи !
		 */
		if( time7ms8125 ) {	
			time7ms8125 = 0;
			UpdateDisplay();
			if( ! --j ) {
				/* През 1 секунда ще се визуализират на 4 разряда всички символи на CharTable */
				j = 128;
				display.lcd[0] = display.lcd[1] = display.lcd[2] = display.lcd[3] = i;
				if( ++i < sizeof(CharTable) )
					i = 0;
			}
		}		
	}

}
