diff --git a/DCCpp.zip b/DCCpp.zip index 1616e33..ce7075e 100644 Binary files a/DCCpp.zip and b/DCCpp.zip differ diff --git a/extras/Doc/Comm_8h_source.html b/extras/Doc/Comm_8h_source.html index a35e10f..6caf37f 100644 --- a/extras/Doc/Comm_8h_source.html +++ b/extras/Doc/Comm_8h_source.html @@ -101,7 +101,7 @@
-
1 /**********************************************************************
2 
3 CurrentMonitor.h
4 COPYRIGHT (c) 2013-2016 Gregg E. Berman
5 
6 Part of DCC++ BASE STATION for the Arduino
7 
8 **********************************************************************/
9 
10 #ifdef ARDUINO_ARCH_AVR
11 #ifndef CurrentMonitor_h
12 #define CurrentMonitor_h
13 
15 #define CURRENT_SAMPLE_SMOOTHING 0.01
16 
20 #if defined(ARDUINO_AVR_UNO) || defined(ARDUINO_AVR_NANO) // Configuration for UNO
21  #define CURRENT_SAMPLE_TIME 10
22 #else // Configuration for MEGA
23  #define CURRENT_SAMPLE_TIME 1
24 #endif
25 
28 struct CurrentMonitor{
29  static long int sampleTime;
30  int pin;
31  float currentSampleMax;
32  float current;
33  const char *msg;
39  void begin(int pin, const char *msg, float inSampleMax = 300);
42  static boolean checkTime();
45  void check();
46 };
47 
48 #endif
49 #endif
+
1 /**********************************************************************
2 
3 CurrentMonitor.h
4 COPYRIGHT (c) 2013-2016 Gregg E. Berman
5 
6 Part of DCC++ BASE STATION for the Arduino
7 
8 **********************************************************************/
9 
10 #ifdef ARDUINO_ARCH_AVR
11 #ifndef CurrentMonitor_h
12 #define CurrentMonitor_h
13 
15 #define CURRENT_SAMPLE_SMOOTHING 0.01
16 
20 #if defined(ARDUINO_AVR_UNO) || defined(ARDUINO_AVR_NANO) // Configuration for UNO
21  #define CURRENT_SAMPLE_TIME 10
22 #else // Configuration for MEGA
23  #define CURRENT_SAMPLE_TIME 1
24 #endif
25 
28 struct CurrentMonitor{
29  static long int sampleTime;
30  int pin;
31  int signalPin;
32  float currentSampleMax;
33  float current;
34  const char *msg;
41  void begin(int pin, int inSignalPin, const char *msg, float inSampleMax = 300);
44  static boolean checkTime();
47  void check();
48 };
49 
50 #endif
51 #endif
-
1 /*************************************************************
2 project: <DCCpp library>
3 author: <Thierry PARIS>
4 description: <DCCpp class>
5 *************************************************************/
6 
7 #include "Arduino.h"
8 #include "DCCpp.h"
9 
10 // NEXT DECLARE GLOBAL OBJECTS TO PROCESS AND STORE DCC PACKETS AND MONITOR TRACK CURRENTS.
11 // NOTE REGISTER LISTS MUST BE DECLARED WITH "VOLATILE" QUALIFIER TO ENSURE THEY ARE PROPERLY UPDATED BY INTERRUPT ROUTINES
12 
13 volatile RegisterList DCCpp::mainRegs(MAX_MAIN_REGISTERS); // create list of registers for MAX_MAIN_REGISTER Main Track Packets
14 volatile RegisterList DCCpp::progRegs(3); // create a shorter list of only two registers for Program Track Packets
15 
16 CurrentMonitor DCCpp::mainMonitor; // create monitor for current on Main Track
17 CurrentMonitor DCCpp::progMonitor; // create monitor for current on Program Track
18 
19 bool DCCpp::programMode;
20 bool DCCpp::panicStopped;
21 
22 // *********************************************************** FunctionsState
23 
25 {
26  this->clear();
27 }
28 
30 {
31  // Clear all functions
32  this->activeFlags[0] = 0;
33  this->activeFlags[1] = 0;
34  this->activeFlags[2] = 0;
35  this->activeFlags[3] = 0;
36 
37  this->statesSent();
38 }
39 
40 void FunctionsState::activate(byte inFunctionNumber)
41 {
42  bitSet(this->activeFlags[inFunctionNumber / 8], inFunctionNumber % 8);
43 }
44 
45 void FunctionsState::inactivate(byte inFunctionNumber)
46 {
47  bitClear(this->activeFlags[inFunctionNumber / 8], inFunctionNumber % 8);
48 }
49 
50 bool FunctionsState::isActivated(byte inFunctionNumber)
51 {
52  return bitRead(this->activeFlags[inFunctionNumber / 8], inFunctionNumber % 8);
53 }
54 
55 bool FunctionsState::isActivationChanged(byte inFunctionNumber)
56 {
57  return bitRead(this->activeFlagsSent[inFunctionNumber / 8], inFunctionNumber % 8) != isActivated(inFunctionNumber);
58 }
59 
61 {
62  for (int i = 0; i < 4; i++)
63  this->activeFlagsSent[i] = this->activeFlags[i];
64 }
65 
66 #ifdef DCCPP_DEBUG_MODE
67 void FunctionsState::printActivated()
68 {
69  for (int i = 0; i < 32; i++)
70  {
71  if (this->isActivated(i))
72  {
73  Serial.print(i);
74  Serial.print(" ");
75  }
76  }
77 
78  Serial.println("");
79 }
80 #endif
81 
82 // *********************************************************** end of FunctionsState
83 
84 // *********************************************************** DCCpp class
85 
86 static bool first = true;
87 
89 // MAIN ARDUINO LOOP
91 
93 {
94 #ifdef USE_TEXTCOMMAND
95  TextCommand::process(); // check for, and process, and new serial commands
96 #endif
97 
98  if (first)
99  {
100  first = false;
101 #if defined(DCCPP_DEBUG_MODE) && defined(DCCPP_PRINT_DCCPP)
102  showConfiguration();
103 #endif
104  }
105 
106  if (CurrentMonitor::checkTime())
107  { // if sufficient time has elapsed since last update, check current draw on Main and Program Tracks
108  mainMonitor.check();
109  progMonitor.check();
110  }
111 
112 #ifdef USE_SENSOR
113  Sensor::check(); // check sensors for activated or not
114 #endif
115 }
116 
117 void DCCpp::beginMain(uint8_t inOptionalDirectionMotor, uint8_t inSignalPin, uint8_t inSignalEnable, uint8_t inCurrentMonitor)
118 {
119  DCCppConfig::DirectionMotorA = inOptionalDirectionMotor;
120  DCCppConfig::SignalEnablePinMain = inSignalEnable; // PWM
121  DCCppConfig::CurrentMonitorMain = inCurrentMonitor;
122 
123  // If no main line, exit.
124  if (DCCppConfig::SignalEnablePinMain == UNDEFINED_PIN)
125  {
126 #ifdef DCCPP_DEBUG_MODE
127  Serial.println("No main track");
128 #endif
129  return;
130  }
131 
132  mainMonitor.begin(DCCppConfig::CurrentMonitorMain, (char *) "<p2>");
133 
134  // CONFIGURE TIMER_1 TO OUTPUT 50% DUTY CYCLE DCC SIGNALS ON OC1B INTERRUPT PINS
135 
136  // Direction Pin for Motor Shield Channel A - MAIN OPERATIONS TRACK
137  // Controlled by Arduino 16-bit TIMER 1 / OC1B Interrupt Pin
138  // Values for 16-bit OCR1A and OCR1B registers calibrated for 1:1 prescale at 16 MHz clock frequency
139  // Resulting waveforms are 200 microseconds for a ZERO bit and 116 microseconds for a ONE bit with exactly 50% duty cycle
140 
141 #define DCC_ZERO_BIT_TOTAL_DURATION_TIMER1 3199
142 #define DCC_ZERO_BIT_PULSE_DURATION_TIMER1 1599
143 
144 #define DCC_ONE_BIT_TOTAL_DURATION_TIMER1 1855
145 #define DCC_ONE_BIT_PULSE_DURATION_TIMER1 927
146  if (DCCppConfig::DirectionMotorA != UNDEFINED_PIN)
147  {
148  pinMode(DCCppConfig::DirectionMotorA, INPUT); // ensure this pin is not active! Direction will be controlled by DCC SIGNAL instead (below)
149  digitalWrite(DCCppConfig::DirectionMotorA, LOW);
150  }
151 
152  if (inSignalPin != UNDEFINED_PIN)
153  pinMode(inSignalPin, OUTPUT); // FOR SHIELDS, THIS ARDUINO OUTPUT PIN MUST BE PHYSICALY CONNECTED TO THE PIN FOR DIRECTION-A OF MOTOR CHANNEL-A
154 
155  bitSet(TCCR1A, WGM10); // set Timer 1 to FAST PWM, with TOP=OCR1A
156  bitSet(TCCR1A, WGM11);
157  bitSet(TCCR1B, WGM12);
158  bitSet(TCCR1B, WGM13);
159 
160  bitSet(TCCR1A, COM1B1); // set Timer 1, OC1B (pin 10/UNO, pin 12/MEGA) to inverting toggle (actual direction is arbitrary)
161  bitSet(TCCR1A, COM1B0);
162 
163  bitClear(TCCR1B, CS12); // set Timer 1 prescale=1
164  bitClear(TCCR1B, CS11);
165  bitSet(TCCR1B, CS10);
166 
167  OCR1A = DCC_ONE_BIT_TOTAL_DURATION_TIMER1;
168  OCR1B = DCC_ONE_BIT_PULSE_DURATION_TIMER1;
169 
170  pinMode(DCCppConfig::SignalEnablePinMain, OUTPUT); // master enable for motor channel A
171 
172  mainRegs.loadPacket(1, RegisterList::idlePacket, 2, 0); // load idle packet into register 1
173 
174  bitSet(TIMSK1, OCIE1B); // enable interrupt vector for Timer 1 Output Compare B Match (OCR1B)
175  digitalWrite(DCCppConfig::SignalEnablePinMain, LOW);
176 
177 #ifdef DCCPP_DEBUG_MODE
178  Serial.println(F("beginMain achivied"));
179 #endif
180 }
181 
182 void DCCpp::beginProg(uint8_t inOptionalDirectionMotor, uint8_t inSignalPin, uint8_t inSignalEnable, uint8_t inCurrentMonitor)
183 {
184  DCCppConfig::DirectionMotorB = inOptionalDirectionMotor;
185  DCCppConfig::SignalEnablePinProg = inSignalEnable;
186  DCCppConfig::CurrentMonitorProg = inCurrentMonitor;
187 
188  // If no programming line, exit.
189  if (DCCppConfig::SignalEnablePinProg == UNDEFINED_PIN)
190  {
191 #ifdef DCCPP_DEBUG_MODE
192  Serial.println("No prog track");
193 #endif
194  return;
195  }
196 
197  progMonitor.begin(DCCppConfig::CurrentMonitorProg, (char *) "<p3>");
198 
199  // CONFIGURE EITHER TIMER_0 (UNO) OR TIMER_3 (MEGA) TO OUTPUT 50% DUTY CYCLE DCC SIGNALS ON OC0B (UNO) OR OC3B (MEGA) INTERRUPT PINS
200 
201 #if defined(ARDUINO_AVR_UNO) || defined(ARDUINO_AVR_NANO) // Configuration for UNO
202 
203  // Direction Pin for Motor Shield Channel B - PROGRAMMING TRACK
204  // Controlled by Arduino 8-bit TIMER 0 / OC0B Interrupt Pin
205  // Values for 8-bit OCR0A and OCR0B registers calibrated for 1:64 prescale at 16 MHz clock frequency
206  // Resulting waveforms are 200 microseconds for a ZERO bit and 116 microseconds for a ONE bit with as-close-as-possible to 50% duty cycle
207 
208 #define DCC_ZERO_BIT_TOTAL_DURATION_TIMER0 49
209 #define DCC_ZERO_BIT_PULSE_DURATION_TIMER0 24
210 
211 #define DCC_ONE_BIT_TOTAL_DURATION_TIMER0 28
212 #define DCC_ONE_BIT_PULSE_DURATION_TIMER0 14
213 
214  if (DCCppConfig::DirectionMotorB != UNDEFINED_PIN)
215  {
216  pinMode(DCCppConfig::DirectionMotorB, INPUT); // ensure this pin is not active! Direction will be controlled by DCC SIGNAL instead (below)
217  digitalWrite(DCCppConfig::DirectionMotorB, LOW);
218  }
219 
220  if (inSignalPin != UNDEFINED_PIN)
221  pinMode(inSignalPin, OUTPUT); // THIS ARDUINO OUTPUT PIN MUST BE PHYSICALY CONNECTED TO THE PIN FOR DIRECTION-B OF MOTOR CHANNEL-B
222 
223  bitSet(TCCR0A, WGM00); // set Timer 0 to FAST PWM, with TOP=OCR0A
224  bitSet(TCCR0A, WGM01);
225  bitSet(TCCR0B, WGM02);
226 
227  bitSet(TCCR0A, COM0B1); // set Timer 0, OC0B (pin 5) to inverting toggle (actual direction is arbitrary)
228  bitSet(TCCR0A, COM0B0);
229 
230  bitClear(TCCR0B, CS02); // set Timer 0 prescale=64
231  bitSet(TCCR0B, CS01);
232  bitSet(TCCR0B, CS00);
233 
234  OCR0A = DCC_ONE_BIT_TOTAL_DURATION_TIMER0;
235  OCR0B = DCC_ONE_BIT_PULSE_DURATION_TIMER0;
236 
237  pinMode(DCCppConfig::SignalEnablePinProg, OUTPUT); // master enable for motor channel B
238 
239  progRegs.loadPacket(1, RegisterList::idlePacket, 2, 0); // load idle packet into register 1
240 
241  bitSet(TIMSK0, OCIE0B); // enable interrupt vector for Timer 0 Output Compare B Match (OCR0B)
242 
243 #else // Configuration for MEGA
244 
245  // Direction Pin for Motor Shield Channel B - PROGRAMMING TRACK
246  // Controlled by Arduino 16-bit TIMER 3 / OC3B Interrupt Pin
247  // Values for 16-bit OCR3A and OCR3B registers calibrated for 1:1 prescale at 16 MHz clock frequency
248  // Resulting waveforms are 200 microseconds for a ZERO bit and 116 microseconds for a ONE bit with exactly 50% duty cycle
249 
250 #define DCC_ZERO_BIT_TOTAL_DURATION_TIMER3 3199
251 #define DCC_ZERO_BIT_PULSE_DURATION_TIMER3 1599
252 
253 #define DCC_ONE_BIT_TOTAL_DURATION_TIMER3 1855
254 #define DCC_ONE_BIT_PULSE_DURATION_TIMER3 927
255 
256  if (DCCppConfig::DirectionMotorB != UNDEFINED_PIN)
257  {
258  pinMode(DCCppConfig::DirectionMotorB, INPUT); // ensure this pin is not active! Direction will be controlled by DCC SIGNAL instead (below)
259  digitalWrite(DCCppConfig::DirectionMotorB, LOW);
260  }
261 
262  pinMode(DCC_SIGNAL_PIN_PROG, OUTPUT); // THIS ARDUINO OUTPUT PIN MUST BE PHYSICALLY CONNECTED TO THE PIN FOR DIRECTION-B OF MOTOR CHANNEL-B
263 
264  bitSet(TCCR3A, WGM30); // set Timer 3 to FAST PWM, with TOP=OCR3A
265  bitSet(TCCR3A, WGM31);
266  bitSet(TCCR3B, WGM32);
267  bitSet(TCCR3B, WGM33);
268 
269  bitSet(TCCR3A, COM3B1); // set Timer 3, OC3B (pin 2) to inverting toggle (actual direction is arbitrary)
270  bitSet(TCCR3A, COM3B0);
271 
272  bitClear(TCCR3B, CS32); // set Timer 3 prescale=1
273  bitClear(TCCR3B, CS31);
274  bitSet(TCCR3B, CS30);
275 
276  OCR3A = DCC_ONE_BIT_TOTAL_DURATION_TIMER3;
277  OCR3B = DCC_ONE_BIT_PULSE_DURATION_TIMER3;
278 
279  pinMode(DCCppConfig::SignalEnablePinProg, OUTPUT); // master enable for motor channel B
280 
281  progRegs.loadPacket(1, RegisterList::idlePacket, 2, 0); // load idle packet into register 1
282 
283  bitSet(TIMSK3, OCIE3B); // enable interrupt vector for Timer 3 Output Compare B Match (OCR3B)
284 
285 #endif
286  digitalWrite(DCCppConfig::SignalEnablePinProg, LOW);
287 
288 #ifdef DCCPP_DEBUG_MODE
289  Serial.println(F("beginProg achivied"));
290 #endif
291 }
292 
294 {
295  programMode = false;
296  panicStopped = false;
297 
298  DCCppConfig::SignalEnablePinMain = UNDEFINED_PIN;
299  DCCppConfig::CurrentMonitorMain = UNDEFINED_PIN;
300 
301  DCCppConfig::SignalEnablePinProg = UNDEFINED_PIN;
302  DCCppConfig::CurrentMonitorProg = UNDEFINED_PIN;
303 
304  DCCppConfig::DirectionMotorA = UNDEFINED_PIN;
305  DCCppConfig::DirectionMotorB = UNDEFINED_PIN;
306 
307  mainMonitor.begin(UNDEFINED_PIN, "");
308  progMonitor.begin(UNDEFINED_PIN, "");
309 
310 #ifdef SDCARD_CS
311  pinMode(SDCARD_CS, OUTPUT);
312  digitalWrite(SDCARD_CS, HIGH); // De-select the SD card
313 #endif
314 
315 #ifdef USE_EEPROM
316  EEStore::init(); // initialize and load Turnout and Sensor definitions stored in EEPROM
317  if (EEStore::needsRefreshing())
318  EEStore::store();
319 #endif
320 
321 #ifdef DCCPP_DEBUG_MODE
322  //pinMode(LED_BUILTIN, OUTPUT);
323  Serial.println(F("begin achieved"));
324 #endif
325 
326 } // begin
327 
328 #ifdef USE_ETHERNET
329 void DCCpp::beginEthernet(uint8_t *inMac, uint8_t *inIp, EthernetProtocol inProtocol)
330 {
331  if (inIp != NULL)
332  for (int i = 0; i < 4; i++)
333  DCCppConfig::EthernetIp[i] = inIp[i];
334 
335  for (int i = 0; i < 6; i++)
336  DCCppConfig::EthernetMac[i] = inMac[i];
337 
338  DCCppConfig::Protocol = inProtocol;
339 
340  if (inIp == NULL)
341  Ethernet.begin(inMac); // Start networking using DHCP to get an IP Address
342  else
343  Ethernet.begin(inMac, inIp); // Start networking using STATIC IP Address
344 
345  INTERFACE.begin();
346 #ifdef DCCPP_DEBUG_MODE
347  //pinMode(LED_BUILTIN, OUTPUT);
348  showConfiguration();
349  Serial.println(F("beginEthernet achieved"));
350 #endif
351 } // beginEthernet
352 #endif
353 
355  // DEFINE THE INTERRUPT LOGIC THAT GENERATES THE DCC SIGNAL
357 
358  // The code below will be called every time an interrupt is triggered on OCNB, where N can be 0 or 1.
359  // It is designed to read the current bit of the current register packet and
360  // updates the OCNA and OCNB counters of Timer-N to values that will either produce
361  // a long (200 microsecond) pulse, or a short (116 microsecond) pulse, which respectively represent
362  // DCC ZERO and DCC ONE bits.
363 
364  // These are hardware-driven interrupts that will be called automatically when triggered regardless of what
365  // DCC++ BASE STATION was otherwise processing. But once inside the interrupt, all other interrupt routines are temporarily disabled.
366  // Since a short pulse only lasts for 116 microseconds, and there are TWO separate interrupts
367  // (one for Main Track Registers and one for the Program Track Registers), the interrupt code must complete
368  // in much less than 58 microseconds, otherwise there would be no time for the rest of the program to run. Worse, if the logic
369  // of the interrupt code ever caused it to run longer than 58 microseconds, an interrupt trigger would be missed, the OCNA and OCNB
370  // registers would not be updated, and the net effect would be a DCC signal that keeps sending the same DCC bit repeatedly until the
371  // interrupt code completes and can be called again.
372 
373  // A significant portion of this entire program is designed to do as much of the heavy processing of creating a properly-formed
374  // DCC bit stream upfront, so that the interrupt code below can be as simple and efficient as possible.
375 
376  // Note that we need to create two very similar copies of the code --- one for the Main Track OC1B interrupt and one for the
377  // Programming Track OCOB interrupt. But rather than create a generic function that incurs additional overhead, we create a macro
378  // that can be invoked with proper parameters for each interrupt. This slightly increases the size of the code base by duplicating
379  // some of the logic for each interrupt, but saves additional time.
380 
381  // As structured, the interrupt code below completes at an average of just under 6 microseconds with a worse-case of just under 11 microseconds
382  // when a new register is loaded and the logic needs to switch active register packet pointers.
383 
384  // THE INTERRUPT CODE MACRO: R=REGISTER LIST (mainRegs or progRegs), and N=TIMER (0 or 1)
385 
386 #define DCC_SIGNAL(R,N)
387  if(R.currentBit==R.currentReg->activePacket->nBits){ /* IF no more bits in this DCC Packet */
388  R.currentBit=0; /* reset current bit pointer and determine which Register and Packet to process next--- */
389  if (R.nRepeat>0 && R.currentReg == R.reg) { /* IF current Register is first Register AND should be repeated */
390  R.nRepeat--; /* decrement repeat count; result is this same Packet will be repeated */
391  }
392  else if (R.nextReg != NULL) { /* ELSE IF another Register has been updated */
393  R.currentReg = R.nextReg; /* update currentReg to nextReg */
394  R.nextReg = NULL; /* reset nextReg to NULL */
395  R.tempPacket = R.currentReg->activePacket; /* flip active and update Packets */
396  R.currentReg->activePacket = R.currentReg->updatePacket;
397  R.currentReg->updatePacket = R.tempPacket;
398  }
399  else { /* ELSE simply move to next Register */
400  if (R.currentReg == R.maxLoadedReg) /* BUT IF this is last Register loaded */
401  R.currentReg = R.reg; /* first reset currentReg to base Register, THEN */
402  R.currentReg++; /* increment current Register (note this logic causes Register[0] to be skipped when simply cycling through all Registers) */
403  } /* END-ELSE */
404  } /* END-IF: currentReg, activePacket, and currentBit should now be properly set to point to next DCC bit */
405 
406  if (R.currentReg->activePacket->buf[R.currentBit / 8] & R.bitMask[R.currentBit % 8]) { /* IF bit is a ONE */
407  OCR ## N ## A = DCC_ONE_BIT_TOTAL_DURATION_TIMER ## N; /* set OCRA for timer N to full cycle duration of DCC ONE bit */
408  OCR ## N ## B=DCC_ONE_BIT_PULSE_DURATION_TIMER ## N; /* set OCRB for timer N to half cycle duration of DCC ONE but */
409  } else{ /* ELSE it is a ZERO */
410  OCR ## N ## A=DCC_ZERO_BIT_TOTAL_DURATION_TIMER ## N; /* set OCRA for timer N to full cycle duration of DCC ZERO bit */
411  OCR ## N ## B=DCC_ZERO_BIT_PULSE_DURATION_TIMER ## N; /* set OCRB for timer N to half cycle duration of DCC ZERO bit */
412  } /* END-ELSE */
413 
414  R.currentBit++; /* point to next bit in current Packet */
415 
417 // NOW USE THE ABOVE MACRO TO CREATE THE CODE FOR EACH INTERRUPT
418 
419 ISR(TIMER1_COMPB_vect) { // set interrupt service for OCR1B of TIMER-1 which flips direction bit of Motor Shield Channel A controlling Main Track
420  DCC_SIGNAL(DCCpp::mainRegs, 1)
421 }
422 
423 #if defined(ARDUINO_AVR_UNO) || defined(ARDUINO_AVR_NANO) // Configuration for UNO
424 
425 ISR(TIMER0_COMPB_vect) { // set interrupt service for OCR1B of TIMER-0 which flips direction bit of Motor Shield Channel B controlling Programming Track
426  DCC_SIGNAL(DCCpp::progRegs, 0)
427 }
428 
429 #else // Configuration for MEGA
430 
431 ISR(TIMER3_COMPB_vect) { // set interrupt service for OCR3B of TIMER-3 which flips direction bit of Motor Shield Channel B controlling Programming Track
432  DCC_SIGNAL(DCCpp::progRegs, 3)
433 }
434 
435 #endif
436 
437 #ifdef DCCPP_PRINT_DCCPP
438 // PRINT CONFIGURATION INFO TO SERIAL PORT REGARDLESS OF INTERFACE TYPE
440 // - ACTIVATED ON STARTUP IF SHOW_CONFIG_PIN IS TIED HIGH
441 
442 void DCCpp::showConfiguration()
443 {
444  Serial.println(F("*** DCCpp LIBRARY ***"));
445 
446  Serial.print(F("VERSION DCC++: "));
447  Serial.println(VERSION);
448  Serial.println(F(DCCPP_LIBRARY_VERSION));
449  Serial.print(F("COMPILED: "));
450  Serial.print(__DATE__);
451  Serial.print(F(" "));
452  Serial.println(__TIME__);
453 
454  //Serial.print(F("nARDUINO: "));
455  //Serial.print(ARDUINO_TYPE);
456 
457  //Serial.print(F("nnMOTOR SHIELD: "));
458  //Serial.print(MOTOR_SHIELD_NAME);
459 
460  if (DCCppConfig::SignalEnablePinMain!= UNDEFINED_PIN)
461  {
462  Serial.print(F("nnDCC SIG MAIN(DIR): "));
463  Serial.println(DCC_SIGNAL_PIN_MAIN);
464  Serial.print(F(" DIRECTION: "));
465  Serial.println(DCCppConfig::DirectionMotorA);
466  Serial.print(F(" ENABLE(PWM): "));
467  Serial.println(DCCppConfig::SignalEnablePinMain);
468  Serial.print(F(" CURRENT: "));
469  Serial.println(DCCppConfig::CurrentMonitorMain);
470  }
471 
472  if (DCCppConfig::SignalEnablePinProg!= UNDEFINED_PIN)
473  {
474  Serial.print(F("nnDCC SIG PROG(DIR): "));
475  Serial.println(DCC_SIGNAL_PIN_PROG);
476  Serial.print(F(" DIRECTION: "));
477  Serial.println(DCCppConfig::DirectionMotorB);
478  Serial.print(F(" ENABLE(PWM): "));
479  Serial.println(DCCppConfig::SignalEnablePinProg);
480  Serial.print(F(" CURRENT: "));
481  Serial.println(DCCppConfig::CurrentMonitorProg);
482  }
483 #if defined(USE_EEPROM)
484 #if defined(USE_TURNOUT)
485  Serial.print(F("nnNUM TURNOUTS: "));
486  Serial.println(EEStore::data.nTurnouts);
487 #endif
488 #if defined(USE_SENSOR)
489  Serial.print(F(" SENSORS: "));
490  Serial.println(EEStore::data.nSensors);
491 #endif
492 #if defined(USE_OUTPUT)
493  Serial.print(F(" OUTPUTS: "));
494  Serial.println(EEStore::data.nOutputs);
495 #endif
496 #endif
497 
498 #ifdef USE_TEXTCOMMAND
499  Serial.print(F("nnINTERFACE: "));
500 #ifdef USE_ETHERNET
501  Serial.println(F("ETHERNET "));
502  Serial.print(F("MAC ADDRESS: "));
503  for (int i = 0; i<5; i++) {
504  Serial.print(DCCppConfig::EthernetMac[i], HEX);
505  Serial.print(F(":"));
506  }
507  Serial.println(DCCppConfig::EthernetMac[5], HEX);
508 // Serial.print(F("PORT: "));
509 // Serial.println(DCCppConfig::EthernetPort);
510  Serial.print(F("IP ADDRESS: "));
511  Serial.println(Ethernet.localIP());
512 
513 /*#ifdef IP_ADDRESS
514  Serial.println(F(" (STATIC)"));
515 #else
516  Serial.println(F(" (DHCP)"));
517 #endif*/
518 
519 #else
520  Serial.println(F("SERIAL"));
521 #endif
522 
523 #endif
524 // Serial.print(F("nnPROGRAM HALTED - PLEASE RESTART ARDUINO"));
525 
526 // while (true);
527 // Serial.println("");
528 }
529 #endif
530 
531 void DCCpp::panicStop(bool inStop)
532 {
533  panicStopped = inStop;
534 
535 #ifdef DCCPP_DEBUG_MODE
536  Serial.print(F("DCCpp PanicStop "));
537  Serial.println(inStop ? F("pressed"):F("canceled"));
538 #endif
539 
540  /* activate or not the power on rails */
541 
542  if (inStop)
543  powerOff();
544  else
545  powerOn();
546 }
547 
548 void DCCpp::powerOn(bool inMain, bool inProg)
549 {
550  if (inProg && DCCppConfig::SignalEnablePinProg != UNDEFINED_PIN)
551  digitalWrite(DCCppConfig::SignalEnablePinProg, HIGH);
552 
553  if (inMain && DCCppConfig::SignalEnablePinMain != UNDEFINED_PIN)
554  digitalWrite(DCCppConfig::SignalEnablePinMain, HIGH);
555  DCCPP_INTERFACE.print("<p1>");
556 #if !defined(USE_ETHERNET)
557  DCCPP_INTERFACE.println("");
558 #endif
559 }
560 
561 void DCCpp::powerOff(bool inMain, bool inProg)
562 {
563  if (inProg && DCCppConfig::SignalEnablePinProg != UNDEFINED_PIN)
564  digitalWrite(DCCppConfig::SignalEnablePinProg, LOW);
565  if (inMain && DCCppConfig::SignalEnablePinMain != UNDEFINED_PIN)
566  digitalWrite(DCCppConfig::SignalEnablePinMain, LOW);
567  DCCPP_INTERFACE.print("<p0>");
568 #if !defined(USE_ETHERNET)
569  DCCPP_INTERFACE.println("");
570 #endif
571 }
572 
573 /***************************** Driving functions */
574 
575 bool DCCpp::setThrottle(volatile RegisterList *inpRegs, int nReg, int inLocoId, int inStepsNumber, int inNewSpeed, bool inForward)
576 {
577  int val = 0;
578 
579  if (panicStopped)
580  val = 1;
581  else
582  if (inNewSpeed > 0)
583  val = map(inNewSpeed, 0, inStepsNumber, 2, 127);
584 
585 #ifdef DCCPP_DEBUG_MODE
586  Serial.print(F("DCCpp SetSpeed "));
587  Serial.print(inForward?inNewSpeed:-inNewSpeed);
588  Serial.print(F("/"));
589  Serial.print(inStepsNumber);
590  Serial.print(F(" (in Dcc "));
591  Serial.print(val);
592  Serial.println(F(" )"));
593 #endif
594 
595  inpRegs->setThrottle(nReg, inLocoId, val, inForward);
596 
597  return true;
598 }
599 
600 void DCCpp::setFunctions(volatile RegisterList *inpRegs, int nReg, int inLocoId, FunctionsState &inStates)
601 {
602 #ifdef DCCPP_DEBUG_MODE
603  if (inpRegs == &mainRegs)
604  {
605  if (nReg > MAX_MAIN_REGISTERS)
606  Serial.println(F("Invalid register number on main track."));
607  }
608  else
609  {
610  if (nReg > MAX_PROG_REGISTERS)
611  Serial.println(F("Invalid register number on programming track."));
612  }
613 #endif
614  byte flags = 0;
615 
616  byte oneByte1 = 128; // Group one functions F0-F4
617  byte twoByte1 = 176; // Group two F5-F8
618  byte threeByte1 = 160; // Group three F9-F12
619  byte fourByte2 = 0; // Group four F13-F20
620  byte fiveByte2 = 0; // Group five F21-F28
621 
622  for (byte func = 0; func <= 28; func++)
623  {
624  if (func <= 4)
625  {
626  /*
627  * To set functions F0 - F4 on(= 1) or off(= 0) :
628  *
629  * BYTE1 : 128 + F1 * 1 + F2 * 2 + F3 * 4 + F4 * 8 + F0 * 16
630  * BYTE2 : omitted
631  */
632 
633  if (inStates.isActivationChanged(func))
634  flags |= 1;
635  if (inStates.isActivated(func))
636  {
637  if (func == 0)
638  oneByte1 += 16;
639  else
640  oneByte1 += (1 << (func - 1));
641  }
642  }
643  else if (func <= 8)
644  {
645  /*
646  * To set functions F5 - F8 on(= 1) or off(= 0) :
647  *
648  * BYTE1 : 176 + F5 * 1 + F6 * 2 + F7 * 4 + F8 * 8
649  * BYTE2 : omitted
650  */
651 
652  if (inStates.isActivationChanged(func))
653  flags |= 2;
654  if (inStates.isActivated(func))
655  twoByte1 += (1 << (func - 5));
656  }
657  else if (func <= 12)
658  {
659  /*
660  * To set functions F9 - F12 on(= 1) or off(= 0) :
661  *
662  * BYTE1 : 160 + F9 * 1 + F10 * 2 + F11 * 4 + F12 * 8
663  * BYTE2 : omitted
664  */
665 
666  if (inStates.isActivationChanged(func))
667  flags |= 4;
668  if (inStates.isActivated(func))
669  threeByte1 += (1 << (func - 9));
670  }
671  else if (func <= 20)
672  {
673  /*
674  * To set functions F13 - F20 on(= 1) or off(= 0) :
675  *
676  * BYTE1 : 222
677  * BYTE2 : F13 * 1 + F14 * 2 + F15 * 4 + F16 * 8 + F17 * 16 + F18 * 32 + F19 * 64 + F20 * 128
678  */
679 
680  if (inStates.isActivationChanged(func))
681  flags |= 8;
682  if (inStates.isActivated(func))
683  fourByte2 += (1 << (func - 13));
684  }
685  else if (func <= 28)
686  {
687  /*
688  * To set functions F21 - F28 on(= 1) of off(= 0) :
689  *
690  * BYTE1 : 223
691  * BYTE2 : F21 * 1 + F22 * 2 + F23 * 4 + F24 * 8 + F25 * 16 + F26 * 32 + F27 * 64 + F28 * 128
692  */
693 
694  if (inStates.isActivationChanged(func))
695  flags |= 16;
696  if (inStates.isActivated(func))
697  fiveByte2 += (1 << (func - 21));
698  }
699  }
700 
701  if (flags & 1)
702  inpRegs->setFunction(nReg, inLocoId, oneByte1, -1);
703  if (flags & 2)
704  inpRegs->setFunction(nReg, inLocoId, twoByte1, -1);
705  if (flags & 4)
706  inpRegs->setFunction(nReg, inLocoId, threeByte1, -1);
707  if (flags & 8)
708  inpRegs->setFunction(nReg, inLocoId, 222, fourByte2);
709  if (flags & 16)
710  inpRegs->setFunction(nReg, inLocoId, 223, fiveByte2);
711 
712  inStates.statesSent();
713 
714 #ifdef DCCPP_DEBUG_MODE
715  Serial.print(F("DCCpp SetFunctions for loco"));
716  Serial.print(inLocoId);
717  Serial.print(" / Activated : ");
718  inStates.printActivated();
719 #endif
720 }
721 
722 int DCCpp::identifyLocoId(volatile RegisterList *inReg)
723 {
724  int id = -1;
725  int temp;
726  temp = inReg->readCV(29, 100, 200);
727  if ((temp != -1) && (bitRead(temp, 5))) {
728  // long address : get CV#17 and CV#18
729  id = inReg->readCV(18, 100, 200);
730  if (id != -1) {
731  temp = inReg->readCV(17, 100, 200);
732  if (temp != -1) {
733  id = id + ((temp - 192) << 8);
734  }
735  }
736  }
737  else {
738  // short address: read only CV#1
739  id = inReg->readCV(1, 100, 200);
740  }
741  return(id);
742 }
743 
744 void DCCpp::writeCv(volatile RegisterList *inReg, int inCv, byte inValue, int callBack, int callBackSub)
745 {
746  inReg->writeCVByte(inCv, inValue, callBack, callBackSub);
747 
748 #ifdef DCCPP_DEBUG_MODE
749  Serial.print(F("DCCpp WriteCv "));
750  Serial.print(inCv);
751  Serial.print(F(" : "));
752  Serial.println(inValue);
753 #endif
754 }
755 
756 void DCCpp::setAccessory(int inAddress, byte inSubAddress, byte inActivate)
757 {
758  mainRegs.setAccessory(inAddress, inSubAddress, inActivate);
759 
760 #ifdef DCCPP_DEBUG_MODE
761  Serial.print(F("DCCpp AccessoryOperation "));
762  Serial.print(inAddress);
763  Serial.print(F(" / "));
764  Serial.print(inSubAddress);
765  Serial.print(F(" : "));
766  Serial.println(inActivate);
767 #endif
768 }
769 
+
1 /*************************************************************
2 project: <DCCpp library>
3 author: <Thierry PARIS>
4 description: <DCCpp class>
5 *************************************************************/
6 
7 #include "Arduino.h"
8 #include "DCCpp.h"
9 
10 // NEXT DECLARE GLOBAL OBJECTS TO PROCESS AND STORE DCC PACKETS AND MONITOR TRACK CURRENTS.
11 // NOTE REGISTER LISTS MUST BE DECLARED WITH "VOLATILE" QUALIFIER TO ENSURE THEY ARE PROPERLY UPDATED BY INTERRUPT ROUTINES
12 
13 volatile RegisterList DCCpp::mainRegs(MAX_MAIN_REGISTERS); // create list of registers for MAX_MAIN_REGISTER Main Track Packets
14 volatile RegisterList DCCpp::progRegs(3); // create a shorter list of only two registers for Program Track Packets
15 
16 CurrentMonitor DCCpp::mainMonitor; // create monitor for current on Main Track
17 CurrentMonitor DCCpp::progMonitor; // create monitor for current on Program Track
18 
19 bool DCCpp::programMode;
20 bool DCCpp::panicStopped;
21 
22 // *********************************************************** FunctionsState
23 
25 {
26  this->clear();
27 }
28 
30 {
31  // Clear all functions
32  this->activeFlags[0] = 0;
33  this->activeFlags[1] = 0;
34  this->activeFlags[2] = 0;
35  this->activeFlags[3] = 0;
36 
37  this->statesSent();
38 }
39 
40 void FunctionsState::activate(byte inFunctionNumber)
41 {
42  bitSet(this->activeFlags[inFunctionNumber / 8], inFunctionNumber % 8);
43 }
44 
45 void FunctionsState::inactivate(byte inFunctionNumber)
46 {
47  bitClear(this->activeFlags[inFunctionNumber / 8], inFunctionNumber % 8);
48 }
49 
50 bool FunctionsState::isActivated(byte inFunctionNumber)
51 {
52  return bitRead(this->activeFlags[inFunctionNumber / 8], inFunctionNumber % 8);
53 }
54 
55 bool FunctionsState::isActivationChanged(byte inFunctionNumber)
56 {
57  return bitRead(this->activeFlagsSent[inFunctionNumber / 8], inFunctionNumber % 8) != isActivated(inFunctionNumber);
58 }
59 
61 {
62  for (int i = 0; i < 4; i++)
63  this->activeFlagsSent[i] = this->activeFlags[i];
64 }
65 
66 #ifdef DCCPP_DEBUG_MODE
67 void FunctionsState::printActivated()
68 {
69  for (int i = 0; i < 32; i++)
70  {
71  if (this->isActivated(i))
72  {
73  Serial.print(i);
74  Serial.print(" ");
75  }
76  }
77 
78  Serial.println("");
79 }
80 #endif
81 
82 // *********************************************************** end of FunctionsState
83 
84 // *********************************************************** DCCpp class
85 
86 static bool first = true;
87 
89 // MAIN ARDUINO LOOP
91 
93 {
94 #ifdef USE_TEXTCOMMAND
95  TextCommand::process(); // check for, and process, and new serial commands
96 #endif
97 
98  if (first)
99  {
100  first = false;
101 #if defined(DCCPP_DEBUG_MODE) && defined(DCCPP_PRINT_DCCPP)
102  showConfiguration();
103 #endif
104  }
105 
106  if (CurrentMonitor::checkTime())
107  { // if sufficient time has elapsed since last update, check current draw on Main and Program Tracks
108  mainMonitor.check();
109  progMonitor.check();
110  }
111 
112 #ifdef USE_SENSOR
113  Sensor::check(); // check sensors for activated or not
114 #endif
115 }
116 
117 void DCCpp::beginMain(uint8_t inOptionalDirectionMotor, uint8_t inSignalPin, uint8_t inSignalEnable, uint8_t inCurrentMonitor)
118 {
119  DCCppConfig::DirectionMotorA = inOptionalDirectionMotor;
120  DCCppConfig::SignalEnablePinMain = inSignalEnable; // PWM
121  DCCppConfig::CurrentMonitorMain = inCurrentMonitor;
122 
123  // If no main line, exit.
124  if (DCCppConfig::SignalEnablePinMain == UNDEFINED_PIN)
125  {
126 #ifdef DCCPP_DEBUG_MODE
127  Serial.println("No main track");
128 #endif
129  return;
130  }
131 
132  mainMonitor.begin(DCCppConfig::CurrentMonitorMain, DCCppConfig::SignalEnablePinMain, (char *) "<p2>");
133 
134  // CONFIGURE TIMER_1 TO OUTPUT 50% DUTY CYCLE DCC SIGNALS ON OC1B INTERRUPT PINS
135 
136  // Direction Pin for Motor Shield Channel A - MAIN OPERATIONS TRACK
137  // Controlled by Arduino 16-bit TIMER 1 / OC1B Interrupt Pin
138  // Values for 16-bit OCR1A and OCR1B registers calibrated for 1:1 prescale at 16 MHz clock frequency
139  // Resulting waveforms are 200 microseconds for a ZERO bit and 116 microseconds for a ONE bit with exactly 50% duty cycle
140 
141 #define DCC_ZERO_BIT_TOTAL_DURATION_TIMER1 3199
142 #define DCC_ZERO_BIT_PULSE_DURATION_TIMER1 1599
143 
144 #define DCC_ONE_BIT_TOTAL_DURATION_TIMER1 1855
145 #define DCC_ONE_BIT_PULSE_DURATION_TIMER1 927
146  if (DCCppConfig::DirectionMotorA != UNDEFINED_PIN)
147  {
148  pinMode(DCCppConfig::DirectionMotorA, INPUT); // ensure this pin is not active! Direction will be controlled by DCC SIGNAL instead (below)
149  digitalWrite(DCCppConfig::DirectionMotorA, LOW);
150  }
151 
152  if (inSignalPin != UNDEFINED_PIN)
153  pinMode(inSignalPin, OUTPUT); // FOR SHIELDS, THIS ARDUINO OUTPUT PIN MUST BE PHYSICALY CONNECTED TO THE PIN FOR DIRECTION-A OF MOTOR CHANNEL-A
154 
155  bitSet(TCCR1A, WGM10); // set Timer 1 to FAST PWM, with TOP=OCR1A
156  bitSet(TCCR1A, WGM11);
157  bitSet(TCCR1B, WGM12);
158  bitSet(TCCR1B, WGM13);
159 
160  bitSet(TCCR1A, COM1B1); // set Timer 1, OC1B (pin 10/UNO, pin 12/MEGA) to inverting toggle (actual direction is arbitrary)
161  bitSet(TCCR1A, COM1B0);
162 
163  bitClear(TCCR1B, CS12); // set Timer 1 prescale=1
164  bitClear(TCCR1B, CS11);
165  bitSet(TCCR1B, CS10);
166 
167  OCR1A = DCC_ONE_BIT_TOTAL_DURATION_TIMER1;
168  OCR1B = DCC_ONE_BIT_PULSE_DURATION_TIMER1;
169 
170  pinMode(DCCppConfig::SignalEnablePinMain, OUTPUT); // master enable for motor channel A
171 
172  mainRegs.loadPacket(1, RegisterList::idlePacket, 2, 0); // load idle packet into register 1
173 
174  bitSet(TIMSK1, OCIE1B); // enable interrupt vector for Timer 1 Output Compare B Match (OCR1B)
175  digitalWrite(DCCppConfig::SignalEnablePinMain, LOW);
176 
177 #ifdef DCCPP_DEBUG_MODE
178  Serial.println(F("beginMain achivied"));
179 #endif
180 }
181 
182 void DCCpp::beginProg(uint8_t inOptionalDirectionMotor, uint8_t inSignalPin, uint8_t inSignalEnable, uint8_t inCurrentMonitor)
183 {
184  DCCppConfig::DirectionMotorB = inOptionalDirectionMotor;
185  DCCppConfig::SignalEnablePinProg = inSignalEnable;
186  DCCppConfig::CurrentMonitorProg = inCurrentMonitor;
187 
188  // If no programming line, exit.
189  if (DCCppConfig::SignalEnablePinProg == UNDEFINED_PIN)
190  {
191 #ifdef DCCPP_DEBUG_MODE
192  Serial.println("No prog track");
193 #endif
194  return;
195  }
196 
197  progMonitor.begin(DCCppConfig::CurrentMonitorProg, DCCppConfig::SignalEnablePinProg, (char *) "<p3>");
198 
199  // CONFIGURE EITHER TIMER_0 (UNO) OR TIMER_3 (MEGA) TO OUTPUT 50% DUTY CYCLE DCC SIGNALS ON OC0B (UNO) OR OC3B (MEGA) INTERRUPT PINS
200 
201 #if defined(ARDUINO_AVR_UNO) || defined(ARDUINO_AVR_NANO) // Configuration for UNO
202 
203  // Direction Pin for Motor Shield Channel B - PROGRAMMING TRACK
204  // Controlled by Arduino 8-bit TIMER 0 / OC0B Interrupt Pin
205  // Values for 8-bit OCR0A and OCR0B registers calibrated for 1:64 prescale at 16 MHz clock frequency
206  // Resulting waveforms are 200 microseconds for a ZERO bit and 116 microseconds for a ONE bit with as-close-as-possible to 50% duty cycle
207 
208 #define DCC_ZERO_BIT_TOTAL_DURATION_TIMER0 49
209 #define DCC_ZERO_BIT_PULSE_DURATION_TIMER0 24
210 
211 #define DCC_ONE_BIT_TOTAL_DURATION_TIMER0 28
212 #define DCC_ONE_BIT_PULSE_DURATION_TIMER0 14
213 
214  if (DCCppConfig::DirectionMotorB != UNDEFINED_PIN)
215  {
216  pinMode(DCCppConfig::DirectionMotorB, INPUT); // ensure this pin is not active! Direction will be controlled by DCC SIGNAL instead (below)
217  digitalWrite(DCCppConfig::DirectionMotorB, LOW);
218  }
219 
220  if (inSignalPin != UNDEFINED_PIN)
221  pinMode(inSignalPin, OUTPUT); // THIS ARDUINO OUTPUT PIN MUST BE PHYSICALY CONNECTED TO THE PIN FOR DIRECTION-B OF MOTOR CHANNEL-B
222 
223  bitSet(TCCR0A, WGM00); // set Timer 0 to FAST PWM, with TOP=OCR0A
224  bitSet(TCCR0A, WGM01);
225  bitSet(TCCR0B, WGM02);
226 
227  bitSet(TCCR0A, COM0B1); // set Timer 0, OC0B (pin 5) to inverting toggle (actual direction is arbitrary)
228  bitSet(TCCR0A, COM0B0);
229 
230  bitClear(TCCR0B, CS02); // set Timer 0 prescale=64
231  bitSet(TCCR0B, CS01);
232  bitSet(TCCR0B, CS00);
233 
234  OCR0A = DCC_ONE_BIT_TOTAL_DURATION_TIMER0;
235  OCR0B = DCC_ONE_BIT_PULSE_DURATION_TIMER0;
236 
237  pinMode(DCCppConfig::SignalEnablePinProg, OUTPUT); // master enable for motor channel B
238 
239  progRegs.loadPacket(1, RegisterList::idlePacket, 2, 0); // load idle packet into register 1
240 
241  bitSet(TIMSK0, OCIE0B); // enable interrupt vector for Timer 0 Output Compare B Match (OCR0B)
242 
243 #else // Configuration for MEGA
244 
245  // Direction Pin for Motor Shield Channel B - PROGRAMMING TRACK
246  // Controlled by Arduino 16-bit TIMER 3 / OC3B Interrupt Pin
247  // Values for 16-bit OCR3A and OCR3B registers calibrated for 1:1 prescale at 16 MHz clock frequency
248  // Resulting waveforms are 200 microseconds for a ZERO bit and 116 microseconds for a ONE bit with exactly 50% duty cycle
249 
250 #define DCC_ZERO_BIT_TOTAL_DURATION_TIMER3 3199
251 #define DCC_ZERO_BIT_PULSE_DURATION_TIMER3 1599
252 
253 #define DCC_ONE_BIT_TOTAL_DURATION_TIMER3 1855
254 #define DCC_ONE_BIT_PULSE_DURATION_TIMER3 927
255 
256  if (DCCppConfig::DirectionMotorB != UNDEFINED_PIN)
257  {
258  pinMode(DCCppConfig::DirectionMotorB, INPUT); // ensure this pin is not active! Direction will be controlled by DCC SIGNAL instead (below)
259  digitalWrite(DCCppConfig::DirectionMotorB, LOW);
260  }
261 
262  pinMode(DCC_SIGNAL_PIN_PROG, OUTPUT); // THIS ARDUINO OUTPUT PIN MUST BE PHYSICALLY CONNECTED TO THE PIN FOR DIRECTION-B OF MOTOR CHANNEL-B
263 
264  bitSet(TCCR3A, WGM30); // set Timer 3 to FAST PWM, with TOP=OCR3A
265  bitSet(TCCR3A, WGM31);
266  bitSet(TCCR3B, WGM32);
267  bitSet(TCCR3B, WGM33);
268 
269  bitSet(TCCR3A, COM3B1); // set Timer 3, OC3B (pin 2) to inverting toggle (actual direction is arbitrary)
270  bitSet(TCCR3A, COM3B0);
271 
272  bitClear(TCCR3B, CS32); // set Timer 3 prescale=1
273  bitClear(TCCR3B, CS31);
274  bitSet(TCCR3B, CS30);
275 
276  OCR3A = DCC_ONE_BIT_TOTAL_DURATION_TIMER3;
277  OCR3B = DCC_ONE_BIT_PULSE_DURATION_TIMER3;
278 
279  pinMode(DCCppConfig::SignalEnablePinProg, OUTPUT); // master enable for motor channel B
280 
281  progRegs.loadPacket(1, RegisterList::idlePacket, 2, 0); // load idle packet into register 1
282 
283  bitSet(TIMSK3, OCIE3B); // enable interrupt vector for Timer 3 Output Compare B Match (OCR3B)
284 
285 #endif
286  digitalWrite(DCCppConfig::SignalEnablePinProg, LOW);
287 
288 #ifdef DCCPP_DEBUG_MODE
289  Serial.println(F("beginProg achivied"));
290 #endif
291 }
292 
294 {
295  programMode = false;
296  panicStopped = false;
297 
298  DCCppConfig::SignalEnablePinMain = UNDEFINED_PIN;
299  DCCppConfig::CurrentMonitorMain = UNDEFINED_PIN;
300 
301  DCCppConfig::SignalEnablePinProg = UNDEFINED_PIN;
302  DCCppConfig::CurrentMonitorProg = UNDEFINED_PIN;
303 
304  DCCppConfig::DirectionMotorA = UNDEFINED_PIN;
305  DCCppConfig::DirectionMotorB = UNDEFINED_PIN;
306 
307  mainMonitor.begin(UNDEFINED_PIN, UNDEFINED_PIN, "");
308  progMonitor.begin(UNDEFINED_PIN, UNDEFINED_PIN, "");
309 
310 #ifdef SDCARD_CS
311  pinMode(SDCARD_CS, OUTPUT);
312  digitalWrite(SDCARD_CS, HIGH); // De-select the SD card
313 #endif
314 
315 #ifdef USE_EEPROM
316  EEStore::init(); // initialize and load Turnout and Sensor definitions stored in EEPROM
317  if (EEStore::needsRefreshing())
318  EEStore::store();
319 #endif
320 
321 #ifdef DCCPP_DEBUG_MODE
322  //pinMode(LED_BUILTIN, OUTPUT);
323  Serial.println(F("begin achieved"));
324 #endif
325 
326 } // begin
327 
328 #ifdef USE_ETHERNET
329 void DCCpp::beginEthernet(uint8_t *inMac, uint8_t *inIp, EthernetProtocol inProtocol)
330 {
331  if (inIp != NULL)
332  for (int i = 0; i < 4; i++)
333  DCCppConfig::EthernetIp[i] = inIp[i];
334 
335  for (int i = 0; i < 6; i++)
336  DCCppConfig::EthernetMac[i] = inMac[i];
337 
338  DCCppConfig::Protocol = inProtocol;
339 
340  if (inIp == NULL)
341  Ethernet.begin(inMac); // Start networking using DHCP to get an IP Address
342  else
343  Ethernet.begin(inMac, inIp); // Start networking using STATIC IP Address
344 
345  DCCPP_INTERFACE.begin();
346 #ifdef DCCPP_DEBUG_MODE
347  //pinMode(LED_BUILTIN, OUTPUT);
348  showConfiguration();
349  Serial.println(F("beginEthernet achieved"));
350 #endif
351 } // beginEthernet
352 #endif
353 
355  // DEFINE THE INTERRUPT LOGIC THAT GENERATES THE DCC SIGNAL
357 
358  // The code below will be called every time an interrupt is triggered on OCNB, where N can be 0 or 1.
359  // It is designed to read the current bit of the current register packet and
360  // updates the OCNA and OCNB counters of Timer-N to values that will either produce
361  // a long (200 microsecond) pulse, or a short (116 microsecond) pulse, which respectively represent
362  // DCC ZERO and DCC ONE bits.
363 
364  // These are hardware-driven interrupts that will be called automatically when triggered regardless of what
365  // DCC++ BASE STATION was otherwise processing. But once inside the interrupt, all other interrupt routines are temporarily disabled.
366  // Since a short pulse only lasts for 116 microseconds, and there are TWO separate interrupts
367  // (one for Main Track Registers and one for the Program Track Registers), the interrupt code must complete
368  // in much less than 58 microseconds, otherwise there would be no time for the rest of the program to run. Worse, if the logic
369  // of the interrupt code ever caused it to run longer than 58 microseconds, an interrupt trigger would be missed, the OCNA and OCNB
370  // registers would not be updated, and the net effect would be a DCC signal that keeps sending the same DCC bit repeatedly until the
371  // interrupt code completes and can be called again.
372 
373  // A significant portion of this entire program is designed to do as much of the heavy processing of creating a properly-formed
374  // DCC bit stream upfront, so that the interrupt code below can be as simple and efficient as possible.
375 
376  // Note that we need to create two very similar copies of the code --- one for the Main Track OC1B interrupt and one for the
377  // Programming Track OCOB interrupt. But rather than create a generic function that incurs additional overhead, we create a macro
378  // that can be invoked with proper parameters for each interrupt. This slightly increases the size of the code base by duplicating
379  // some of the logic for each interrupt, but saves additional time.
380 
381  // As structured, the interrupt code below completes at an average of just under 6 microseconds with a worse-case of just under 11 microseconds
382  // when a new register is loaded and the logic needs to switch active register packet pointers.
383 
384  // THE INTERRUPT CODE MACRO: R=REGISTER LIST (mainRegs or progRegs), and N=TIMER (0 or 1)
385 
386 #define DCC_SIGNAL(R,N)
387  if(R.currentBit==R.currentReg->activePacket->nBits){ /* IF no more bits in this DCC Packet */
388  R.currentBit=0; /* reset current bit pointer and determine which Register and Packet to process next--- */
389  if (R.nRepeat>0 && R.currentReg == R.reg) { /* IF current Register is first Register AND should be repeated */
390  R.nRepeat--; /* decrement repeat count; result is this same Packet will be repeated */
391  }
392  else if (R.nextReg != NULL) { /* ELSE IF another Register has been updated */
393  R.currentReg = R.nextReg; /* update currentReg to nextReg */
394  R.nextReg = NULL; /* reset nextReg to NULL */
395  R.tempPacket = R.currentReg->activePacket; /* flip active and update Packets */
396  R.currentReg->activePacket = R.currentReg->updatePacket;
397  R.currentReg->updatePacket = R.tempPacket;
398  }
399  else { /* ELSE simply move to next Register */
400  if (R.currentReg == R.maxLoadedReg) /* BUT IF this is last Register loaded */
401  R.currentReg = R.reg; /* first reset currentReg to base Register, THEN */
402  R.currentReg++; /* increment current Register (note this logic causes Register[0] to be skipped when simply cycling through all Registers) */
403  } /* END-ELSE */
404  } /* END-IF: currentReg, activePacket, and currentBit should now be properly set to point to next DCC bit */
405 
406  if (R.currentReg->activePacket->buf[R.currentBit / 8] & R.bitMask[R.currentBit % 8]) { /* IF bit is a ONE */
407  OCR ## N ## A = DCC_ONE_BIT_TOTAL_DURATION_TIMER ## N; /* set OCRA for timer N to full cycle duration of DCC ONE bit */
408  OCR ## N ## B=DCC_ONE_BIT_PULSE_DURATION_TIMER ## N; /* set OCRB for timer N to half cycle duration of DCC ONE but */
409  } else{ /* ELSE it is a ZERO */
410  OCR ## N ## A=DCC_ZERO_BIT_TOTAL_DURATION_TIMER ## N; /* set OCRA for timer N to full cycle duration of DCC ZERO bit */
411  OCR ## N ## B=DCC_ZERO_BIT_PULSE_DURATION_TIMER ## N; /* set OCRB for timer N to half cycle duration of DCC ZERO bit */
412  } /* END-ELSE */
413 
414  R.currentBit++; /* point to next bit in current Packet */
415 
417 // NOW USE THE ABOVE MACRO TO CREATE THE CODE FOR EACH INTERRUPT
418 
419 ISR(TIMER1_COMPB_vect) { // set interrupt service for OCR1B of TIMER-1 which flips direction bit of Motor Shield Channel A controlling Main Track
420  DCC_SIGNAL(DCCpp::mainRegs, 1)
421 }
422 
423 #if defined(ARDUINO_AVR_UNO) || defined(ARDUINO_AVR_NANO) // Configuration for UNO
424 
425 ISR(TIMER0_COMPB_vect) { // set interrupt service for OCR1B of TIMER-0 which flips direction bit of Motor Shield Channel B controlling Programming Track
426  DCC_SIGNAL(DCCpp::progRegs, 0)
427 }
428 
429 #else // Configuration for MEGA
430 
431 ISR(TIMER3_COMPB_vect) { // set interrupt service for OCR3B of TIMER-3 which flips direction bit of Motor Shield Channel B controlling Programming Track
432  DCC_SIGNAL(DCCpp::progRegs, 3)
433 }
434 
435 #endif
436 
437 #ifdef DCCPP_PRINT_DCCPP
438 // PRINT CONFIGURATION INFO TO SERIAL PORT REGARDLESS OF INTERFACE TYPE
440 // - ACTIVATED ON STARTUP IF SHOW_CONFIG_PIN IS TIED HIGH
441 
442 void DCCpp::showConfiguration()
443 {
444  Serial.println(F("*** DCCpp LIBRARY ***"));
445 
446  Serial.print(F("VERSION DCC++: "));
447  Serial.println(VERSION);
448  Serial.println(F(DCCPP_LIBRARY_VERSION));
449  Serial.print(F("COMPILED: "));
450  Serial.print(__DATE__);
451  Serial.print(F(" "));
452  Serial.println(__TIME__);
453 
454  //Serial.print(F("nARDUINO: "));
455  //Serial.print(ARDUINO_TYPE);
456 
457  //Serial.print(F("nnMOTOR SHIELD: "));
458  //Serial.print(MOTOR_SHIELD_NAME);
459 
460  if (DCCppConfig::SignalEnablePinMain!= UNDEFINED_PIN)
461  {
462  Serial.print(F("nnDCC SIG MAIN(DIR): "));
463  Serial.println(DCC_SIGNAL_PIN_MAIN);
464  Serial.print(F(" DIRECTION: "));
465  Serial.println(DCCppConfig::DirectionMotorA);
466  Serial.print(F(" ENABLE(PWM): "));
467  Serial.println(DCCppConfig::SignalEnablePinMain);
468  Serial.print(F(" CURRENT: "));
469  Serial.println(DCCppConfig::CurrentMonitorMain);
470  }
471 
472  if (DCCppConfig::SignalEnablePinProg!= UNDEFINED_PIN)
473  {
474  Serial.print(F("nnDCC SIG PROG(DIR): "));
475  Serial.println(DCC_SIGNAL_PIN_PROG);
476  Serial.print(F(" DIRECTION: "));
477  Serial.println(DCCppConfig::DirectionMotorB);
478  Serial.print(F(" ENABLE(PWM): "));
479  Serial.println(DCCppConfig::SignalEnablePinProg);
480  Serial.print(F(" CURRENT: "));
481  Serial.println(DCCppConfig::CurrentMonitorProg);
482  }
483 #if defined(USE_EEPROM)
484 #if defined(USE_TURNOUT)
485  Serial.print(F("nnNUM TURNOUTS: "));
486  Serial.println(EEStore::data.nTurnouts);
487 #endif
488 #if defined(USE_SENSOR)
489  Serial.print(F(" SENSORS: "));
490  Serial.println(EEStore::data.nSensors);
491 #endif
492 #if defined(USE_OUTPUT)
493  Serial.print(F(" OUTPUTS: "));
494  Serial.println(EEStore::data.nOutputs);
495 #endif
496 #endif
497 
498 #ifdef USE_TEXTCOMMAND
499  Serial.print(F("nnINTERFACE: "));
500 #ifdef USE_ETHERNET
501  Serial.println(F("ETHERNET "));
502  Serial.print(F("MAC ADDRESS: "));
503  for (int i = 0; i<5; i++) {
504  Serial.print(DCCppConfig::EthernetMac[i], HEX);
505  Serial.print(F(":"));
506  }
507  Serial.println(DCCppConfig::EthernetMac[5], HEX);
508 // Serial.print(F("PORT: "));
509 // Serial.println(DCCppConfig::EthernetPort);
510  Serial.print(F("IP ADDRESS: "));
511  Serial.println(Ethernet.localIP());
512 
513 /*#ifdef IP_ADDRESS
514  Serial.println(F(" (STATIC)"));
515 #else
516  Serial.println(F(" (DHCP)"));
517 #endif*/
518 
519 #else
520  Serial.println(F("SERIAL"));
521 #endif
522 
523 #endif
524 // Serial.print(F("nnPROGRAM HALTED - PLEASE RESTART ARDUINO"));
525 
526 // while (true);
527 // Serial.println("");
528 }
529 #endif
530 
531 void DCCpp::panicStop(bool inStop)
532 {
533  panicStopped = inStop;
534 
535 #ifdef DCCPP_DEBUG_MODE
536  Serial.print(F("DCCpp PanicStop "));
537  Serial.println(inStop ? F("pressed"):F("canceled"));
538 #endif
539 
540  /* activate or not the power on rails */
541 
542  if (inStop)
543  powerOff();
544  else
545  powerOn();
546 }
547 
548 void DCCpp::powerOn(bool inMain, bool inProg)
549 {
550  if (inProg && DCCppConfig::SignalEnablePinProg != UNDEFINED_PIN)
551  digitalWrite(DCCppConfig::SignalEnablePinProg, HIGH);
552 
553  if (inMain && DCCppConfig::SignalEnablePinMain != UNDEFINED_PIN)
554  digitalWrite(DCCppConfig::SignalEnablePinMain, HIGH);
555  DCCPP_INTERFACE.print("<p1>");
556 #if !defined(USE_ETHERNET)
557  DCCPP_INTERFACE.println("");
558 #endif
559 }
560 
561 void DCCpp::powerOff(bool inMain, bool inProg)
562 {
563  if (inProg && DCCppConfig::SignalEnablePinProg != UNDEFINED_PIN)
564  digitalWrite(DCCppConfig::SignalEnablePinProg, LOW);
565  if (inMain && DCCppConfig::SignalEnablePinMain != UNDEFINED_PIN)
566  digitalWrite(DCCppConfig::SignalEnablePinMain, LOW);
567  DCCPP_INTERFACE.print("<p0>");
568 #if !defined(USE_ETHERNET)
569  DCCPP_INTERFACE.println("");
570 #endif
571 }
572 
573 /***************************** Driving functions */
574 
575 bool DCCpp::setThrottle(volatile RegisterList *inpRegs, int nReg, int inLocoId, int inStepsNumber, int inNewSpeed, bool inForward)
576 {
577  int val = 0;
578 
579  if (panicStopped)
580  val = 1;
581  else
582  if (inNewSpeed > 0)
583  val = map(inNewSpeed, 0, inStepsNumber, 2, 127);
584 
585 #ifdef DCCPP_DEBUG_MODE
586  Serial.print(F("DCCpp SetSpeed "));
587  Serial.print(inForward?inNewSpeed:-inNewSpeed);
588  Serial.print(F("/"));
589  Serial.print(inStepsNumber);
590  Serial.print(F(" (in Dcc "));
591  Serial.print(val);
592  Serial.println(F(" )"));
593 #endif
594 
595  inpRegs->setThrottle(nReg, inLocoId, val, inForward);
596 
597  return true;
598 }
599 
600 void DCCpp::setFunctions(volatile RegisterList *inpRegs, int nReg, int inLocoId, FunctionsState &inStates)
601 {
602 #ifdef DCCPP_DEBUG_MODE
603  if (inpRegs == &mainRegs)
604  {
605  if (nReg > MAX_MAIN_REGISTERS)
606  Serial.println(F("Invalid register number on main track."));
607  }
608  else
609  {
610  if (nReg > MAX_PROG_REGISTERS)
611  Serial.println(F("Invalid register number on programming track."));
612  }
613 #endif
614  byte flags = 0;
615 
616  byte oneByte1 = 128; // Group one functions F0-F4
617  byte twoByte1 = 176; // Group two F5-F8
618  byte threeByte1 = 160; // Group three F9-F12
619  byte fourByte2 = 0; // Group four F13-F20
620  byte fiveByte2 = 0; // Group five F21-F28
621 
622  for (byte func = 0; func <= 28; func++)
623  {
624  if (func <= 4)
625  {
626  /*
627  * To set functions F0 - F4 on(= 1) or off(= 0) :
628  *
629  * BYTE1 : 128 + F1 * 1 + F2 * 2 + F3 * 4 + F4 * 8 + F0 * 16
630  * BYTE2 : omitted
631  */
632 
633  if (inStates.isActivationChanged(func))
634  flags |= 1;
635  if (inStates.isActivated(func))
636  {
637  if (func == 0)
638  oneByte1 += 16;
639  else
640  oneByte1 += (1 << (func - 1));
641  }
642  }
643  else if (func <= 8)
644  {
645  /*
646  * To set functions F5 - F8 on(= 1) or off(= 0) :
647  *
648  * BYTE1 : 176 + F5 * 1 + F6 * 2 + F7 * 4 + F8 * 8
649  * BYTE2 : omitted
650  */
651 
652  if (inStates.isActivationChanged(func))
653  flags |= 2;
654  if (inStates.isActivated(func))
655  twoByte1 += (1 << (func - 5));
656  }
657  else if (func <= 12)
658  {
659  /*
660  * To set functions F9 - F12 on(= 1) or off(= 0) :
661  *
662  * BYTE1 : 160 + F9 * 1 + F10 * 2 + F11 * 4 + F12 * 8
663  * BYTE2 : omitted
664  */
665 
666  if (inStates.isActivationChanged(func))
667  flags |= 4;
668  if (inStates.isActivated(func))
669  threeByte1 += (1 << (func - 9));
670  }
671  else if (func <= 20)
672  {
673  /*
674  * To set functions F13 - F20 on(= 1) or off(= 0) :
675  *
676  * BYTE1 : 222
677  * BYTE2 : F13 * 1 + F14 * 2 + F15 * 4 + F16 * 8 + F17 * 16 + F18 * 32 + F19 * 64 + F20 * 128
678  */
679 
680  if (inStates.isActivationChanged(func))
681  flags |= 8;
682  if (inStates.isActivated(func))
683  fourByte2 += (1 << (func - 13));
684  }
685  else if (func <= 28)
686  {
687  /*
688  * To set functions F21 - F28 on(= 1) of off(= 0) :
689  *
690  * BYTE1 : 223
691  * BYTE2 : F21 * 1 + F22 * 2 + F23 * 4 + F24 * 8 + F25 * 16 + F26 * 32 + F27 * 64 + F28 * 128
692  */
693 
694  if (inStates.isActivationChanged(func))
695  flags |= 16;
696  if (inStates.isActivated(func))
697  fiveByte2 += (1 << (func - 21));
698  }
699  }
700 
701  if (flags & 1)
702  inpRegs->setFunction(nReg, inLocoId, oneByte1, -1);
703  if (flags & 2)
704  inpRegs->setFunction(nReg, inLocoId, twoByte1, -1);
705  if (flags & 4)
706  inpRegs->setFunction(nReg, inLocoId, threeByte1, -1);
707  if (flags & 8)
708  inpRegs->setFunction(nReg, inLocoId, 222, fourByte2);
709  if (flags & 16)
710  inpRegs->setFunction(nReg, inLocoId, 223, fiveByte2);
711 
712  inStates.statesSent();
713 
714 #ifdef DCCPP_DEBUG_MODE
715  Serial.print(F("DCCpp SetFunctions for loco"));
716  Serial.print(inLocoId);
717  Serial.print(" / Activated : ");
718  inStates.printActivated();
719 #endif
720 }
721 
722 int DCCpp::identifyLocoId(volatile RegisterList *inReg)
723 {
724  int id = -1;
725  int temp;
726  temp = inReg->readCV(29, 100, 200);
727  if ((temp != -1) && (bitRead(temp, 5))) {
728  // long address : get CV#17 and CV#18
729  id = inReg->readCV(18, 100, 200);
730  if (id != -1) {
731  temp = inReg->readCV(17, 100, 200);
732  if (temp != -1) {
733  id = id + ((temp - 192) << 8);
734  }
735  }
736  }
737  else {
738  // short address: read only CV#1
739  id = inReg->readCV(1, 100, 200);
740  }
741  return(id);
742 }
743 
744 void DCCpp::writeCv(volatile RegisterList *inReg, int inCv, byte inValue, int callBack, int callBackSub)
745 {
746  inReg->writeCVByte(inCv, inValue, callBack, callBackSub);
747 
748 #ifdef DCCPP_DEBUG_MODE
749  Serial.print(F("DCCpp WriteCv "));
750  Serial.print(inCv);
751  Serial.print(F(" : "));
752  Serial.println(inValue);
753 #endif
754 }
755 
756 void DCCpp::setAccessory(int inAddress, byte inSubAddress, byte inActivate)
757 {
758  mainRegs.setAccessory(inAddress, inSubAddress, inActivate);
759 
760 #ifdef DCCPP_DEBUG_MODE
761  Serial.print(F("DCCpp AccessoryOperation "));
762  Serial.print(inAddress);
763  Serial.print(F(" / "));
764  Serial.print(inSubAddress);
765  Serial.print(F(" : "));
766  Serial.println(inActivate);
767 #endif
768 }
769 
+
static void powerOn(bool inMain = true, bool inProg = true)
Definition: DCCpp.cpp:548
static void powerOff(bool inMain = true, bool inProg = true)
Definition: DCCpp.cpp:561
static void setAccessory(int inAddress, byte inSubAddress, byte inActivate)
Definition: DCCpp.cpp:756
static void panicStop(bool inStop)
Definition: DCCpp.cpp:531
static void begin()
Definition: DCCpp.cpp:293
static void beginProg(uint8_t inOptionalDirectionMotor, uint8_t inSignalPin, uint8_t inSignalEnablePin, uint8_t inCurrentMonitor)
Definition: DCCpp.cpp:182
+
bool isActivationChanged(byte inFunctionNumber)
Definition: DCCpp.cpp:55
-
static void powerOn(bool inMain = true, bool inProg = true)
Definition: DCCpp.cpp:548
void clear()
Definition: DCCpp.cpp:29
- -
void inactivate(byte inFunctionNumber)
Definition: DCCpp.cpp:45
void statesSent()
Definition: DCCpp.cpp:60
void activate(byte inFunctionNumber)
Definition: DCCpp.cpp:40
FunctionsState()
Definition: DCCpp.cpp:24
bool isActivated(byte inFunctionNumber)
Definition: DCCpp.cpp:50
static void loop()
Definition: DCCpp.cpp:92
+
void inactivate(byte inFunctionNumber)
Definition: DCCpp.cpp:45
static void beginMain(uint8_t inOptionalDirectionMotor, uint8_t inSignalPin, uint8_t inSignalEnablePin, uint8_t inCurrentMonitor)
Definition: DCCpp.cpp:117
@@ -118,7 +118,7 @@ @@ -138,7 +138,7 @@