Skip to content

Commit ca837e1

Browse files
author
Railstars
committedJun 29, 2011
Initial commit; code compiles, but does little (only direct mode byte write implemented) and is utterly untested (yet).
1 parent 8829e40 commit ca837e1

7 files changed

+708
-0
lines changed
 

‎DCCServiceModePacket.cpp

+55
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
#include "DCCServiceModePacket.h"
2+
3+
4+
DCCServiceModePacket::DCCServiceModePacket(): _size_repeat(0x40) //size(1), repeat(0)
5+
{
6+
_data[0] = 0xFF; //default to idle packet
7+
_data[1] = 0x00;
8+
}
9+
10+
uint8_t DCCServiceModePacket::getBitstream(uint8_t rawuint8_ts[]) //returns size of array.
11+
{
12+
uint8_t i;
13+
uint8_t XOR = 0;
14+
for(i = 0; i < (_size_repeat>>6); ++i)
15+
{
16+
rawuint8_ts[i] = _data[i];
17+
XOR ^= _data[i];
18+
}
19+
20+
rawuint8_ts[i] = XOR;
21+
return i;
22+
}
23+
24+
uint8_t DCCServiceModePacket::getSize(void)
25+
{
26+
return (_size_repeat>>6);
27+
}
28+
29+
void DCCServiceModePacket::setData(uint8_t value)
30+
{
31+
_data[0] = value;
32+
_size_repeat = (_size_repeat & 0x3F) | (1<<6);
33+
}
34+
35+
void DCCServiceModePacket::setData(uint8_t value1, uint8_t value2)
36+
{
37+
_data[0] = value1;
38+
_data[1] = value2;
39+
_size_repeat = (_size_repeat & 0x3F) | (2<<6);
40+
}
41+
42+
void DCCServiceModePacket::setData(uint8_t value1, uint8_t value2, uint8_t value3)
43+
{
44+
_data[0] = value1;
45+
_data[1] = value2;
46+
_data[2] = value3;
47+
_size_repeat = (_size_repeat & 0x3F) | (3<<6);
48+
}
49+
50+
void DCCServiceModePacket::makeReset(void)
51+
{
52+
_data[0] = 0;
53+
_data[1] = 0;
54+
_size_repeat = (2<<6);
55+
}

‎DCCServiceModePacket.h

+31
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
#ifndef __DCCSERVICEMODEPACKET_H__
2+
#define __DCCSERVICEMODEPACKET_H__
3+
4+
#include "WProgram.h"
5+
6+
typedef unsigned char byte;
7+
8+
9+
class DCCServiceModePacket
10+
{
11+
public:
12+
DCCServiceModePacket();
13+
14+
uint8_t getBitstream(byte rawbytes[]); //returns size of array.
15+
uint8_t getSize(void);
16+
void setData(uint8_t value);
17+
void setData(uint8_t value1, uint8_t value2);
18+
void setData(uint8_t value1, uint8_t value2, uint8_t value3);
19+
20+
inline void setRepeat(byte new_repeat) { _size_repeat = (_size_repeat&0xC0 | new_repeat&0x3F) ;}
21+
inline uint8_t getRepeat(void) { return _size_repeat & 0x3F; }//return repeat; }
22+
23+
void makeReset(void);
24+
25+
private:
26+
//A DCC service mode packet is at most 4 bytes: 3 of data, one of XOR
27+
byte _data[4];
28+
byte _size_repeat; //a bit field! 0b11000000 = 0xC0 = size; 0x00111111 = 0x3F = repeat
29+
};
30+
31+
#endif //__DCCSERVICEMODEPACKET_H__

‎DCCServiceModeQueue.cpp

