AskSin++
BatterySensor.h
1 //- -----------------------------------------------------------------------------------------------------------------------
2 // AskSin++
3 // 2016-10-31 papa Creative Commons - http://creativecommons.org/licenses/by-nc-sa/3.0/de/
4 //- -----------------------------------------------------------------------------------------------------------------------
5 
6 #ifndef __BATTERYSENSOR_H__
7 #define __BATTERYSENSOR_H__
8 
9 #include <Debug.h>
10 #include <AlarmClock.h>
11 
12 #ifdef ARDUINO_ARCH_AVR
13 
14 #ifndef __AVR_ATmega128__
15 #include <avr/power.h>
16 #endif
17 
18 #if defined(__AVR_ATmega32U4__) || defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__) || defined(__AVR_ATmega644P__) || defined(__AVR_ATmega1284P__)
19 #define ADMUX_VCCWRT1V1 (_BV(REFS0) | _BV(MUX4) | _BV(MUX3) | _BV(MUX2) | _BV(MUX1))
20 #elif defined (__AVR_ATtiny24__) || defined(__AVR_ATtiny44__) || defined(__AVR_ATtiny84__)
21 #define ADMUX_VCCWRT1V1 (_BV(MUX5) | _BV(MUX0))
22 #elif defined (__AVR_ATtiny25__) || defined(__AVR_ATtiny45__) || defined(__AVR_ATtiny85__)
23 #define ADMUX_VCCWRT1V1 (_BV(MUX3) | _BV(MUX2))
24 #else
25 #define ADMUX_VCCWRT1V1 (_BV(REFS0) | _BV(MUX3) | _BV(MUX2) | _BV(MUX1))
26 #endif
27 
28 #endif
29 
30 
31 namespace as {
32 
37 class NoBattery {
38 public:
40  uint8_t current () const { return 33; }
42  bool critical () const { return false; }
44  void critical (__attribute__((unused)) uint8_t value ) {}
46  bool low () const { return false; }
48  void low (__attribute__((unused)) uint8_t value ) {}
50  void setIdle () {}
52  void unsetIdle () {}
54  void resetCurrent () {}
55 };
56 
57 #ifdef ARDUINO_ARCH_AVR
58 
59 #if defined(__AVR_ATmega32U4__) || defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__) || defined(__AVR_ATmega644P__) || defined(__AVR_ATmega1284P__)
60 #define ADMUX_ADCMASK ((1 << MUX4)|(1 << MUX3)|(1 << MUX2)|(1 << MUX1)|(1 << MUX0))
61 #else
62 #define ADMUX_ADCMASK ((1 << MUX3)|(1 << MUX2)|(1 << MUX1)|(1 << MUX0))
63 #endif
64 #define ADMUX_REFMASK ((1 << REFS1)|(1 << REFS0))
65 
66 #define ADMUX_REF_AREF ((0 << REFS1)|(0 << REFS0))
67 #define ADMUX_REF_AVCC ((0 << REFS1)|(1 << REFS0))
68 #define ADMUX_REF_RESV ((1 << REFS1)|(0 << REFS0))
69 #define ADMUX_REF_VBG ((1 << REFS1)|(1 << REFS0))
70 
71 #if defined(__AVR_ATmega32U4__) || defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__) || defined(__AVR_ATmega644P__) || defined(__AVR_ATmega1284P__)
72 #define ADMUX_ADC_VBG ((1 << MUX4)|(1 << MUX3)|(1 << MUX2)|(1 << MUX1)|(0 << MUX0))
73 #else
74 #define ADMUX_ADC_VBG ((1 << MUX3)|(1 << MUX2)|(1 << MUX1)|(0 << MUX0))
75 #endif
76 
77 #endif
78 
79 
80 class InternalVCC {
81 public:
82  typedef uint16_t ValueType;
83  static const int DefaultDelay = 0;
84 
85  void init () {
86 #ifdef ARDUINO_ARCH_STM32F1
87  adc_reg_map *regs = ADC1->regs;
88  regs->CR2 |= ADC_CR2_TSVREFE; // enable VREFINT and temp sensor
89  regs->SMPR1 = ADC_SMPR1_SMP17; // sample rate for VREFINT ADC channel
90 #endif
91  }
92 
93  void start () {}
94 
95  uint16_t finish () {
96  uint16_t vcc=0;
97 #ifdef ARDUINO_ARCH_AVR
98  // Read 1.1V reference against AVcc
99  // set the reference to Vcc and the measurement to the internal 1.1V reference
100  ADMUX &= ~(ADMUX_REFMASK | ADMUX_ADCMASK);
101  ADMUX |= ADMUX_REF_AVCC; // select AVCC as reference
102  ADMUX |= ADMUX_ADC_VBG; // measure bandgap reference voltage
103  _delay_us(350);
104  ADCSRA |= (1 << ADSC); // start conversion
105  while (ADCSRA & (1 << ADSC)) ; // wait to finish
106  vcc = 1100UL * 1024 / ADC;
107 #elif defined ARDUINO_ARCH_STM32F1
108  vcc = 1200 * 4096 / adc_read(ADC1, 17); // ADC sample to millivolts
109 #endif
110  DPRINT(F("iVcc: ")); DDECLN(vcc);
111  return vcc;
112  }
113 };
114 
115 template<uint8_t SENSPIN, uint8_t ACTIVATIONPIN, uint8_t ACTIVATIONSTATE=LOW, uint16_t VCC=3300, uint8_t FACTOR=57>
116 class ExternalVCC : public InternalVCC {
117 public:
118  static const int DefaultDelay = 250;
119 
120  void init () {
121  pinMode(SENSPIN, INPUT);
122  pinMode(ACTIVATIONPIN, INPUT);
123  }
124 
125  void start () {
126  pinMode(ACTIVATIONPIN, OUTPUT);
127  digitalWrite(ACTIVATIONPIN, ACTIVATIONSTATE==LOW ? LOW : HIGH);
128  digitalWrite(SENSPIN,LOW);
129 // analogRead(SENSPIN);
130  }
131 
132  uint16_t finish () {
133  uint32_t value = analogRead(SENSPIN);
134  digitalWrite(SENSPIN,HIGH);
135  digitalWrite(ACTIVATIONPIN, ACTIVATIONSTATE==LOW ? HIGH : LOW);
136  pinMode(ACTIVATIONPIN,INPUT);
137 
138  uint16_t refvcc = VCC;
139  if( refvcc == 0 ) {
140  InternalVCC::start(); // in case we add something here later
141  refvcc = InternalVCC::finish();
142  }
143  uint16_t vin = (value * refvcc * FACTOR) / 1024 / 10;
144 
145  DPRINT(F("eVcc: ")); DDECLN(vin);
146  return vin;
147  }
148 };
149 
150 
151 template <class SENSOR,int DELAY=SENSOR::DefaultDelay>
152 class SyncMeter {
153  SENSOR m_Sensor;
154  volatile typename SENSOR::ValueType m_Value;
155 public:
156  SyncMeter () : m_Value(0) {}
157 
158  void resetCurrent() { m_Value = 0; }
159 
160  void start () {
161  sensor().start();
162  if( DELAY > 0 ) {
163  _delay_ms(DELAY);
164  }
165  typename SENSOR::ValueType tmp = sensor().finish();
166  if( m_Value == 0 || tmp < m_Value ) {
167  m_Value = tmp;
168  }
169  }
170  typename SENSOR::ValueType value () const { return m_Value; }
171  SENSOR& sensor () { return m_Sensor; }
172  typename SENSOR::ValueType measure () {
173  start();
174  return value();
175  }
176 };
177 
178 template <class SENSOR,int DELAY=SENSOR::DefaultDelay>
179 class AsyncMeter : public Alarm {
180  SENSOR m_Sensor;
181  volatile typename SENSOR::ValueType m_Value;
182 public:
183  AsyncMeter () : Alarm(0), m_Value(0) {}
184  virtual ~AsyncMeter () {}
185  virtual void trigger (__attribute__((unused)) AlarmClock& clock) {
186  typename SENSOR::ValueType tmp = sensor().finish();
187  if( m_Value == 0 || tmp < m_Value ) {
188  m_Value = tmp;
189  }
190  }
191 
192  void resetCurrent() { m_Value = 0; }
193 
194  void start () {
195  sensor().start();
196  set(millis2ticks(DELAY));
197  sysclock.add(*this);
198  }
199  typename SENSOR::ValueType value () const { return m_Value; }
200  SENSOR& sensor () { return m_Sensor; }
201  typename SENSOR::ValueType measure () {
202  sensor().start();
203  _delay_ms(DELAY);
204  m_Value = sensor().finish();
205  return value();
206  }
207 };
208 
209 template <class METER>
210 class BattSensor : public Alarm {
211  uint32_t m_Period;
212  uint8_t m_Low, m_Critical;
213  METER m_Meter;
214 public:
215  BattSensor () : Alarm(0), m_Period(0), m_Low(0), m_Critical(0) {}
216  virtual ~BattSensor() {}
217 
218  virtual void trigger (AlarmClock& clock) {
219  tick = m_Period;
220  clock.add(*this);
221  m_Meter.start();
222  }
223 
224  uint8_t current () const { return (m_Meter.value() + 50) / 100; }
225  bool critical () const { return current() < m_Critical; }
226  void critical (uint8_t value ) { m_Critical = value; }
227  bool low () const { return current() < m_Low; }
228  void low (uint8_t value ) { m_Low = value; }
229  void resetCurrent() { m_Meter.resetCurrent(); }
230 
231  void init(uint32_t period,AlarmClock& clock) {
232  m_Meter.sensor().init();
233  m_Meter.measure();
234  m_Period = period;
235  set(m_Period);
236  clock.add(*this);
237  }
238  // for backward compatibility
239  uint16_t voltageHighRes() { return m_Meter.value(); }
240  uint8_t voltage() { return current(); }
241 
242  METER& meter () { return m_Meter; }
243 
244  void setIdle () {}
245  void unsetIdle () {}
246 };
247 
249 
250 template <uint8_t SENSPIN,uint8_t ACTIVATIONPIN,uint16_t VCC=3300>
251 class BatterySensorUni : public BattSensor<SyncMeter<ExternalVCC<SENSPIN,ACTIVATIONPIN,LOW,VCC,57> > > {
252 public:
253  BatterySensorUni () {}
254  virtual ~BatterySensorUni () {}
255 };
256 
257 
262 template <uint8_t SENSPIN,uint8_t ACTIVATIONPIN,uint16_t VCC=3300>
264  uint8_t m_SensePin;
265  uint8_t m_ActivationPin;
266  uint8_t m_DividerRatio;
267  uint16_t m_RefVoltage;
268 public:
269 
271  m_SensePin(SENSPIN), m_ActivationPin(ACTIVATIONPIN), m_DividerRatio(2), m_RefVoltage(VCC) {}
272  virtual ~BatterySensorExt () {}
273 
274  void init(uint32_t period,AlarmClock& clock,uint16_t refvolt=VCC,uint8_t divider=2) {
275  m_DividerRatio=divider;
276  m_RefVoltage = refvolt;
277  pinMode(m_SensePin, INPUT);
278  if (m_ActivationPin < 0xFF) {
279  pinMode(m_ActivationPin, OUTPUT);
280  }
281  BatterySensor::init(period,clock);
282  }
283 
284 
285  virtual uint8_t voltage () {
286  if (m_ActivationPin != 0xFF) {
287  digitalWrite(m_ActivationPin, HIGH);
288  _delay_us(10); // copes with slow switching activation circuits
289  }
290  analogRead(m_SensePin);
291  _delay_ms(2); // allow the ADC to stabilize
292  uint32_t value = analogRead(m_SensePin);
293  uint16_t vcc = (value * m_DividerRatio * m_RefVoltage) / 1024 / 100;
294  if (m_ActivationPin != 0xFF) {
295  digitalWrite(m_ActivationPin, LOW);
296  }
297  DPRINT(F("Bat: ")); DDECLN(vcc);
298  return (uint8_t)vcc;
299  }
300 
301 };
302 
303 #ifdef ARDUINO_ARCH_AVR
304 
305 class IrqBaseBatt {
306 protected:
308  uint8_t m_Low;
310  uint8_t m_Critical;
311 
312  static volatile uint16_t __gb_BatCurrent;
313  static volatile uint8_t __gb_BatCount;
314  static uint16_t (*__gb_BatIrq)();
315  uint8_t m_BatSkip;
316 
317  IrqBaseBatt () : m_Low(0), m_Critical(0), m_BatSkip(0) {}
318  ~IrqBaseBatt () {}
319 
320 public:
324  uint8_t current () const { return (__gb_BatCurrent + 50) / 100; }
328  bool critical () const { return current() < m_Critical; }
332  void critical (uint8_t value ) { m_Critical = value; }
336  bool low () const { return current() < m_Low; }
340  void low (uint8_t value ) { m_Low = value; }
341 
343  uint16_t voltageHighRes() { return __gb_BatCurrent; }
345  uint8_t voltage() { return current(); }
346 
348  void resetCurrent() { __gb_BatCurrent = 0; }
349 
350 protected:
356  void setIdle () {
357  if( __gb_BatCount < 10 ) {
358  // if we skip to often - force reading
359  if( ++m_BatSkip > 10 ) {
360  // wait for valid bat value
361  while( __gb_BatCount++ < 10 ) {
362  while (ADCSRA & (1 << ADSC)) ; // wait ADC finish
363  ADCSRA |= (1 << ADSC); // start conversion again
364  }
365  m_BatSkip = 0;
366  }
367  }
368  ATOMIC_BLOCK( ATOMIC_RESTORESTATE ) {
369  __gb_BatIrq = 0;
370  }
371 
372  ADCSRA &= ~((1 << ADIE) | (1 << ADIF)); // disable interrupt
373  while (ADCSRA & (1 << ADSC)) ; // wait finish
374  __vectorfunc(); // ensure value is read and stored
375  }
376 
377  void unsetIdle (uint16_t (*irqfunc)()) {
378  //DDECLN(__gb_BatCurrent);
379  ATOMIC_BLOCK( ATOMIC_RESTORESTATE ) {
380  __gb_BatCount = 0; // reset irq counter
381  __gb_BatIrq = irqfunc; // set irq method
382  }
383  ADMUX &= ~(ADMUX_REFMASK | ADMUX_ADCMASK);
384  ADMUX |= ADMUX_REF_AVCC; // select AVCC as reference
385  ADMUX |= ADMUX_ADC_VBG; // measure bandgap reference voltage
386  ADCSRA |= (1 << ADIE) | (1<<ADPS0) | (1<<ADPS1) | (1<<ADPS2); // enable interrupt & 128 prescaler
387  ADCSRA |= (1 << ADSC); // start conversion
388  }
389 
390 #if defined(__AVR_ATmega32U4__) || defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__) || defined(__AVR_ATmega644P__) || defined(__AVR_ATmega1284P__)
391  static void __vectorfunc() __asm__("__vector_24") __attribute__((__signal__, __used__, __externally_visible__));
392 #else
393  static void __vectorfunc() __asm__("__vector_21") __attribute__((__signal__, __used__, __externally_visible__));
394 #endif
395 };
396 
402 class IrqInternalBatt : public IrqBaseBatt {
403 
404 public:
415  void init(__attribute__((unused)) uint32_t period,__attribute__((unused)) AlarmClock& clock) {
416  unsetIdle();
417  }
418 
424  void setIdle () {
426  }
432  void unsetIdle () {
433  IrqBaseBatt::unsetIdle(irq);
434  // wait for stable values
435  /*
436  int maxnum = 50; // we will wait max 50
437  uint16_t last=0 ,current=0;
438  do {
439  last = current;
440  while (ADCSRA & (1 << ADSC)) ; // wait ADC finish
441  current = ADC >> 2; // remove some bits ???
442  } while( current != last && --maxnum > 0);
443  */
444  }
447  static uint16_t irq () {
448  return 1100UL * 1024 / ADC;
449  }
450 
451 };
452 
453 extern volatile uint16_t intVCC;
454 template <uint8_t SENSPIN,uint8_t ACTIVATIONPIN,uint8_t FACTOR=57>
455 class IrqExternalBatt : public IrqBaseBatt {
456 public:
467  void init(__attribute__((unused)) uint32_t period,__attribute__((unused)) AlarmClock& clock) {
468  pinMode(SENSPIN, INPUT);
469  unsetIdle();
470  }
471 
472  uint16_t getInternalVcc() {
473  //read internal Vcc as reference voltage
474  ADMUX &= ~(ADMUX_REFMASK | ADMUX_ADCMASK);
475  ADMUX |= ADMUX_REF_AVCC; // select AVCC as reference
476  ADMUX |= ADMUX_ADC_VBG; // measure bandgap reference voltage
477  _delay_us(350);
478  ADCSRA |= (1 << ADSC); // start conversion
479  while (ADCSRA & (1 << ADSC)) ; // wait to finish
480  return 1100UL * 1024 / ADC;
481  }
482 
488  void setIdle () {
490  pinMode(ACTIVATIONPIN, INPUT);
491  }
498 #ifndef analogPinToChannel
499  #define analogPinToChannel(p) ((p)-14)
500 #endif
501 
502  void unsetIdle () {
503  pinMode(ACTIVATIONPIN, OUTPUT);
504  digitalWrite(ACTIVATIONPIN, LOW);
505  // _delay_ms(5);
506  ATOMIC_BLOCK( ATOMIC_RESTORESTATE ) {
507  __gb_BatCount = 0; // reset irq counter
508  __gb_BatIrq = irq; // set irq method
509  }
510 
511  intVCC = getInternalVcc();
512 
513  ADMUX &= ~(ADMUX_REFMASK | ADMUX_ADCMASK);
514  ADMUX |= ADMUX_REF_AVCC; // select AVCC as reference
515  ADMUX |= analogPinToChannel(SENSPIN); // select channel
516  ADCSRA |= (1 << ADIE) | (1<<ADPS0) | (1<<ADPS1) | (1<<ADPS2); // enable interrupt & 128 prescaler
517  ADCSRA |= (1 << ADSC); // start conversion*/
518  }
521  static uint16_t irq () {
522  return 1UL * intVCC * FACTOR * ADC / 1024 / 10;
523  }
524 };
525 
526 #endif
527 
528 }
529 
530 #endif
as::IrqBaseBatt::resetCurrent
void resetCurrent()
reset current battery value
Definition: BatterySensor.h:348
as::IrqBaseBatt::critical
void critical(uint8_t value)
Definition: BatterySensor.h:332
as::AsyncMeter
Definition: BatterySensor.h:179
as::IrqInternalBatt::setIdle
void setIdle()
Definition: BatterySensor.h:424
as::NoBattery::setIdle
void setIdle()
called when systems enter idle state
Definition: BatterySensor.h:50
as::IrqBaseBatt::setIdle
void setIdle()
Definition: BatterySensor.h:356
as::IrqInternalBatt::~IrqInternalBatt
~IrqInternalBatt()
Definition: BatterySensor.h:410
as::IrqBaseBatt::voltageHighRes
uint16_t voltageHighRes()
for backward compatibility
Definition: BatterySensor.h:343
as::Alarm
Definition: Alarm.h:15
as::NoBattery::critical
bool critical() const
check if battery voltage is below critical value - returns always false
Definition: BatterySensor.h:42
as::IrqInternalBatt::irq
static uint16_t irq()
Definition: BatterySensor.h:447
as::IrqInternalBatt::unsetIdle
void unsetIdle()
Definition: BatterySensor.h:432
as::IrqBaseBatt::m_Critical
uint8_t m_Critical
value for critical battery
Definition: BatterySensor.h:310
as::IrqBaseBatt::voltage
uint8_t voltage()
for backward compatibility
Definition: BatterySensor.h:345
as::IrqExternalBatt::init
void init(__attribute__((unused)) uint32_t period, __attribute__((unused)) AlarmClock &clock)
Definition: BatterySensor.h:467
as::IrqExternalBatt::irq
static uint16_t irq()
Definition: BatterySensor.h:521
as::NoBattery::current
uint8_t current() const
get current battery voltage - returns always 3.3
Definition: BatterySensor.h:40
as::NoBattery::unsetIdle
void unsetIdle()
called when systems returns from idle state
Definition: BatterySensor.h:52
as::BatterySensorUni
Definition: BatterySensor.h:251
as::AlarmClock
Definition: AlarmClock.h:32
as::IrqExternalBatt::~IrqExternalBatt
~IrqExternalBatt()
Definition: BatterySensor.h:462
as::IrqBaseBatt::current
uint8_t current() const
Definition: BatterySensor.h:324
as::IrqExternalBatt::setIdle
void setIdle()
Definition: BatterySensor.h:488
as::IrqBaseBatt
Definition: BatterySensor.h:305
as::IrqExternalBatt::IrqExternalBatt
IrqExternalBatt()
Definition: BatterySensor.h:459
as::IrqBaseBatt::low
bool low() const
Definition: BatterySensor.h:336
as::IrqInternalBatt::IrqInternalBatt
IrqInternalBatt()
Definition: BatterySensor.h:407
as::IrqExternalBatt
Definition: BatterySensor.h:455
as::NoBattery::low
bool low() const
check if battery voltage is below low value - returns always false
Definition: BatterySensor.h:46
as::NoBattery::low
void low(__attribute__((unused)) uint8_t value)
set low value - do nothing
Definition: BatterySensor.h:48
as::NoBattery
Definition: BatterySensor.h:37
as::InternalVCC
Definition: BatterySensor.h:80
as::SyncMeter
Definition: BatterySensor.h:152
as::IrqInternalBatt::init
void init(__attribute__((unused)) uint32_t period, __attribute__((unused)) AlarmClock &clock)
Definition: BatterySensor.h:415
as::IrqBaseBatt::critical
bool critical() const
Definition: BatterySensor.h:328
as::NoBattery::critical
void critical(__attribute__((unused)) uint8_t value)
set critical value - do nothing
Definition: BatterySensor.h:44
as::BatterySensorExt
Definition: BatterySensor.h:263
as::IrqBaseBatt::low
void low(uint8_t value)
Definition: BatterySensor.h:340
as::IrqBaseBatt::m_Low
uint8_t m_Low
value for low battery
Definition: BatterySensor.h:308
as::NoBattery::resetCurrent
void resetCurrent()
reset current battery value
Definition: BatterySensor.h:54
as::BattSensor
Definition: BatterySensor.h:210
as::ExternalVCC
Definition: BatterySensor.h:116
as::IrqInternalBatt
Definition: BatterySensor.h:402