first commit

This commit is contained in:
Jérôme Delacotte
2025-03-06 11:15:32 +01:00
commit 7b30d6e298
5276 changed files with 2108927 additions and 0 deletions

View File

@@ -0,0 +1,37 @@
#include <stdint.h>
#include "TinyBuffer.h"
#define next_buffer_index(p) ((p+1) % TINY_BUFFER_SIZE)
#define TINY_BUFFER_CAPACITY (TINY_BUFFER_SIZE - 1)
uint8_t TinyBuffer::countBusy() {
uint8_t h = head;
uint8_t t = tail;
return (h < t) ? (TINY_BUFFER_SIZE + h - t) : (h - t);
}
uint8_t TinyBuffer::countFree() {
uint8_t h = head;
uint8_t t = tail;
return (h < t) ? (t - h - 1) : (TINY_BUFFER_CAPACITY + t - h);
}
void TinyBuffer::clear() {
tail = head;
}
void TinyBuffer::put(const uint8_t v) {
data[head = next_buffer_index(head)] = v;
}
uint8_t TinyBuffer::get() {
return data[tail = next_buffer_index(tail)];
}
uint8_t TinyBuffer::peek() {
return data[next_buffer_index(tail)];
}
void TinyBuffer::skip() {
tail = next_buffer_index(tail);
}

View File

@@ -0,0 +1,48 @@
#ifndef TinyBuffer_h
#define TinyBuffer_h
#include <stdint.h>
#define TINY_BUFFER_SIZE 16
/**
* The 16-byte circular (ring) buffer implementation.
* This implementation does not perform any validation (i.e. it is possible to "get" when "empty" and "put" when "full"), the caller is expected to maintain the buffer consistency.
*/
class TinyBuffer {
private:
volatile uint8_t data[TINY_BUFFER_SIZE];
volatile uint8_t head = 0;
volatile uint8_t tail = 0;
public:
/**
* Get the count of bytes in this buffer.
*/
uint8_t countBusy();
/**
* Get the size of free space in this buffer.
*/
uint8_t countFree();
/**
* Mark this buffer as empty.
*/
void clear();
/**
* Write one byte.
*/
void put(const uint8_t);
/**
* Read next byte.
*/
uint8_t get();
/**
* Get next byte from this buffer but do not mark it as read.
*/
uint8_t peek();
/**
* Mark next byte as read without returning it.
*/
void skip();
};
#endif

View File

@@ -0,0 +1,12 @@
#include <stdint.h>
#include "TinyExternalInterrupt.h"
void TinyExternalInterrupt::on(ExternalInterruptDetection detection, ExternalInterruptCallback callback) {
onInterrupt = callback;
setup(detection);
}
void TinyExternalInterrupt::off() {
teardown();
onInterrupt = 0;
}

View File

@@ -0,0 +1,47 @@
#ifndef TinyExternalInterrupt_h
#define TinyExternalInterrupt_h
enum ExternalInterruptDetection {
LOW, ANY, FALLING, RISING
};
typedef void (*ExternalInterruptCallback)();
typedef void (*ExternalInterruptSetup)(ExternalInterruptDetection);
typedef void (*ExternalInterruptTeardown)();
/**
* The interface of an external interrupt handler implementation.
*/
class TinyExternalInterrupt {
friend void _ISRExternalInterruptCallbackFunction();
public:
/**
* Instantiate the handler using the provided setup and teardown functions.
* Normally, the constructor should be invoked only once by an implementation
* to instantiate the wrapper and make it available for the "ISR" and "main".
* @param setup A pointer to ExternalInterruptSetup function.
* The function should accept "ExternalInterruptDetection" as an argument and should return void.
* @param teardown A pointer to ExternalInterruptTeardown function.
* The function should have no arguments and should return void.
*/
TinyExternalInterrupt(ExternalInterruptSetup setup, ExternalInterruptTeardown teardown) : setup(setup), teardown(teardown) {}
/**
* Register the callback function to run when the configured INT0 pin level is detected.
* @param detection One of available ExternalInterruptDetection types: LOW, ANY, FALLING, RISING.
* @param callback A pointer to a callback function.
* The function should have no arguments and should return void.
*/
void on(ExternalInterruptDetection detection, ExternalInterruptCallback callback);
/**
* Unregister the callback function.
*/
void off();
private:
volatile ExternalInterruptCallback onInterrupt;
ExternalInterruptSetup setup;
ExternalInterruptTeardown teardown;
};
#endif

View File

@@ -0,0 +1,28 @@
#include "TinyExternalInterrupt0.h"
#include <avr/interrupt.h>
volatile uint8_t storedGIMSK;
volatile uint8_t storedMCUCR;
#define maskGIMSK (1 << INT0)
#define maskMCUCR ( (1 << ISC01) | (1 << ISC00) )
TinyExternalInterrupt ExternalInterrupt0(
[](ExternalInterruptDetection detection) {
storedGIMSK = GIMSK & maskGIMSK;
storedMCUCR = MCUCR & maskMCUCR;
MCUCR |= (MCUCR & ~maskMCUCR) | detection;
GIMSK |= maskGIMSK;
},
[]() {
GIMSK = (GIMSK & ~maskGIMSK) | storedGIMSK;
MCUCR = (MCUCR & ~maskMCUCR) | storedMCUCR;
});
inline void _ISRExternalInterruptCallbackFunction() {
if (ExternalInterrupt0.onInterrupt) ExternalInterrupt0.onInterrupt();
}
ISR(INT0_vect) {
_ISRExternalInterruptCallbackFunction();
}