+67
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
#include "DCCServiceModeQueue.h"
2+
3+
DCCServiceModeQueue::DCCServiceModeQueue(void) : _read_pos(0), _size(0)
4+
{
5+
return;
6+
}
7+
8+
void DCCServiceModeQueue::setup(void)
9+
{
10+
// for(uint8_t i = 0; i<QUEUE_SIZE; ++i)
11+
// {
12+
// _queue[i].setup();
13+
// }
14+
}
15+
16+
bool DCCServiceModeQueue::insertPacket(DCCServiceModePacket &packet)
17+
{
18+
if(_size == QUEUE_SIZE) //queue is already full!
19+
return false;
20+
21+
if(!packet.getRepeat()) //zero repeat set
22+
return false;
23+
24+
memcpy(&_queue[_size],&packet,sizeof(DCCServiceModePacket));
25+
++_size;
26+
return true;
27+
}
28+
29+
// void DCCServiceModeQueue::printQueue(void)
30+
// {
31+
// byte i, j;
32+
// for(i = 0; i < size; ++i)
33+
// {
34+
// for(j = 0; j < (queue[i].size_repeat>>4); ++j)
35+
// {
36+
// Serial.print(queue[i].data[j],BIN);
37+
// Serial.print(" ");
38+
// }
39+
// if(i == _read_pos) Serial.println(" r");
40+
// else if(i == write_pos) Serial.println(" w");
41+
// else Serial.println("");
42+
// }
43+
// }
44+
45+
/* Goes through each packet in the queue, repeats it getRepeat() times, and discards it */
46+
bool DCCServiceModeQueue::readPacket(DCCServiceModePacket &packet)
47+
{
48+
if(isEmpty())
49+
return false;
50+
51+
if(_queue[_read_pos].getRepeat()) //if the topmost packet needs repeating
52+
{
53+
_queue[_read_pos].setRepeat(_queue[_read_pos].getRepeat()-1); //decrement the current packet's repeat count
54+
}
55+
else //the topmost packet is ready to be discarded; use the DCCServiceModeQueue mechanism
56+
{
57+
++_read_pos;
58+
}
59+
60+
if(!isEmpty()) //anything remaining in the queue?
61+
{
62+
memcpy(&packet,&_queue[_read_pos],sizeof(DCCServiceModePacket));
63+
return true;
64+
}
65+
66+
return false;
67+
}

‎DCCServiceModeQueue.h

+33
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
#ifndef __DCCSERVICEMODEQUEUE_H__
2+
#define __DCCSERVICEMODEQUEUE_H__
3+
4+
#include "WProgram.h"
5+
6+
/**
7+
* A FIFO queue for holding DCC service mode packets.
8+
* Copyright 2011 D.E. Goodman-Wilson
9+
**/
10+
11+
#include "DCCServiceModePacket.h"
12+
13+
#define QUEUE_SIZE 5 //TODO!!
14+
15+
class DCCServiceModeQueue
16+
{
17+
private:
18+
DCCServiceModePacket _queue[QUEUE_SIZE]; //TODO!!
19+
byte _read_pos;
20+
byte _size;
21+
public:
22+
DCCServiceModeQueue(void);
23+
24+
void setup(void);
25+
void clear(void) {_read_pos = _size = 0;}
26+
27+
bool isEmpty(void) {return _read_pos == _size;}
28+
29+
virtual bool insertPacket(DCCServiceModePacket &packet); //makes a local copy, does not take over memory management!
30+
virtual bool readPacket(DCCServiceModePacket &packet); //does not hand off memory management of packet. used immediately.
31+
};
32+
33+
#endif //__DCCSERVICEMODEQUEUE_H__

‎DCCServiceModeScheduler.cpp

