]> git.tdb.fi Git - model-railway-devices.git/blob - arducontrol/arducontrol.c
Implement current and voltage monitoring
[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 ADC connections:
15 ADC0 - current sensor (adjusted for 185 mV/A, centered at Vcc/2)
16 ADC1 - input voltage (adjusted for divisor of 11)
17
18 This is not a complete control device.  Rather, it is a low-level bus driver,
19 intended to be controlled with a computer program.  In particular, no state is
20 kept about locomotives or solenoids.  It is up to the controlling program to
21 maintain a refresh cycle, turn off solenoids after a sufficient delay, etc.
22
23 Commands are received as a packetized stream over the serial interface.  Each
24 packet begins with a header byte, which is bitwise not of the packet's length,
25 not including the header byte itself.  The longest allowed packet is 15 bytes.
26 Thus, the high four bits of the header byte are always ones.  The first data
27 byte specifies the command, and the rest of the packet is command arguments.
28
29 After a command has been processed, a similarly packetized response is sent.
30
31 Track output is asynchronous up to a single packet.  If an output command is
32 received while a previous packet is still being sent to the track, execution
33 blocks until the operation completes.  The command returns as soon as sending
34 its data to the track can start.
35 */
36 #include <avr/io.h>
37 #include "adc.h"
38 #include "commands.h"
39 #include "motorola.h"
40 #include "packet.h"
41 #include "serial.h"
42 #include "timer.h"
43
44 Packet packet;
45 uint8_t out_bit;
46 uint8_t out_time;
47 uint8_t out_data;
48 uint8_t delay_time;
49
50 volatile uint8_t recv_buf[32];
51 uint8_t recv_head = 0;
52 uint8_t recv_tail = 0;
53 volatile uint8_t recv_fill = 0;
54 volatile uint8_t recv_overrun = 0;
55 uint8_t cmd_buf[15];
56 uint8_t cmd_length;
57
58 uint16_t track_current_samples[16] = { 0 };
59 uint8_t track_current_head = 0;
60 volatile uint16_t track_current_sum = 0;
61 uint16_t overcurrent_limit = 1000<<4;
62 uint8_t overcurrent_sent = 0;
63
64 uint16_t input_voltage_samples[16] = { 0 };
65 uint8_t input_voltage_head = 0;
66 volatile uint16_t input_voltage_sum = 0;
67
68 volatile uint8_t adc_state = 0;
69
70 void process_commands();
71 uint8_t process_command();
72
73 int main()
74 {
75         DDRD = 0x0E;
76         PORTD = 0;
77
78         serial_init(9600);
79         timer_start_hz(0, 80000, 1);
80         adc_init();
81
82         sei();
83
84         while(1)
85         {
86                 if(recv_overrun)
87                 {
88                         serial_write(0xFE);
89                         serial_write(RECEIVE_OVERRUN);
90                         recv_overrun = 0;
91                 }
92                 if(recv_fill>0)
93                         process_commands();
94                 if(!(adc_state&1))
95                 {
96                         ++adc_state;
97                         adc_read_async(adc_state>>1);
98                 }
99                 if(track_current_sum>overcurrent_limit)
100                 {
101                         PORTD &= ~0x08;
102                         if(!overcurrent_sent)
103                         {
104                                 overcurrent_sent = 1;
105                                 serial_write(0xFE);
106                                 serial_write(OVERCURRENT);
107                         }
108                 }
109                 else
110                         overcurrent_sent = 0;
111         }
112
113         return 0;
114 }
115
116 static inline void receive(uint8_t c)
117 {
118         if(recv_fill>=sizeof(recv_buf))
119         {
120                 recv_overrun = 1;
121                 return;
122         }
123
124         recv_buf[recv_head++] = c;
125         if(recv_head>=sizeof(recv_buf))
126                 recv_head = 0;
127         ++recv_fill;
128 }
129
130 SERIAL_SET_CALLBACK(receive)
131
132 void process_commands()
133 {
134         while(recv_fill>0)
135         {
136                 uint8_t consumed;
137                 uint8_t c = recv_buf[recv_tail];
138
139                 cmd_length = 0;
140
141                 if(c>=0xF0)
142                 {
143                         cmd_length = ~c;
144                         if(recv_fill<=cmd_length)
145                                 break;
146
147                         uint8_t i, j;
148                         for(i=0, j=recv_tail+1; i<cmd_length; ++i, ++j)
149                         {
150                                 if(j>=sizeof(recv_buf))
151                                         j = 0;
152                                 cmd_buf[i] = recv_buf[j];
153                         }
154
155                         consumed = 1+cmd_length;
156                 }
157                 else
158                 {
159                         serial_write(0xFE);
160                         serial_write(FRAMING_ERROR);
161                         consumed = 1;
162                 }
163
164                 recv_tail += consumed;
165                 if(recv_tail>=sizeof(recv_buf))
166                         recv_tail -= sizeof(recv_buf);
167                 recv_fill -= consumed;
168
169                 if(cmd_length>0)
170                 {
171                         uint8_t result = process_command();
172                         serial_write(0xFE);
173                         serial_write(result);
174                 }
175         }
176 }
177
178 uint8_t process_command()
179 {
180         if(cmd_buf[0]==POWER_ON || cmd_buf[0]==POWER_OFF)
181         {
182                 if(cmd_length!=1)
183                         return LENGTH_ERROR;
184
185                 if(cmd_buf[0]==POWER_ON)
186                         PORTD |= 0x08;
187                 else
188                         PORTD &= ~0x08;
189         }
190         else if(cmd_buf[0]==READ_TRACK_CURRENT)
191         {
192                 if(cmd_length!=1)
193                         return LENGTH_ERROR;
194
195                 serial_write(0xFC);
196                 serial_write(TRACK_CURRENT);
197                 uint16_t value = track_current_sum>>4;
198                 serial_write(value>>8);
199                 serial_write(value);
200         }
201         else if(cmd_buf[0]==SET_OVERCURRENT_LIMIT)
202         {
203                 if(cmd_length!=3)
204                         return LENGTH_ERROR;
205
206                 if(cmd_buf[1]&0xF0)
207                         return INVALID_VALUE;
208
209                 overcurrent_limit = (cmd_buf[1]<<12) | (cmd_buf[2]<<4);
210         }
211         else if(cmd_buf[0]==READ_INPUT_VOLTAGE)
212         {
213                 if(cmd_length!=1)
214                         return LENGTH_ERROR;
215
216                 serial_write(0xFC);
217                 serial_write(INPUT_VOLTAGE);
218                 uint16_t value = (input_voltage_sum>>3)*5;
219                 serial_write(value>>8);
220                 serial_write(value);
221         }
222         else if(cmd_buf[0]==MOTOROLA_SPEED || cmd_buf[0]==MOTOROLA_SPEED_DIRECTION || cmd_buf[0]==MOTOROLA_SPEED_FUNCTION)
223         {
224                 if(cmd_length!=4)
225                         return LENGTH_ERROR;
226
227                 uint8_t addr = cmd_buf[1];
228                 if(addr>80)
229                         return INVALID_VALUE;
230
231                 if(cmd_buf[2]&0x0E)
232                         return INVALID_VALUE;
233                 uint8_t aux = cmd_buf[2]&0x01;
234
235                 uint8_t func = (cmd_buf[2]&0xF0)>>4;
236                 if(cmd_buf[0]==MOTOROLA_SPEED_FUNCTION)
237                 {
238                         if(func<1 || func>4)
239                                 return INVALID_VALUE;
240                 }
241                 else if(cmd_buf[2]&0xFE)
242                         return INVALID_VALUE;
243                 uint8_t state = cmd_buf[2]&0x02;
244
245                 uint8_t speed = cmd_buf[3]&0x7F;
246                 if(speed>14)
247                         return INVALID_VALUE;
248
249                 uint8_t dir = !(cmd_buf[3]&0x80);
250
251                 while(packet.ready && !packet.done) ;
252
253                 if(cmd_buf[0]==MOTOROLA_SPEED)
254                         motorola_locomotive_speed_packet(addr, aux, speed);
255                 else if(cmd_buf[0]==MOTOROLA_SPEED_DIRECTION)
256                         motorola_locomotive_speed_direction_packet(addr, aux, speed, dir);
257                 else if(cmd_buf[0]==MOTOROLA_SPEED_FUNCTION)
258                         motorola_locomotive_speed_function_packet(addr, aux, speed, func, state);
259         }
260         else if(cmd_buf[0]==MOTOROLA_REVERSE)
261         {
262                 if(cmd_length!=3)
263                         return LENGTH_ERROR;
264
265                 uint8_t addr = cmd_buf[1];
266                 if(addr>80)
267                         return INVALID_VALUE;
268
269                 if(cmd_buf[2]&0xFE)
270                         return INVALID_VALUE;
271                 uint8_t aux = cmd_buf[2]&0x01;
272
273                 while(packet.ready && !packet.done) ;
274
275                 motorola_locomotive_reverse_packet(addr, aux);
276         }
277         else if(cmd_buf[0]==MOTOROLA_SOLENOID)
278         {
279                 if(cmd_length!=3)
280                         return LENGTH_ERROR;
281
282                 uint8_t addr = cmd_buf[1];
283                 if(addr>80)
284                         return INVALID_VALUE;
285
286                 if(cmd_buf[2]&0x8E)
287                         return INVALID_VALUE;
288                 uint8_t output = (cmd_buf[2]&0x70)>>4;
289                 uint8_t state = cmd_buf[2]&1;
290
291                 while(packet.ready && !packet.done) ;
292
293                 motorola_solenoid_packet(addr, output, state);
294         }
295         else
296                 return INVALID_COMMAND;
297
298         return COMMAND_OK;
299 }
300
301 static inline void tick()
302 {
303         if(delay_time && --delay_time)
304                 return;
305
306         if(out_time && !--out_time)
307         {
308                 ++out_bit;
309                 if(out_bit>=packet.length)
310                 {
311                         PORTD &= ~0x04;
312                         if(packet.repeat_count>1)
313                         {
314                                 if(packet.repeat_count<0xFF)
315                                         --packet.repeat_count;
316                                 delay_time = packet.repeat_delay;
317                                 packet.sending = 0;
318                         }
319                         else
320                         {
321                                 delay_time = packet.final_delay;
322                                 packet.done = 1;
323                         }
324                 }
325                 else
326                 {
327                         if((out_bit&7)==0)
328                                 out_data = packet.data[out_bit>>3];
329                         else
330                                 out_data >>= 1;
331
332                         if(out_data&1)
333                                 PORTD |= 0x04;
334                         else
335                                 PORTD &= ~0x04;
336
337                         out_time = packet.bit_duration;
338                 }
339
340                 return;
341         }
342
343         if(packet.ready && !packet.sending)
344         {
345                 packet.sending = 1;
346                 out_bit = 0;
347                 out_time = packet.bit_duration;
348                 out_data = packet.data[0];
349                 if(out_data&1)
350                         PORTD |= 0x04;
351                 else
352                         PORTD &= ~0x04;
353         }
354 }
355
356 TIMER_SET_CALLBACK(0, tick)
357
358 static inline void adc_complete(uint16_t value)
359 {
360         if(adc_state==1)
361         {
362                 // Convert to milliamps: (v*5/1024-2.5)*1000/0.185
363                 if(value<512)  // Ignore negative current readings
364                         value = 0;
365                 else if(value>663)  // Limit range so averaging won't overflow
366                         value = 4000;
367                 else
368                         value = (value-512)*132/5;
369
370                 uint8_t i = track_current_head;
371                 track_current_sum -= track_current_samples[i];
372                 track_current_samples[i] = value;
373                 track_current_sum += value;
374                 track_current_head = (i+1)&15;
375         }
376         else if(adc_state==3)
377         {
378                 // Convert to centivolts: (v*5/1024)*100*11
379                 if(value>744) // Limit range so averaging won't overflow
380                         value = 4000;
381                 else
382                         value = value*43/8;
383
384                 uint8_t i = input_voltage_head;
385                 input_voltage_sum -= input_voltage_samples[i];
386                 input_voltage_samples[i] = value;
387                 input_voltage_sum += value;
388                 input_voltage_head = (i+1)&15;
389         }
390
391         ++adc_state;
392 }
393
394 ADC_SET_CALLBACK(adc_complete)