View File

@@ -0,0 +1,11 @@
#ifndef TinyExternalInterrupt0_h
#define TinyExternalInterrupt0_h
#include "TinyExternalInterrupt.h"
/**
* The instance of external interrupt handler for INT0 pin: ISR(INT0_vect)
*/
extern TinyExternalInterrupt ExternalInterrupt0;
#endif

View File

@@ -0,0 +1,90 @@
#include <stdint.h>
#include <stdlib.h>
#include "TinyNmea.h"
enum NmeaMarker: char {
START_MARKER = '$',
SPLIT_MARKER = ',',
STOP_MARKER = '*'
};
void TinyNmea::next(const char x) {
switch (state) {
case NONE:
switch (x) {
case START_MARKER:
state = TYPE;
charIndex = 0;
termsCount = 0;
checksum = 0;
break;
}
break;
case TYPE:
checksum ^= x;
switch (x) {
case SPLIT_MARKER:
if (charIndex > 2) {
for (parserIndex = 0; parserIndex < parsersCount; parserIndex++) {
if (parsers[parserIndex].type[0] == temp[charIndex-3] &&
parsers[parserIndex].type[1] == temp[charIndex-2] &&
parsers[parserIndex].type[2] == temp[charIndex-1]) {
state = DATA;
charIndex = 0;
termsCount = 0;
return;
}
}
}
state = NONE;
break;
default:
if (charIndex < 5) {
temp[charIndex++] = x;
}
break;
}
break;
case DATA:
switch (x) {
case SPLIT_MARKER:
checksum ^= x;
buffer[charIndex++] = 0;
termsCount++;
break;
case STOP_MARKER:
buffer[charIndex] = 0;
termsCount++;
state = SUM;
charIndex = 0;
break;
default:
checksum ^= x;
if (charIndex < TINY_NMEA_SENTENCE_BUFFER_SIZE) {
buffer[charIndex++] = x;
}
break;
}
break;
case SUM:
if (charIndex > 1) {
state = NONE;
temp[charIndex] = 0;
if (strtol(temp, 0, 16) == checksum) {
dispatch();
}
}
else {
temp[charIndex++] = x;
};
break;
}
}
void TinyNmea::dispatch() {
uint8_t position = 0;
for (uint8_t termIndex = 0; termIndex < termsCount; termIndex++) {
parsers[parserIndex].call(&buffer[position], termIndex);
while (buffer[position++]);
}
}

View File

@@ -0,0 +1,45 @@
#ifndef TinyNmea_h
#define TinyNmea_h
#include <stdint.h>
struct NmeaParser {
/**
* Last three symbols of NMEA sentence type designator. Used in parser lookup, type match is "ends with", case-sensitive.
*/
char type[3];
/**
* A pointer to parser function to invoke when the matching NMEA sentence is received.
* @param term a C-string containing one term from the last NMEA sentence.
* @param termIndex a 0-based index of the term in the sentence.
*/
void (*call) (const char* term, const uint8_t termIndex);
};
#define TINY_NMEA_SENTENCE_BUFFER_SIZE 70
class TinyNmea {
public:
/**
* Instantiate the parser.
* @param parsers A pointer to an array of "NmeaParser" structures.
* @param parsersCount Size of the "parsers" array.
*/
TinyNmea(const NmeaParser parsers[], const uint8_t parsersCount) : parsers(parsers), parsersCount(parsersCount) {}
/**
* Feed the parser with one character.
* @param x the next character to process.
*/
void next(const char x);
private:
void dispatch();
const NmeaParser* parsers;
const uint8_t parsersCount;
uint8_t parserIndex;
uint8_t charIndex, termsCount, checksum;
enum NmeaState: uint8_t { NONE, TYPE, DATA, SUM } state = NONE;
char temp[5];
char buffer[TINY_NMEA_SENTENCE_BUFFER_SIZE + 1] = {}; // ${talker:1-2}{type:3},{data:70}*{sum:2} total max length is 80
};
#endif

View File

@@ -0,0 +1,10 @@
#include <stdint.h>
#include "TinyPinChange.h"
void TinyPinChange::on(uint8_t pin, PinChangeCallback callback) {
setup(pin, callback);
}
void TinyPinChange::off(uint8_t pin) {
teardown(pin);
}

View File

