2 Firmware for model railway control interface
10 Connections for Pololu high-powered motor driver:
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.
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.
25 After a command has been processed, a similarly packetized response is sent.
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.
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;
53 void process_commands();
54 uint8_t process_command();
62 timer_start_hz(0, 80000, 1);
71 serial_write(RECEIVE_OVERRUN);
81 static inline void receive(uint8_t c)
83 if(recv_fill>=sizeof(recv_buf))
89 recv_buf[recv_head++] = c;
90 if(recv_head>=sizeof(recv_buf))
95 SERIAL_SET_CALLBACK(receive)
97 void process_commands()
102 uint8_t c = recv_buf[recv_tail];
109 if(recv_fill<=cmd_length)
113 for(i=0, j=recv_tail+1; i<cmd_length; ++i, ++j)
115 if(j>=sizeof(recv_buf))
117 cmd_buf[i] = recv_buf[j];
120 consumed = 1+cmd_length;
125 serial_write(FRAMING_ERROR);
129 recv_tail += consumed;
130 if(recv_tail>=sizeof(recv_buf))
131 recv_tail -= sizeof(recv_buf);
132 recv_fill -= consumed;
136 uint8_t result = process_command();
138 serial_write(result);
143 uint8_t process_command()
145 if(cmd_buf[0]==POWER_ON || cmd_buf[0]==POWER_OFF)
150 if(cmd_buf[0]==POWER_ON)
155 else if(cmd_buf[0]==MOTOROLA_SPEED || cmd_buf[0]==MOTOROLA_SPEED_DIRECTION || cmd_buf[0]==MOTOROLA_SPEED_FUNCTION)
160 uint8_t addr = cmd_buf[1];
162 return INVALID_VALUE;
165 return INVALID_VALUE;
166 uint8_t aux = cmd_buf[2]&0x01;
168 uint8_t func = (cmd_buf[2]&0xF0)>>4;
169 if(cmd_buf[0]==MOTOROLA_SPEED_FUNCTION)
172 return INVALID_VALUE;
174 else if(cmd_buf[2]&0xFE)
175 return INVALID_VALUE;
176 uint8_t state = cmd_buf[2]&0x02;
178 uint8_t speed = cmd_buf[3]&0x7F;
180 return INVALID_VALUE;
182 uint8_t dir = !(cmd_buf[3]&0x80);
184 while(packet.ready && !packet.done) ;
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);
193 else if(cmd_buf[0]==MOTOROLA_REVERSE)
198 uint8_t addr = cmd_buf[1];
200 return INVALID_VALUE;
203 return INVALID_VALUE;
204 uint8_t aux = cmd_buf[2]&0x01;
206 while(packet.ready && !packet.done) ;
208 motorola_locomotive_reverse_packet(addr, aux);
210 else if(cmd_buf[0]==MOTOROLA_SOLENOID)
215 uint8_t addr = cmd_buf[1];
217 return INVALID_VALUE;
220 return INVALID_VALUE;
221 uint8_t output = (cmd_buf[2]&0x70)>>4;
222 uint8_t state = cmd_buf[2]&1;
224 while(packet.ready && !packet.done) ;
226 motorola_solenoid_packet(addr, output, state);
229 return INVALID_COMMAND;
234 static inline void tick()
236 if(delay_time && --delay_time)
239 if(out_time && !--out_time)
242 if(out_bit>=packet.length)
245 if(packet.repeat_count>1)
247 if(packet.repeat_count<0xFF)
248 --packet.repeat_count;
249 delay_time = packet.repeat_delay;
254 delay_time = packet.final_delay;
261 out_data = packet.data[out_bit>>3];
270 out_time = packet.bit_duration;
276 if(packet.ready && !packet.sending)
280 out_time = packet.bit_duration;
281 out_data = packet.data[0];
289 TIMER_SET_CALLBACK(0, tick)