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,21 @@
MIT License
Copyright (c) 2021 Alex079
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View File

@@ -0,0 +1,149 @@
# TinySuite
A collection of utilities for ATTiny85.
## Pin change interrupt
### [TinyPinChangeB](src/TinyPinChangeB.h)
The implementation of ATTiny85 port B pin change interrupt handler.
This header exposes an object **PinChangeB** of class [TinyPinChange](src/TinyPinChange.h).
## External interrupt
### [TinyExternalInterrupt0](src/TinyExternalInterrupt0.h)
The implementation of ATTiny85 external interrupt handler.
This header exposes an object **ExternalInterrupt0** of class [TinyExternalInterrupt](src/TinyExternalInterrupt.h).
[Example](examples/external-interrupt/main.cpp)
## Timer interrupt
### [TinyTimer0Compare](src/TinyTimer0Compare.h)
The implementation of ATTiny85 Timer0 "on compare" interrupt handler.
This header exposes an object **Timer0Compare** of class [TinyTimer](src/TinyTimer.h).
### [TinyTimer0Overflow](src/TinyTimer0Overflow.h)
The implementation of ATTiny85 Timer0 "on overflow" interrupt handler.
This header exposes an object **Timer0Overflow** of class [TinyTimer](src/TinyTimer.h).
### [TinyTimer1Compare](src/TinyTimer1Compare.h)
The implementation of ATTiny85 Timer1 "on compare" interrupt handler.
This header exposes an object **Timer1Compare** of class [TinyTimer](src/TinyTimer.h).
### [TinyTimer1Overflow](src/TinyTimer1Overflow.h)
The implementation of ATTiny85 Timer1 "on overflow" interrupt handler.
This header exposes an object **Timer1Overflow** of class [TinyTimer](src/TinyTimer.h).
[Example](examples/timer-overflow/main.cpp)
## Watchdog interrupt
### [TinyWatchdog](src/TinyWatchdog.h)
The implementation of ATTiny85 Watchdog controller.
This header exposes an object **Watchdog** of class TinyWatchdog.
[Example](examples/watchdog/main.cpp)
## Utilities
### [TinySleep](src/TinySleep.h)
A set of shortcut defines to put the ATTiny85 in sleep mode. PRR and ADCSRA are intact.
This header exposes:
|Name|Description|
|-|-|
|sleep(mode)|Set the specified mode and sleep.|
|deepSleep|Set power down mode, disable BOD, and sleep.|
|idleSleep|Set idle mode and sleep.|
### [TinyBuffer](src/TinyBuffer.h)
The 16-byte 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.
This header exposes the class **TinyBuffer**.
## UART
### [TinyUart](src/TinyUart.h)
The implementation of UART protocol:
- 8-bit value transmission
- one start and one stop bit, no parity bit
- no overrun / underrun / break detection
Features:
- 16-byte input and 16-byte output buffers
- full duplex transmission
- up to 19200 baud at 16 MHz internal oscillator
- can use any pins for RX and TX
- can use Timer0 or Timer1
- RX and TX are the IDs from the board definition (i.e. PB0)
This header exposes an object **uart** of class TinyUart.
[Example](examples/uart-loop/main.cpp)
### [TinyUartReader](src/TinyUartReader.h)
The implementation of receive-only UART.
This header exposes an object **uartReader** of class TinyUartReader.
[Example](examples/uart-reader/main.cpp)
### [TinyUartWriter](src/TinyUartWriter.h)
The implementation of transmit-only UART.
This header exposes an object **uartWriter** of class TinyUartWriter.md.
[Example](examples/uart-writer/main.cpp)
### [TinySerial](src/TinySerial.h)
The implementation of UART using Stream interface (Stream.h) and Tiny UART (TinyUart.h) backend.
This header exposes the class **TinySerial**.
This class inherits from Stream. See Stream.h from Arduino for details.
[Example](examples/uart-print/main.cpp)
## NMEA parser
### [TinyNmea](src/TinyNmea.h)
The NMEA sentence delegating parser implementation.
Features:
- checksum validation
- sentence filtering by type
- delegation of value extraction to provided parsers
- orchestration of parsing
This header exposes
- NmeaParser structure
- TinyNmea class
[Example](examples/nmea-time/main.cpp)
## Known Problems
- Timer0 and Timer1 may not work correctly with ATTinyCore because of the core-specific initialization.
- UART has explicit dependency on port B registers, which makes it harder to use on other ATTinys.