@@ -0,0 +1,43 @@
#ifndef TinyPinChange_h
#define TinyPinChange_h
#include <stdint.h>
typedef void (*PinChangeCallback)(uint8_t pin, bool level);
typedef void (*PinChangeSetup)(uint8_t pin, PinChangeCallback callback);
typedef void (*PinChangeTeardown)(uint8_t pin);
/**
* The interface of a pin change interrupt handler implementation.
*/
class TinyPinChange {
public:
/**
* Instantiate the handler using the provided setup and teardown functions.
* Normally, this constructor should be invoked only once by an implementation
* to instantiate the wrapper and make it available for the "ISR" and "main".
* @param setup A pointer to PinChangeSetup function.
* The function should accept "pin" and "callback" as arguments and should return void.
* @param teardown A pointer to PinChangeTeardown function.
* The function should accept "pin" as an argument and should return void.
*/
TinyPinChange(PinChangeSetup setup, PinChangeTeardown teardown) : setup(setup), teardown(teardown) {}
/**
* Register the callback function for the pin.
* @param pin The pin identifier (i.e. PB0, PB1 etc.)
* @param callback A pointer to PinChangeCallback function.
* The function should accept "pin" and "level" as arguments and should return void.
*/
void on(uint8_t pin, PinChangeCallback callback);
/**
* Unregister the callback function.
*/
void off(uint8_t pin);
private:
PinChangeSetup setup;
PinChangeTeardown teardown;
};
#endif

View File

@@ -0,0 +1,37 @@
#include "TinyPinChangeB.h"
#include <avr/interrupt.h>
#define TINY_PINB_CHANGE_MAX 6
volatile PinChangeCallback onPinChangeB[TINY_PINB_CHANGE_MAX] = {};
volatile uint8_t storedPINB;
volatile uint8_t storedGIMSKB;
TinyPinChange PinChangeB(
[](uint8_t pin, PinChangeCallback callback) {
storedGIMSKB = GIMSK & (1 << PCIE);
GIMSK |= (1 << PCIE);
onPinChangeB[pin] = callback;
storedPINB = PINB;
PCMSK |= (1 << pin);
},
[](uint8_t pin) {
PCMSK &= ~(1 << pin);
onPinChangeB[pin] = 0;
GIMSK = (GIMSK & ~(1 << PCIE)) | storedGIMSKB;
});
ISR(PCINT0_vect) {
uint8_t data = PINB;
uint8_t changes = (data ^ storedPINB);
storedPINB = data;
uint8_t pin = 0;
while (changes) {
if ( (changes & 1) && (onPinChangeB[pin]) ) {
onPinChangeB[pin](pin, data & 1);
}
changes >>= 1;
data >>= 1;
pin++;
}
}

View File

@@ -0,0 +1,11 @@
#ifndef TinyPinChangeB_h
#define TinyPinChangeB_h
#include "TinyPinChange.h"
/**
* The instance of pin change interrupt handler on portB: ISR(PCINT0_vect)
*/
extern TinyPinChange PinChangeB;
#endif

View File

@@ -0,0 +1,39 @@
#include <stdint.h>
#include "TinySerial.h"
#include "TinyUart.h"
TinySerial::TinySerial(const uint8_t& rx, const uint8_t& tx, TinyTimer& clockInterrupt, TinyPinChange& rxInterrupt) {
timer = &clockInterrupt;
pinChange = &rxInterrupt;
input = rx;
output = tx;
}
void TinySerial::begin(const uint32_t& baud) {
uart.on(input, output, baud, *timer, *pinChange);
}
void TinySerial::end() {
uart.off();
}
int TinySerial::peek() {
while (!uart.inputAvailable()) { idleSleep(); }
return uart.peek();
}
int TinySerial::available() {
return uart.inputAvailable();
}
int TinySerial::read() {
return uart.blockingRead();
}
size_t TinySerial::write(uint8_t v) {
uart.blockingWrite(v);
return 1;
}
void TinySerial::flush() {
}

View File

@@ -0,0 +1,25 @@
#ifndef TinySerial_h
#define TinySerial_h
#include <stdint.h>
#include <Stream.h>
#include "TinyPinChange.h"
#include "TinyTimer.h"
class TinySerial : public Stream {
private:
uint8_t input, output;
TinyTimer* timer;
TinyPinChange* pinChange;
public:
TinySerial(const uint8_t& rx, const uint8_t& tx, TinyTimer& clockInterrupt, TinyPinChange& rxInterrupt);
void begin(const uint32_t& baud);
void end();
virtual int peek() override;
virtual int available() override;
virtual int read() override;
virtual size_t write(uint8_t) override;
virtual void flush() override;
};
#endif

View File

@@ -0,0 +1,12 @@
#ifndef TinySleep_h
#define TinySleep_h
#include <avr/sleep.h>
#define sleep(mode) set_sleep_mode(mode); sleep_mode()
#define deepSleep() set_sleep_mode(SLEEP_MODE_PWR_DOWN); sleep_bod_disable(); sleep_mode()
#define idleSleep() sleep(SLEEP_MODE_IDLE)
#endif

View File

@@ -0,0 +1,12 @@
#include <stdint.h>
#include "TinyTimer.h"
void TinyTimer::on(const uint32_t match, TimerCallback callback) {
onTimer = callback;
setup(match);
}
void TinyTimer::off() {
teardown();
onTimer = 0;
}

View File

