--- /dev/null
+/*
+Firmware for wireless S88 receiver module
+
+S88 pinout:
+1 - DATA
+2 - GND
+3 - CLOCK
+4 - LOAD
+5 - RESET
+6 - POWER
+
+ATMega pinout:
+D0 - serial RX
+D1 - serial TX
+D2 - S88 DATA
+D3 - S88 CLOCK
+D4 - S88 LOAD
+D5 - S88 RESET
+*/
+
+#include <avr/io.h>
+#include <avr/interrupt.h>
+#include "lcd.h"
+#include "serial.h"
+#include "delay.h"
+
+#define DATA_OUT PORTD2
+#define CLOCK PIND3
+#define LOAD PIND4
+#define RESET PIND5
+
+#define LCD_DISABLE PINB4
+
+#define BIT(n) (1<<(n))
+
+void receive(uint8_t);
+uint8_t hexdigit(uint8_t);
+uint8_t decode_hex(uint8_t);
+
+volatile uint8_t rx_buf[7];
+volatile uint8_t rx_fill = 0xFF;
+volatile uint8_t input[128] = { 0 };
+volatile uint8_t latch[128] = { 0 };
+volatile uint8_t lcd_enabled = 0;
+uint8_t log_row = 0;
+uint8_t log_col = 0;
+
+int main()
+{
+ uint8_t clock_high = 0;
+ uint8_t bits = 0;
+ uint8_t n_bits = 8;
+ uint8_t offset = 0;
+ uint8_t i;
+
+ DDRD = 0x06; // 00000110
+ PORTD = 0xC0; // 11000000
+ DDRB = 0x20; // 00100000
+ PORTB = 0x1F; // 00011111
+
+ serial_init(9600);
+ lcd_init();
+
+ sei();
+
+ while(1)
+ {
+ uint8_t b_pins, d_pins;
+
+ b_pins = PINB;
+ d_pins = PIND;
+
+ if(d_pins&BIT(CLOCK))
+ {
+ if(!clock_high)
+ {
+ if(d_pins&BIT(LOAD))
+ {
+ offset = 0;
+ bits = latch[0];
+ n_bits = 8;
+ }
+ else
+ {
+ bits >>= 1;
+ if(!--n_bits)
+ {
+ ++offset;
+ bits = latch[offset];
+ n_bits = 8;
+ }
+ }
+
+ if(bits&1)
+ PORTD |= BIT(DATA_OUT);
+ else
+ PORTD &= ~BIT(DATA_OUT);
+
+ clock_high = 1;
+ }
+ }
+ else if(clock_high)
+ clock_high = 0;
+
+ if(d_pins&BIT(RESET))
+ {
+ uint8_t i;
+ for(i=0; i<128; ++i)
+ latch[i] = input[i];
+ }
+
+ if(b_pins&BIT(LCD_DISABLE))
+ {
+ if(lcd_enabled)
+ {
+ lcd_enabled = 0;
+
+ lcd_clear();
+ }
+ }
+ else if(!lcd_enabled)
+ {
+ lcd_enabled = 1;
+ log_row = 0;
+ log_col = 0;
+
+ lcd_clear();
+ for(i=0; i<20; ++i)
+ lcd_write(hexdigit(input[9-i/2]>>(4-i%2*4)));
+ lcd_gotoxy(0, 1);
+ lcd_write(255);
+ }
+ }
+}
+
+void receive(uint8_t c)
+{
+ if(rx_fill==0xFF)
+ {
+ if(c==':')
+ rx_fill = 0;
+ }
+ else if(c=='.')
+ {
+ if(rx_fill>=4)
+ {
+ uint16_t offset;
+ uint8_t nibbles;
+ uint8_t i;
+
+ offset = (decode_hex(rx_buf[0])<<8) | (decode_hex(rx_buf[1])<<4) | decode_hex(rx_buf[2]);
+ nibbles = (offset&3);
+ offset >>= 2;
+ if(rx_fill>3+nibbles)
+ {
+ for(i=0; i<=nibbles; ++i)
+ {
+ uint16_t j = offset+nibbles-i;
+ uint8_t shift = 4*(j&1);
+ uint8_t bits = decode_hex(rx_buf[3+i]);
+ input[j/2] = (input[j/2]&~(0xF<<shift)) | (bits<<shift);
+ latch[j/2] = input[j/2];
+ }
+
+ if(lcd_enabled)
+ {
+ lcd_gotoxy(19-offset-nibbles, 0);
+ for(i=0; i<=nibbles; ++i)
+ lcd_write(rx_buf[3+i]);
+ }
+ }
+ }
+ rx_fill = 0xFF;
+ }
+ else
+ {
+ if(rx_fill<sizeof(rx_buf))
+ rx_buf[rx_fill++] = c;
+ else
+ rx_fill = 0xFF;
+ }
+
+ if(lcd_enabled)
+ {
+ lcd_gotoxy(log_col, 1+log_row);
+ lcd_write(c);
+ ++log_col;
+ if(log_col>=20)
+ {
+ log_col = 0;
+ ++log_row;
+ if(log_row>=3)
+ log_row = 0;
+ lcd_gotoxy(log_col, 1+log_row);
+ }
+ lcd_write(255);
+ }
+}
+
+SERIAL_SET_CALLBACK(receive)
+
+uint8_t hexdigit(uint8_t n)
+{
+ n &= 0xF;
+ if(n<10)
+ return '0'+n;
+ else
+ return 'A'+(n-0xA);
+}
+
+uint8_t decode_hex(uint8_t c)
+{
+ if(c>='0' && c<='9')
+ return c-'0';
+ else if(c>='A' && c<='F')
+ return 0xA+(c-'A');
+ else
+ return 0;
+}