View File

@@ -0,0 +1,18 @@
#include <avr/interrupt.h>
#include <TinyExternalInterrupt0.h>
#include <TinySleep.h>
void onInterrupt() {
// wakeup and do something
}
int main()
{
sei();
// interrupt when INT0 (pin 7) has LOW level (RISING or FALLING do not wake up from deep sleep)
ExternalInterrupt0.on(LOW, onInterrupt);
while (1) {
deepSleep();
}
return 0;
}

View File

@@ -0,0 +1,67 @@
#include <string.h>
#include <stdint.h>
#include <avr/interrupt.h>
#include <TinySleep.h>
#include <TinyTimer1Compare.h>
#include <TinyPinChangeB.h>
#include <TinyUartReader.h>
#include <TinyNmea.h>
struct NmeaData {
bool fresh; // custom data readiness indicator
char time[18]; // "hh:mm:ss DD/MM/YY"
};
// result data holder
NmeaData data;
// parser configuration array
NmeaParser config[] = {
// this function will be called for every term in ZDA statement
{{'Z','D','A'}, [](const char* term, const uint8_t index) {
// i.e. $GPZDA,201530.00,04,07,2002,00,00*60
switch (index) {
case 0: // time (term = "201530.00")
for (uint8_t i = 0, j = 0; i < 6; i+=2, j+=3) {
memcpy(&data.time[j], &term[i], 2);
}
data.time[2] = ':';
data.time[5] = ':';
data.time[8] = ' ';
break;
case 1: // day (term = "04")
memcpy(&data.time[9], &term[0], 2);
data.time[11] = '/';
break;
case 2: // month (term = "07")
memcpy(&data.time[12], &term[0], 2);
data.time[14] = '/';
break;
case 3: // year (term = "2002")
memcpy(&data.time[15], &term[2], 2);
data.time[17] = 0;
data.fresh = true;
break;
}
}}
};
TinyNmea nmea(config, sizeof(config));
int main() {
sei();
// continuously read from PB2 (pin 7) at 9600 baud using Timer1 in compare mode
uartReader.on(PB2, 9600, Timer1Compare, PinChangeB);
while (1) {
while (uartReader.inputAvailable()) {
// send each symbol to NMEA parser
nmea.next(uartReader.read());
}
if (data.fresh) {
data.fresh = false;
// make use of data.time value
}
idleSleep();
}
return 0;
}

View File

@@ -0,0 +1,21 @@
#include <avr/interrupt.h>
#include <TinyTimer0Overflow.h>
#include <TinyTimer1Overflow.h>
void runOnTimer0() {
//
}
void runOnTimer1() {
//
}
int main() {
sei();
Timer0Overflow.on(0x3fc00, runOnTimer0);
Timer1Overflow.on(0x3fc000, runOnTimer1);
while (1) {
// do nothing
}
return 0;
}

View File

@@ -0,0 +1,27 @@
#include <avr/interrupt.h>
#include <TinySleep.h>
#include <TinyUart.h>
#include <TinyTimer1Compare.h>
#include <TinyPinChangeB.h>
// continuously read bytes from PB2 (pin 7) and repeat them to PB1 (pin 6)
// at 9600 baud using Timer1 in compare mode
int main() {
sei();
uart.on(PB2, PB1, 9600, Timer1Compare, PinChangeB);
while (1) {
if (uart.inputAvailable()) {
uart.write(uart.read());
}
idleSleep();
}
return 0;
}
/* Commented out because Arduino dumps ELF file into HEX file for flashing
// FUSES = {
// .low = 0xFF & FUSE_CKSEL1 & FUSE_CKSEL2 & FUSE_CKSEL3, // F_CPU: 16 MHz
// .high = 0xFF & FUSE_SPIEN & FUSE_EESAVE,
// .extended = 0xFF
// };
*/

View File

@@ -0,0 +1,18 @@
#include <avr/interrupt.h>
#include <Arduino.h>
#include <TinySerial.h>
#include <TinyTimer1Compare.h>
#include <TinyPinChangeB.h>
TinySerial serial(PB2, PB1, Timer1Compare, PinChangeB);
void setup() {
sei();
serial.begin(9600);
}
// continuously output on PB1 (pin 6) at 9600 baud using Timer1 in compare mode
void loop() {
delay(5000);
serial.print(F("this is a test\n"));
}