@@ -0,0 +1,46 @@
#ifndef TinyTimer_h
#define TinyTimer_h
#include <stdint.h>
typedef void (*TimerCallback)();
typedef void (*TimerSetup)(uint32_t match);
typedef void (*TimerTeardown)();
/**
* An interface of a timer interrupt handler implementation.
*/
class TinyTimer {
friend void _ISRTimerCallbackFunction();
public:
/**
* Instantiate the wrapper using the provided setup and teardown functions.
* Normally, the constructor should be invoked only once by an implementation
* to instantiate the wrapper and make it available for the "ISR" and "main".
* @param setup A pointer to TimerSetup function.
* The function should accept "match" value as an argument and should return void.
* @param teardown A pointer to TimerTeardown function.
* The function should accept no arguments and return void.
*/
TinyTimer(TimerSetup setup, TimerTeardown teardown) : setup(setup), teardown(teardown) {}
/**
* Register the callback function to run when a condition is met.
* @param match An amount of timer ticks to count before the callback is invoked.
* High match values may be not precise because of coarse step of timer prescaler.
* @param callback A pointer to TimerCallback function.
* The function should accept no arguments and should return void.
*/
void on(const uint32_t match, TimerCallback callback);
/**
* Unregister the callback function.
*/
void off();
private:
volatile TimerCallback onTimer;
TimerSetup setup;
TimerTeardown teardown;
};
#endif

View File

@@ -0,0 +1,44 @@
#include "TinyTimer0Compare.h"
#include <avr/interrupt.h>
#define onCompareEnable0A() TIMSK |= (1 << OCIE0A)
#define onCompareDisable0A() TIMSK &= ~(1 << OCIE0A)
#define setPrescale0(prescale) TCCR0B |= (prescale % 6)
#define cleanPrescale0() TCCR0B &= 0xF8
#define normalMode0() TCCR0A &= ~(1 << WGM00) & ~(1 << WGM01); TCCR0B &= ~(1 << WGM02)
#define compareMode0() TCCR0A &= ~(1 << WGM00); TCCR0A |= (1 << WGM01); TCCR0B &= ~(1 << WGM02)
#define setMatch0A(match) OCR0A = match
TinyTimer Timer0Compare(
[](uint32_t match) {
if (match > 0x3FC00) return;
compareMode0();
cleanPrescale0();
uint8_t prescale = 1;
while (match > 256) {
prescale++;
if (prescale > 3) {
match = (match + 2) >> 2;
}
else {
match = (match + 4) >> 3;
}
}
setPrescale0(prescale);
setMatch0A(match - 1);
onCompareEnable0A();
},
[]() {
onCompareDisable0A();
normalMode0();
});
inline void _ISRTimerCallbackFunction() {
if (Timer0Compare.onTimer) Timer0Compare.onTimer();
}
ISR(TIMER0_COMPA_vect) {
_ISRTimerCallbackFunction();
}

View File

@@ -0,0 +1,22 @@
#ifndef TinyTimer0Compare_h
#define TinyTimer0Compare_h
#include "TinyTimer.h"
/**
* The instance of Timer0 Compare interrupt handler: ISR(TIMER0_COMPA_vect)
*
* Max match value: 0x3FC00
*
* Prescaler reference:
* CS02 CS01 CS00 Result
* 0 0 0 stopped
* 0 0 1 clock
* 0 1 0 clock /8
* 0 1 1 clock /64
* 1 0 0 clock /256
* 1 0 1 clock /1024
*/
extern TinyTimer Timer0Compare;
#endif

View File

@@ -0,0 +1,39 @@
#include <avr/interrupt.h>
#include "TinyTimer0Overflow.h"
#define onOverflowEnable0() TIMSK |= (1 << TOIE0)
#define onOverflowDisable0() TIMSK &= ~(1 << TOIE0)
#define setPrescale0(prescale) TCCR0B |= (prescale % 6)
#define cleanPrescale0() TCCR0B &= 0xF8
#define normalMode0() TCCR0A &= ~(1 << WGM00) & ~(1 << WGM01); TCCR0B &= ~(1 << WGM02)
TinyTimer Timer0Overflow(
[](uint32_t match) {
if (match > 0x3FC00) return;
normalMode0();
cleanPrescale0();
uint8_t prescale = 1;
while (match > 256) {
prescale++;
if (prescale > 3) {
match = (match + 2) >> 2;
}
else {
match = (match + 4) >> 3;
}
}
setPrescale0(prescale);
onOverflowEnable0();
},
[]() {
onOverflowDisable0();
});
inline void _ISRTimerCallbackFunction() {
if (Timer0Overflow.onTimer) Timer0Overflow.onTimer();
}
ISR(TIMER0_OVF_vect) {
_ISRTimerCallbackFunction();
}

View File

@@ -0,0 +1,22 @@
#ifndef TinyTimer0Overflow_h
#define TinyTimer0Overflow_h
#include "TinyTimer.h"
/**
* The instance of Timer0 Overflow interrupt handler: ISR(TIMER0_OVF_vect)
*
* Max match value: 0x3FC00
*
* Prescaler reference:
* CS02 CS01 CS00 Result
* 0 0 0 stopped
* 0 0 1 clock
* 0 1 0 clock /8
* 0 1 1 clock /64
* 1 0 0 clock /256
* 1 0 1 clock /1024
*/
extern TinyTimer Timer0Overflow;
#endif

View File

