2 Firmware for model railway control interface
10 Connections for Pololu high-powered motor driver:
15 ADC0 - current sensor (adjusted for 185 mV/A, centered at Vcc/2)
16 ADC1 - input voltage (adjusted for divisor of 11)
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.
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.
29 After a command has been processed, a similarly packetized response is sent.
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.
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;
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;
64 uint16_t input_voltage_samples[16] = { 0 };
65 uint8_t input_voltage_head = 0;
66 volatile uint16_t input_voltage_sum = 0;
68 volatile uint8_t adc_state = 0;
70 void process_commands();
71 uint8_t process_command();
79 timer_start_hz(0, 80000, 1);
89 serial_write(RECEIVE_OVERRUN);
97 adc_read_async(adc_state>>1);
99 if(track_current_sum>overcurrent_limit)
102 if(!overcurrent_sent)
104 overcurrent_sent = 1;
106 serial_write(OVERCURRENT);
110 overcurrent_sent = 0;
116 static inline void receive(uint8_t c)
118 if(recv_fill>=sizeof(recv_buf))
124 recv_buf[recv_head++] = c;
125 if(recv_head>=sizeof(recv_buf))
130 SERIAL_SET_CALLBACK(receive)
132 void process_commands()
137 uint8_t c = recv_buf[recv_tail];
144 if(recv_fill<=cmd_length)
148 for(i=0, j=recv_tail+1; i<cmd_length; ++i, ++j)
150 if(j>=sizeof(recv_buf))
152 cmd_buf[i] = recv_buf[j];
155 consumed = 1+cmd_length;
160 serial_write(FRAMING_ERROR);
164 recv_tail += consumed;
165 if(recv_tail>=sizeof(recv_buf))
166 recv_tail -= sizeof(recv_buf);
167 recv_fill -= consumed;
171 uint8_t result = process_command();
173 serial_write(result);
178 uint8_t process_command()
180 if(cmd_buf[0]==POWER_ON || cmd_buf[0]==POWER_OFF)
185 if(cmd_buf[0]==POWER_ON)
190 else if(cmd_buf[0]==READ_TRACK_CURRENT)
196 serial_write(TRACK_CURRENT);
197 uint16_t value = track_current_sum>>4;
198 serial_write(value>>8);
201 else if(cmd_buf[0]==SET_OVERCURRENT_LIMIT)
207 return INVALID_VALUE;
209 overcurrent_limit = (cmd_buf[1]<<12) | (cmd_buf[2]<<4);
211 else if(cmd_buf[0]==READ_INPUT_VOLTAGE)
217 serial_write(INPUT_VOLTAGE);
218 uint16_t value = (input_voltage_sum>>3)*5;
219 serial_write(value>>8);
222 else if(cmd_buf[0]==MOTOROLA_SPEED || cmd_buf[0]==MOTOROLA_SPEED_DIRECTION || cmd_buf[0]==MOTOROLA_SPEED_FUNCTION)
227 uint8_t addr = cmd_buf[1];
229 return INVALID_VALUE;
232 return INVALID_VALUE;
233 uint8_t aux = cmd_buf[2]&0x01;
235 uint8_t func = (cmd_buf[2]&0xF0)>>4;
236 if(cmd_buf[0]==MOTOROLA_SPEED_FUNCTION)
239 return INVALID_VALUE;
241 else if(cmd_buf[2]&0xFE)
242 return INVALID_VALUE;
243 uint8_t state = cmd_buf[2]&0x02;
245 uint8_t speed = cmd_buf[3]&0x7F;
247 return INVALID_VALUE;
249 uint8_t dir = !(cmd_buf[3]&0x80);
251 while(packet.ready && !packet.done) ;
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);
260 else if(cmd_buf[0]==MOTOROLA_REVERSE)
265 uint8_t addr = cmd_buf[1];
267 return INVALID_VALUE;
270 return INVALID_VALUE;
271 uint8_t aux = cmd_buf[2]&0x01;
273 while(packet.ready && !packet.done) ;
275 motorola_locomotive_reverse_packet(addr, aux);
277 else if(cmd_buf[0]==MOTOROLA_SOLENOID)
282 uint8_t addr = cmd_buf[1];
284 return INVALID_VALUE;
287 return INVALID_VALUE;
288 uint8_t output = (cmd_buf[2]&0x70)>>4;
289 uint8_t state = cmd_buf[2]&1;
291 while(packet.ready && !packet.done) ;
293 motorola_solenoid_packet(addr, output, state);
296 return INVALID_COMMAND;
301 static inline void tick()
303 if(delay_time && --delay_time)
306 if(out_time && !--out_time)
309 if(out_bit>=packet.length)
312 if(packet.repeat_count>1)
314 if(packet.repeat_count<0xFF)
315 --packet.repeat_count;
316 delay_time = packet.repeat_delay;
321 delay_time = packet.final_delay;
328 out_data = packet.data[out_bit>>3];
337 out_time = packet.bit_duration;
343 if(packet.ready && !packet.sending)
347 out_time = packet.bit_duration;
348 out_data = packet.data[0];
356 TIMER_SET_CALLBACK(0, tick)
358 static inline void adc_complete(uint16_t value)
362 // Convert to milliamps: (v*5/1024-2.5)*1000/0.185
363 if(value<512) // Ignore negative current readings
365 else if(value>663) // Limit range so averaging won't overflow
368 value = (value-512)*132/5;
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;
376 else if(adc_state==3)
378 // Convert to centivolts: (v*5/1024)*100*11
379 if(value>744) // Limit range so averaging won't overflow
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;
394 ADC_SET_CALLBACK(adc_complete)