Follow along with the video below to see how to install our site as a web app on your home screen.
Note: This feature may not be available in some browsers.
#include <90s8535.h>
#include <delay.h>
// led display
#define LED_RESET PORTB.0
#define LED_CLOCK PORTB.1
#define LED_DATA PORTB.4
#define SCROLL_DELAY 140 // multiple of 7 (use smaller numbers for faster scroll)
// keyboard
#define KBD_CLOCK PORTD.2 // to be used for host -> keyboard communication
#define KBD_DATA PIND.4
#define BUFF_SIZE 64
// lcd
#define LCD_RS PORTD.5
#define LCD_RW PORTD.6
#define LCD_E PORTD.7
#define COMMAND_MODE 0
#define DATA_MODE 1
#define WRITE_MODE 0
#define READ_MODE 1
#define LCD_DATA_PORT PORTC
// functions
void lcd_init(void);
void lcd_clear(void);
void lcd_putc(unsigned char);
void lcd_putsf(unsigned char flash *);
void lcd_puts(unsigned char *str);
interrupt [EXT_INT0] keyboard_isr(void);
void put_char_kbbuff(unsigned char);
void put_scancode_kbbuff(unsigned char);
int getchar(void);
int kbhit(void);
void decode(unsigned char);
// global variables ***************************
unsigned char kb_buffer[BUFF_SIZE];
unsigned char bitcount;
unsigned char *inpt, *outpt;
unsigned char buffcnt = 0;
// led character definitions
// 5 data columns + 1 space
// for each character
flash unsigned char led_chars[94][6] = {
0x00,0x00,0x00,0x00,0x00,0x00, // space
0x00,0x00,0xfa,0x00,0x00,0x00, // !
0x00,0xe0,0x00,0xe0,0x00,0x00, // "
0x28,0xfe,0x28,0xfe,0x28,0x00, // #
0x24,0x54,0xfe,0x54,0x48,0x00, // $
0xc4,0xc8,0x10,0x26,0x46,0x00, // %
0x6c,0x92,0xaa,0x44,0x0a,0x00, // &
0x00,0xa0,0xc0,0x00,0x00,0x00, // '
0x00,0x38,0x44,0x82,0x00,0x00, // (
0x00,0x82,0x44,0x38,0x00,0x00, // )
0x28,0x10,0x7c,0x10,0x28,0x00, // *
0x10,0x10,0x7c,0x10,0x10,0x00, // +
0x00,0x0a,0x0c,0x00,0x00,0x00, // ,
0x10,0x10,0x10,0x10,0x10,0x00, // -
0x00,0x06,0x06,0x00,0x00,0x00, // .
0x04,0x08,0x10,0x20,0x40,0x00, // /
0x7c,0x8a,0x92,0xa2,0x7c,0x00, // 0
0x00,0x42,0xfe,0x02,0x00,0x00, // 1
0x42,0x86,0x8a,0x92,0x62,0x00, // 2
0x84,0x82,0xa2,0xd2,0x8c,0x00, // 3
0x18,0x28,0x48,0xfe,0x08,0x00, // 4
0xe5,0xa2,0xa2,0xa2,0x9c,0x00, // 5
0x3c,0x52,0x92,0x92,0x0c,0x00, // 6
0x80,0x8e,0x90,0xa0,0xc0,0x00, // 7
0x6c,0x92,0x92,0x92,0x6c,0x00, // 8
0x60,0x92,0x92,0x94,0x78,0x00, // 9
0x00,0x6c,0x6c,0x00,0x00,0x00, // :
0x00,0x6a,0x6c,0x00,0x00,0x00, // ;
0x10,0x28,0x44,0x82,0x00,0x00, // <
0x28,0x28,0x28,0x28,0x28,0x00, // =
0x00,0x82,0x44,0x28,0x10,0x00, // >
0x40,0x80,0x8a,0x90,0x60,0x00, // ?
0x4c,0x92,0x9e,0x82,0x7c,0x00, // @
0x7e,0x88,0x88,0x88,0x7e,0x00, // A
0xfe,0x92,0x92,0x92,0x6c,0x00, // B
0x7c,0x82,0x82,0x82,0x44,0x00, // C
0xfe,0x82,0x82,0x44,0x38,0x00, // D
0xfe,0x92,0x92,0x92,0x82,0x00, // E
0xfe,0x90,0x90,0x90,0x80,0x00, // F
0x7c,0x82,0x92,0x92,0x5e,0x00, // G
0xfe,0x10,0x10,0x10,0xfe,0x00, // H
0x00,0x82,0xfe,0x82,0x00,0x00, // I
0x04,0x02,0x82,0xfc,0x80,0x00, // J
0xfe,0x10,0x28,0x44,0x82,0x00, // K
0xfe,0x02,0x02,0x02,0x02,0x00, // L
0xfe,0x40,0x30,0x40,0xfe,0x00, // M
0xfe,0x20,0x10,0x08,0xfe,0x00, // N
0x7c,0x82,0x82,0x82,0x7c,0x00, // O
0xfe,0x90,0x90,0x90,0x60,0x00, // P
0x7c,0x82,0x8a,0x84,0x7a,0x00, // Q
0xfe,0x90,0x98,0x94,0x62,0x00, // R
0x62,0x92,0x92,0x92,0x8c,0x00, // S
0x80,0x80,0xfe,0x80,0x80,0x00, // T
0xfc,0x02,0x02,0x02,0xfc,0x00, // U
0xf8,0x04,0x02,0x04,0xf8,0x00, // V
0xfc,0x02,0x1c,0x02,0xfc,0x00, // W
0xc6,0x28,0x10,0x28,0xc6,0x00, // X
0xe0,0x10,0x0e,0x10,0xe0,0x00, // Y
0x86,0x8b,0x92,0xa2,0xc2,0x00, // Z
0x00,0xfe,0x82,0x82,0x00,0x00, // [
0x00,0x00,0x00,0x00,0x00,0x00, // *** do not remove this empty char ***
0x00,0x82,0x82,0xfe,0x00,0x00, // ]
0x20,0x40,0x80,0x40,0x20,0x00, // ^
0x02,0x02,0x02,0x02,0x02,0x00, // _
0x00,0x80,0x40,0x20,0x00,0x00, // `
0x04,0x2a,0x2a,0x2a,0x1e,0x00, // a
0xfe,0x12,0x22,0x22,0x1c,0x00, // b
0x1c,0x22,0x22,0x22,0x04,0x00, // c
0x1c,0x22,0x22,0x12,0xfe,0x00, // d
0x1c,0x2a,0x2a,0x2a,0x18,0x00, // e
0x10,0x7e,0x90,0x80,0x40,0x00, // f
0x30,0x4a,0x4a,0x4a,0x7c,0x00, // g
0xfe,0x10,0x20,0x20,0x1e,0x00, // h
0x00,0x22,0xbe,0x02,0x00,0x00, // i
0x04,0x02,0x22,0xbc,0x00,0x00, // j
0xfe,0x08,0x14,0x22,0x00,0x00, // k
0x00,0x82,0xfe,0x02,0x00,0x00, // l
0x3e,0x20,0x18,0x20,0x1e,0x00, // m
0x3e,0x10,0x20,0x20,0x1e,0x00, // n
0x1c,0x22,0x22,0x22,0x1c,0x00, // o
0x3e,0x28,0x28,0x28,0x10,0x00, // p
0x10,0x28,0x28,0x18,0x3e,0x00, // q
0x3e,0x10,0x20,0x20,0x10,0x00, // r
0x12,0x2a,0x2a,0x2a,0x04,0x00, // s
0x20,0xfc,0x22,0x02,0x04,0x00, // t
0x3c,0x02,0x02,0x04,0x3e,0x00, // u
0x38,0x04,0x02,0x04,0x38,0x00, // v
0x3c,0x02,0x0c,0x02,0x3c,0x00, // w
0x22,0x14,0x08,0x14,0x22,0x00, // x
0x30,0x0a,0x0a,0x0a,0x3c,0x00, // y
0x22,0x26,0x2a,0x32,0x22,0x00, // z
0x00,0x10,0x6c,0x82,0x00,0x00, // {
0x00,0x00,0xfe,0x00,0x00,0x00, // |
0x00,0x82,0x6c,0x10,0x00,0x00 }; // }
// keyboard scan codes (without & with shift key pressed)
flash unsigned char unshifted[67][2] = {
//0x0d,9,
0x0e,'`',0x15,'q',0x16,'1',0x1a,'z',0x1b,'s',0x1c,'a',0x1d,'w',0x1e,'2',0x21,'c',0x22,'x',0x23,'d',0x24,'e',
0x25,'4',0x26,'3',0x29,' ',0x2a,'v',0x2b,'f',0x2c,'t',0x2d,'r',0x2e,'5',0x31,'n',0x32,'b',0x33,'h',0x34,'g',
0x35,'y',0x36,'6',0x39,',',0x3a,'m',0x3b,'j',0x3c,'u',0x3d,'7',0x3e,'8',0x41,',',0x42,'k',0x43,'i',0x44,'o',
0x45,'0',0x46,'9',0x49,'.',0x4a,'/',0x4b,'l',0x4c,';',0x4d,'p',0x4e,'-',0x52,'`',0x54,'[',0x55,'=',0x5a,13,
0x5b,']',0x5d,'/',0x61,'<',0x66,8, 0x69,'1',0x6b,'4',0x6c,'7',0x70,'0',0x71,',',0x72,'2',0x73,'5',0x74,'6',
0x75,'8',0x79,'+',0x7a,'3',0x7b,'-',0x7c,'*',0x7d,'9',0,0 };
flash unsigned char shifted[67][2] = {
//0x0d,9,
0x0e,'`',0x15,'Q',0x16,'!',0x1a,'Z',0x1b,'S',0x1c,'A',0x1d,'W',0x1e,'@',0x21,'C',0x22,'X',0x23,'D',0x24,'E',
0x25,'$',0x26,'#',0x29,' ',0x2a,'V',0x2b,'F',0x2c,'T',0x2d,'R',0x2e,'%',0x31,'N',0x32,'B',0x33,'H',0x34,'G',
0x35,'Y',0x36,'^',0x39,'L',0x3a,'M',0x3b,'J',0x3c,'U',0x3d,'&',0x3e,'*',0x41,'<',0x42,'K',0x43,'I',0x44,'O',
0x45,')',0x46,'(',0x49,'>',0x4a,'?',0x4b,'L',0x4c,':',0x4d,'P',0x4e,'_',0x52,'"',0x54,'{',0x55,'+',0x5a,13,
0x5b,'}',0x5d,'|',0x61,'>',0x66,8, 0x69,'1',0x6b,'4',0x6c,'7',0x70,'0',0x71,',',0x72,'2',0x73,'5',0x74,'6',
0x75,'8',0x79,'+',0x7a,'3',0x7b,'-',0x7c,'*',0x7d,'9',0,0 };
//***********************************************
void main() {
int i,j;
unsigned char ch,row;
flash unsigned char *dataPtr, *tmpDataPtr;
unsigned char messageLength, currentChar, charOffset, tmpCurrentChar, tmpCharOffset, ledArray[23];
unsigned char message[308]; // 300 chars + 4 spaces at start and end of message
#asm("cli"); // disable all interrupts
// initialise ports
DDRA = 0xff; // port A output
DDRB = 0xff; // port B output
DDRC = 0xff; // port C output
DDRD = 0xeb; // pins 2 & 4 inputs
// initialise keyboard variables
inpt = kb_buffer;
outpt = kb_buffer;
buffcnt = 0;
bitcount = 11;
// initialise lcd
lcd_init();
// initialise LED display (shift registers)
LED_RESET = 0;
LED_CLOCK = 0;
LED_RESET = 1;
// initialise interrupt registers
GIMSK = 0x40; // enable INT0 interrupt
MCUCR = 0x02; // INT0 interrupt on falling edge
#asm("sei"); // enable all interrupts
while(1) {
ch = 0x00;
i = 0;
// fill the array with 4 empty spaces at the start and the end of the message
// as the screen has 4 chars
// (it looks better if we start scrolling with empty screen and end
// with empty screen , rather than displaying four chars immediatelly and
// then start scrolling)
message[i++] = ' ';
message[i++] = ' ';
message[i++] = ' ';
message[i++] = ' ';
// enter message until 300 characters entered or <enter> key pressed
while((ch != 13) || (i < 308)) {
ch = getchar();
if ((ch != 13) && (ch != 'q')) {
lcd_putc(ch);
message[i++] = ch;
}
// if q is pressed clear the LCD
// (code only writes to the first LCD line so
// if the line is longer than 16 chars i cant see the rest of the chars
// , there will be a menu system in the future software version
// and the line on the LCD will be scrolled
// to the left automatically as the cursor gets to the end of the line
//, for now use this dirty solution:))
if (ch == 'q')
lcd_clear();
}
message[i++] = ' ';
message[i++] = ' ';
message[i++] = ' ';
message[i++] = ' ';
messageLength = i - 4;
charOffset = 0;
currentChar = 0;
dataPtr = &led_chars[message[0] - 0x20][0];
// this loop is the heart of the scroll
// message is scrolled until a key is hit on the keyboard
while (!kbhit()) {
// load led array
tmpDataPtr = dataPtr;
tmpCharOffset = charOffset;
tmpCurrentChar = currentChar;
for (i = 0; i <= 22; i++) {
ledArray[i] = *tmpDataPtr++;
if (++tmpCharOffset==0x06) {
tmpCharOffset = 0;
if (++tmpCurrentChar == messageLength)
tmpCurrentChar = 0;
tmpDataPtr = &led_chars[message[tmpCurrentChar] - 0x20][0];
}
}
if (++charOffset == 0x06) {
charOffset = 0;
if (++currentChar == messageLength)
currentChar = 0;
}
dataPtr = &led_chars[message[currentChar] - 0x20][charOffset];
// display led array
row = 0x02;
for (j = 0;j <= SCROLL_DELAY; j++) {
for (i = 0; i <= 22; i++) {
LED_DATA = (ledArray[i] & row) ? 1 : 0;
LED_CLOCK = 1;
LED_CLOCK = 0;
}
PORTA = row;
row <<= 1; // next line to be switched on
if (!row) // if last line go back to the first line
row = 0x02;
delay_us(800);
PORTA = 0x00;
}
}
lcd_clear();
}
}
//***********************************************
// return 1 if a key is pressed (non blocking)
// else return 0
int kbhit(void) {
if (buffcnt) {
// reset buffer variables (flush the buffer)
inpt = kb_buffer;
outpt = kb_buffer;
buffcnt = 0;
bitcount = 11;
return 1;
}
return 0;
}
// puts scan code (in hex format) into keyboard buffer
// (used for debugging purposes)
//***********************************************
void put_scancode_kbbuff(unsigned char sc) {
unsigned char h,l;
// convert hi and low nibbles of the scancode
// into ascii and store them into keyboard buffer
h = ((sc & 0xf0 ) >> 0x04) & 0x0f;
if ( h > 9)
h = h + 7;
h = h + 0x30;
put_char_kbbuff(h);
l = sc & 0x0f;
if ( l > 9)
l = l + 7;
l = l + 0x30;
put_char_kbbuff(l);
}
//***********************************************
// keyboard ISR
interrupt [EXT_INT0] keyboard_isr(void) {
static unsigned char data; // holds the received scan code
// function entered at falling edge of the kbd clock signal
// if data bit is the next bit to be read
// (bit 3 to 10 is data, start, stop & parity bis are ignored
if((bitcount < 11) && (bitcount > 2)) {
data = (data >> 1);
if (KBD_DATA) // if next bit is 1
data = data | 0x80; // store a '1'
else
data = data & 0x7f; // else store a '0'
}
if(--bitcount == 0) { // all bits received ?
decode(data); // decode received byte
bitcount = 11; // reset bit counter
}
}
//***********************************************
// decode scan code
void decode(unsigned char sc) {
static unsigned char is_up=0, shift = 0, mode = 0;
unsigned char i;
if (!is_up) {
switch (sc) {
case 0xF0 :// The up-key identifier
is_up = 1;
break;
case 0x12 :// Left SHIFT
shift = 1;
break;
case 0x59 :// Right SHIFT
shift = 1;
break;
case 0x05 :// F1
if(mode == 0)
mode = 1;// Enter scan code mode
if(mode == 2)
mode = 3;// Leave scan code mode
break;
default:
if(mode == 0 || mode == 3) {// If ASCII mode
if(!shift) {// If shift not pressed, do a table look-up
for(i = 0; unshifted[i][0]!=sc && unshifted[i][0]; i++);
if (unshifted[i][0] == sc) {
put_char_kbbuff(unshifted[i][1]);
}
}
else {// If shift pressed
for(i = 0; shifted[i][0]!=sc && shifted[i][0]; i++);
if (shifted[i][0] == sc) {
put_char_kbbuff(shifted[i][1]);
}
}
}
else put_scancode_kbbuff(sc); // scan code mode (debugging mode)
break;
}
}
else {
is_up = 0;// Two 0xF0 in a row not allowed
switch (sc) {
case 0x12 :// Left SHIFT
shift = 0;
break;
case 0x59 :// Right SHIFT
shift = 0;
break;
case 0x05 :// F1 -- F1 puts you in debugging mode
// pressing F1 again gets you out of debugging mode
// in debugging mode hex code of the scan codes
// are stored in the buffer instead of their ascii codes
if(mode == 1)
mode = 2;
if(mode == 3)
mode = 0;
break;
}
}
}
//***********************************************
// store character in the keyboard ring buffer
void put_char_kbbuff(unsigned char c) {
if (buffcnt < BUFF_SIZE) { // if buffer is not full
*(inpt++) = c;
buffcnt++;
if (inpt >= kb_buffer + BUFF_SIZE) // pointer wrapping
inpt = kb_buffer;
}
}
//***********************************************
// get next available character from the keyboard ring buffer
// (waits until a character is available in the buffer)
int getchar(void) {
int byte;
while (buffcnt == 0); // wait for data
byte = *outpt; // get byte
outpt++;
if (outpt >= kb_buffer + BUFF_SIZE) // pointer wrapping
outpt = kb_buffer;
buffcnt--; // decrement buffer count
return byte;
}
//***********************************************
void lcd_init(void) {
delay_ms(50);
LCD_E = 0;
LCD_RS = COMMAND_MODE;
LCD_RW = WRITE_MODE;
delay_ms(1);
LCD_DATA_PORT = 0x38; // 8 bit, 2 lines, 5x8 font
LCD_E = 1; delay_ms(1);
LCD_E = 0; delay_ms(1);
LCD_DATA_PORT = 0x0f; // display & cursor ON, blink & underlined
LCD_E = 1; delay_ms(1);
LCD_E = 0; delay_ms(2);
lcd_clear();
}
//***********************************************
void lcd_clear(void) {
LCD_RS = COMMAND_MODE;
LCD_DATA_PORT = 0x01; // clear display
LCD_E = 1; delay_ms(1);
LCD_E = 0; delay_ms(2);
}
//***********************************************
// display char on lcd
void lcd_putc(unsigned char ch) {
LCD_RS = DATA_MODE;
LCD_DATA_PORT = ch;
LCD_E = 1; delay_us(40);
LCD_E = 0; delay_us(40);
}
//***********************************************
// display string on lcd
// input: pointer to string stored in flash ROM
void lcd_putsf(unsigned char flash *str) {
while (*str!= '\0') {
lcd_putc(*(str++));
}
}
//************************************************
// same as above except the string is in RAM
void lcd_puts(unsigned char *str) {
while (*str!= '\0') {
lcd_putc(*(str++));
}
}
:cry::cry:
/***********************************************
// display string on lcd
// input: pointer to string stored in flash ROM
void lcd_putsf(unsigned char flash *str) {
while (*str!= '\0') {
lcd_putc(*(str++));
}
}
//************************************************
// same as above except the string is in RAM
void lcd_puts(unsigned char *str) {
while (*str!= '\0') {
lcd_putc(*(str++));
}
} [code]