View File

@@ -0,0 +1,19 @@
#include <avr/interrupt.h>
#include <TinyUartReader.h>
#include <TinySleep.h>
#include <TinyTimer1Compare.h>
#include <TinyPinChangeB.h>
// continuously read from PB2 (pin 7) at 9600 baud using Timer1 in compare mode
int main() {
sei();
uartReader.on(PB2, 9600, Timer1Compare, PinChangeB);
while (1) {
if (uartReader.inputAvailable()) {
uint8_t value = uartReader.read();
// do something with the value
}
idleSleep();
}
return 0;
}

View File

@@ -0,0 +1,16 @@
#include <avr/interrupt.h>
#include <TinyUartWriter.h>
#include <TinySleep.h>
#include <TinyTimer1Compare.h>
#include <TinyPinChangeB.h>
// output "A"+<LF> on PB1 (pin 6) at 9600 baud using Timer1 in compare mode and then stop
int main() {
sei();
uartWriter.on(PB1, 9600, Timer1Compare);
uartWriter.write('A');
uartWriter.write(10);
while (uartWriter.outputRemaining()) idleSleep();
uartWriter.off();
return 0;
}

View File

@@ -0,0 +1,14 @@
#include <avr/interrupt.h>
#include <TinyWatchdog.h>
#include <TinySleep.h>
int main() {
sei();
Watchdog.arm(9, [](){
// do something after 8 seconds (prescaler value of 9 translates to 8 seconds)
});
deepSleep();
// at this point the watchdog will interrupt the sleep
cli();
deepSleep();
}

View File

@@ -0,0 +1,78 @@
#######################################
# Syntax Coloring Map
#######################################
#######################################
# Datatypes (KEYWORD1)
#######################################
TinyPinChange KEYWORD1
TinyExternalInterrupt KEYWORD1
TinyTimer KEYWORD1
TinyWatchdog KEYWORD1
TinyUart KEYWORD1
TinyUartReader KEYWORD1
TinyUartWriter KEYWORD1
TinySerial KEYWORD1
TinyBuffer KEYWORD1
NmeaParser KEYWORD1
TinyNmea KEYWORD1
#######################################
# Methods and Functions (KEYWORD2)
#######################################
on KEYWORD2
off KEYWORD2
begin KEYWORD2
end KEYWORD2
inputAvailable KEYWORD2
inputCapacity KEYWORD2
read KEYWORD2
blockingRead KEYWORD2
peek KEYWORD2
skip KEYWORD2
skipAll KEYWORD2
outputRemaining KEYWORD2
outputCapacity KEYWORD2
write KEYWORD2
blockingWrite KEYWORD2
countBusy KEYWORD2
countFree KEYWORD2
clear KEYWORD2
put KEYWORD2
get KEYWORD2
peek KEYWORD2
skip KEYWORD2
arm KEYWORD2
armReset KEYWORD2
disarm KEYWORD2
sleep KEYWORD2
deepSleep KEYWORD2
idleSleep KEYWORD2
next KEYWORD2
#######################################
# Instances (KEYWORD2)
#######################################
uart KEYWORD2
uartReader KEYWORD2
uartWriter KEYWORD2
PinChangeB KEYWORD2
ExternalInterrupt0 KEYWORD2
Timer0Compare KEYWORD2
Timer0Overflow KEYWORD2
Timer1Compare KEYWORD2
Timer1Overflow KEYWORD2
Watchdog KEYWORD2
#######################################
# Constants (LITERAL1)
#######################################

View File

@@ -0,0 +1,10 @@
name=TinySuite
version=2.1.0
author=Alex079
maintainer=Alex079
sentence=A collection of tools for ATTiny85
paragraph=Includes wrapper classes for timer, pin change, watchdog, UART, NMEA
category=
url=https://github.com/Alex079/TinySuite
architectures=avr
includes=TinyPinChangeB.h, TinyExternalInterrupt0.h, TinyTimer0Compare.h, TinyTimer0Overflow.h, TinyTimer1Compare.h, TinyTimer1Overflow.h, TinyUart.h, TinyUartReader.h, TinyUartWriter.h, TinySerial.h, TinySleep.h, TinyWatchdog.h, TinyBuffer.h, TinyNmea.h

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