@@ -0,0 +1,39 @@
#include <avr/interrupt.h>
#include "TinyTimer1Compare.h"
#define onCompareEnable1A() TIMSK |= (1 << OCIE1A)
#define onCompareDisable1A() TIMSK &= ~(1 << OCIE1A)
#define setPrescale1(prescale) TCCR1 |= (prescale & 0x0F)
#define cleanPrescale1() TCCR1 &= 0xF0
#define normalMode1() TCCR1 &= ~(1 << CTC1)
#define compareMode1() TCCR1 |= (1 << CTC1)
#define setMatch1C(match) OCR1C = match
TinyTimer Timer1Compare(
[](uint32_t match) {
if (match > 0x3FC000) return;
compareMode1();
cleanPrescale1();
uint8_t prescale = 1;
while (match > 256) {
prescale++;
match = (match + 1) >> 1;
}
setPrescale1(prescale);
setMatch1C(match - 1);
onCompareEnable1A();
},
[]() {
onCompareDisable1A();
normalMode1();
});
inline void _ISRTimerCallbackFunction() {
if (Timer1Compare.onTimer) Timer1Compare.onTimer();
}
ISR(TIMER1_COMPA_vect) {
_ISRTimerCallbackFunction();
}

View File

@@ -0,0 +1,32 @@
#ifndef TinyTimer1Compare_h
#define TinyTimer1Compare_h
#include "TinyTimer.h"
/**
* The instance of Timer1 Compare interrupt handler: ISR(TIMER1_COMPA_vect)
*
* Max match value: 0x3FC000
*
* Prescaler reference:
* CS13 CS12 CS11 CS10 Result
* 0 0 0 0 stopped
* 0 0 0 1 clock
* 0 0 1 0 clock /2
* 0 0 1 1 clock /4
* 0 1 0 0 clock /8
* 0 1 0 1 clock /16
* 0 1 1 0 clock /32
* 0 1 1 1 clock /64
* 1 0 0 0 clock /128
* 1 0 0 1 clock /256
* 1 0 1 0 clock /512
* 1 0 1 1 clock /1024
* 1 1 0 0 clock /2048
* 1 1 0 1 clock /4096
* 1 1 1 0 clock /8192
* 1 1 1 1 clock /16384
*/
extern TinyTimer Timer1Compare;
#endif

View File

@@ -0,0 +1,34 @@
#include <avr/interrupt.h>
#include "TinyTimer1Overflow.h"
#define onOverflowEnable1() TIMSK |= (1 << TOIE1)
#define onOverflowDisable1() TIMSK &= ~(1 << TOIE1)
#define setPrescale1(prescale) TCCR1 |= (prescale & 0x0F)
#define cleanPrescale1() TCCR1 &= 0xF0
#define normalMode1() TCCR1 &= ~(1 << CTC1)
TinyTimer Timer1Overflow(
[](uint32_t match) {
if (match > 0x3FC000) return;
normalMode1();
cleanPrescale1();
uint8_t prescale = 1;
while (match > 256) {
prescale++;
match = (match + 1) >> 1;
}
setPrescale1(prescale);
onOverflowEnable1();
},
[]() {
onOverflowDisable1();
});
inline void _ISRTimerCallbackFunction() {
if (Timer1Overflow.onTimer) Timer1Overflow.onTimer();
}
ISR(TIMER1_OVF_vect) {
_ISRTimerCallbackFunction();
}

View File

@@ -0,0 +1,32 @@
#ifndef TinyTimer1Overflow_h
#define TinyTimer1Overflow_h
#include "TinyTimer.h"
/**
* The instance of Timer1 Overflow interrupt handler: ISR(TIMER1_OVF_vect)
*
* Max match value: 0x3FC000
*
* Prescaler reference:
* CS13 CS12 CS11 CS10 Result
* 0 0 0 0 stopped
* 0 0 0 1 clock
* 0 0 1 0 clock /2
* 0 0 1 1 clock /4
* 0 1 0 0 clock /8
* 0 1 0 1 clock /16
* 0 1 1 0 clock /32
* 0 1 1 1 clock /64
* 1 0 0 0 clock /128
* 1 0 0 1 clock /256
* 1 0 1 0 clock /512
* 1 0 1 1 clock /1024
* 1 1 0 0 clock /2048
* 1 1 0 1 clock /4096
* 1 1 1 0 clock /8192
* 1 1 1 1 clock /16384
*/
extern TinyTimer Timer1Overflow;
#endif

View File

@@ -0,0 +1,45 @@
#include <stdint.h>
#include <avr/io.h>
#include "TinyUart.h"
#include "TinyUartConst.h"
void TinyUart::on(const uint8_t rx, const uint8_t tx, const uint32_t baud, TinyTimer& clockInterrupt, TinyPinChange& rxInterrupt) {
input = rx;
output = tx;
timer = &clockInterrupt;
pinChange = &rxInterrupt;
storedMask = (1 << input) | (1 << output);
inputBuffer.clear();
outputBuffer.clear();
inputState = TINY_UART_STATE_IDLE;
outputState = TINY_UART_STATE_IDLE;
storedDDR = DDRB & storedMask;
storedPORT = PORTB & storedMask;
PORTB |= storedMask;
DDRB &= ~(1 << input);
DDRB |= (1 << output);
uint32_t match = (F_CPU * 2) / (baud * TINY_UART_ONE_BIT_CLK);
if (1 & match) match++;
match >>= 1;
timer->on(match, [](){
uart.onTimerRx();
uart.onTimerTx();
});
pinChange->on(input, [](uint8_t, bool value){
uart.onRxPinChange(value);
});
}
void TinyUart::off() {
timer->off();
pinChange->off(input);
DDRB = (DDRB & ~storedMask) | storedDDR;
PORTB = (PORTB & ~storedMask) | storedPORT;
}
TinyUart uart;

