/*
DISPLAY.C drives three kinds of displays:
1. LCD is HD44780-based 2x16 character display LM-11 in 4 bit mode
connected to port P5
2. ICM is ICM7218A 8 Digit LED Display Driver memory mapped at 0x8000
with 8 LEDs for status display instead of D.P. segment.
The display is broken into two 4 digit integer displays icm1 and icm2
3. Static serial display with four 3 1/2 (-188.8) displays with sign
consists of twelve 74HC595 shift registers with each segment
connected via resistor to 595. Decimal point is fixed.
f_osc = 11.059 MHz -> 1 machine cycle = 12/f_osc = 1.085us
*/
#include <reg515.h>
#include <intrins.h>
#include "display.h"
/* LCD display pin definition */
sbit LCD_E = 0xFB; /* P5.3 enable LCD */
sbit LCD_RW = 0xFA; /* P5.2 Read/Write LCD */
sbit LCD_RS = 0xF9; /* P5.1 Data/Instruction LCD */
sbit BUSY = 0xFF; /* P5.7 LCD Busy bit 7 */
sfr LCD_PORT=0xF8; /* P5 port high nibbles P5.4-P5.7 */
sbit IR7 = 0xFF; /* P5.7 */
sbit IR6 = 0xFE; /* P5.6 */
sbit IR5 = 0xFD; /* P5.5 */
sbit IR4 = 0xFC; /* P5.4 */
bit
lcd_busy()
{
bit busy;
IR7 = IR6 = IR5 = IR4 = 1; /* Prepare for read */
LCD_RS = 0; /* Instruction */
LCD_RW = 1; /* Read DISPLAY */
LCD_E = 1; /* enable LCD */
busy = BUSY;
LCD_E = 0;
LCD_E = 1;
LCD_E = 0;
return busy;
}
/* if RS == 0 then write instruction else write character to LCD */
void
lcd_send(bit RS, unsigned char c)
{
unsigned int count = 0;
while (lcd_busy())
{
count++;
if (count == 10000)
{
GATE = 1;
icm_error(01); /* LCD failure - short circuit */
while (1)
{
WDT = 1; /* ensure WDT is reset */
SWDT = 1;
}
}
}
LCD_RS = RS; /* Data/Instruction select */
LCD_RW = 0; /* set write */
LCD_PORT = (c & 0xF0) | (LCD_PORT & 0x0F); /* high nibble to the LCD */
LCD_E = 1; /* enable LCD */
LCD_E = 0; /* write to LCD */
LCD_PORT = (c << 4 & 0xF0) | (LCD_PORT &0x0F);
LCD_E = 1;
LCD_E = 0;
}
void
lcd_cgram_caron_init()
{
char i;
char code *caron =
"\x0a\x04\x0e\x10\x10\x11\x0e\x00" /* 1. c caron */
"\x0a\x04\x0e\x10\x0e\x01\x1e\x00" /* 2. s caron */
"\x0a\x04\x1f\x02\x04\x08\x1f\x00" /* 3. z caron */
"\x0a\x0e\x11\x10\x10\x11\x0e\x00" /* 4. C caron */
"\x0a\x0e\x11\x0e\x01\x11\x0e\x00" /* 5. S caron */
"\x0a\x1f\x02\x04\x08\x10\x1f\x00" /* 6. Z caron */
"\x00\x0e\x15\x1f\x15\x0e\x00\x00" /* 7. Selected */
"\x00\x0e\x11\x11\x11\x0e\x00\x00" /* 7. DeSelected */
;
lcd_send(0, 0x48); /* set CGRAM start address */
for (i = 0; i < 8*8; i++)
lcd_send(1, caron[i]);
}
void
lcd_cgram_symbols_init()
{
char i;
char code *symbols =
"\x00\x00\x1f\x1d\x05\x07\x00\x00" /* Burner */
"\x00\x0e\x04\x1e\x1f\x03\x00\x02" /* Water circulation */
"\x1f\x01\x1d\x05\x1d\x15\x11\x1f" /* Under floor heating */
"\x04\x15\x0e\x1b\x0e\x15\x04\x00" /* Sun */
"\x0e\x0b\x0a\x0a\x0a\x1a\x0e\x00" /* Solar Tank */
"\x02\x1d\x02\x00\x02\x1f\x02\x00" /* Preferences */
;
lcd_send(0, 0x48); /* set CGRAM start address */
for (i = 0; i < 6*8; i++)
lcd_send(1, symbols[i]);
}
/* Initialise LCD in 4 bit without cursor display */
void lcd_init(void)
{
unsigned int i;
LCD_E = 0; /* disable LCD */
LCD_RW = 0; /* we will Write */
LCD_RS = 0; /* instructions */
LCD_PORT = 0x30 | (LCD_PORT & 0x0f);
for (i = 1800; i > 0; i--); /* wait 15 ms or more after poweron*/
/* i*8+i/256+4 cycles */
LCD_E=1; LCD_E=0;
for (i = 600; i > 0; i--); /* wait 4.1 ms or more */
LCD_E=1; LCD_E=0;
for (i = 19; i > 0; i--); /* wait 160 us or more */
LCD_E=1; LCD_E=0;
LCD_PORT = 0x20 | (LCD_PORT & 0x0f);
LCD_E=1; LCD_E=0;
lcd_send(0, 0x28); /* set interface length */
lcd_send(0, 0x08); /* turn OFF */
lcd_send(0, 0x01); /* clear display */
lcd_send(0, 0x06); /* Enty mode set, Set cursor move direction */
lcd_send(0, 0x0C); /* Enable display without cursor and blink*/
lcd_cgram_symbols_init();
}
void
lcd_print(char code *message)
{
unsigned char i = 0;
lcd_send(0, 0x01); /* clear display */
while(message[i])
{
if (message[i] == '\n')
{
lcd_send(0, 0x80+0x40); /* new line */
i++;
continue;
}
lcd_send(1, message[i++]);
}
}
void
lcd_princ(unsigned char position, char code *message)
{
unsigned char i = 0;
lcd_send(0, 0x80|position); /* go to the left position */
while (message[i])
{
if (message[i] == '\n')
{
lcd_send(0, 0x80+0x40); /* new line */
i++;
continue;
}
lcd_send(1, message[i++]);
}
}
void
lcd_print_ulong(unsigned char position, unsigned long value)
{
char i = 8;
lcd_send(0, 0x80|position); /* go to rightmost digit*/
lcd_send(0, 0x04); /* left move */
while(i--)
{
lcd_send(1, (value % 10)+'0');
value /= 10;
}
lcd_send(0, 0x06); /* back to right move */
}
lcd_print_hms(unsigned char position, unsigned long value)
{
lcd_send(0, 0x80|position); /* go to rightmost digit*/
lcd_send(0, 0x04); /* left move */
lcd_send(1, (value % 10)+'0'); /* seconds */
value /= 10;
lcd_send(1, (value % 6)+'0');
value /= 6;
lcd_send(1, ':');
lcd_send(1, (value % 10)+'0'); /* minutes */
value /= 10;
lcd_send(1, (value % 6)+'0');
value /= 6;
lcd_send(1, ':');
do
{
lcd_send(1, (value % 10)+'0');
value /= 10;
}
while(value);
lcd_send(0, 0x06); /* back to right move */
}
void
lcd_print_uchar(unsigned char position, unsigned long value)
{
char i = 3;
lcd_send(0, 0x80|position); /* go to rightmost digit*/
lcd_send(0, 0x04); /* left move */
while(i--)
{
lcd_send(1, (value % 10) + '0');
value /= 10;
}
lcd_send(0, 0x06); /* back to right move */
}
void
lcd_print_temp(unsigned char position_right, int value)
{
char i = 4;
lcd_send(0, 0x80|position_right); /* go to rightmost digit*/
lcd_send(0, 0x04); /* left move */
lcd_send(1, 'C');
lcd_send(1, 0xDF);
while(i--)
{
lcd_send(1, (value % 10) + '0');
if (i == 2)
lcd_send(1, 0x2E);
value /= 10;
}
lcd_send(0, 0x06); /* back to right move */
}
void
lcd_print_time_of_day(unsigned char time_of_day)
{
if (time_of_day > 239)
{
lcd_princ(0x43, "Izklju\01eno");
}
else
{
unsigned char minutes = (time_of_day % 10) * 6;
lcd_princ(0x43, " ");
lcd_send(0, 0xc8); /* go to rightmost digit*/
lcd_send(0, 0x04); /* left move */
lcd_send(1, (minutes % 10) + '0');
minutes /= 10;
lcd_send(1, minutes + '0');
time_of_day /= 10;
lcd_send(1, ':');
lcd_send(1, (time_of_day % 10)+'0');
time_of_day /= 10;
lcd_send(1, time_of_day+'0');
lcd_send(0, 0x06); /* back to right move */
}
}
/*------------------------ ICM7218A section --------------------------*/
/* MODE is on A0 pin, ID is on DATA bus, WR\ in on A15\ pin */
#define icm_data *((unsigned char volatile xdata *)0x8000)
#define icm_ctrl *((unsigned char volatile xdata *)0x8001)
/* ICM mode control masks */
#define ICM_NORMAL 0x10
#define ICM_NO_DECODE 0x20
#define ICM_HEX 0x40
#define ICM_DATA_COMING 0x80
static unsigned char _leds;
void
icm1(unsigned int i) /* first line */
{
char digit;
lcd_print_temp(0x06, i);
for (digit = 3; digit >=0 ; digit--)
{
icm_ctrl = ICM_NORMAL | digit;
icm_data = i % 10 | ( _leds & 1 << digit ? 0x00 : 0x80);
i /= 10;
}
}
void
icm2(unsigned int i) /* second line of the four digits */
{
char digit;
lcd_print_temp(0x46, i);
for (digit = 7; digit >=4 ; digit--)
{
icm_ctrl = ICM_NORMAL | digit;
icm_data = i % 10 | ( _leds & 1 << digit ? 0x00 : 0x80);
i /= 10;
}
}
/* Writes error number message to ICM display */
void
icm_error(unsigned char number)
{
#define SEG_A 0x40 /* aaaa */
#define SEG_B 0x20 /* f b */
#define SEG_C 0x10 /* f b */
#define SEG_D 0x01 /* gggg */
#define SEG_E 0x08 /* e c */
#define SEG_F 0x02 /* e c */
#define SEG_G 0x04 /* dddd */
#define LED(i) (leds & 1 << i ? 0x00 : 0x80)
icm_ctrl = ICM_NORMAL | ICM_NO_DECODE | ICM_DATA_COMING;
icm_data = SEG_A | SEG_D | SEG_E | SEG_F | SEG_G | LED(0); /* E */
icm_data = SEG_E | SEG_G | LED(1); /* r */
icm_data = SEG_C | SEG_D | SEG_E | SEG_G | LED(2); /* o */
icm_data = SEG_E | SEG_G | LED(3); /* r */
icm_data = SEG_C | SEG_E | SEG_G | LED(4); /* n */
icm_data = SEG_C | SEG_D | SEG_E | SEG_G | LED(5); /* o */
icm_ctrl = ICM_NORMAL | 6;
icm_data = (number / 10 ? number / 10 : 0xFF) | LED(6);
icm_ctrl = ICM_NORMAL | 7 ;
icm_data = (number % 10) | LED(7);
}
void
icm_blank(void)
{
icm_ctrl = ICM_NORMAL | ICM_NO_DECODE | ICM_DATA_COMING;
icm_data = LED(0);
icm_data = LED(1);
icm_data = LED(2);
icm_data = LED(3);
icm_data = LED(4);
icm_data = LED(5);
icm_data = LED(6);
icm_data = LED(7);
icm_ctrl = ICM_NORMAL;
}
/* Call icm1() or icm2() for leds update! */
void
set_leds(unsigned char leds)
{
_leds = leds;
}
/* Writes tests to ICM display */
void
icm_test(unsigned char number)
{
switch (number)
{
case 0:
icm_ctrl = ICM_NORMAL | ICM_NO_DECODE | ICM_DATA_COMING;
icm_data = 0x00; icm_data = 0x00;
icm_data = 0x00; icm_data = 0x00;
icm_data = 0x00; icm_data = 0x00;
icm_data = 0x00; icm_data = 0x00;
icm_ctrl = ICM_NORMAL;
break;
case 1:
icm_ctrl = ICM_NORMAL | ICM_NO_DECODE | ICM_DATA_COMING;
icm_data = 0xFF; icm_data = 0xFF;
icm_data = 0xFF; icm_data = 0xFF;
icm_data = 0xFF; icm_data = 0xFF;
icm_data = 0xFF; icm_data = 0xFF;
icm_ctrl = ICM_NORMAL;
break;
default:
icm_ctrl = ICM_NORMAL | ICM_NO_DECODE | ICM_DATA_COMING;
icm_data = SEG_A | SEG_D | SEG_E | SEG_F | SEG_G | LED(0); /* E */
icm_data = SEG_E | SEG_G | LED(1); /* r */
icm_data = SEG_C | SEG_D | SEG_E | SEG_G | LED(2); /* o */
icm_data = SEG_E | SEG_G | LED(3); /* r */
icm_data = SEG_C | SEG_E | SEG_G | LED(4); /* n */
icm_data = SEG_C | SEG_D | SEG_E | SEG_G | LED(5); /* o */
icm_ctrl = ICM_NORMAL | 6;
icm_data = (number / 10 ? number / 10 : 0xFF) | LED(6);
icm_ctrl = ICM_NORMAL | 7 ;
icm_data = (number % 10) | LED(7);
icm_ctrl = ICM_NORMAL;
}
}
/* ------------------ static serial display --------------------- */
sbit SI = 0xEC; /* P4.4 negated serial input */
sbit SCK = 0xED; /* P4.5 negated serial clock */
sbit RCK = 0xEE; /* P4.6 negated reload clock */
static signed int idata display_data[4]; /* pomnozeno s 100 */
static void
send_digit(unsigned char segment_data)
{
unsigned char i;
for(i = 0; i < 8; i++)
{
SCK = 1;
SI = segment_data & 0x80;
SCK = 0;
segment_data = segment_data << 1;
}
}
/* Sends display_data via serial protocol to HC595 shits registers
If blanking set then display is cleared! */
void
update_serial_display(bit blanking)
{
unsigned char code segment[10] =
{ 0xEE, 0xC0, 0xB6, 0xF4, 0xD8,
0x7C, 0x7E, 0xE0, 0xFE, 0xFC};
unsigned char num; /* row counter */
unsigned char buffer[3]; /* segment buffer */
int value; /* current display_data */
bit negative; /* negative sign */
RCK = 1;
for ( num = 0; num < 4; num ++)
{
if (blanking)
{
buffer[0] = buffer[1] = buffer[2] = 0;
}
else
{
value = display_data[3 - num];
if ( value < 0)
{
negative = 1;
value = -value;
}
else
negative = 0;
value /= 10;
buffer[2] = segment[value % 10] | negative;
value /= 10;
buffer[1] = segment[value % 10];
value /= 10;
buffer[0] = segment[value % 10];
if ( value > 9 ) /* hundrets */
{
buffer[1] |= 1;
}
else
{
if (value == 0)
buffer[0] = negative; /* leading zero suppresion */
}
}
send_digit(buffer[0]);
send_digit(buffer[1]);
send_digit(buffer[2]);
}
RCK = 0;
}
/* The last digit is missing! To display a value multiply ii with 10
e.g. -12345 will be shown as -123.4 (no rounding is performed!)
*/
void
set_serial_display(unsigned char display_number, signed int value)
{
display_data[display_number] = value;
}
#if 0
signed int
get_serial_display(unsigned char display_number)
{
return display_data[display_number];
}
#endif
/* Generic part */
void
halt(unsigned char errno, char code *message)
{
GATE = 1; /* disable all output */
icm_error(errno);
if (errno != 0)
{
/* lcd_init(); */
lcd_princ(0x00, message);
}
while(1)
{
unsigned int i;
for ( i = 10000; i > 0; i--) /* must wait*/
{
WDT = 1; /* ensure WDT is reset */
SWDT = 1;
}
leds++;
icm_error(errno); /* for leds refresh */
}
}