SapFlow Probe
A low-cost HRM probe for measuring a tree's water consumption
debug.cpp
Go to the documentation of this file.
1 #include "debug.h"
2 #include <FlashStorage.h>
3 #include <sam.h>
4 
6 
7 // Object for reading and writing from flash
8 FlashStorage(persistent, class FunctionMarker);
9 
10 // Instance of the watchdog
11 static class WatchdogSAMD wdt;
12 
13 // Stores the line number
14 int line;
15 // Stores a pointer to the function name
16 const char * function;
17 
18 void FunctionMarker::set(int l, const char * str){
19  // Feed the watchdog
20  wdt.reset();
21  // Record the line number and function this was called from
22  line = l;
23  function = str;
24 }
25 
26 void FunctionMarker::init(int milliseconds){
27  read(); //< Read the previous watchdog status
28  print();//< Print it out
29  period = milliseconds; //< Remember the period selected
30  wdt.enable(period); //< Enable the watchdog
31 }
32 
34  wdt.disable(); //< Disable the watchdog for now
35 }
36 
38  // Re-enable the watchdog, using the same period as before
39  wdt.enable(period);
40 }
41 
43  Serial.print("Halted at ");
44  Serial.print(function);
45  Serial.print(", line ");
46  Serial.println(line);
47 }
48 
50  // Copy pointed value to buffer
51  strncpy(buffer, function, 100);
52  function = buffer;
53  line2 = line;
54  // Then save to the flash
55  persistent.write(*this);
56 }
57 
59  // Read the value from memory
60  *this = persistent.read();
61  line = line2;
62  function = buffer;
63  // If we stored this before, then the line number is not 0.
64  return (line!=0);
65 }
66 
67 // Requires Adafruit_ASFcore library!
68 // Be careful to use a platform-specific conditional include to only make the
69 // code visible for the appropriate platform. Arduino will try to compile and
70 // link all .cpp files regardless of platform.
71 #if defined(ARDUINO_ARCH_SAMD)
72 
73 void WDT_Handler(void) {
74  WDT->INTFLAG.bit.EW = 1; //< Clear interrupt flag
75  Serial.println("Watchdog!");
76  // Turn off the heater so we don't burn the tree
77  digitalWrite(HEATER, LOW);
78  // Print where we halted and write it to flash
81 }
82 
83 int WatchdogSAMD::enable(int maxPeriodMS) {
92  int cycles;
93  uint8_t bits;
94 
95  if(!_initialized) _initialize_wdt();
96 
97 #if defined(__SAMD51__)
98  WDT->CTRLA.reg = 0; //< Disable watchdog for config
99  while(WDT->SYNCBUSY.reg);
100 #else
101  WDT->CTRL.reg = 0; //< Disable watchdog for config
102  while(WDT->STATUS.bit.SYNCBUSY);
103 #endif
104 
105  if((maxPeriodMS >= 8000) || !maxPeriodMS) {
106  bits = 0xA;
107  } else {
108  cycles = maxPeriodMS >> 2; //< min delay is 8ms
109  bits = 0;
110  // Cycle choices are in powers of 2
111  while( cycles = cycles>>1){
112  ++bits;
113  }
114  }
115  cycles = 8<<bits;
116 
117  // Watchdog timer on SAMD is a slightly different animal than on AVR.
118  // On AVR, the WTD timeout is configured in one register and then an
119  // interrupt can optionally be enabled to handle the timeout in code
120  // (as in waking from sleep) vs resetting the chip. Easy.
121  // On SAMD, when the WDT fires, that's it, the chip's getting reset.
122  // Instead, it has an "early warning interrupt" with a different set
123  // interval prior to the reset. For equivalent behavior to the AVR
124  // library, this requires a slightly different configuration depending
125  // whether we're coming from the sleep() function (which needs the
126  // interrupt), or just enable() (no interrupt, we want the chip reset
127  // unless the WDT is cleared first). In the sleep case, 'windowed'
128  // mode is used in order to allow access to the longest available
129  // sleep interval (about 16 sec); the WDT 'period' (when a reset
130  // occurs) follows this and is always just set to the max, since the
131  // interrupt will trigger first. In the enable case, windowed mode
132  // is not used, the WDT period is set and that's that.
133 
134 #if defined(__SAMD51__)
135  WDT->INTFLAG.bit.EW = 1; //< Clear interrupt flag
136  WDT->INTENSET.bit.EW = 1; //< Enable early warning interrupt
137  WDT->CONFIG.bit.PER = bits+1;//< Period = twice
138  WDT->EWCTRL.bit.EWOFFSET = bits; //< Set time of interrupt
139  WDT->CTRLA.bit.WEN = 0; //< Disable window mode
140  while(WDT->SYNCBUSY.reg); //< Sync CTRL write
141  reset(); //< Clear watchdog interval
142  WDT->CTRLA.bit.ENABLE = 1; //< Start watchdog now!
143  while(WDT->SYNCBUSY.reg);
144 #else
145  WDT->INTENSET.bit.EW = 1; //< Enable early warning interrupt
146  WDT->CONFIG.bit.PER = bits+1; //< Period = twice
147  WDT->EWCTRL.bit.EWOFFSET = bits; //< Set time of interrupt
148  WDT->CTRL.bit.WEN = 0; //< Disable window mode
149  while(WDT->STATUS.bit.SYNCBUSY); //< Sync CTRL write
150  reset(); //< Clear watchdog interval
151  WDT->CTRL.bit.ENABLE = 1; //< Start watchdog now!
152  while(WDT->STATUS.bit.SYNCBUSY);
153 #endif
154 
155  return cycles;
156 }
157 
158 void WatchdogSAMD::reset() {
159  // Write the watchdog clear key value (0xA5) to the watchdog
160  // clear register to clear the watchdog timer and reset it.
161 #if defined(__SAMD51__)
162  while(WDT->SYNCBUSY.reg);
163 #else
164  while(WDT->STATUS.bit.SYNCBUSY);
165 #endif
166  WDT->CLEAR.reg = WDT_CLEAR_CLEAR_KEY;
167 }
168 
169 
170 uint8_t WatchdogSAMD::resetCause() {
171 #if defined(__SAMD51__)
172  return RSTC->RCAUSE.reg;
173 #else
174  return PM->RCAUSE.reg;
175 #endif
176 }
177 
178 void WatchdogSAMD::disable() {
179 #if defined(__SAMD51__)
180  WDT->CTRLA.bit.ENABLE = 0;
181  while(WDT->SYNCBUSY.reg);
182 #else
183  WDT->CTRL.bit.ENABLE = 0;
184  while(WDT->STATUS.bit.SYNCBUSY);
185 #endif
186 }
187 
188 void WatchdogSAMD::_initialize_wdt() {
189  // One-time initialization of watchdog timer.
190  // Insights from rickrlh and rbrucemtl in Arduino forum!
191 
192 #if defined(__SAMD51__)
193  // SAMD51 WDT uses OSCULP32k as input clock now
194  // section: 20.5.3
195  OSC32KCTRL->OSCULP32K.bit.EN1K = 1; //< Enable out 1K (for WDT)
196  OSC32KCTRL->OSCULP32K.bit.EN32K = 0; //< Disable out 32K
197 
198  // Enable WDT early-warning interrupt
199  NVIC_DisableIRQ(WDT_IRQn);
200  NVIC_ClearPendingIRQ(WDT_IRQn);
201  NVIC_SetPriority(WDT_IRQn, 0); //< Top priority
202  NVIC_EnableIRQ(WDT_IRQn);
203 
204  while(WDT->SYNCBUSY.reg);
205 
206  USB->DEVICE.CTRLA.bit.ENABLE = 0; //< Disable the USB peripheral
207  while(USB->DEVICE.SYNCBUSY.bit.ENABLE); //< Wait for synchronization
208  USB->DEVICE.CTRLA.bit.RUNSTDBY = 0; //< Deactivate run on standby
209  USB->DEVICE.CTRLA.bit.ENABLE = 1; //< Enable the USB peripheral
210  while(USB->DEVICE.SYNCBUSY.bit.ENABLE); //< Wait for synchronization
211 #else
212  // Generic clock generator 2, divisor = 32 (2^(DIV+1))
213  GCLK->GENDIV.reg = GCLK_GENDIV_ID(2) | GCLK_GENDIV_DIV(4);
214  // Enable clock generator 2 using low-power 32KHz oscillator.
215  // With /32 divisor above, this yields 1024Hz(ish) clock.
216  GCLK->GENCTRL.reg = GCLK_GENCTRL_ID(2) |
217  GCLK_GENCTRL_GENEN |
218  GCLK_GENCTRL_SRC_OSCULP32K |
219  GCLK_GENCTRL_DIVSEL;
220  while(GCLK->STATUS.bit.SYNCBUSY);
221  // WDT clock = clock gen 2
222  GCLK->CLKCTRL.reg = GCLK_CLKCTRL_ID_WDT |
223  GCLK_CLKCTRL_CLKEN |
224  GCLK_CLKCTRL_GEN_GCLK2;
225 
226  // Enable WDT early-warning interrupt
227  NVIC_DisableIRQ(WDT_IRQn);
228  NVIC_ClearPendingIRQ(WDT_IRQn);
229  NVIC_SetPriority(WDT_IRQn, 0); //< Top priority
230  NVIC_EnableIRQ(WDT_IRQn);
231 #endif
232 
233  _initialized = true;
234 }
235 
236 #endif // defined(ARDUINO_ARCH_SAMD)
FunctionMarker::set
void set(int l, const char *str)
Record the current line number and function name. Also feeds the watchdog.
Definition: debug.cpp:18
FunctionMarker
Used for debugging.
Definition: debug.h:14
FunctionMarker::init
void init(int period)
Initialize the watchdog at the period given.
Definition: debug.cpp:26
WatchdogSAMD::resetCause
uint8_t resetCause()
Find out the cause of the last reset - see datasheet for bitmask.
FunctionMarker::print
void print(void)
Prints the most recently recorded value.
Definition: debug.cpp:42
WatchdogSAMD::reset
void reset()
Reset or 'kick' the watchdog timer to prevent a reset of the device.
WatchdogSAMD::enable
int enable(int PeriodMS)
Enable the watchdog timer to reset the machine if it hangs.
FunctionMarker::resume
void resume(void)
Re-enable the watchdog. You should do this right after waking from sleep.
Definition: debug.cpp:37
FunctionMarker::pause
void pause(void)
Pause the watchdog. You'll want to do this before sleeping.
Definition: debug.cpp:33
HEATER
@ HEATER
Control pin for heater switch. Output, Active-high.
Definition: pinout.h:15
WatchdogSAMD::disable
void disable()
Completely disable the watchdog timer.
halt_location
static class FunctionMarker halt_location
Singleton of our debug class.
Definition: debug.h:64
FunctionMarker::read
bool read(void)
Read the recorded value from flash.
Definition: debug.cpp:58
FunctionMarker::write
void write(void)
Actually write the recorded value to flash.
Definition: debug.cpp:49
debug.h
WatchdogSAMD
Watchdog for SAMD21.
Definition: debug.h:40