View File

@@ -0,0 +1,41 @@
#ifndef TinyUart_h
#define TinyUart_h
#include <stdint.h>
#include <avr/io.h>
#include "TinyUartBase.h"
#include "TinyPinChange.h"
#include "TinyTimer.h"
/**
* Bi-directional UART.
*/
class TinyUart : public TinyUartRead, public TinyUartWrite {
public:
/**
* Start the UART.
* The "timer" will be enabled to interrupt at around "baud" * "TINY_UART_ONE_BIT_CLK" rate.
* The "pinChange" will be enabled to interrupt on "rx" pin level change.
* @param rx The receiving pin identifier (i.e. PB0, PB1 etc.)
* @param tx The tansmitting pin identifier (i.e. PB0, PB1 etc.)
* @param baud The baud rate for this communication session.
* @param clockInterrupt Clock interrupt handler, an instance of "TinyTimer" class.
* @param rxInterrupt RX pin interrupt handler, an instance of "TinyPinChange" class.
*/
void on(const uint8_t rx, const uint8_t tx, const uint32_t baud, TinyTimer& clockInterrupt, TinyPinChange& rxInterrupt);
/**
* Stop the UART. The "timer" will be stopped, the "pinChange" will be disabled.
*/
void off();
private:
TinyTimer* timer;
TinyPinChange* pinChange;
uint8_t storedDDR, storedPORT, storedMask;
};
/**
* The instance of bi-directional UART.
*/
extern TinyUart uart;
#endif

View File

@@ -0,0 +1,166 @@
#include <avr/io.h>
#include "TinyUartBase.h"
#include "TinyUartConst.h"
#define setHigh(p) PORTB |= (1 << p)
#define setLow(p) PORTB &= ~(1 << p)
uint8_t TinyUartRead::inputAvailable() {
return inputBuffer.countBusy();
}
uint8_t TinyUartRead::inputCapacity() {
return inputBuffer.countFree();
}
uint8_t TinyUartRead::peek() {
return inputBuffer.peek();
}
void TinyUartRead::skip() {
inputBuffer.skip();
}
void TinyUartRead::skipAll() {
inputBuffer.clear();
}
uint8_t TinyUartRead::read() {
return inputBuffer.get();
}
uint8_t TinyUartRead::blockingRead() {
while (!inputBuffer.countBusy()) {
idleSleep();
}
return inputBuffer.get();
}
void TinyUartRead::onRxPinChange(bool value) {
inputBit = value;
switch (inputState) {
case TINY_UART_STATE_IDLE:
if (!inputBit) {
inputBitInProgress = TINY_UART_HALF_BIT_CLK;
inputState++;
}
break;
case TINY_UART_STATE_STOP:
if (inputBit) {
inputBuffer.put(inputByte);
inputState = TINY_UART_STATE_IDLE;
}
else { // emergency restart
inputBitInProgress = TINY_UART_HALF_BIT_CLK;
inputState = TINY_UART_STATE_START;
}
break;
default:
inputByte >>= 1;
if (inputBit) inputByte |= 128;
inputState++;
if (inputState == TINY_UART_STATE_STOP) { // early stop on level change
inputBuffer.put(inputByte);
inputState = TINY_UART_STATE_IDLE;
}
else {
inputBitInProgress = TINY_UART_ONE_AND_HALF_BIT_CLK;
}
break;
}
}
void TinyUartRead::onTimerRx() {
if (!inputState) return; // idle line state
if (--inputBitInProgress) return; // keep current state
switch (inputState) {
case TINY_UART_STATE_START:
if (inputBit) {
inputState = TINY_UART_STATE_IDLE;
}
else {
inputBitInProgress = TINY_UART_ONE_BIT_CLK;
inputState++;
}
break;
case TINY_UART_STATE_STOP:
if (inputBit) inputBuffer.put(inputByte);
inputState = TINY_UART_STATE_IDLE;
break;
default:
inputByte >>= 1;
if (inputBit) inputByte |= 128;
inputBitInProgress = TINY_UART_ONE_BIT_CLK;
inputState++;
break;
}
}
uint8_t TinyUartWrite::outputRemaining() {
return outputBuffer.countBusy();
}
uint8_t TinyUartWrite::outputCapacity() {
return outputBuffer.countFree();
}
void TinyUartWrite::write(const uint8_t v) {
outputBuffer.put(v);
}
void TinyUartWrite::blockingWrite(const uint8_t v) {
while (!outputBuffer.countFree()) {
idleSleep();
}
outputBuffer.put(v);
while (outputBuffer.countBusy()) {
idleSleep();
}
}
void TinyUartWrite::onTimerTx() {
if (!outputState) { // idle line state
if (outputBuffer.countBusy()) {
outputBitInProgress = TINY_UART_ONE_BIT_CLK;
outputState++;
}
return;
}
if (--outputBitInProgress) return; // hold the line
switch (outputState) {
case TINY_UART_STATE_START:
setLow(output);
outputByte = outputBuffer.peek();
outputBitInProgress = TINY_UART_ONE_BIT_CLK;
outputState++;
break;
case TINY_UART_STATE_STOP:
setHigh(output);
outputBuffer.skip();
outputState = TINY_UART_STATE_IDLE;
break;
default:
(outputByte & 1) ? setHigh(output) : setLow(output);
outputByte >>= 1;
outputBitInProgress = TINY_UART_ONE_BIT_CLK;
outputState++;
break;
}
}

