]> git.tdb.fi Git - model-railway-devices.git/blob - firmware/s88w-t.c
Support multiple timers (currently 0 and 1)
[model-railway-devices.git] / firmware / s88w-t.c
1 /* $Id$
2
3 This file is part of the MSP Märklin suite
4 Copyright © 2010  Mikkosoft Productions, Mikko Rasa
5 Distributed under the GPL
6
7 Firmware for wireless S88 transmitter module
8
9 ATMega pinout:
10 D0 - serial RX
11 D1 - serial TX
12 D2 - input 1
13 D3 - input 2
14 D4 - input 3
15 D5 - input 4
16 D6 - input 5
17 D7 - input 6
18 B0 - input 7
19 B1 - input 8
20 B2 - input 9
21 B3 - input 10
22 B4 - input 11
23 B5 - input 12
24
25 Inputs are pulled high by internal pull-up resistors.  Connect to GND to
26 activate.
27
28 The module can be configured by sending a string of form ":Shhh." over the
29 serial port, where hhh is a hex number.  Lower two bits indicate the number of
30 inputs (00=4, 01=8, 10=12, 11=16) and upper ten bits indicate offset of lowest
31 input in multiples of 4 bits.  At the moment there are no provisions for
32 having 16 physical inputs.
33
34 Example: ":S016." would configure the module to have 12 inputs at offset 20.
35 */
36
37 #include <avr/io.h>
38 #include <avr/interrupt.h>
39 #include "eeprom.h"
40 #include "serial.h"
41 #include "timer.h"
42
43 #define BIT(n) (1<<(n))
44
45 void receive(uint8_t);
46 void send_state(void);
47 uint8_t hexdigit(uint8_t);
48 uint8_t decode_hex(uint8_t);
49
50 uint8_t rx_buf[4];
51 uint8_t rx_fill = 0xFF;
52 volatile uint8_t nibbles = 2;
53 volatile uint8_t offset = 0;
54 volatile uint16_t state = 0;
55
56 int main()
57 {
58         if(eeprom_read(0)==0xA5)
59         {
60                 offset = eeprom_read(1)<<8;
61                 offset |= eeprom_read(2);
62                 nibbles = eeprom_read(3);
63         }
64
65         DDRD = 0x02;   // 00000010
66         PORTD = 0xFC;  // 11111100
67         DDRB = 0x00;   // 00000000
68         PORTB = 0x3F;  // 00111111
69
70         serial_init(9600);
71         timer_start_hz(1, 1, 2);
72
73         sei();
74
75         while(1)
76         {
77                 uint8_t i;
78                 uint16_t input = 0;
79                 uint16_t valid = 0xFFF;
80
81                 for(i=0; i<100; ++i)
82                 {
83                         uint16_t pins;
84
85                         pins = ~((PIND>>2) | ((PINB&0x3F)<<6));
86                         if(i==0)
87                                 input = pins;
88                         valid &= ~(pins^input);
89                 }
90
91                 input &= valid;
92                 input |= state&~valid;
93                 input &= (1<<(nibbles*4))-1;
94
95                 if(input!=state)
96                 {
97                         state = input;
98                         send_state();
99                 }
100         }
101
102         return 0;
103 }
104
105 void receive(uint8_t c)
106 {
107         if(rx_fill==0xFF)
108         {
109                 if(c==':')
110                         rx_fill = 0;
111         }
112         else if(c=='.')
113         {
114                 if(rx_buf[0]=='S' && rx_fill==4)
115                 {
116                         offset = (decode_hex(rx_buf[1])<<8) | (decode_hex(rx_buf[2])<<4) | decode_hex(rx_buf[3]);
117                         nibbles = (offset&3)+1;
118                         offset &= 0xFFC;
119
120                         eeprom_write(0, 0xA5);
121                         eeprom_write(1, offset>>8);
122                         eeprom_write(2, offset);
123                         eeprom_write(3, nibbles);
124                 }
125                 rx_fill = 0xFF;
126         }
127         else
128         {
129                 if(rx_fill<sizeof(rx_buf))
130                         rx_buf[rx_fill++] = c;
131                 else
132                         rx_fill = 0xFF;
133         }
134 }
135
136 SERIAL_SET_CALLBACK(receive)
137
138 void send_state(void)
139 {
140         uint8_t i;
141         serial_write(':');
142         serial_write(hexdigit(offset>>8));
143         serial_write(hexdigit(offset>>4));
144         serial_write(hexdigit(offset|(nibbles-1)));
145         for(i=nibbles; i--;)
146                 serial_write(hexdigit(state>>(i*4)));
147         serial_write('.');
148 }
149
150 TIMER_SET_CALLBACK(1, send_state)
151
152 uint8_t hexdigit(uint8_t n)
153 {
154         n &= 0xF;
155         if(n<10)
156                 return '0'+n;
157         else
158                 return 'A'+(n-0xA);
159 }
160
161 uint8_t decode_hex(uint8_t c)
162 {
163         if(c>='0' && c<='9')
164                 return c-'0';
165         else if(c>='A' && c<='F')
166                 return 0xA+(c-'A');
167         else
168                 return 0;
169 }