Skip to content
Newer
Older
100644 1030 lines (928 sloc) 26.6 KB
fa906da @jcw files imported
authored Aug 29, 2011
1 // Ports library definitions
9c0edcc @jcw get rid of svn $Id keyword & update email address
authored Aug 29, 2011
2 // 2009-02-13 <jc@wippler.nl> http://opensource.org/licenses/mit-license.php
fa906da @jcw files imported
authored Aug 29, 2011
3
4 #include "Ports.h"
5 #include <avr/sleep.h>
6 #include <util/atomic.h>
7
8 // flag bits sent to the receiver
9 #define MODE_CHANGE 0x80 // a pin mode was changed
10 #define DIG_CHANGE 0x40 // a digital output was changed
11 #define PWM_CHANGE 0x30 // an analog (pwm) value was changed on port 2..3
12 #define ANA_MASK 0x0F // an analog read was requested on port 1..4
13
14 uint16_t Port::shiftRead(uint8_t bitOrder, uint8_t count) const {
15 uint16_t value = 0, mask = bit(LSBFIRST ? 0 : count - 1);
16 for (uint8_t i = 0; i < count; ++i) {
17 digiWrite2(1);
18 delayMicroseconds(5);
19 if (digiRead())
20 value |= mask;
21 if (bitOrder == LSBFIRST)
22 mask <<= 1;
23 else
24 mask >>= 1;
25 digiWrite2(0);
26 delayMicroseconds(5);
27 }
28 return value;
29 }
30
31 void Port::shiftWrite(uint8_t bitOrder, uint16_t value, uint8_t count) const {
32 uint16_t mask = bit(LSBFIRST ? 0 : count - 1);
33 for (uint8_t i = 0; i < count; ++i) {
34 digiWrite((value & mask) != 0);
35 if (bitOrder == LSBFIRST)
36 mask <<= 1;
37 else
38 mask >>= 1;
39 digiWrite2(1);
40 digiWrite2(0);
41 }
42 }
43
44 RemoteNode::RemoteNode (char id, uint8_t band, uint8_t group)
45 : nid (id & 0x1F)
46 {
47 memset(&data, 0, sizeof data);
48 RemoteHandler::setup(nid, band, group);
49 }
50
51 void RemoteNode::poll(uint16_t msecs) {
52 uint8_t pending = millis() >= lastPoll + msecs;
53 if (RemoteHandler::poll(*this, pending))
54 lastPoll = millis();
55 }
56
57 void RemotePort::mode(uint8_t value) const {
58 node.data.flags |= MODE_CHANGE;
59 bitWrite(node.data.modes, pinBit(), value);
60 }
61
62 uint8_t RemotePort::digiRead() const {
63 return bitRead(node.data.digiIO, pinBit());
64 }
65
66 void RemotePort::digiWrite(uint8_t value) const {
67 node.data.flags |= DIG_CHANGE;
68 bitWrite(node.data.digiIO, pinBit(), value);
69 }
70
71 void RemotePort::anaWrite(uint8_t val) const {
72 if (portNum == 2 || portNum == 3) {
73 bitSet(node.data.flags, portNum + 2);
74 node.data.anaOut[portNum - 2] = val;
75 } else
76 digiWrite2(val >= 128);
77 }
78
79 void RemotePort::mode2(uint8_t value) const {
80 node.data.flags |= MODE_CHANGE;
81 bitWrite(node.data.modes, pinBit2(), value);
82 }
83
84 uint16_t RemotePort::anaRead() const {
85 bitSet(node.data.flags, pinBit());
86 return node.data.anaIn[pinBit()];
87 }
88
89 uint8_t RemotePort::digiRead2() const {
90 return bitRead(node.data.digiIO, pinBit2());
91 }
92
93 void RemotePort::digiWrite2(uint8_t value) const {
94 node.data.flags |= DIG_CHANGE;
95 bitWrite(node.data.digiIO, pinBit2(), value);
96 }
97
98 PortI2C::PortI2C (uint8_t num, uint8_t rate)
99 : Port (num), uswait (rate)
100 {
101 sdaOut(1);
102 mode2(OUTPUT);
103 sclHi();
104 }
105
106 uint8_t PortI2C::start(uint8_t addr) const {
107 sclLo();
108 sclHi();
109 sdaOut(0);
110 return write(addr);
111 }
112
113 void PortI2C::stop() const {
114 sdaOut(0);
115 sclHi();
116 sdaOut(1);
117 }
118
119 uint8_t PortI2C::write(uint8_t data) const {
120 sclLo();
121 for (uint8_t mask = 0x80; mask != 0; mask >>= 1) {
122 sdaOut(data & mask);
123 sclHi();
124 sclLo();
125 }
126 sdaOut(1);
127 sclHi();
128 uint8_t ack = ! sdaIn();
129 sclLo();
130 return ack;
131 }
132
133 uint8_t PortI2C::read(uint8_t last) const {
134 uint8_t data = 0;
135 for (uint8_t mask = 0x80; mask != 0; mask >>= 1) {
136 sclHi();
137 if (sdaIn())
138 data |= mask;
139 sclLo();
140 }
141 sdaOut(last);
142 sclHi();
143 sclLo();
144 if (last)
145 stop();
146 sdaOut(1);
147 return data;
148 }
149
150 bool DeviceI2C::isPresent () const {
151 byte ok = send();
152 stop();
153 return ok;
154 }
155
156 byte MilliTimer::poll(word ms) {
157 byte ready = 0;
158 if (armed) {
159 word remain = next - millis();
160 // since remain is unsigned, it will overflow to large values when
161 // the timeout is reached, so this test works as long as poll() is
162 // called no later than 5535 millisecs after the timer has expired
163 if (remain <= 60000)
164 return 0;
165 // return a value between 1 and 255, being msecs+1 past expiration
166 // note: the actual return value is only reliable if poll() is
167 // called no later than 255 millisecs after the timer has expired
168 ready = -remain;
169 }
170 set(ms);
171 return ready;
172 }
173
174 word MilliTimer::remaining() const {
175 word remain = armed ? next - millis() : 0;
176 return remain <= 60000 ? remain : 0;
177 }
178
179 void MilliTimer::set(word ms) {
180 armed = ms != 0;
181 if (armed)
182 next = millis() + ms - 1;
183 }
184
185 void BlinkPlug::ledOn (byte mask) {
186 if (mask & 1) {
187 digiWrite(0);
188 mode(OUTPUT);
189 }
190 if (mask & 2) {
191 digiWrite2(0);
192 mode2(OUTPUT);
193 }
194 leds |= mask; //TODO could be read back from pins, i.s.o. saving here
195 }
196
197 void BlinkPlug::ledOff (byte mask) {
198 if (mask & 1) {
199 mode(INPUT);
200 digiWrite(1);
201 }
202 if (mask & 2) {
203 mode2(INPUT);
204 digiWrite2(1);
205 }
206 leds &= ~ mask; //TODO could be read back from pins, i.s.o. saving here
207 }
208
209 byte BlinkPlug::state () {
210 byte saved = leds;
211 ledOff(1+2);
212 byte result = !digiRead() | (!digiRead2() << 1);
213 ledOn(saved);
214 return result;
215 }
216
217 //TODO deprecated, use buttonCheck() !
218 byte BlinkPlug::pushed () {
219 if (debounce.idle() || debounce.poll()) {
220 byte newState = state();
221 if (newState != lastState) {
222 debounce.set(100); // don't check again for at least 100 ms
223 byte nowOn = (lastState ^ newState) & newState;
224 lastState = newState;
225 return nowOn;
226 }
227 }
228 return 0;
229 }
230
231 byte BlinkPlug::buttonCheck () {
232 // collect button changes in the checkFlags bits, with proper debouncing
233 if (debounce.idle() || debounce.poll()) {
234 byte newState = state();
235 if (newState != lastState) {
236 debounce.set(100); // don't check again for at least 100 ms
237 if ((lastState ^ newState) & 1)
238 bitSet(checkFlags, newState & 1 ? ON1 : OFF1);
239 if ((lastState ^ newState) & 2)
240 bitSet(checkFlags, newState & 2 ? ON2 : OFF2);
241 lastState = newState;
242 }
243 }
244 // note that simultaneous button events will be returned in successive calls
245 if (checkFlags)
246 for (byte i = ON1; i <= OFF2; ++i) {
247 if (bitRead(checkFlags, i)) {
248 bitClear(checkFlags, i);
249 return i;
250 }
251 }
252 // if there are no button events, return the overall current button state
253 return lastState == 3 ? ALL_ON : lastState ? SOME_ON : ALL_OFF;
254 }
255
256 void MemoryPlug::load (word page, void* buf, byte offset, int count) {
67a4d79 @jcw fix Memory Plug load after save (awightma)
authored Sep 5, 2011
257 // also don't load right after a save, see http://forum.jeelabs.net/node/469
258 while (millis() < nextSave)
259 ;
260
fa906da @jcw files imported
authored Aug 29, 2011
261 setAddress(0x50 + (page >> 8));
262 send();
263 write((byte) page);
264 write(offset);
265 receive();
266 byte* p = (byte*) buf;
267 while (--count >= 0)
268 *p++ = read(count == 0);
269 stop();
270 }
271
272 void MemoryPlug::save (word page, const void* buf, byte offset, int count) {
273 // don't do back-to-back saves, last one must have had time to finish!
274 while (millis() < nextSave)
275 ;
276
277 setAddress(0x50 + (page >> 8));
278 send();
279 write((byte) page);
280 write(offset);
281 const byte* p = (const byte*) buf;
282 while (--count >= 0)
283 write(*p++);
284 stop();
285
286 nextSave = millis() + 6;
287 // delay(5);
288 }
289
290 long MemoryStream::position (byte writing) const {
291 long v = (curr - start) * step;
292 if (pos > 0 && !writing)
293 --v; // get() advances differently than put()
294 return (v << 8) | pos;
295 }
296
297 byte MemoryStream::get () {
298 if (pos == 0) {
299 dev.load(curr, buffer);
300 curr += step;
301 }
302 return buffer[pos++];
303 }
304
305 void MemoryStream::put (byte data) {
306 buffer[pos++] = data;
307 if (pos == 0) {
308 dev.save(curr, buffer);
309 curr += step;
310 }
311 }
312
313 word MemoryStream::flush () {
314 if (pos != 0) {
315 memset(buffer + pos, 0xFF, 256 - pos);
316 dev.save(curr, buffer);
317 }
318 return curr;
319 }
320
321 void MemoryStream::reset () {
322 curr = start;
323 pos = 0;
324 }
325
326 // uart register definitions
327 #define RHR (0 << 3)
328 #define THR (0 << 3)
329 #define DLL (0 << 3)
330 #define DLH (1 << 3)
331 #define FCR (2 << 3)
332 #define LCR (3 << 3)
333 #define RXLVL (9 << 3)
334
335 void UartPlug::regSet (byte reg, byte value) {
336 dev.send();
337 dev.write(reg);
338 dev.write(value);
339 }
340
341 void UartPlug::regRead (byte reg) {
342 dev.send();
343 dev.write(reg);
344 dev.receive();
345 }
346
347 void UartPlug::begin (long baud) {
348 word divisor = 230400 / baud;
349 regSet(LCR, 0x80); // divisor latch enable
350 regSet(DLL, divisor); // low byte
351 regSet(DLH, divisor >> 8); // high byte
352 regSet(LCR, 0x03); // 8 bits, no parity
353 regSet(FCR, 0x07); // fifo enable (and flush)
354 dev.stop();
355 }
356
357 byte UartPlug::available () {
358 if (in != out)
359 return 1;
360 out = 0;
361 regRead(RXLVL);
362 in = dev.read(1);
363 if (in == 0)
364 return 0;
365 if (in > sizeof rxbuf)
366 in = sizeof rxbuf;
367 regRead(RHR);
368 for (byte i = 0; i < in; ++i)
369 rxbuf[i] = dev.read(i == in - 1);
370 return 1;
371 }
372
373 int UartPlug::read () {
374 return available() ? rxbuf[out++] : -1;
375 }
376
377 void UartPlug::flush () {
378 regSet(FCR, 0x07); // flush both RX and TX queues
379 dev.stop();
380 in = out;
381 }
382
3e08ca2 @jcw add the "tiny50hz" sample sketch for ATtiny85
authored Oct 10, 2011
383 WRITE_RESULT UartPlug::write (byte data) {
fa906da @jcw files imported
authored Aug 29, 2011
384 regSet(THR, data);
385 dev.stop();
5165eb5 @jcw add ATtiny84 support
authored Oct 17, 2011
386 #if ARDUINO >= 100 && !defined(__AVR_ATtiny84__) && !defined(__AVR_ATtiny85__)
87df32e @jcw fix a few IDE 1.0 differences
authored Aug 29, 2011
387 return 1;
3e08ca2 @jcw add the "tiny50hz" sample sketch for ATtiny85
authored Oct 10, 2011
388 #endif
fa906da @jcw files imported
authored Aug 29, 2011
389 }
390
391 void DimmerPlug::begin () {
392 setReg(MODE1, 0x00); // normal
393 setReg(MODE2, 0x14); // inverted, totem-pole
394 setReg(GRPPWM, 0xFF); // set group dim to max brightness
395 setMulti(LEDOUT0, 0xFF, 0xFF, 0xFF, 0xFF, -1); // all LEDs group-dimmable
396 }
397
398 byte DimmerPlug::getReg(byte reg) const {
399 send();
400 write(reg);
401 receive();
402 byte result = read(1);
403 stop();
404 return result;
405 }
406
407 void DimmerPlug::setReg(byte reg, byte value) const {
408 send();
409 write(reg);
410 write(value);
411 stop();
412 }
413
414 void DimmerPlug::setMulti(byte reg, ...) const {
415 va_list ap;
416 va_start(ap, reg);
417 send();
418 write(0xE0 | reg); // auto-increment
419 for (;;) {
420 int v = va_arg(ap, int);
421 if (v < 0) break;
422 write(v);
423 }
424 stop();
425 }
426
427 void LuxPlug::setGain(byte high) {
428 send();
429 write(0x81); // write to Timing regiser
430 write(high ? 0x12 : 0x02);
431 stop();
432 }
433
434 const word* LuxPlug::getData() {
435 send();
436 write(0xA0 | DATA0LOW);
437 receive();
438 data.b[0] = read(0);
439 data.b[1] = read(0);
440 data.b[2] = read(0);
441 data.b[3] = read(1);
442 stop();
443 return data.w;
444 }
445
446 #define LUX_SCALE 14 // scale by 2^14
447 #define RATIO_SCALE 9 // scale ratio by 2^9
448 #define CH_SCALE 10 // scale channel values by 2^10
449
450 word LuxPlug::calcLux(byte iGain, byte tInt) const
451 {
452 unsigned long chScale;
453 switch (tInt) {
454 case 0: chScale = 0x7517; break;
455 case 1: chScale = 0x0fe7; break;
456 default: chScale = (1 << CH_SCALE); break;
457 }
458 if (!iGain)
459 chScale <<= 4;
460 unsigned long channel0 = (data.w[0] * chScale) >> CH_SCALE;
461 unsigned long channel1 = (data.w[1] * chScale) >> CH_SCALE;
462
463 unsigned long ratio1 = 0;
464 if (channel0 != 0)
465 ratio1 = (channel1 << (RATIO_SCALE+1)) / channel0;
466 unsigned long ratio = (ratio1 + 1) >> 1;
467
468 word b, m;
469 if (ratio <= 0x0040) { b = 0x01F2; m = 0x01BE; }
470 else if (ratio <= 0x0080) { b = 0x0214; m = 0x02D1; }
471 else if (ratio <= 0x00C0) { b = 0x023F; m = 0x037B; }
472 else if (ratio <= 0x0100) { b = 0x0270; m = 0x03FE; }
473 else if (ratio <= 0x0138) { b = 0x016F; m = 0x01FC; }
474 else if (ratio <= 0x019A) { b = 0x00D2; m = 0x00FB; }
475 else if (ratio <= 0x029A) { b = 0x0018; m = 0x0012; }
476 else { b = 0x0000; m = 0x0000; }
477
478 unsigned long temp = channel0 * b - channel1 * m;
479 temp += 1 << (LUX_SCALE-1);
480 return temp >> LUX_SCALE;
481 }
482
483 const int* GravityPlug::getAxes() {
484 send();
485 write(0x02);
486 receive();
487 for (byte i = 0; i < 5; ++i)
488 data.b[i] = read(0);
489 data.b[5] = read(1);
490 stop();
491 data.w[0] = (data.b[0] >> 6) | (data.b[1] << 2);
492 data.w[1] = (data.b[2] >> 6) | (data.b[3] << 2);
493 data.w[2] = (data.b[4] >> 6) | (data.b[5] << 2);
494 for (byte i = 0; i < 3; ++i)
495 data.w[i] = (data.w[i] ^ 0x200) - 0x200; // sign extends bit 9
496 return data.w;
497 }
498
499 void InputPlug::select(uint8_t channel) {
500 digiWrite(0);
501 mode(OUTPUT);
502
503 delayMicroseconds(slow ? 400 : 50);
504 byte data = 0x10 | (channel & 0x0F);
505 byte mask = 1 << (portNum + 3); // digitalWrite is too slow
506
507 ATOMIC_BLOCK(ATOMIC_FORCEON) {
508 for (byte i = 0; i < 5; ++i) {
509 byte us = bitRead(data, 4 - i) ? 9 : 3;
510 if (slow)
511 us <<= 3;
512 #ifdef PORTD
513 PORTD |= mask;
514 delayMicroseconds(us);
515 PORTD &= ~ mask;
516 #else
517 //XXX TINY!
518 #endif
519 delayMicroseconds(slow ? 32 : 4);
520 }
521 }
522 }
523
524 byte HeadingBoard::eepromByte(byte reg) const {
525 eeprom.send();
526 eeprom.write(reg);
527 eeprom.receive();
528 byte result = eeprom.read(1);
529 eeprom.stop();
530 return result;
531 }
532
533 void HeadingBoard::getConstants() {
534 for (byte i = 0; i < 18; ++i)
535 ((byte*) &C1)[i < 14 ? i^1 : i] = eepromByte(16 + i);
536 // Serial.println(C1);
537 // Serial.println(C2);
538 // Serial.println(C3);
539 // Serial.println(C4);
540 // Serial.println(C5);
541 // Serial.println(C6);
542 // Serial.println(C7);
543 // Serial.println(A, DEC);
544 // Serial.println(B, DEC);
545 // Serial.println(C, DEC);
546 // Serial.println(D, DEC);
547 }
548
549 word HeadingBoard::adcValue(byte press) const {
550 aux.digiWrite(1);
551 adc.send();
552 adc.write(0xFF);
553 adc.write(0xE0 | (press << 4));
554 adc.stop();
555 delay(40);
556 adc.send();
557 adc.write(0xFD);
558 adc.receive();
559 byte msb = adc.read(0);
560 int result = (msb << 8) | adc.read(1);
561 adc.stop();
562 aux.digiWrite(0);
563 return result;
564 }
565
566 void HeadingBoard::begin() {
567 // prepare ADC
568 aux.mode(OUTPUT);
569 aux.digiWrite(0);
570
571 // generate 32768 Hz on IRQ pin (OC2B)
572 #ifdef TCCR2A
573 TCCR2A = bit(COM2B0) | bit(WGM21);
574 TCCR2B = bit(CS20);
575 OCR2A = 243;
576 #else
577 //XXX TINY!
578 #endif
579 aux.mode3(OUTPUT);
580
581 getConstants();
582 }
583
584 void HeadingBoard::pressure(int& temp, int& pres) const {
585 word D2 = adcValue(0);
586 // Serial.print("D2 = ");
587 // Serial.println(D2);
588 int corr = (D2 - C5) >> 7;
589 // Serial.print("corr = ");
590 // Serial.println(corr);
591 int dUT = (D2 - C5) - (corr * (long) corr * (D2 >= C5 ? A : B) >> C);
592 // Serial.print("dUT = ");
593 // Serial.println(dUT);
594 temp = 250 + (dUT * C6 >> 16) - (dUT >> D);
595
596 word D1 = adcValue(1);
597 // Serial.print("D1 = ");
598 // Serial.println(D1);
599 word OFF = (C2 + ((C4 - 1024) * dUT >> 14)) << 2;
600 // Serial.print("OFF = ");
601 // Serial.println(OFF);
602 word SENS = C1 + (C3 * dUT >> 10);
603 // Serial.print("SENS = ");
604 // Serial.println(SENS);
605 word X = (SENS * (D1 - 7168L) >> 14) - OFF;
606 // Serial.print("X = ");
607 // Serial.println(X);
608 pres = (X * 10L >> 5) + C7;
609 }
610
611 void HeadingBoard::heading(int& xaxis, int& yaxis) {
612 // set or reset the magnetometer coil
613 compass.send();
614 compass.write(0x00);
615 compass.write(setReset);
616 compass.stop();
617 delayMicroseconds(50);
618 setReset = 6 - setReset;
619 // perform measurement
620 compass.send();
621 compass.write(0x00);
622 compass.write(0x01);
623 compass.stop();
624 delay(5);
625 compass.send();
626 compass.write(0x00);
627 compass.receive();
628 byte tmp, reg = compass.read(0);
629 tmp = compass.read(0);
630 xaxis = ((tmp << 8) | compass.read(0)) - 2048;
631 tmp = compass.read(0);
632 yaxis = ((tmp << 8) | compass.read(1)) - 2048;
633 compass.stop();
634 }
635
636 InfraredPlug::InfraredPlug (uint8_t num)
637 : Port (num), slot (140), gap (80), fill (-1), prev (0) {
638 digiWrite(0);
639 mode(OUTPUT);
640 mode2(INPUT);
641 digiWrite2(1); // pull-up
642 }
643
644 void InfraredPlug::configure(uint8_t slot4, uint8_t gap256) {
645 slot = slot4;
646 gap = gap256;
647 fill = -1;
648 }
649
650 void InfraredPlug::poll() {
651 byte bit = digiRead2(); // 0 is interpreted as pulse ON
652 if (fill < 0) {
653 if (fill < -1 || bit == 1)
654 return;
655 fill = 0;
656 prev = micros();
657 memset(buf, 0, sizeof buf);
658 }
659 // act only if the bit changed, using the low bit of the nibble fill count
660 if (bit != (fill & 1) && fill < 2 * sizeof buf) {
661 uint32_t curr = micros(), diff = (curr - prev + 2) >> 2;
662 if (diff > 65000)
663 diff = 65000; // * 4 us, i.e. 260 ms
664 // convert to a slot number, with rounding halfway between each slot
665 word ticks = ((word) diff + slot / 2) / slot;
666 if (ticks > 20)
667 ticks = 20;
668 // condense upper values to fit in the range 0..15
669 byte nibble = ticks;
670 if (nibble > 10)
671 nibble -= (nibble - 10) / 2;
672 buf[fill>>1] |= nibble << ((fill & 1) << 2);
673 ++fill;
674 prev = curr;
675 }
676 }
677
678 uint8_t InfraredPlug::done() {
679 byte result = 0;
680 if (fill > 0)
681 ATOMIC_BLOCK(ATOMIC_RESTORESTATE) {
682 if (((micros() - prev) >> 8) >= gap) {
683 result = fill;
684 fill = -2; // prevent new pulses from clobbering buf
685 }
686 }
687 else if (fill < -1)
688 fill = -1; // second call to done() release buffer again for capture
689 return result;
690 }
691
692 uint8_t InfraredPlug::decoder(uint8_t nibbles) {
693 switch (nibbles) {
694 case 67: // 2 + 64 + 1 nibbles could be a NEC packet
695 if (buf[0] == 0x8D && buf[33] == 0x01) {
696 // check that all nibbles are either 1 or 3
697 for (byte i = 1; i < 33; ++i)
698 if ((buf[i] & ~0x20) != 0x11)
699 return UNKNOWN;
700 // valid packet, convert in-place
701 for (byte i = 0; i < 4; ++i) {
702 byte v;
703 for (byte j = 0; j < 8; ++j)
704 v = (v << 1) | (buf[1+j+8*i] >> 5);
705 buf[i] = v;
706 }
707 return NEC;
708 }
709 break;
710 case 3: // 2 + 1 nibbles could be a NEC repeat packet
711 if (buf[0] == 0x4D && buf[1] == 0x01)
712 return NEC_REP;
713 break;
714 }
715 return UNKNOWN;
716 }
717
718 void InfraredPlug::send(const uint8_t* data, uint16_t bits) {
719 // TODO: switch to an interrupt-driven design
720 for (byte i = 0; i < bits; ++i) {
721 digiWrite(bitRead(data[i/8], i%8));
722 delayMicroseconds(4 * slot);
723 }
724 digiWrite(0);
725 }
726
727 void ProximityPlug::begin() {
728 delay(100);
729 setReg(CONFIG, 0x04); // reset, STOP1
730 delay(100);
731 // setReg(TPCONFIG, 0xB5); // TPSE, BKA, ACE, TPTBE, TPE
732 setReg(TPCONFIG, 0xB1); // TPSE, BKA, ACE, TPE
733 setReg(CONFIG, 0x15); // RUN1
734 delay(100);
735 }
736
737 void ProximityPlug::setReg(byte reg, byte value) const {
738 send();
739 write(reg);
740 write(value);
741 stop();
742 }
743
744 byte ProximityPlug::getReg(byte reg) const {
745 send();
746 write(reg);
747 receive();
748 byte result = read(1);
749 stop();
750 return result;
751 }
752
753 // ISR(WDT_vect) { Sleepy::watchdogEvent(); }
754
755 static volatile byte watchdogCounter;
756
757 void Sleepy::watchdogInterrupts (char mode) {
758 // correct for the fact that WDP3 is *not* in bit position 3!
759 if (mode & bit(3))
760 mode ^= bit(3) | bit(WDP3);
761 // pre-calculate the WDTCSR value, can't do it inside the timed sequence
762 // we only generate interrupts, no reset
763 byte wdtcsr = mode >= 0 ? bit(WDIE) | mode : 0;
764 MCUSR &= ~(1<<WDRF);
765 ATOMIC_BLOCK(ATOMIC_FORCEON) {
3e08ca2 @jcw add the "tiny50hz" sample sketch for ATtiny85
authored Oct 10, 2011
766 #ifndef WDTCSR
767 #define WDTCSR WDTCR
768 #endif
fa906da @jcw files imported
authored Aug 29, 2011
769 WDTCSR |= (1<<WDCE) | (1<<WDE); // timed sequence
770 WDTCSR = wdtcsr;
771 }
772 }
773
774 void Sleepy::powerDown (byte prrOff) {
775 byte adcsraSave = ADCSRA;
776 ADCSRA &= ~ bit(ADEN); // disable the ADC
777 #ifdef PRR
778 byte prrSave = PRR;
779 PRR = prrOff;
780 #endif
781 // see http://www.nongnu.org/avr-libc/user-manual/group__avr__sleep.html
782 set_sleep_mode(SLEEP_MODE_PWR_DOWN);
783 ATOMIC_BLOCK(ATOMIC_FORCEON) {
784 sleep_enable();
785 // sleep_bod_disable(); // can't use this - not in my avr-libc version!
786 #ifdef BODSE
787 MCUCR = MCUCR | bit(BODSE) | bit(BODS); // timed sequence
788 MCUCR = MCUCR & ~ bit(BODSE) | bit(BODS);
789 #endif
790 }
791 sleep_cpu();
792 sleep_disable();
793 // re-enable what we disabled
794 #ifdef PRR
795 PRR = prrSave;
796 #endif
797 ADCSRA = adcsraSave;
798 }
799
800 byte Sleepy::loseSomeTime (word msecs) {
801 // only slow down for periods longer than the watchdog granularity
802 while (msecs >= 16) {
803 char wdp = 0; // wdp 0..9 corresponds to roughly 16..8192 ms
804 while (msecs >= (32 << wdp) && wdp < 9)
805 ++wdp;
806 watchdogCounter = 0;
807 watchdogInterrupts(wdp);
808 powerDown();
809 watchdogInterrupts(-1); // off
810 if (watchdogCounter == 0)
811 return 0; // lost some time, but got interrupted
812 // adjust the milli ticks, since we will have missed several
813 extern volatile unsigned long timer0_millis;
814 timer0_millis += 16 << wdp;
815 msecs -= 16 << wdp;
816 }
817 return 1; // lost some time as planned
818 }
819
820 void Sleepy::watchdogEvent() {
821 ++watchdogCounter;
822 }
823
6f84156 @jcw removed some test code leftovers
authored Dec 21, 2011
824 Scheduler::Scheduler (byte size) : maxTasks (size), remaining (~0) {
fa906da @jcw files imported
authored Aug 29, 2011
825 byte bytes = size * sizeof *tasks;
826 tasks = (word*) malloc(bytes);
827 memset(tasks, 0xFF, bytes);
828 }
829
fc9fc6c @stumo Added a new return state of -2 to Scheduler::poll() for the case when…
stumo authored Oct 24, 2011
830 Scheduler::Scheduler (word* buf, byte size) : tasks (buf), maxTasks (size), remaining(~0) {
fa906da @jcw files imported
authored Aug 29, 2011
831 byte bytes = size * sizeof *tasks;
832 memset(tasks, 0xFF, bytes);
833 }
834
835 char Scheduler::poll() {
836 // all times in the tasks array are relative to the "remaining" value
837 // i.e. only remaining counts down while waiting for the next timeout
838 if (remaining == 0) {
839 word lowest = ~0;
840 for (byte i = 0; i < maxTasks; ++i) {
841 if (tasks[i] == 0) {
842 tasks[i] = ~0;
843 return i;
844 }
845 if (tasks[i] < lowest)
846 lowest = tasks[i];
847 }
76cdf33 @stumo Bugfix for scheduler: did not take into account stopped tasks when de…
stumo authored Sep 28, 2011
848 if (lowest != ~0) {
849 for (byte i = 0; i < maxTasks; ++i) {
850 if(tasks[i] != ~0) {
851 tasks[i] -= lowest;
852 }
853 }
854 }
fa906da @jcw files imported
authored Aug 29, 2011
855 remaining = lowest;
fc9fc6c @stumo Added a new return state of -2 to Scheduler::poll() for the case when…
stumo authored Oct 24, 2011
856 } else if (remaining == ~0) //remaining == ~0 means nothing running
857 return -2;
6f84156 @jcw removed some test code leftovers
authored Dec 21, 2011
858 else if (ms100.poll(100))
fa906da @jcw files imported
authored Aug 29, 2011
859 --remaining;
860 return -1;
861 }
862
863 char Scheduler::pollWaiting() {
fc9fc6c @stumo Added a new return state of -2 to Scheduler::poll() for the case when…
stumo authored Oct 24, 2011
864 if(remaining == ~0) // Nothing running!
865 return -2;
fa906da @jcw files imported
authored Aug 29, 2011
866 // first wait until the remaining time we need to wait is less than 0.1s
867 while (remaining > 0) {
f9fbea4 @jcw power optimization in the Scheduler class
authored Dec 21, 2011
868 word step = remaining > 600 ? 600 : remaining;
869 if (!Sleepy::loseSomeTime(100 * step)) // uses least amount of power
fa906da @jcw files imported
authored Aug 29, 2011
870 return -1;
f9fbea4 @jcw power optimization in the Scheduler class
authored Dec 21, 2011
871 remaining -= step;
fa906da @jcw files imported
authored Aug 29, 2011
872 }
873 // now lose some more time until that 0.1s mark
874 if (!Sleepy::loseSomeTime(ms100.remaining()))
875 return -1;
876 // lastly, just ignore the 0..15 ms still left to go until the 0.1s mark
877 return poll();
878 }
879
880 void Scheduler::timer(byte task, word tenths) {
881 // if new timer will go off sooner than the rest, then adjust all entries
882 if (tenths < remaining) {
883 word diff = remaining - tenths;
884 for (byte i = 0; i < maxTasks; ++i)
885 if (tasks[i] != ~0)
886 tasks[i] += diff;
887 remaining = tenths;
888 }
889 tasks[task] = tenths - remaining;
890 }
891
892 void Scheduler::cancel(byte task) {
893 tasks[task] = ~0;
894 }
895
896 #ifdef Stream_h // only available in recent Arduino IDE versions
897
898 InputParser::InputParser (byte* buf, byte size, Commands* ctab, Stream& stream)
899 : buffer (buf), limit (size), cmds (ctab), io (stream) {
900 reset();
901 }
902
903 InputParser::InputParser (byte size, Commands* ctab, Stream& stream)
904 : limit (size), cmds (ctab), io (stream) {
905 buffer = (byte*) malloc(size);
906 reset();
907 }
908
909 void InputParser::reset() {
910 fill = next = 0;
911 instring = hexmode = hasvalue = 0;
912 top = limit;
913 }
914
915 void InputParser::poll() {
916 if (!io.available())
917 return;
918 char ch = io.read();
919 if (ch < ' ' || fill >= top) {
920 reset();
921 return;
922 }
923 if (instring) {
924 if (ch == '"') {
925 buffer[fill++] = 0;
926 do
927 buffer[--top] = buffer[--fill];
928 while (fill > value);
929 ch = top;
930 instring = 0;
931 }
932 buffer[fill++] = ch;
933 return;
934 }
935 if (hexmode && ('0' <= ch && ch <= '9' ||
936 'A' <= ch && ch <= 'F' ||
937 'a' <= ch && ch <= 'f')) {
938 if (!hasvalue)
939 value = 0;
940 if (ch > '9')
941 ch += 9;
942 value <<= 4;
943 value |= (byte) (ch & 0x0F);
944 hasvalue = 1;
945 return;
946 }
947 if ('0' <= ch && ch <= '9') {
948 if (!hasvalue)
949 value = 0;
950 value = 10 * value + (ch - '0');
951 hasvalue = 1;
952 return;
953 }
954 hexmode = 0;
955 switch (ch) {
956 case '$': hexmode = 1;
957 return;
958 case '"': instring = 1;
959 value = fill;
960 return;
961 case ':': (word&) buffer[fill] = value;
962 fill += 2;
963 value >>= 16;
964 // fall through
965 case '.': (word&) buffer[fill] = value;
966 fill += 2;
967 hasvalue = 0;
968 return;
969 case '-': value = - value;
970 hasvalue = 0;
971 return;
972 case ' ': if (!hasvalue)
973 return;
974 // fall through
975 case ',': buffer[fill++] = value;
976 hasvalue = 0;
977 return;
978 }
979 if (hasvalue) {
980 io.print("Unrecognized character: ");
981 io.print(ch);
982 io.println();
983 reset();
984 return;
985 }
986
987 for (Commands* p = cmds; ; ++p) {
988 char code = pgm_read_byte(&p->code);
989 if (code == 0)
990 break;
991 if (ch == code) {
992 byte bytes = pgm_read_byte(&p->bytes);
993 if (fill < bytes) {
994 io.print("Not enough data, need ");
995 io.print((int) bytes);
996 io.println(" bytes");
997 } else {
998 memset(buffer + fill, 0, top - fill);
999 ((void (*)()) pgm_read_word(&p->fun))();
1000 }
1001 reset();
1002 return;
1003 }
1004 }
1005
1006 io.print("Known commands:");
1007 for (Commands* p = cmds; ; ++p) {
1008 char code = pgm_read_byte(&p->code);
1009 if (code == 0)
1010 break;
1011 io.print(' ');
1012 io.print(code);
1013 }
1014 io.println();
1015 }
1016
1017 InputParser& InputParser::get(void* ptr, byte len) {
1018 memcpy(ptr, buffer + next, len);
1019 next += len;
1020 return *this;
1021 }
1022
1023 InputParser& InputParser::operator >> (const char*& v) {
1024 byte offset = buffer[next++];
1025 v = top <= offset && offset < limit ? (char*) buffer + offset : "";
1026 return *this;
1027 }
1028
1029 #endif // Stream_h
Something went wrong with that request. Please try again.