]> git.tdb.fi Git - model-railway-devices.git/blobdiff - firmware/s88w-t.c
Rename the project to R²C²
[model-railway-devices.git] / firmware / s88w-t.c
index e88a1f509eb6fc24aec4c4a8e05e83bce6a4fd7e..b7d0a94f74a7ce89b8152aaf4a5bc5cd53f30c06 100644 (file)
@@ -1,6 +1,6 @@
 /* $Id$
 
-This file is part of the MSP Märklin suite
+This file is part of R²C²
 Copyright © 2010  Mikkosoft Productions, Mikko Rasa
 Distributed under the GPL
 
@@ -17,64 +17,85 @@ D6 - input 5
 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();
@@ -84,28 +105,78 @@ int main()
        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;
+}