-/* $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 transmitter module
ATMega pinout:
D7 - input 6
B0 - input 7
B1 - input 8
+B2 - input 9
+B3 - input 10
+B4 - input 11
+B5 - input 12
Inputs are pulled high by internal pull-up resistors. Connect to GND to
activate.
+
+The module can be configured by sending a string of form ":Shhh." over the
+serial port, where hhh is a hex number. Lower two bits indicate the number of
+inputs (00=4, 01=8, 10=12, 11=16) and upper ten bits indicate offset of lowest
+input in multiples of 4 bits. At the moment there are no provisions for
+having 16 physical inputs.
+
+Example: ":S016." would configure the module to have 12 inputs at offset 20.
*/
#include <avr/io.h>
#include <avr/interrupt.h>
+#include "eeprom.h"
+#include "serial.h"
+#include "timer.h"
#define BIT(n) (1<<(n))
+void receive(uint8_t);
void send_state(void);
-void write_serial(uint8_t);
uint8_t hexdigit(uint8_t);
+uint8_t decode_hex(uint8_t);
-volatile uint8_t state = 0;
+uint8_t rx_buf[4];
+uint8_t rx_fill = 0xFF;
+volatile uint8_t nibbles = 2;
+volatile uint8_t offset = 0;
+volatile uint16_t state = 0;
+volatile uint8_t time_since_send = 0;
int main()
{
+ if(eeprom_read(0)==0xA5)
+ {
+ offset = eeprom_read(1)<<8;
+ offset |= eeprom_read(2);
+ nibbles = eeprom_read(3);
+ }
+
DDRD = 0x02; // 00000010
PORTD = 0xFC; // 11111100
- DDRB = 0x20; // 00100000
+ DDRB = 0x00; // 00000000
PORTB = 0x3F; // 00111111
- // 9600 baud, 8N1
- UBRR0H = 0;
- UBRR0L = 103;
- UCSR0C = BIT(UCSZ00) | BIT(UCSZ01);
- UCSR0B = BIT(RXEN0) | BIT(TXEN0);
-
- // 0.5 Hz (CK/1024, TOP=31250)
- TCCR1A = 0;
- TCCR1B = BIT(WGM12) | BIT(CS12) | BIT(CS10);
- OCR1AH = 122;
- OCR1AL = 18;
- TIMSK1 = BIT(OCIE1A);
+ serial_init(9600);
+ timer_start_hz(1, 100, 1);
sei();
while(1)
{
- uint8_t i;
- uint8_t input = 0;
- uint8_t valid = 0xFF;
+ uint8_t i, j;
+ uint16_t input = 0;
+ uint16_t valid = 0xFFF;
for(i=0; i<100; ++i)
{
- uint8_t pins;
+ uint16_t pins = 0;
+ for(j=0; j<100; ++j)
+ pins |= ~((PIND>>2) | ((PINB&0x3F)<<6));
- pins = ~((PIND>>2) | (PINB<<6));
if(i==0)
input = pins;
+
valid &= ~(pins^input);
}
input &= valid;
input |= state&~valid;
+ input &= (1<<(nibbles*4))-1;
- if(input!=state)
+ if(input!=state && time_since_send>5)
{
state = input;
send_state();
return 0;
}
-ISR(TIMER1_COMPA_vect)
+void receive(uint8_t c)
{
- send_state();
+ if(rx_fill==0xFF)
+ {
+ if(c==':')
+ rx_fill = 0;
+ }
+ else if(c=='.')
+ {
+ if(rx_buf[0]=='S' && rx_fill==4)
+ {
+ offset = (decode_hex(rx_buf[1])<<8) | (decode_hex(rx_buf[2])<<4) | decode_hex(rx_buf[3]);
+ nibbles = (offset&3)+1;
+ offset &= 0xFFC;
+
+ eeprom_write(0, 0xA5);
+ eeprom_write(1, offset>>8);
+ eeprom_write(2, offset);
+ eeprom_write(3, nibbles);
+ }
+ rx_fill = 0xFF;
+ }
+ else
+ {
+ if(rx_fill<sizeof(rx_buf))
+ rx_buf[rx_fill++] = c;
+ else
+ rx_fill = 0xFF;
+ }
}
+SERIAL_SET_CALLBACK(receive)
+
void send_state(void)
{
- write_serial(':');
- write_serial(hexdigit(state>>4));
- write_serial(hexdigit(state&0xF));
+ uint8_t i;
+
+ serial_write(':');
+ serial_write(hexdigit(offset>>8));
+ serial_write(hexdigit(offset>>4));
+ serial_write(hexdigit(offset|(nibbles-1)));
+ for(i=nibbles; i--;)
+ serial_write(hexdigit(state>>(i*4)));
+ serial_write('.');
+
+ time_since_send = 0;
}
-void write_serial(uint8_t c)
+void timer(void)
{
- while(!(UCSR0A&(1<<UDRE0))) ;
- UDR0 = c;
+ ++time_since_send;
+ if(time_since_send>200)
+ send_state();
}
+TIMER_SET_CALLBACK(1, timer)
+
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;
+}