View File

@@ -0,0 +1,83 @@
#ifndef TinyUartBase_h
#define TinyUartBase_h
#include <stdint.h>
#include "TinySleep.h"
#include "TinyBuffer.h"
class TinyUartRead {
public:
/**
* Get the amount of buffered incoming bytes.
*/
uint8_t inputAvailable();
/**
* Get the amount of free space left in the input buffer.
*/
uint8_t inputCapacity();
/**
* Get one byte from the input buffer. The buffer is not changed.
*/
uint8_t peek();
/**
* Remove one byte from the input buffer.
*/
void skip();
/**
* Clear the input buffer.
*/
void skipAll();
/**
* Get one byte from the input buffer. The value is removed from the buffer.
*/
uint8_t read();
/**
* Perform checked "read".
* If there is no data in the input buffer, the main control flow will idle until the data is available.
*/
uint8_t blockingRead();
protected:
void onRxPinChange(bool);
void onTimerRx();
uint8_t input;
volatile uint8_t inputState;
TinyBuffer inputBuffer;
private:
volatile uint8_t inputBitInProgress;
volatile uint8_t inputByte;
volatile bool inputBit;
};
class TinyUartWrite {
public:
/**
* Get the amount of buffered outgoing bytes.
*/
uint8_t outputRemaining();
/**
* Get the amount of free space left in the output buffer.
*/
uint8_t outputCapacity();
/**
* Put one byte into the output buffer.
* @param x The value to output.
*/
void write(const uint8_t x);
/**
* Perform checked "write".
* If there is no free space left in the output buffer, the main control flow will idle until the free space is available.
* Additionally, the main control flow will idle until all the outgoing data is sent.
* @param x The value to output.
*/
void blockingWrite(const uint8_t x);
protected:
void onTimerTx();
uint8_t output;
volatile uint8_t outputState;
TinyBuffer outputBuffer;
private:
volatile uint8_t outputBitInProgress;
volatile uint8_t outputByte;
};
#endif

View File

@@ -0,0 +1,13 @@
#ifndef TinyUartConst_h
#define TinyUartConst_h
#define TINY_UART_HALF_BIT_CLK 2
#define TINY_UART_ONE_BIT_CLK 3
#define TINY_UART_ONE_AND_HALF_BIT_CLK 5
#define TINY_UART_STATE_IDLE 0
#define TINY_UART_STATE_START 1
// ... data bit states in between
#define TINY_UART_STATE_STOP 10
#endif

View File

@@ -0,0 +1,40 @@
#include <stdint.h>
#include <avr/io.h>
#include "TinyUartReader.h"
#include "TinyUartConst.h"
void TinyUartReader::on(const uint8_t rx, const uint32_t baud, TinyTimer& clockInterrupt, TinyPinChange& rxInterrupt) {
input = rx;
timer = &clockInterrupt;
pinChange = &rxInterrupt;
inputBuffer.clear();
inputState = TINY_UART_STATE_IDLE;
storedMask = (1 << input);
storedDDR = DDRB & storedMask;
storedPORT = PORTB & storedMask;
PORTB |= storedMask;
DDRB &= ~storedMask;
uint32_t match = (F_CPU * 2) / (baud * TINY_UART_ONE_BIT_CLK);
if (1 & match) match++;
match >>= 1;
timer->on(match, [](){
uartReader.onTimerRx();
});
pinChange->on(input, [](uint8_t, bool value){
uartReader.onRxPinChange(value);
});
}
void TinyUartReader::off() {
timer->off();
pinChange->off(input);
DDRB = (DDRB & ~storedMask) | storedDDR;
PORTB = (PORTB & ~storedMask) | storedPORT;
}
TinyUartReader uartReader;

View File

@@ -0,0 +1,39 @@
#ifndef TinyUartReader_h
#define TinyUartReader_h
#include <stdint.h>
#include "TinyUartBase.h"
#include "TinyPinChange.h"
#include "TinyTimer.h"
/**
* Receive-only UART.
*/
class TinyUartReader : public TinyUartRead {
public:
/**
* Start the UART.
* The "timer" will be enabled to interrupt at around "baud" * "TINY_UART_ONE_BIT_CLK" rate.
* The "pinChange" will be enabled to interrupt on "rx" pin level change.
* @param rx The receiving pin identifier (i.e. PB0, PB1 etc.)
* @param baud The baud rate for this communication session.
* @param clockInterrupt Clock interrupt handler, an instance of "TinyTimer" class.
* @param rxInterrupt RX pin interrupt handler, an instance of "TinyPinChange" class.
*/
void on(const uint8_t rx, const uint32_t baud, TinyTimer& clockInterrupt, TinyPinChange& rxInterrupt);
/**
* Stop the UART. The "timer" will be stopped, the "pinChange" will be disabled.
*/
void off();
private:
TinyTimer* timer;
TinyPinChange* pinChange;
uint8_t storedDDR, storedPORT, storedMask;
};
/**
* The instance of receive-only UART.
*/
extern TinyUartReader uartReader;
#endif

