diff --git a/.gitignore b/.gitignore index c0c215d..0671825 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,6 @@ +.pioenvs +.clang_complete +.gcc-flags.json *.bin *.eep *.elf @@ -12,3 +15,4 @@ libcore.a .clang_complete .gcc-flags.json .piolibdeps + diff --git a/.library.json b/.library.json new file mode 100644 index 0000000..b635827 --- /dev/null +++ b/.library.json @@ -0,0 +1,22 @@ +{ + "name": "Interrupted", + "authors": + [ + { + "name": "Stephen Crane", + "email": "jscrane@gmail.com", + "url": "https://www.linkedin.com/in/jscrane", + "maintainer": true + } + ], + "version" : "1.1.0", + "description" : "Interrupt driven device framework", + "keywords" : "avr, Arduino", + "repository": + { + "type" : "git", + "url": "https://github.com/jscrane/Interrupted.git" + }, + "frameworks": ["arduino"], + "platforms": ["atmelavr"] +} diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000..72c6e43 --- /dev/null +++ b/.travis.yml @@ -0,0 +1,65 @@ +# Continuous Integration (CI) is the practice, in software +# engineering, of merging all developer working copies with a shared mainline +# several times a day < http://docs.platformio.org/en/stable/ci/index.html > +# +# Documentation: +# +# * Travis CI Embedded Builds with PlatformIO +# < https://docs.travis-ci.com/user/integration/platformio/ > +# +# * PlatformIO integration with Travis CI +# < http://docs.platformio.org/en/stable/ci/travis.html > +# +# * User Guide for `platformio ci` command +# < http://docs.platformio.org/en/stable/userguide/cmd_ci.html > +# +# +# Please choice one of the following templates (proposed below) and uncomment +# it (remove "# " before each line) or use own configuration according to the +# Travis CI documentation (see above). +# + + +# +# Template #1: General project. Test it using existing `platformio.ini`. +# + +# language: python +# python: +# - "2.7" +# +# sudo: false +# cache: +# directories: +# - "~/.platformio" +# +# install: +# - pip install -U platformio +# +# script: +# - platformio run + + +# +# Template #2: The project is intended to by used as a library with examples +# + +# language: python +# python: +# - "2.7" +# +# sudo: false +# cache: +# directories: +# - "~/.platformio" +# +# env: +# - PLATFORMIO_CI_SRC=path/to/test/file.c +# - PLATFORMIO_CI_SRC=examples/file.ino +# - PLATFORMIO_CI_SRC=path/to/test/directory +# +# install: +# - pip install -U platformio +# +# script: +# - platformio ci --lib="." --board=ID_1 --board=ID_2 --board=ID_N diff --git a/analog.h b/analog.h index 4e323cd..fce9ea8 100644 --- a/analog.h +++ b/analog.h @@ -1,16 +1,19 @@ #ifndef __ANALOG_H__ #define __ANALOG_H__ +#include +#include "device.h" + /** * Analog-to-Digital conversion. - * + * * FIXME: analogReadResolution() */ class Analog: public Device { public: // pin is the analog input, e.g., A0-A7 Analog(int pin, unsigned ref = DEFAULT): - Device(pin), _pin(pin), _ref(ref) {} + Device(pin), _pin(pin), _ref(ref), _next_reading_valid(false) {} // not enabled by default bool begin(); @@ -24,6 +27,13 @@ class Analog: public Device { // returns last converted value or 0xffff if not ready unsigned read(); + // call to turn off ADC altogether and back on again + virtual void sleep(); + virtual void wake(); + + bool next_reading_valid(void) {return _next_reading_valid;} + void next_reading_will_be_valid() {_next_reading_valid = true; } + protected: void _enable(bool enabled); @@ -33,6 +43,7 @@ class Analog: public Device { int _pin; unsigned _ref; + volatile bool _next_reading_valid; }; #endif diff --git a/atmega328p/analog.cpp b/atmega328p/analog.cpp index 04747f9..7cfb0ee 100644 --- a/atmega328p/analog.cpp +++ b/atmega328p/analog.cpp @@ -6,7 +6,7 @@ #include "device.h" #include "analog.h" -static Device *adc; +static Analog *adc; static volatile unsigned reading = 0xffff; ISR(ADC_vect) { @@ -14,7 +14,14 @@ ISR(ADC_vect) { uint8_t low = ADCL; uint8_t high = ADCH; reading = (high << 8) | low; - adc->ready(); + if (adc->_next_reading_valid) + adc->ready(); + else + { + // Can't use this conversion so start a new conversion + ADCSRA |= bit(ADSC) | bit(ADIE); + adc->_next_reading_valid = true; + } } } @@ -39,13 +46,24 @@ void Analog::_init() { ADMUX = (_ref << 6) | ((_pin - A0) & 0x0f); } +void Analog::sleep() { + ADCSRA &= ~_BV(ADEN); + ACSR |= _BV(ACD); + power_adc_disable(); +} + +void Analog::wake() { + power_adc_enable(); + _next_reading_valid = false; // Ignore first reading after power on + ADCSRA |= _BV(ADEN); + ACSR &= ~_BV(ACD); +} + bool Analog::begin() { adc = this; unsigned sreg = SREG; cli(); - power_adc_enable(); - ADCSRA |= _BV(ADEN); - ACSR &= ~_BV(ACD); + wake(); _init(); SREG = sreg; return false; diff --git a/atmega328p/device.cpp b/atmega328p/device.cpp index 5b439f5..38db8c2 100644 --- a/atmega328p/device.cpp +++ b/atmega328p/device.cpp @@ -6,6 +6,10 @@ #include "device.h" +// Ensure that these are defined properly (there appears to be a bug in avr/power.h for ATMega328[P]) +#define power_all_enable() (PRR &= (uint8_t)~((1<enable(_devices[i]->begin()); + { + if (_devices[i]->begin()) + { + _devices[i]->wake(); + _devices[i]->enable(true); + } + } sei(); } diff --git a/atmega328p/serial.cpp b/atmega328p/serial.cpp index 674213c..04adba8 100644 --- a/atmega328p/serial.cpp +++ b/atmega328p/serial.cpp @@ -1,24 +1,38 @@ #include -#include #include +#include #include +#include #include "device.h" #include "serial.h" void SerialDevice::init() { if (_baud) { - power_usart0_enable(); unsigned prescale = ((F_CPU) / 16 + (_baud / 2)) / _baud - 1; - uint8_t sreg = SREG; - cli(); + + uint8_t saved_SREG = SREG; // Save the interrupt flag + cli(); // disable interrupts while initializing the USART UBRR0H = (prescale >> 8) & 0xff; UBRR0L = prescale & 0xff; - UCSR0C = _BV(UCSZ00) | _BV(UCSZ01); // 8N1 - SREG = sreg; + UCSR0C = bit(UCSZ00) | bit(UCSZ01); // 8 bits, no parity, 1 stop bit + SREG = saved_SREG; // restore the interrupt flag } } unsigned SerialDevice::_sleepmode() { return SLEEP_MODE_IDLE; } + +// call to turn off device altogether +void SerialDevice::sleep() +{ + power_usart0_disable(); // Power down the USART +} + +// call to turn device on again +void SerialDevice::wake() +{ + power_usart0_enable(); // Ensure USART powered up + this->init(); // After power on, the module must be initialized +} diff --git a/atmega328p/serialin.cpp b/atmega328p/serialin.cpp index cec9b58..a57bfe5 100644 --- a/atmega328p/serialin.cpp +++ b/atmega328p/serialin.cpp @@ -10,7 +10,8 @@ static SerialIn *device; bool SerialIn::begin() { device = this; - init(); + wake(); + sleep(); return true; } diff --git a/atmega328p/serialout.cpp b/atmega328p/serialout.cpp index c5decc7..7bdae66 100644 --- a/atmega328p/serialout.cpp +++ b/atmega328p/serialout.cpp @@ -1,4 +1,5 @@ #include +#include #include #include #include @@ -11,8 +12,9 @@ static SerialOut *device; bool SerialOut::begin() { device = this; - init(); + wake(); UCSR0B |= _BV(TXEN0); + sleep(); return false; } @@ -30,10 +32,13 @@ bool SerialOut::write(char const *ptr) { return false; } +// Output a single character; may be called from interrupt service routine +// If no more characters, then signal that the device is ready void SerialOut::do_output() { byte b = *_tx_ptr; if (b) { _tx_ptr++; + bitClear(UCSR0B, TXC0); UDR0 = b; } else { _tx_ptr = 0; @@ -47,3 +52,4 @@ ISR(USART_TX_vect) if (device) device->do_output(); } + diff --git a/atmega328p/timer.cpp b/atmega328p/timer.cpp index 33b342c..a8bf2fd 100644 --- a/atmega328p/timer.cpp +++ b/atmega328p/timer.cpp @@ -1,4 +1,5 @@ #include +#include #include #include #include @@ -6,22 +7,46 @@ #include "device.h" #include "timer.h" +// Timer 1 is used so as to avoid conflict with the timer 0, used by the delay() function in the Arduino library static Device *t1; +// Ensure that these are defined properly +#ifndef power_timer1_enable +#define power_timer1_enable() (PRR &= (uint8_t)~(1 << PRTIM1)) +#endif +#ifndef power_timer1_disable +#define power_timer1_disable() (PRR |= (uint8_t)(1 << PRTIM1)) +#endif + ISR(TIMER1_COMPA_vect) { if (t1) t1->ready(); } +// call to turn off device altogether +void Timer::sleep() +{ + power_timer1_disable(); +} + +// call to turn device on again +void Timer::wake() +{ + power_timer1_enable(); +} + + bool Timer::begin() { t1 = this; + power_timer1_enable(); TCCR1A = 0; // CTC mode, 1024 prescaler TCCR1B = _BV(WGM12) | _BV(CS10) | _BV(CS12); - OCR1A = F_CPU / 1024000 - 1; // 1 ms + OCR1A = (uint16_t) (F_CPU / 1024000L) - 1; // approximately 1 ms divided by the divisor + sleep(); return false; } @@ -34,9 +59,9 @@ void Timer::_enable(bool e) { else TIMSK1 &= ~_BV(OCIE1A); SREG = sreg; + } unsigned Timer::_sleepmode() { return SLEEP_MODE_IDLE; } - diff --git a/atmega328p/watchdog.cpp b/atmega328p/watchdog.cpp index 366cfef..683f389 100644 --- a/atmega328p/watchdog.cpp +++ b/atmega328p/watchdog.cpp @@ -26,7 +26,7 @@ void Watchdog::_enable(bool e) { cli(); uint8_t b = 0; if (e) { - wdt_reset(); + wdt_reset(); // Ensure that the timer will start from 0 again b = _BV(WDIE) | _scale; } WDTCSR = _BV(WDCE) | _BV(WDE); diff --git a/attiny84/analog.cpp b/attiny84/analog.cpp index 25c265f..158b461 100644 --- a/attiny84/analog.cpp +++ b/attiny84/analog.cpp @@ -6,7 +6,7 @@ #include "device.h" #include "analog.h" -static Device *adc; +static Analog *adc; static volatile unsigned reading = 0xffff; ISR(ADC_vect) { @@ -15,7 +15,14 @@ ISR(ADC_vect) { low = ADCL; high = ADCH; reading = (high << 8) | low; - adc->ready(); + if (adc->next_reading_valid()) + adc->ready(); + else + { + // Can't use this conversion so start a new conversion + ADCSRA |= bit(ADSC) | bit(ADIE); + adc->next_reading_will_be_valid(); + } } } @@ -47,6 +54,7 @@ void Analog::sleep() { void Analog::wake() { power_adc_enable(); + _next_reading_valid = false; // Ignore first reading after power on ADCSRA |= bit(ADEN); ACSR &= ~bit(ACD); } diff --git a/attiny84/device.cpp b/attiny84/device.cpp index e7003d0..3f3f55e 100644 --- a/attiny84/device.cpp +++ b/attiny84/device.cpp @@ -9,6 +9,10 @@ #define BODS 7 // BOD Sleep bit in MCUCR #define BODSE 2 // BOD Sleep enable bit in MCUCR +#ifndef SLEEP_MODE_STANDBY +#define SLEEP_MODE_STANDBY SLEEP_MODE_PWR_SAVE +#endif + void Devices::begin() { // "...[it] is therefore required to turn off the watchdog // early during program startup..." (from avr/wdt.h) @@ -17,7 +21,7 @@ void Devices::begin() { // turn off ADC and analog comparator ADCSRA = 0; ACSR |= bit(ACD); - power_adc_disable(); // FIXME: power_all_disable()? + power_all_disable(); // turn off the brown-out detector MCUCR |= _BV(BODS) | _BV(BODSE); @@ -26,7 +30,13 @@ void Devices::begin() { digitalWrite(i, LOW); for (int i = 0; i < _n; i++) - _devices[i]->enable(_devices[i]->begin()); + { + if (_devices[i]->begin()) + { + _devices[i]->wake(); + _devices[i]->enable(true); + } + } sei(); } @@ -37,6 +47,7 @@ unsigned Device::_sleepmode() { unsigned Devices::compare_modes(unsigned sys, unsigned dev) { return dev < sys? dev: sys; + } void Devices::sleep(unsigned mode) { diff --git a/attiny84/serial.cpp b/attiny84/serial.cpp new file mode 100644 index 0000000..83374b0 --- /dev/null +++ b/attiny84/serial.cpp @@ -0,0 +1,56 @@ +#include +#include +#include +#include +#include + +#include "device.h" +#include "serial.h" + +#define IOCLK_PRESCALE 256 + +void SerialDevice::init() { + if (_baud) { + unsigned bit_timing_count = ((F_CPU) / IOCLK_PRESCALE + (_baud / 2)) / _baud - 1; + + uint8_t saved_SREG = SREG; // Save the interrupt flag + cli(); // disable interrupts while initializing the timer 0 + + // Timer 0 is an 8 bit timer, and used for the timing of the serial port + TCCR0A = 0; + TCCR0B = 0; + + // Put Timer/Counter0 in CTC mode + bitSet(TCCR0A, WGM01); + + // Count cycles for each bit + OCR0A = bit_timing_count; + + SREG = saved_SREG; // restore the interrupt flag + } +} + +unsigned SerialDevice::_sleepmode() { + return SLEEP_MODE_IDLE; +} + +// call to turn off device altogether +void SerialDevice::sleep() +{ + if (true) //(_awake) + { + power_timer0_disable(); // Timer 0 is used to time the bits + _awake = false; + } +} + +// call to turn device on again +void SerialDevice::wake() +{ + if (true) //(!_awake) + { + power_timer0_enable(); // Timer 0 is used to time the bits + this->init(); // After power on, initialize the module + _awake = true; + } +} diff --git a/attiny84/serialout.cpp b/attiny84/serialout.cpp new file mode 100644 index 0000000..bc4a9d1 --- /dev/null +++ b/attiny84/serialout.cpp @@ -0,0 +1,165 @@ +// Serial output for the ATTiny84 +// This code is derived from the SerialOut class from the atmega328p +// implementation in the Interrupted library and ideas from +// http://electronut.in/serial-communications-with-the-attiny84/ + +#include +#include +#include +#include +#include + +#include "device.h" +#include "serial.h" +#include "serialout.h" + +#define TRANSMISSION_SIZE 8 // not including 1 start bit + 1 stop bit +// #define TRANSMISSION_SIZE 10 // Add 2 idle bits but not including 1 start bit + 1 stop bit + +volatile unsigned char current_byte = 0; +volatile int next_bit_index = -1; // Index of next bit to send; -1 = not sending data yet + +static SerialOut *device; + +void send_bit_using_pin(unsigned int pinTX) +{ + // serial data sent least significant bit first + // start(low)-0-1-2-3-4-5-6-7-stop(high) + // 10 bits sent per packet + + if (next_bit_index < 0) + { + // start bit is low + bitClear(PORTA, pinTX); + + // set bit index for first bit + next_bit_index = 0; + } + else if (next_bit_index >= TRANSMISSION_SIZE) + { + // idle or stop bit is high + bitSet(PORTA, pinTX); + next_bit_index = -1; + } + #if TRANSMISSION_SIZE > 8 + else if (next_bit_index >= 8) + { + // Tranmission frame has idle bits, idle and stop bits are high + bitSet(PORTA, pinTX); + next_bit_index++; + } + #endif + else + { + // data bits: + // next_bit_index is in [0, 7] + // extract relevant bit from data + bitWrite(PORTA, pinTX, bitRead(current_byte, next_bit_index)); + next_bit_index++; + } + +} + +bool SerialOut::begin() { + device = this; + _awake = false; + + _next_char_index = -1; + + // use the id as the TX pin, set the idle line state to high + digitalWrite(this->id(), HIGH); + pinMode(this->id(), OUTPUT); + + return false; +} + +void SerialOut::_enable(bool e) { + bitSet(TIFR0, OCF0A); // Ensure the interrupt flag is clear to start + bitWrite(TIMSK0, OCIE0A, e); +} + +void SerialOut::start(char const *ptr, int buffer_offset) +{ + if (_next_char_index < 0) + { + // Not currently transmitting, so start one + _next_char_index = buffer_offset; + wake(); + enable(); + write(ptr); + } +} + +// Prepare for transmission of characters from the given buffer +void SerialOut::write(char const *ptr) { + if (is_enabled()) + { + uint8_t saved_status = SREG; + cli(); + _tx_ptr = ptr; + bitSet(TIFR0, OCF0A); // Ensure the interrupt flag is clear to start + + TCNT0 = 0; // Reset the timer + bitSet(TCCR0B, CS02); // Set the clock prescale bit for divide by 256 to start the clock + + next_bit_index = -1; // Indicate that a new character is to be started + do_output(); + SREG = saved_status; + } +} + +const char SerialOut::more() +{ + return _tx_ptr && _next_char_index >= 0 && _tx_ptr[_next_char_index]; +} + +char SerialOut::next() +{ + char next_char = _tx_ptr[_next_char_index]; + this->advance_to_next_character(); + return next_char; +} + +void SerialOut::advance_to_next_character() +{ + _next_char_index++; +} + +void SerialOut::finished() +{ + _tx_ptr = 0; + _next_char_index = -1; +} + +// Output a single bit of a transmission frame; may called from interrupt service routine +// If no more characters, then signal that the device is ready +void SerialOut::do_output() +{ + if (next_bit_index >= 0) + { + // Handle the transmission of the next bit + send_bit_using_pin(this->id()); + } + else + { + // Check to see if the current null terminated string of bytes has been sent + if (more()) + { + current_byte = next(); + send_bit_using_pin(this->id()); + } + else + { + finished(); + bitClear(TCCR0B, CS02); // stop the clock for the timer + ready(); + } + } +} + +// Output the next bit of the current byte +ISR(TIM0_COMPA_vect) +{ + if (device) + device->do_output(); +} diff --git a/attiny84/timer.cpp b/attiny84/timer.cpp index 40653ba..2cf66e1 100644 --- a/attiny84/timer.cpp +++ b/attiny84/timer.cpp @@ -5,33 +5,59 @@ #include "device.h" #include "timer.h" +// Timer 1 is used so as to avoid conflict with the timer 0, used by the delay() function in the Arduino library static Device *t1; +// Ensure that these are defined properly +#define power_timer1_enable() (PRR &= (uint8_t)~(1 << PRTIM1)) +#define power_timer1_disable() (PRR |= (uint8_t)(1 << PRTIM1)) + ISR(TIM1_COMPA_vect) { if (t1) t1->ready(); } +// call to turn off device altogether +void Timer::sleep() +{ + power_timer1_disable(); +} + +// call to turn device on again +void Timer::wake() +{ + power_timer1_enable(); +} + + bool Timer::begin() { t1 = this; + wake(); // Timer must be powered on for the register changes to take effect TCCR1A = 0; - // CTC mode, no prescaler - TCCR1B = _BV(WGM12) | _BV(CS10); + // CTC mode, clock stopped + TCCR1B = _BV(WGM12); + + uint8_t saved_SREG = SREG; // Save the interrupt flag + cli(); // disable interrupts while resetting the timer/counter + OCR1A = (uint16_t) (F_CPU / (1000 * (uint32_t) _ms_divisor)) - 1; // 1ms divided by the divisor + SREG = saved_SREG; // restore the interrupt flag + sleep(); - OCR1A = F_CPU / 1000 - 1; // 1ms return false; } +// Timer must be awake before calling this function void Timer::_enable(bool e) { uint8_t sreg = SREG; cli(); TCNT1 = 0; - SREG = sreg; + bitWrite(TCCR1B, CS10, e); // Start or stop the clock if (e) TIMSK1 |= _BV(OCIE1A); else TIMSK1 &= ~_BV(OCIE1A); + SREG = sreg; } unsigned Timer::_sleepmode() { diff --git a/attiny84/watchdog.cpp b/attiny84/watchdog.cpp index 5c06367..4b8a2e4 100644 --- a/attiny84/watchdog.cpp +++ b/attiny84/watchdog.cpp @@ -23,6 +23,7 @@ bool Watchdog::begin() { } void Watchdog::_enable(bool e) { + wdt_reset(); // Ensure that the timer will start from 0 again cli(); uint8_t b = e? _BV(WDIE) | _scale: 0; WDTCSR = _BV(WDCE) | _BV(WDE); @@ -33,4 +34,3 @@ void Watchdog::_enable(bool e) { unsigned Watchdog::_sleepmode() { return SLEEP_MODE_PWR_DOWN; } - diff --git a/device.h b/device.h index 89b1140..0ef75c3 100644 --- a/device.h +++ b/device.h @@ -28,7 +28,7 @@ class Devices { } static void sleep(unsigned mode); -private: + int _n; Device *_devices[MAX_DEVICES]; }; @@ -62,6 +62,11 @@ class Device { bool is_enabled() { return _enabled; } + // calls to turn off device altogether and back on again + virtual void power_module(bool turn_on) { if (turn_on) wake(); else sleep(); }; + virtual void sleep() {}; + virtual void wake() {}; + int id() { return _id; } inline unsigned negotiate_mode(unsigned sys) { diff --git a/examples/attiny84/timer.ino b/examples/attiny84/timer.ino index f162273..86a1e45 100644 --- a/examples/attiny84/timer.ino +++ b/examples/attiny84/timer.ino @@ -19,6 +19,7 @@ void setup(void) void loop(void) { timer.enable(); + if (devices.select() == 1) { digitalWrite(LED, !digitalRead(LED)); dt *= 2; diff --git a/pinchange.h b/pinchange.h index a4cc10a..7c4fe64 100644 --- a/pinchange.h +++ b/pinchange.h @@ -1,6 +1,9 @@ #ifndef __PINCHANGE_H__ #define __PINCHANGE_H__ +#include +#include "device.h" + class Pin; class Port { diff --git a/platformio.ini b/platformio.ini new file mode 100644 index 0000000..98ecbdd --- /dev/null +++ b/platformio.ini @@ -0,0 +1,20 @@ +; PlatformIO Project Configuration File +; +; Build options: build flags, source filter, extra scripting +; Upload options: custom port, speed and extra flags +; Library options: dependencies, extra library storages +; +; Please visit documentation for the other options and examples +; http://docs.platformio.org/en/stable/projectconf.html + +[env:attiny84] +platform = atmelavr +board = attiny84 +framework = arduino +src_filter = ""-<*> +"" + +[env:diecimilaatmega328] +platform = atmelavr +board = diecimilaatmega328 +framework = arduino +src_filter = ""-<*> +"" diff --git a/serial.h b/serial.h index 4d62880..2b38586 100644 --- a/serial.h +++ b/serial.h @@ -1,12 +1,22 @@ #ifndef __SERIALDEVICE_H__ #define __SERIALDEVICE_H__ +#include "device.h" + class SerialDevice: public Device { public: - SerialDevice(unsigned id, unsigned long baud): - Device(id), _baud(baud) {} + SerialDevice(unsigned id, unsigned long baud): + Device(id), _awake(false), _baud(baud) {} void init(); + virtual void sleep(); + virtual void wake(); + + virtual bool is_awake() {return _awake; } + +protected: + bool _awake; + private: unsigned _sleepmode(); unsigned long _baud; diff --git a/serialout.h b/serialout.h index ae5ddd7..bc7796e 100644 --- a/serialout.h +++ b/serialout.h @@ -1,26 +1,39 @@ #ifndef __SERIALOUT_H__ #define __SERIALOUT_H__ +#include "serial.h" + /** * Simple Serial output device */ class SerialOut: public SerialDevice { public: - SerialOut(unsigned id, unsigned long baud = 0): - SerialDevice(id, baud), _tx_ptr(0) {} + SerialOut(unsigned id, unsigned long baud = 0): + SerialDevice(id, baud), _tx_ptr(0), _next_char_index(-1) {} // not enabled by default bool begin(); // writes a string + bool write(char const *ptr); void do_output(); + // Return true when there are still bytes to output + bool transmitting() {return _tx_ptr != 0; } + virtual void start(char const *ptr, int offset=0); + virtual const char more(); + virtual char next(); + virtual void advance_to_next_character(); + virtual void finished(); + + protected: - void _enable(bool) {} + void _enable(bool e); + volatile char const *_tx_ptr; + volatile int _next_char_index; // Index of next byte to output private: - volatile char const *_tx_ptr; }; #endif diff --git a/timer.h b/timer.h index 6fe839e..0464858 100644 --- a/timer.h +++ b/timer.h @@ -6,8 +6,10 @@ */ class Timer: public Device { public: - Timer(int id, uint32_t millis): - Device(id), _delay(millis), _ticks(millis) {} + + Timer(int id, uint32_t millis, uint32_t ms_divisor=1L): + Device(id), _delay(millis), _ticks(millis), _ms_divisor(ms_divisor) {} + void ready() { if (_ticks <= 1) { @@ -27,12 +29,18 @@ class Timer: public Device { // not enabled by default bool begin(); + // virtual void ready(); + + virtual void sleep(); + virtual void wake(); + protected: void _enable(bool); unsigned _sleepmode(); uint32_t _delay; volatile uint32_t _ticks; + uint32_t _ms_divisor; }; #endif