]> git.tdb.fi Git - model-railway-devices.git/blob - arducontrol/arducontrol.c
7b0c636b9ffb926ae811a9605a3b17e29806c633
[model-railway-devices.git] / arducontrol / arducontrol.c
1 /*
2 Firmware for model railway control interface
3
4 ATMega pinout:
5 D0 - serial RX
6 D1 - serial TX
7 D2 - Polarity control
8 D3 - Power control
9
10 Connections for Pololu high-powered motor driver:
11 D2 <-> DIR
12 D3 <-> PWM
13
14 This is not a complete control device.  Rather, it is a low-level bus driver,
15 intended to be controlled with a computer program.  In particular, no state is
16 kept about locomotives or solenoids.  It is up to the controlling program to
17 maintain a refresh cycle, turn off solenoids after a sufficient delay, etc.
18
19 Commands are received as a packetized stream over the serial interface.  Each
20 packet begins with a header byte, which is bitwise not of the packet's length,
21 not including the header byte itself.  The longest allowed packet is 15 bytes.
22 Thus, the high four bits of the header byte are always ones.  The first data
23 byte specifies the command, and the rest of the packet is command arguments.
24
25 After a command has been processed, a similarly packetized response is sent.
26
27 Track output is asynchronous up to a single packet.  If an output command is
28 received while a previous packet is still being sent to the track, execution
29 blocks until the operation completes.  The command returns as soon as sending
30 its data to the track can start.
31 */
32 #include <avr/io.h>
33 #include "commands.h"
34 #include "motorola.h"
35 #include "packet.h"
36 #include "serial.h"
37 #include "timer.h"
38
39 Packet packet;
40 uint8_t out_bit;
41 uint8_t out_time;
42 uint8_t out_data;
43 uint8_t delay_time;
44
45 volatile uint8_t recv_buf[32];
46 uint8_t recv_head = 0;
47 uint8_t recv_tail = 0;
48 volatile uint8_t recv_fill = 0;
49 volatile uint8_t recv_overrun = 0;
50 uint8_t cmd_buf[15];
51 uint8_t cmd_length;
52
53 void process_commands();
54 uint8_t process_command();
55
56 int main()
57 {
58         DDRD = 0x0E;
59         PORTD = 0;
60
61         serial_init(9600);
62         timer_start_hz(0, 80000, 1);
63
64         sei();
65
66         while(1)
67         {
68                 if(recv_overrun)
69                 {
70                         serial_write(0xFE);
71                         serial_write(RECEIVE_OVERRUN);
72                         recv_overrun = 0;
73                 }
74                 if(recv_fill>0)
75                         process_commands();
76         }
77
78         return 0;
79 }
80
81 static inline void receive(uint8_t c)
82 {
83         if(recv_fill>=sizeof(recv_buf))
84         {
85                 recv_overrun = 1;
86                 return;
87         }
88
89         recv_buf[recv_head++] = c;
90         if(recv_head>=sizeof(recv_buf))
91                 recv_head = 0;
92         ++recv_fill;
93 }
94
95 SERIAL_SET_CALLBACK(receive)
96
97 void process_commands()
98 {
99         while(recv_fill>0)
100         {
101                 uint8_t consumed;
102                 uint8_t c = recv_buf[recv_tail];
103
104                 cmd_length = 0;
105
106                 if(c>=0xF0)
107                 {
108                         cmd_length = ~c;
109                         if(recv_fill<=cmd_length)
110                                 break;
111
112                         uint8_t i, j;
113                         for(i=0, j=recv_tail+1; i<cmd_length; ++i, ++j)
114                         {
115                                 if(j>=sizeof(recv_buf))
116                                         j = 0;
117                                 cmd_buf[i] = recv_buf[j];
118                         }
119
120                         consumed = 1+cmd_length;
121                 }
122                 else
123                 {
124                         serial_write(0xFE);
125                         serial_write(FRAMING_ERROR);
126                         consumed = 1;
127                 }
128
129                 recv_tail += consumed;
130                 if(recv_tail>=sizeof(recv_buf))
131                         recv_tail -= sizeof(recv_buf);
132                 recv_fill -= consumed;
133
134                 if(cmd_length>0)
135                 {
136                         uint8_t result = process_command();
137                         serial_write(0xFE);
138                         serial_write(result);
139                 }
140         }
141 }
142
143 uint8_t process_command()
144 {
145         if(cmd_buf[0]==POWER_ON || cmd_buf[0]==POWER_OFF)
146         {
147                 if(cmd_length!=1)
148                         return LENGTH_ERROR;
149
150                 if(cmd_buf[0]==POWER_ON)
151                         PORTD |= 0x08;
152                 else
153                         PORTD &= ~0x08;
154         }
155         else if(cmd_buf[0]==MOTOROLA_SPEED || cmd_buf[0]==MOTOROLA_SPEED_DIRECTION || cmd_buf[0]==MOTOROLA_SPEED_FUNCTION)
156         {
157                 if(cmd_length!=4)
158                         return LENGTH_ERROR;
159
160                 uint8_t addr = cmd_buf[1];
161                 if(addr>80)
162                         return INVALID_VALUE;
163
164                 if(cmd_buf[2]&0x0E)
165                         return INVALID_VALUE;
166                 uint8_t aux = cmd_buf[2]&0x01;
167
168                 uint8_t func = (cmd_buf[2]&0xF0)>>4;
169                 if(cmd_buf[0]==MOTOROLA_SPEED_FUNCTION)
170                 {
171                         if(func<1 || func>4)
172                                 return INVALID_VALUE;
173                 }
174                 else if(cmd_buf[2]&0xFE)
175                         return INVALID_VALUE;
176                 uint8_t state = cmd_buf[2]&0x02;
177
178                 uint8_t speed = cmd_buf[3]&0x7F;
179                 if(speed>14)
180                         return INVALID_VALUE;
181
182                 uint8_t dir = !(cmd_buf[3]&0x80);
183
184                 while(packet.ready && !packet.done) ;
185
186                 if(cmd_buf[0]==MOTOROLA_SPEED)
187                         motorola_locomotive_speed_packet(addr, aux, speed);
188                 else if(cmd_buf[0]==MOTOROLA_SPEED_DIRECTION)
189                         motorola_locomotive_speed_direction_packet(addr, aux, speed, dir);
190                 else if(cmd_buf[0]==MOTOROLA_SPEED_FUNCTION)
191                         motorola_locomotive_speed_function_packet(addr, aux, speed, func, state);
192         }
193         else if(cmd_buf[0]==MOTOROLA_REVERSE)
194         {
195                 if(cmd_length!=3)
196                         return LENGTH_ERROR;
197
198                 uint8_t addr = cmd_buf[1];
199                 if(addr>80)
200                         return INVALID_VALUE;
201
202                 if(cmd_buf[2]&0xFE)
203                         return INVALID_VALUE;
204                 uint8_t aux = cmd_buf[2]&0x01;
205
206                 while(packet.ready && !packet.done) ;
207
208                 motorola_locomotive_reverse_packet(addr, aux);
209         }
210         else if(cmd_buf[0]==MOTOROLA_SOLENOID)
211         {
212                 if(cmd_length!=3)
213                         return LENGTH_ERROR;
214
215                 uint8_t addr = cmd_buf[1];
216                 if(addr>80)
217                         return INVALID_VALUE;
218
219                 if(cmd_buf[2]&0x8E)
220                         return INVALID_VALUE;
221                 uint8_t output = (cmd_buf[2]&0x70)>>4;
222                 uint8_t state = cmd_buf[2]&1;
223
224                 while(packet.ready && !packet.done) ;
225
226                 motorola_solenoid_packet(addr, output, state);
227         }
228         else
229                 return INVALID_COMMAND;
230
231         return COMMAND_OK;
232 }
233
234 static inline void tick()
235 {
236         if(delay_time && --delay_time)
237                 return;
238
239         if(out_time && !--out_time)
240         {
241                 ++out_bit;
242                 if(out_bit>=packet.length)
243                 {
244                         PORTD &= ~0x04;
245                         if(packet.repeat_count>1)
246                         {
247                                 if(packet.repeat_count<0xFF)
248                                         --packet.repeat_count;
249                                 delay_time = packet.repeat_delay;
250                                 packet.sending = 0;
251                         }
252                         else
253                         {
254                                 delay_time = packet.final_delay;
255                                 packet.done = 1;
256                         }
257                 }
258                 else
259                 {
260                         if((out_bit&7)==0)
261                                 out_data = packet.data[out_bit>>3];
262                         else
263                                 out_data >>= 1;
264
265                         if(out_data&1)
266                                 PORTD |= 0x04;
267                         else
268                                 PORTD &= ~0x04;
269
270                         out_time = packet.bit_duration;
271                 }
272
273                 return;
274         }
275
276         if(packet.ready && !packet.sending)
277         {
278                 packet.sending = 1;
279                 out_bit = 0;
280                 out_time = packet.bit_duration;
281                 out_data = packet.data[0];
282                 if(out_data&1)
283                         PORTD |= 0x04;
284                 else
285                         PORTD &= ~0x04;
286         }
287 }
288
289 TIMER_SET_CALLBACK(0, tick)