View File

@@ -0,0 +1,34 @@
#include <stdint.h>
#include <avr/io.h>
#include "TinyUartWriter.h"
#include "TinyUartConst.h"
void TinyUartWriter::on(const uint8_t tx, const uint32_t baud, TinyTimer& clockInterrupt) {
output = tx;
timer = &clockInterrupt;
outputBuffer.clear();
outputState = TINY_UART_STATE_IDLE;
storedMask = (1 << output);
storedDDR = DDRB & storedMask;
storedPORT = PORTB & storedMask;
PORTB |= storedMask;
DDRB |= storedMask;
uint32_t match = (F_CPU * 2) / (baud * TINY_UART_ONE_BIT_CLK);
if (1 & match) match++;
match >>= 1;
timer->on(match, [](){
uartWriter.onTimerTx();
});
}
void TinyUartWriter::off() {
timer->off();
DDRB = (DDRB & ~storedMask) | storedDDR;
PORTB = (PORTB & ~storedMask) | storedPORT;
}
TinyUartWriter uartWriter;

View File

@@ -0,0 +1,35 @@
#ifndef TinyUartWriter_h
#define TinyUartWriter_h
#include <stdint.h>
#include "TinyUartBase.h"
#include "TinyTimer.h"
/**
* Transmit-only UART.
*/
class TinyUartWriter : public TinyUartWrite {
public:
/**
* Start the UART.
* The "timer" will be enabled to interrupt at around "baud" * "TINY_UART_ONE_BIT_CLK" rate.
* @param tx The tansmitting pin identifier (i.e. PB0, PB1 etc.)
* @param baud The baud rate for this communication session.
* @param clockInterrupt Clock interrupt handler, an instance of "TinyTimer" class.
*/
void on(const uint8_t tx, const uint32_t baud, TinyTimer& clockInterrupt);
/**
* Stop the UART. The "timer" will be stopped.
*/
void off();
private:
TinyTimer* timer;
uint8_t storedDDR, storedPORT, storedMask;
};
/**
* The instance of transmit-only UART.
*/
extern TinyUartWriter uartWriter;
#endif

View File

@@ -0,0 +1,31 @@
#include <stdint.h>
#include <avr/interrupt.h>
#include "TinyWatchdog.h"
#define watchdogArm(i, r, p) WDTCR = ((i) << WDIE) | ((r) << WDE) | (((p) & 0b1000) << 2) | ((p) & 0b0111)
#define watchdogDisarm() WDTCR = (1 << WDCE) | (1 << WDE); WDTCR = 0
void TinyWatchdog::arm(const uint8_t prescaler, WatchdogCallback callback) {
onEvent = callback;
watchdogArm(true, false, prescaler);
}
void TinyWatchdog::armReset(const uint8_t prescaler, WatchdogCallback callback) {
onEvent = callback;
watchdogArm(callback!=0, true, prescaler);
}
void TinyWatchdog::disarm() {
onEvent = 0;
watchdogDisarm();
}
TinyWatchdog Watchdog;
inline void _ISRWatchdogEventCallback() {
if (Watchdog.onEvent) Watchdog.onEvent();
}
ISR(WDT_vect) {
_ISRWatchdogEventCallback();
}

View File

@@ -0,0 +1,56 @@
#ifndef TinyWatchdog_h
#define TinyWatchdog_h
#include <stdint.h>
typedef void (*WatchdogCallback)();
/**
* An interface of the watchog interrupt handler.
*/
class TinyWatchdog {
friend void _ISRWatchdogEventCallback();
public:
/**
* Run the callback inside an ISR after timeout.
* @param prescaler An integer value from 0 to 9.
* Higher prescaler value will cause longer delay.
* @param callback A pointer to WatchdogCallback function.
* The function should accept no arguments and return void.
*/
void arm(const uint8_t prescaler, WatchdogCallback callback);
/**
* Run the callback inside an ISR after timeout and then reset after another timeout.
* If the callback is null, the reset is done after the first timeout.
* @param prescaler An integer value from 0 to 9.
* Higher prescaler value will cause longer delay.
* @param callback A pointer to WatchdogCallback function.
* The function should accept no arguments and return void.
*/
void armReset(const uint8_t prescaler, WatchdogCallback callback);
/**
* Disable the watchdog.
*/
void disarm();
private:
volatile WatchdogCallback onEvent;
};
/**
* The instance of watchdog interrupt handler: ISR(WDT_vect)
*
* Watchdog prescaler reference:
* 0 -> 16 ms
* 1 -> 32 ms
* 2 -> 64 ms
* 3 -> 0.125 s
* 4 -> 0.25 s
* 5 -> 0.5 s
* 6 -> 1.0 s
* 7 -> 2.0 s
* 8 -> 4.0 s
* 9 -> 8.0 s
*/
extern TinyWatchdog Watchdog;
#endif