+397
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,397 @@
1+
#include "DCCServiceModeScheduler.h"
2+
3+
/*
4+
* DCC Service Mode Waveform Generator
5+
*
6+
*
7+
* Hardware requirements:
8+
* *An h-bridge with inputs wired as indicated below, and outputs to a programming track.
9+
* *A current sensor on the h-bridge, wired to the analog input as below.
10+
* *A locomotive with a decoder installed, reset to factory defaults.
11+
*
12+
* Author: D.E. Goodman-Wilson dgoodman@railstars.com
13+
* Website: http://railstars.com/software/PrgmrArduino/
14+
*
15+
* Copyright 2011 Don Goodman-Wilson
16+
* This program is free software: you can redistribute it and/or modify
17+
* it under the terms of the GNU General Public License as published by
18+
* the Free Software Foundation, either version 3 of the License, or
19+
* at your option) any later version.
20+
*
21+
* This program is distributed in the hope that it will be useful,
22+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
23+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
24+
* GNU General Public License for more details.
25+
*
26+
* You should have received a copy of the GNU General Public License
27+
* along with this program. If not, see <http://www.gnu.org/licenses/>.
28+
*
29+
*/
30+
31+
/*****************************************************************/
32+
33+
34+
///An enumerated type for keeping track of the state machine used in the timer2 ISR
35+
enum DCC_output_state_t {
36+
dos_idle,
37+
dos_send_preamble,
38+
dos_send_bstart,
39+
dos_send_byte,
40+
dos_end_bit
41+
};
42+
43+
DCC_output_state_t DCC_state = dos_idle; //just to start out
44+
45+
#define PREAMBLE_LENGTH 20 //at least 20 '1's in a service mode preamble.
46+
47+
#define ACK_TIMEOUT 5 //6ms +- 1ms
48+
49+
//TODO
50+
51+
/// The currently queued packet to be put on the rails.
52+
byte current_packet[6] = {0x00,0x00,0x00,0x00,0x00,0x00};
53+
/// How many data bytes in the queued packet?
54+
volatile byte current_packet_size = 0;
55+
/// How many bytes remain to be put on the rails?
56+
volatile byte current_byte_counter = 0;
57+
/// How many bits remain in the current data byte/preamble before changing states?
58+
volatile byte current_bit_counter = PREAMBLE_LENGTH; //init to 28 1's for the preamble
59+
/// A fixed-content packet to send when idle
60+
//byte DCC_Idle_Packet[3] = {255,0,255};
61+
/// A fixed-content packet to send to reset all decoders on layout
62+
//byte DCC_Reset_Packet[3] = {0,0,0};
63+
64+
/** S 9.1 A specifies that '1's are represented by a square wave with a half-period of 58us (valid range: 55-61us)
65+
and '0's with a half-period of >100us (valid range: 95-9900us)
66+
Because '0's are stretched to provide DC power to non-DCC locos, we need two zero counters,
67+
one for the top half, and one for the bottom half.
68+
69+
Here is how to calculate the timer1 counter values (from ATMega328 datasheet, chapter 17.7.2):
70+
f_{OC2A} = \frac{f_{clk_I/O}}{2*N*(1+OCR2A)})
71+
where N = prescalar, and OCR2A is the TOP we need to calculate.
72+
We know the desired half period for each case, 58us and >100us.
73+
So:
74+
for ones:
75+
58us = (8*(1+OCR2A)) / (16MHz)
76+
58us * 16MHz = 8*(1+OCR2A)
77+
58us * 2MHz = 1+OCR2A
78+
OCR2A = 115
79+
80+
for zeros:
81+
100us * 2MHz = 1+OCR2A
82+
OCR2A = 199
83+
*/
84+
unsigned int one_count=115; //58us
85+
unsigned int zero_count=199; //100us
86+
87+
88+
/** On ATmega168 and ATmega328, we will use the 8-bit Timer2.
89+
But because the AT90CAN128 does not offer an OC2B pin (!?), even though the ATmega2560 does,
90+
we will use the 16-bit Timer3 on these chips. TODO
91+
*/
92+
93+
/// Setup phase: configure and enable Timer2/ CTC interrupt, set OC2A and OC2B to toggle on CTC
94+
void setup_DCC_service_mode_waveform_generator()
95+
{
96+
97+
//Set the OC2A and OC2B pins (Timer2 output pins A and B) to output mode
98+
//On Arduino UNO, etc, OC2A is Port B/Pin 3 = digital pin 11; and OC2B Port D/Pin 3 = digital pin 3
99+
#if defined(__AVR_ATmega48__) || defined(__AVR_ATmega88__) || defined(__AVR_ATmega168__) || defined(__AVR_ATmega328__)
100+
DDRB |= _BV(DDB3);
101+
DDRD |= _BV(DDD3);
102+
#endif
103+
104+
// Configure Timer2 in CTC mode, for waveform generation, set to toggle OC2A, OC2B, at /8 prescalar, interrupt at CTC
105+
TCCR2A = _BV(COM2A0) | _BV(COM2B0) | _BV(WGM21);
106+
TCCR2B = _BV(CS21);
107+
108+
// start by outputting a '1'
109+
OCR2A = OCR2B = one_count; //Whenever we set OCR2A, we must also set OCR2B, or else pin OC2B will get out of sync with OC2A!
110+
TCNT2 = 0; //get the timer rolling (not really necessary? defaults to 0. Just in case.)
111+
112+
//finally, force a toggle on OC2A so that pin OC2A will always complement pin OC2B
113+
TCCR2B |= _BV(FOC2A);
114+
115+
}
116+
117+
void DCC_service_mode_waveform_generation_hajime()
118+
{
119+
//force a toggle on OC2A so that pin OC2A will always complement pin OC2B
120+
TCCR2B |= _BV(FOC2A);
121+
//enable the compare match interrupt
122+
TIMSK2 |= _BV(OCIE2A);
123+
}
124+
125+
void DCC_service_mode_waveform_generation_yame()
126+
{
127+
//disable the compare match interrupt
128+
TIMSK2 &= ~_BV(OCIE2A);
129+
//set both outputs to zero to disable H-Bridge
130+
#if defined(__AVR_ATmega48__) || defined(__AVR_ATmega88__) || defined(__AVR_ATmega168__) || defined(__AVR_ATmega328__)
131+
PINB &= ~_BV(PINB3);
132+
PIND &= ~_BV(PIND3);
133+
#endif
134+
}
135+
136+
/// This is the Interrupt Service Routine (ISR) for Timer1 compare match on output A. We don't worry about the interrupt on output B, since we want B to mirror A; we'll update them both here.
137+
ISR(TIMER2_COMPA_vect)
138+
{
139+
//in CTC mode, timer TCINT2 automatically resets to 0 when it matches OCR2A. Depending on the next bit to output,
140+
//we may have to alter the value in OCR2A, maybe.
141+
//to switch between "one" waveform and "zero" waveform, we assign a value to OCR2A.
142+
143+
//remember, anything we set for OCR2A takes effect IMMEDIATELY, so we are working within the cycle we are setting.
144+
//first, check to see if we're in the second half of a byte; only act on the first half of a byte
145+
//On Arduino UNO, etc, OC2A is digital pin 11, or Port B/Pin 3
146+
if(!(PINB & _BV(PINB3))) //if the pin is high, time to do our work setting up whether this should be a zero or one.
147+
{
148+
//time to switch things up, maybe. send the current bit in the current packet.
149+
//if this is the last bit to send, queue up another packet (might be the idle packet).
150+
switch(DCC_state)
151+
{
152+
/// Idle: Check if a new packet is ready. If it is, fall through to dos_send_premable. Otherwise just stick a '1' out there.
153+
case dos_idle:
154+
if(!current_byte_counter) //if no new packet
155+
{
156+
// Serial.println("X");
157+
OCR2A = OCR2B = one_count; //just send ones if we don't know what else to do. safe bet.
158+
break;
159+
}
160+
//looks like there's a new packet for us to dump on the wire!
161+
//for debugging purposes, let's print it out
162+
// if(current_packet[1] != 0xFF)
163+
// {
164+
// Serial.print("Packet: ");
165+
// for(byte j = 0; j < current_packet_size; ++j)
166+
// {
167+
// Serial.print(current_packet[j],HEX);
168+
// Serial.print(" ");
169+
// }
170+
// Serial.println("");
171+
// }
172+
DCC_state = dos_send_preamble; //and fall through to dos_send_preamble
173+
/// Preamble: In the process of producing 14 '1's, counter by current_bit_counter; when complete, move to dos_send_bstart
174+
case dos_send_preamble:
175+
OCR2A = OCR2B = one_count;
176+
// Serial.print("P");
177+
if(!--current_bit_counter)
178+
DCC_state = dos_send_bstart;
179+
break;
180+
/// About to send a data byte, but have to peceed the data with a '0'. Send that '0', then move to dos_send_byte
181+
case dos_send_bstart:
182+
OCR2A = OCR2B = zero_count;
183+
DCC_state = dos_send_byte;
184+
current_bit_counter = 8;
185+
// Serial.print(" 0 ");
186+
break;
187+
/// Sending a data byte; current bit is tracked with current_bit_counter, and current byte with current_byte_counter
188+
case dos_send_byte:
189+
if(((current_packet[current_packet_size-current_byte_counter])>>(current_bit_counter-1)) & 1) //is current bit a '1'?
190+
{
191+
OCR2A = OCR2B = one_count;
192+
// Serial.print("1");
193+
}
194+
else //or is it a '0'
195+
{
196+
OCR2A = OCR2B = zero_count;
197+
// Serial.print("0");
198+
}
199+
if(!--current_bit_counter) //out of bits! time to either send a new byte, or end the packet
200+
{
201+
if(!--current_byte_counter) //if not more bytes, move to dos_end_bit
202+
{
203+
DCC_state = dos_end_bit;
204+
}
205+
else //there are more bytes…so, go back to dos_send_bstart
206+
{
207+
DCC_state = dos_send_bstart;
208+
}
209+
}
210+
break;
211+
/// Done with the packet. Send out a final '1', then head back to dos_idle to check for a new packet.
212+
case dos_end_bit:
213+
OCR2A = OCR2B = one_count;
214+
DCC_state = dos_idle;
215+
current_bit_counter = PREAMBLE_LENGTH; //in preparation for a premable...
216+
// Serial.println(" 1");
217+
break;
218+
}
219+
}
220+
}
221+
222+
///////////////////////////////////////////////
223+
///////////////////////////////////////////////
224+
///////////////////////////////////////////////
225+
226+
DCCServiceModeScheduler::DCCServiceModeScheduler(void) : _accepts_direct_mode(false), _mode(idle_mode), _wasACKd(false), _over_current_timer(0), _ack_timer(0)
227+
{
228+
_mode = idle_mode;
229+
_wasACKd = false;
230+
_power_on_queue.setup();
231+
_instruction_queue.setup();
232+
_conditional_queue.setup();
233+
_over_current_timer = 0;
234+
_ack_timer = 0;
235+
}
236+
237+
void DCCServiceModeScheduler::reset(void)
238+
{
239+
_mode = idle_mode;
240+
_wasACKd = false;
241+
_power_on_queue.clear();
242+
_instruction_queue.clear();
243+
_conditional_queue.clear();
244+
_over_current_timer = 0;
245+
_ack_timer = 0;
246+
}
247+
248+
void DCCServiceModeScheduler::setup(void) //for any post-constructor initialization
249+
{
250+
setup_DCC_service_mode_waveform_generator();
251+
}
252+
253+
//to be called periodically within loop()
254+
//TODO This function is pretty much a mess! clean this up once we have a better idea of what we're doing.
255+
256+
//modes:
257+
// power-on: 20+ packets to permit decoder to power on. A load of 250mA sustained after 100ms = short circuit.
258+
// instruction transmit:
259+
// wait for ack: watch for an increase in current draw (at least 60mA) for TODO ms to count as ACK
260+
// else timeout at end of packet queue and register as NAK
261+
// power-off:
262+
//
263+
264+
void DCCServiceModeScheduler::update(void) //checks queues, puts whatever's pending on the rails via global current_packet. easy-peasy
265+
{
266+
267+
//first, check for an over-current; abort current operation if detected.
268+
if(_over_current_timer) //if the timer is started
269+
{
270+
uint16_t check = analogRead(CURRENT_SENSE_PIN);
271+
uint16_t time = millis();
272+
if( (check >= OVER_CURRENT_VALUE) && ( (time - _over_current_timer) >= 100) )
273+
{
274+
//over current detected! shut it down!
275+
DCC_service_mode_waveform_generation_yame();
276+
_mode = idle_mode;
277+
_power_on_queue.clear();
278+
_instruction_queue.clear();
279+
_conditional_queue.clear();
280+
_over_current_timer = 0;
281+
return;
282+
}
283+
}
284+
285+
286+
if(_mode == idle_mode)
287+
return;
288+
289+
//Feed me! The ISR needs a packet!
290+
if(!current_byte_counter)
291+
{
292+
//now, queue up the next packet
293+
DCCServiceModePacket p = DCCServiceModePacket();
294+
295+
switch(_mode)
296+
{
297+
case power_on_initial_mode:
298+
//turn on the h-bridge
299+
//start the over-current timer
300+
_wasACKd = false; //reset ack
301+
_over_current_timer = millis();
302+
DCC_service_mode_waveform_generation_hajime();
303+
_mode = power_on_mode;
304+
//no break
305+
case power_on_mode:
306+
if(!_power_on_queue.readPacket(p))
307+
{
308+
_instruction_queue.readPacket(p);
309+
_mode = recovery_mode;
310+
}
311+
break;
312+
313+
case recovery_mode:
314+
//need to watch for an ack from here forward!
315+
uint16_t check = analogRead(CURRENT_SENSE_PIN);
316+
uint16_t time = millis();
317+
if(check >= ACK_THRESHOLD)
318+
{
319+
//check/start the timer
320+
if(_ack_timer)
321+
{
322+
if( (time - _ack_timer) >= ACK_TIMEOUT )
323+
{
324+
//got an ack!! What to do with it!?
325+
reset();
326+
_wasACKd = true;
327+
return;
328+
}
329+
}
330+
}
331+
if(!_instruction_queue.readPacket(p))
332+
{
333+
//done with sequence. power off and reset.
334+
DCC_service_mode_waveform_generation_yame();
335+
reset();
336+
return;
337+
}
338+
break;
339+
}
340+
341+
//feed the ISR:
342+
current_packet_size = p.getBitstream(current_packet); //feed to the starving ISR.
343+
current_byte_counter = current_packet_size;
344+
}
345+
}
346+
347+
348+
/** Here's the general gist of how we're going to write a CV (or part thereof):
349+
set up the repeat queue with the necessary packets for the mode, operation, CV, and value
350+
set a flag to indicate how many packets should pass before we start watching for an ack
351+
allow repeat() to run...
352+
when the indicated number of packets begin to pass, watch for an ACK. Will need a pin change
353+
interrupt on one of the analog pins, and a timer? use millis().
354+
if ACK received, shut down programming track
355+
if no ack received by the time we run out of packets to send, count as NAK
356+
Will use a similar procedure for verifying a CV.
357+
**/
358+
359+
bool DCCServiceModeScheduler::directModeWriteByte(uint16_t CV, uint8_t value)
360+
{
361+
//Packet stream:
362+
// power-on (see above)
363+
// 3 or more reset packets
364+
// 5 or more writes to a single CV
365+
// 6 or more identical write or reset packets (decoder recovery time)
366+
// power off
367+
368+
DCCServiceModePacket p;
369+
370+
//first, check to see if something is currently being put on the rails.
371+
if(_mode != idle_mode)
372+
return false;
373+
374+
// load up 20 idle packets for power-on
375+
p.setRepeat(20);
376+
_power_on_queue.insertPacket(p); //p defaults to idle packet, no need to set it up.
377+
378+
// load up 3 or more reset packets
379+
p.makeReset();
380+
_power_on_queue.insertPacket(p);
381+
382+
383+
//Instructions packets using Direct CV Addressing are 4 byte packets of the format:
384+
// long-preamble 0 011111AA 0 AAAAAAAA 0 DDDDDDDD 0 EEEEEEEE 1
385+
p.setData( (0b01111100 | ((CV>>8)&0x03)), CV&0xFF, value);
386+
p.setRepeat(5);
387+
_instruction_queue.insertPacket(p);
388+
389+
//and send one or more reset packets if an ack is found. Ugh.
390+
p.makeReset();
391+
p.setRepeat(6);
392+
_instruction_queue.insertPacket(p);
393+
394+
_mode = power_on_initial_mode;
395+
396+
return true;
397+
}

