-/* $Id$
-
-This file is part of the MSP Märklin suite
-Copyright © 2010 Mikkosoft Productions, Mikko Rasa
-Distributed under the GPL
-
+/*
Firmware for wireless S88 receiver module
S88 pinout:
#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[3];
-volatile uint8_t rx_fill = 0;
-volatile uint8_t input = 0;
-volatile uint8_t latch = 0;
+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
- PIND = 0xC0; // 11000000
- DDRB = 0x20; // 00100000
- PINB = 0x1F; // 00011111
+ DDRD = 0x06; // 00000110
+ PORTD = 0xC0; // 11000000
+ DDRB = 0x20; // 00100000
+ PORTB = 0x1F; // 00011111
- // 9600 baud, 8N1
- UBRR0H = 0;
- UBRR0L = 103;
- UCSR0C = BIT(UCSZ00) | BIT(UCSZ01);
- UCSR0B = BIT(RXEN0) | BIT(TXEN0) | BIT(RXCIE0);
+ serial_init(9600);
+ lcd_init();
sei();
while(1)
{
- uint8_t d_pins;
+ 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))
- bits = latch;
+ {
+ 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);
clock_high = 0;
if(d_pins&BIT(RESET))
- latch = input;
+ {
+ 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);
+ }
}
}
-ISR(USART_RX_vect)
+void receive(uint8_t c)
{
- uint8_t c = UDR0;
- if(rx_fill==0)
+ if(rx_fill==0xFF)
{
if(c==':')
- rx_buf[rx_fill++] = 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
{
- rx_buf[rx_fill++] = c;
- if(rx_buf[0]==':' && rx_fill==3)
+ 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)
{
- input = (decode_hex(rx_buf[1])<<4) | decode_hex(rx_buf[2]);
- latch |= input;
- rx_fill = 0;
+ 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')