‎DCCServiceModeScheduler.h

+95
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
#ifndef __DCCSERVICEMODESCHEDULER_H__
2+
#define __DCCSERVICEMODESCHEDULER_H__
3+
#include "DCCServiceModePacket.h"
4+
#include "DCCServiceModeQueue.h"
5+
#include "WProgram.h"
6+
#include <avr/io.h>
7+
#include <avr/interrupt.h>
8+
9+
#ifndef CURRENT_SENSE_PIN
10+
#define CURRENT_SENSE_PIN 3
11+
#endif
12+
13+
#ifndef OVER_CURRENT_VALUE
14+
#define OVER_CURRENT_VALUE 200
15+
#endif
16+
17+
#ifndef ACK_THRESHOLD
18+
#define ACK_THRESHOLD 100
19+
#endif
20+
21+
enum physical_register_t
22+
{
23+
address_register,
24+
start_voltage_register,
25+
acceleration_register,
26+
deceleration_register,
27+
basic_configuration_register,
28+
reserved_register,
29+
version_number_register,
30+
manufacturer_ID_register
31+
};
32+
33+
enum programmer_mode_t
34+
{
35+
idle_mode,
36+
power_on_initial_mode,
37+
power_on_mode,
38+
recovery_mode
39+
};
40+
41+
class DCCServiceModeScheduler
42+
{
43+
public:
44+
45+
DCCServiceModeScheduler(void);
46+
47+
// bool writeByte(uint16_t CV, uint8_t value);
48+
// uint8_t readByte(uint16_t CV);
49+
// bool writeAddress(uint_16t address);
50+
// uint_16t readAddress();
51+
// bool hardReset(void); //reset decoder to factory state.
52+
53+
void setup(void); //for any post-constructor initialization
54+
55+
//to be called periodically within loop()
56+
void update(void);
57+
58+
//reset software
59+
void reset(void);
60+
61+
62+
bool checkACK(void) {return _wasACKd;}
63+
64+
65+
// bool checkSupportDirectMode(void);
66+
bool directModeWriteByte(uint16_t CV, uint8_t value);
67+
// bool directModeVerifyByte(uint16_t CV, uint8_t value);
68+
// bool directModeWriteBit(uint16_t CV, uint8_t bit, uint8_t value);
69+
// bool directModeVerifyBit(uint16_t CV, uint8_t bit, uint8_t value);
70+
71+
// bool addressOnlyModeWriteAddress(uint8_t address);
72+
// bool addressOnlyModeVerifyAddress(uint8_t address);
73+
74+
// bool physicalRegsiterModeWriteByte(physical_register_t register, uint8_t value);
75+
// bool physicalRegsiterModeVerifyByte(physical_register_t register, uint8_t value);
76+
77+
// bool pagedRegisterModeWriteByte(uint16_t CV, uint8_t value);
78+
// bool pagedRegisterModeVerifyByte(uint16_t CV, uint8_t value);
79+
80+
81+
private:
82+
83+
bool _accepts_direct_mode;
84+
bool _tested_direct_mode;
85+
bool _wasACKd;
86+
programmer_mode_t _mode;
87+
uint16_t _over_current_timer;
88+
uint16_t _ack_timer;
89+
DCCServiceModeQueue _power_on_queue;
90+
DCCServiceModeQueue _instruction_queue;
91+
DCCServiceModeQueue _conditional_queue;
92+
};
93+
94+
95+
#endif //__DCCSERVICEMODESCHEDULER_H__
+30
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
#include <DCCServiceModePacket.h>
2+
#include <DCCServiceModeQueue.h>
3+
#include <DCCServiceModeScheduler.h>
4+
5+
/********************
6+
* Creates a minimum DCC command station from a potentiometer connected to analog pin 0,
7+
* and a button connected to ground on one end and digital pin 4 on the other end. See this link
8+
* http://www.arduino.cc/en/Tutorial/AnalogInput
9+
* The DCC waveform is output on Pin 9, and is suitable for connection to an LMD18200-based booster directly,
10+
* or to a single-ended-to-differential driver, to connect with most other kinds of boosters.
11+
********************/
12+
13+
DCCServiceModeScheduler dps;
14+
15+
void setup()
16+
{
17+
Serial.begin(115200);
18+
Serial.println("Hello!");
19+
20+
dps.setup();
21+
22+
////////
23+
24+
dps.directModeWriteByte(29,6); //a safe value for CV 29
25+
}
26+
27+
void loop()
28+
{
29+
dps.update();
30+
}

0 commit comments

Comments
 (0)
Please sign in to comment.