mirror of
https://github.com/DCC-EX/CommandStation-EX.git
synced 2025-01-27 12:48:52 +01:00
Cleanup
This commit is contained in:
parent
740dcc7db4
commit
cb0d2bcdc5
@ -1,194 +0,0 @@
|
|||||||
#ifndef ATMEGA2560Timer_h
|
|
||||||
#define ATMEGA2560Timer_h
|
|
||||||
|
|
||||||
#include "../VirtualTimer.h"
|
|
||||||
#include <Arduino.h>
|
|
||||||
|
|
||||||
class Timer : public VirtualTimer {
|
|
||||||
private:
|
|
||||||
int pwmPeriod;
|
|
||||||
unsigned long timer_resolution;
|
|
||||||
unsigned char clockSelectBits;
|
|
||||||
int timer_num;
|
|
||||||
unsigned long lastMicroseconds;
|
|
||||||
public:
|
|
||||||
void (*isrCallback)();
|
|
||||||
Timer(int timer_num) {
|
|
||||||
switch (timer_num)
|
|
||||||
{
|
|
||||||
case 1:
|
|
||||||
case 3:
|
|
||||||
case 4:
|
|
||||||
case 5:
|
|
||||||
timer_resolution = 65536;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
this->timer_num = timer_num;
|
|
||||||
lastMicroseconds = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
void initialize() {
|
|
||||||
switch (timer_num)
|
|
||||||
{
|
|
||||||
case 1:
|
|
||||||
TCCR1B = _BV(WGM13) | _BV(WGM12);
|
|
||||||
TCCR1A = _BV(WGM11);
|
|
||||||
break;
|
|
||||||
case 3:
|
|
||||||
TCCR3B = _BV(WGM33) | _BV(WGM32);
|
|
||||||
TCCR3A = _BV(WGM31);
|
|
||||||
break;
|
|
||||||
case 4:
|
|
||||||
TCCR4B = _BV(WGM43) | _BV(WGM42);
|
|
||||||
TCCR4A = _BV(WGM41);
|
|
||||||
break;
|
|
||||||
case 5:
|
|
||||||
TCCR5B = _BV(WGM53) | _BV(WGM52);
|
|
||||||
TCCR5A = _BV(WGM51);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void setPeriod(unsigned long microseconds) {
|
|
||||||
if(microseconds == lastMicroseconds)
|
|
||||||
return;
|
|
||||||
lastMicroseconds = microseconds;
|
|
||||||
const unsigned long cycles = (F_CPU / 1000000) * microseconds;
|
|
||||||
if (cycles < timer_resolution) {
|
|
||||||
clockSelectBits = 1 << 0;
|
|
||||||
pwmPeriod = cycles;
|
|
||||||
} else
|
|
||||||
if (cycles < timer_resolution * 8) {
|
|
||||||
clockSelectBits = 1 << 1;
|
|
||||||
pwmPeriod = cycles / 8;
|
|
||||||
} else
|
|
||||||
if (cycles < timer_resolution * 64) {
|
|
||||||
clockSelectBits = (1 << 0) | (1 << 1);
|
|
||||||
pwmPeriod = cycles / 64;
|
|
||||||
} else
|
|
||||||
if (cycles < timer_resolution * 256) {
|
|
||||||
clockSelectBits = 1 << 2;
|
|
||||||
pwmPeriod = cycles / 256;
|
|
||||||
} else
|
|
||||||
if (cycles < timer_resolution * 1024) {
|
|
||||||
clockSelectBits = (1 << 2) | (1 << 0);
|
|
||||||
pwmPeriod = cycles / 1024;
|
|
||||||
} else {
|
|
||||||
clockSelectBits = (1 << 2) | (1 << 0);
|
|
||||||
pwmPeriod = timer_resolution - 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (timer_num)
|
|
||||||
{
|
|
||||||
case 1:
|
|
||||||
ICR1 = pwmPeriod;
|
|
||||||
TCCR1B = _BV(WGM13) | _BV(WGM12) | clockSelectBits;
|
|
||||||
break;
|
|
||||||
case 3:
|
|
||||||
ICR3 = pwmPeriod;
|
|
||||||
TCCR3B = _BV(WGM33) | _BV(WGM32) | clockSelectBits;
|
|
||||||
break;
|
|
||||||
case 4:
|
|
||||||
ICR4 = pwmPeriod;
|
|
||||||
TCCR4B = _BV(WGM43) | _BV(WGM42) | clockSelectBits;
|
|
||||||
break;
|
|
||||||
case 5:
|
|
||||||
ICR5 = pwmPeriod;
|
|
||||||
TCCR5B = _BV(WGM53) | _BV(WGM52) | clockSelectBits;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
void start() {
|
|
||||||
switch (timer_num)
|
|
||||||
{
|
|
||||||
case 1:
|
|
||||||
TCCR1B = 0;
|
|
||||||
TCNT1 = 0; // TODO: does this cause an undesired interrupt?
|
|
||||||
TCCR1B = _BV(WGM13) | _BV(WGM12) | clockSelectBits;
|
|
||||||
break;
|
|
||||||
case 3:
|
|
||||||
TCCR3B = 0;
|
|
||||||
TCNT3 = 0; // TODO: does this cause an undesired interrupt?
|
|
||||||
TCCR3B = _BV(WGM33) | _BV(WGM32) | clockSelectBits;
|
|
||||||
break;
|
|
||||||
case 4:
|
|
||||||
TCCR4B = 0;
|
|
||||||
TCNT4 = 0; // TODO: does this cause an undesired interrupt?
|
|
||||||
TCCR4B = _BV(WGM43) | _BV(WGM42) | clockSelectBits;
|
|
||||||
break;
|
|
||||||
case 5:
|
|
||||||
TCCR5B = 0;
|
|
||||||
TCNT5 = 0; // TODO: does this cause an undesired interrupt?
|
|
||||||
TCCR5B = _BV(WGM53) | _BV(WGM52) | clockSelectBits;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
void stop() {
|
|
||||||
switch (timer_num)
|
|
||||||
{
|
|
||||||
case 1:
|
|
||||||
TCCR1B = _BV(WGM13) | _BV(WGM12);
|
|
||||||
break;
|
|
||||||
case 3:
|
|
||||||
TCCR3B = _BV(WGM33) | _BV(WGM32);
|
|
||||||
break;
|
|
||||||
case 4:
|
|
||||||
TCCR4B = _BV(WGM43) | _BV(WGM42);
|
|
||||||
break;
|
|
||||||
case 5:
|
|
||||||
TCCR5B = _BV(WGM53) | _BV(WGM52);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void attachInterrupt(void (*isr)()) {
|
|
||||||
isrCallback = isr;
|
|
||||||
|
|
||||||
switch (timer_num)
|
|
||||||
{
|
|
||||||
case 1:
|
|
||||||
TIMSK1 = _BV(TOIE1);
|
|
||||||
break;
|
|
||||||
case 3:
|
|
||||||
TIMSK3 = _BV(TOIE3);
|
|
||||||
break;
|
|
||||||
case 4:
|
|
||||||
TIMSK4 = _BV(TOIE4);
|
|
||||||
break;
|
|
||||||
case 5:
|
|
||||||
TIMSK5 = _BV(TOIE5);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void detachInterrupt() {
|
|
||||||
switch (timer_num)
|
|
||||||
{
|
|
||||||
case 1:
|
|
||||||
TIMSK1 = 0;
|
|
||||||
break;
|
|
||||||
case 3:
|
|
||||||
TIMSK3 = 0;
|
|
||||||
break;
|
|
||||||
case 4:
|
|
||||||
TIMSK4 = 0;
|
|
||||||
break;
|
|
||||||
case 5:
|
|
||||||
TIMSK5 = 0;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
extern Timer TimerA;
|
|
||||||
extern Timer TimerB;
|
|
||||||
extern Timer TimerC;
|
|
||||||
extern Timer TimerD;
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#endif
|
|
@ -1,208 +0,0 @@
|
|||||||
#ifndef ATMEGA328Timer_h
|
|
||||||
#define ATMEGA328Timer_h
|
|
||||||
|
|
||||||
#include "../VirtualTimer.h"
|
|
||||||
#include <Arduino.h>
|
|
||||||
|
|
||||||
class Timer : public VirtualTimer {
|
|
||||||
private:
|
|
||||||
int pwmPeriod;
|
|
||||||
unsigned long timer_resolution;
|
|
||||||
unsigned char clockSelectBits;
|
|
||||||
int timer_num;
|
|
||||||
unsigned long lastMicroseconds;
|
|
||||||
public:
|
|
||||||
void (*isrCallback)();
|
|
||||||
Timer(int timer_num) {
|
|
||||||
switch (timer_num)
|
|
||||||
{
|
|
||||||
//case 0:
|
|
||||||
case 2:
|
|
||||||
timer_resolution = 256;
|
|
||||||
break;
|
|
||||||
case 1:
|
|
||||||
timer_resolution = 65536;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
this->timer_num = timer_num;
|
|
||||||
lastMicroseconds = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
void initialize() {
|
|
||||||
switch (timer_num)
|
|
||||||
{
|
|
||||||
// case 0:
|
|
||||||
// TCCR0B = _BV(WGM02);
|
|
||||||
// TCCR0A = _BV(WGM00) | _BV(WGM01);
|
|
||||||
// break;
|
|
||||||
case 1:
|
|
||||||
TCCR1B = _BV(WGM13) | _BV(WGM12);
|
|
||||||
TCCR1A = _BV(WGM11);
|
|
||||||
break;
|
|
||||||
case 2:
|
|
||||||
TCCR2B = _BV(WGM22);
|
|
||||||
TCCR2A = _BV(WGM20) | _BV(WGM21);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void setPeriod(unsigned long microseconds) {
|
|
||||||
if(microseconds == lastMicroseconds)
|
|
||||||
return;
|
|
||||||
lastMicroseconds = microseconds;
|
|
||||||
const unsigned long cycles = (F_CPU / 1000000) * microseconds;
|
|
||||||
|
|
||||||
switch(timer_num) {
|
|
||||||
case 2:
|
|
||||||
if (cycles < timer_resolution) {
|
|
||||||
clockSelectBits = 1 << 0;
|
|
||||||
pwmPeriod = cycles;
|
|
||||||
} else
|
|
||||||
if (cycles < timer_resolution * 8) {
|
|
||||||
clockSelectBits = 1 << 1;
|
|
||||||
pwmPeriod = cycles / 8;
|
|
||||||
} else
|
|
||||||
if (cycles < timer_resolution * 32) {
|
|
||||||
clockSelectBits = 1 << 0 | 1 << 1;
|
|
||||||
pwmPeriod = cycles / 32;
|
|
||||||
} else
|
|
||||||
if (cycles < timer_resolution * 64) {
|
|
||||||
clockSelectBits = 1 << 2;
|
|
||||||
pwmPeriod = cycles / 64;
|
|
||||||
} else
|
|
||||||
if (cycles < timer_resolution * 128) {
|
|
||||||
clockSelectBits = 1 << 2 | 1 << 0;
|
|
||||||
pwmPeriod = cycles / 128;
|
|
||||||
} else
|
|
||||||
if (cycles < timer_resolution * 256) {
|
|
||||||
clockSelectBits = 1 << 2 | 1 << 1;
|
|
||||||
pwmPeriod = cycles / 256;
|
|
||||||
} else
|
|
||||||
if (cycles < timer_resolution * 1024) {
|
|
||||||
clockSelectBits = 1 << 2 | 1 << 1 | 1 << 0;
|
|
||||||
pwmPeriod = cycles / 1024;
|
|
||||||
} else {
|
|
||||||
clockSelectBits = 1 << 2 | 1 << 1 | 1 << 0;
|
|
||||||
pwmPeriod = timer_resolution - 1;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
//case 0:
|
|
||||||
case 1:
|
|
||||||
if (cycles < timer_resolution) {
|
|
||||||
clockSelectBits = 1 << 0;
|
|
||||||
pwmPeriod = cycles;
|
|
||||||
} else
|
|
||||||
if (cycles < timer_resolution * 8) {
|
|
||||||
clockSelectBits = 1 << 1;
|
|
||||||
pwmPeriod = cycles / 8;
|
|
||||||
} else
|
|
||||||
if (cycles < timer_resolution * 64) {
|
|
||||||
clockSelectBits = (1 << 0) | (1 << 1);
|
|
||||||
pwmPeriod = cycles / 64;
|
|
||||||
} else
|
|
||||||
if (cycles < timer_resolution * 256) {
|
|
||||||
clockSelectBits = 1 << 2;
|
|
||||||
pwmPeriod = cycles / 256;
|
|
||||||
} else
|
|
||||||
if (cycles < timer_resolution * 1024) {
|
|
||||||
clockSelectBits = (1 << 2) | (1 << 0);
|
|
||||||
pwmPeriod = cycles / 1024;
|
|
||||||
} else {
|
|
||||||
clockSelectBits = (1 << 2) | (1 << 0);
|
|
||||||
pwmPeriod = timer_resolution - 1;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (timer_num)
|
|
||||||
{
|
|
||||||
// case 0:
|
|
||||||
// OCR0A = pwmPeriod;
|
|
||||||
// TCCR0B = _BV(WGM02) | clockSelectBits;
|
|
||||||
// break;
|
|
||||||
case 1:
|
|
||||||
ICR1 = pwmPeriod;
|
|
||||||
TCCR1B = _BV(WGM13) | _BV(WGM12) | clockSelectBits;
|
|
||||||
break;
|
|
||||||
case 2:
|
|
||||||
OCR2A = pwmPeriod;
|
|
||||||
TCCR2B = _BV(WGM22) | clockSelectBits;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
void start() {
|
|
||||||
switch (timer_num)
|
|
||||||
{
|
|
||||||
// case 0:
|
|
||||||
// TCCR0B = 0;
|
|
||||||
// TCNT0 = 0; // TODO: does this cause an undesired interrupt?
|
|
||||||
// TCCR0B = _BV(WGM02) | clockSelectBits;
|
|
||||||
// break;
|
|
||||||
case 1:
|
|
||||||
TCCR1B = 0;
|
|
||||||
TCNT1 = 0; // TODO: does this cause an undesired interrupt?
|
|
||||||
TCCR1B = _BV(WGM13) | _BV(WGM12) | clockSelectBits;
|
|
||||||
break;
|
|
||||||
case 2:
|
|
||||||
TCCR2B = 0;
|
|
||||||
TCNT2 = 0; // TODO: does this cause an undesired interrupt?
|
|
||||||
TCCR2B = _BV(WGM22) | clockSelectBits;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
void stop() {
|
|
||||||
switch (timer_num)
|
|
||||||
{
|
|
||||||
// case 0:
|
|
||||||
// TCCR0B = _BV(WGM02);
|
|
||||||
// break;
|
|
||||||
case 1:
|
|
||||||
TCCR1B = _BV(WGM13) | _BV(WGM12);
|
|
||||||
break;
|
|
||||||
case 2:
|
|
||||||
TCCR2B = _BV(WGM22);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void attachInterrupt(void (*isr)()) {
|
|
||||||
isrCallback = isr;
|
|
||||||
|
|
||||||
switch (timer_num)
|
|
||||||
{
|
|
||||||
// case 0:
|
|
||||||
// TIMSK0 = _BV(TOIE0);
|
|
||||||
// break;
|
|
||||||
case 1:
|
|
||||||
TIMSK1 = _BV(TOIE1);
|
|
||||||
break;
|
|
||||||
case 2:
|
|
||||||
TIMSK2 = _BV(TOIE2);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void detachInterrupt() {
|
|
||||||
switch (timer_num)
|
|
||||||
{
|
|
||||||
// case 0:
|
|
||||||
// TIMSK0 = 0;
|
|
||||||
// break;
|
|
||||||
case 1:
|
|
||||||
TIMSK1 = 0;
|
|
||||||
break;
|
|
||||||
case 2:
|
|
||||||
TIMSK2 = 0;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
extern Timer TimerA;
|
|
||||||
extern Timer TimerB;
|
|
||||||
|
|
||||||
#endif
|
|
@ -1,474 +0,0 @@
|
|||||||
// EveryTimerB library.
|
|
||||||
// by Kees van der Oord Kees.van.der.Oord@inter.nl.net
|
|
||||||
|
|
||||||
// Timer library for the TCB timer of the AtMega4809 processor.
|
|
||||||
// tested on the Arduino Nano Every (AtMega4809) and the Arduino 1.8.12 IDE
|
|
||||||
// support for the Every is the 'Arduino MegaAVR' boards module (Tools | Board | Boards Manager)
|
|
||||||
|
|
||||||
// usage:
|
|
||||||
/*
|
|
||||||
#ifdef ARDUINO_ARCH_MEGAAVR
|
|
||||||
#include "EveryTimerB.h"
|
|
||||||
#define Timer1 TimerB2 // use TimerB2 as a drop in replacement for Timer1
|
|
||||||
#else // assume architecture supported by TimerOne library ....
|
|
||||||
#include "TimerOne.h"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// code below will now work both on the MegaAVR and AVR processors
|
|
||||||
|
|
||||||
void setup() {
|
|
||||||
Timer1.initialize();
|
|
||||||
Timer1.attachInterrupt(myisr);
|
|
||||||
Timer1.setPeriod(1000000UL); // like the TimerOne library this will start the timer as well
|
|
||||||
}
|
|
||||||
|
|
||||||
void myisr() {
|
|
||||||
// do something useful every second
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
// clock source options:
|
|
||||||
// The TCB clock source is specified in the initialize() function with default value EveryTimerB_CLOCMODE.
|
|
||||||
// define this macro before including this file to use a different default clock mode
|
|
||||||
// e.g.:
|
|
||||||
// #define EveryTimerB_CLOCMODE TCB_CLKSEL_CLKTCA_gc // 250 kHz ~ 4 us
|
|
||||||
// #define EveryTimerB_CLOCMODE TCB_CLKSEL_CLKDIV2_gc // 8 MHz ~ 0.125 us
|
|
||||||
// #define EveryTimerB_CLOCMODE TCB_CLKSEL_CLKDIV_gc // 16 MHz ~ 0.0625 us
|
|
||||||
|
|
||||||
// timer options
|
|
||||||
// The 4809 has one A timer (TCA) and four B timers (TCB).
|
|
||||||
// TCA and TCB3 are used by the arduino core to generate the clock used by millis() and micros().
|
|
||||||
// TCB0 generates the PWM timing for pin D6, TCB1 for pin D3.
|
|
||||||
// By default Timer Control B2 is defined as TimerB2 in the EveryTimerB library.
|
|
||||||
// If you would like to use the TCB0 and TCB1 as well you have to copy the code
|
|
||||||
// from the EveryTimerB.cpp into your product file and adapt for B0 and B1 timers.
|
|
||||||
//
|
|
||||||
// for information on the 4809 TCA and TCB timers:
|
|
||||||
// http://ww1.microchip.com/downloads/en/AppNotes/TB3217-Getting-Started-with-TCA-90003217A.pdf
|
|
||||||
// http://ww1.microchip.com/downloads/en/Appnotes/TB3214-Getting-Started-with-TCB-90003214A.pdf
|
|
||||||
// %LOCALAPPDATA%\Arduino15\packages\arduino\hardware\megaavr\1.8.5\cores\arduino\wiring.c
|
|
||||||
// %LOCALAPPDATA%\Arduino15\packages\arduino\hardware\megaavr\1.8.5\variants\nona4809\variant.c
|
|
||||||
// %LOCALAPPDATA%\Arduino15\packages\arduino\tools\avr-gcc\7.3.0-atmel3.6.1-arduino5\avr\include\avr\iom4809.h
|
|
||||||
|
|
||||||
// 20 MHz system clock
|
|
||||||
// to run the Every at 20 MHz, add the lines below to the nona4809 section of the boards.txt file
|
|
||||||
// in %LOCALAPPDATA%\Arduino15\packages\arduino\hardware\megaavr\1.8.5.
|
|
||||||
// they add the sub menu 'Tools | Clock' to choose between 16MHz and 20MHz.
|
|
||||||
/*
|
|
||||||
menu.clock=Clock
|
|
||||||
nona4809.menu.clock.16internal=16MHz
|
|
||||||
nona4809.menu.clock.16internal.build.f_cpu=16000000L
|
|
||||||
nona4809.menu.clock.16internal.bootloader.OSCCFG=0x01
|
|
||||||
nona4809.menu.clock.20internal=20MHz
|
|
||||||
nona4809.menu.clock.20internal.build.f_cpu=20000000L
|
|
||||||
nona4809.menu.clock.20internal.bootloader.OSCCFG=0x02
|
|
||||||
*/
|
|
||||||
// On 20Mhz, the 1.8.12 IDE MegaAvr core library implementation
|
|
||||||
// of the millis() and micros() functions is not accurate.
|
|
||||||
// the file "MegaAvr20MHz.h" implements a quick hack to correct for this
|
|
||||||
//
|
|
||||||
// to do:
|
|
||||||
// there is no range check on the 'period' arguments of setPeriod ...
|
|
||||||
// check if it is necessary to set the CNT register to 0 in start()
|
|
||||||
|
|
||||||
#ifndef EveryTimerB_h_
|
|
||||||
#define EveryTimerB_h_
|
|
||||||
#ifdef ARDUINO_ARCH_MEGAAVR
|
|
||||||
|
|
||||||
#ifndef EveryTimerB_CLOCMODE
|
|
||||||
#define EveryTimerB_CLOCMODE TCB_CLKSEL_CLKTCA_gc
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if defined(ARDUINO) && ARDUINO >= 100
|
|
||||||
#include "Arduino.h"
|
|
||||||
#else
|
|
||||||
#include "WProgram.h"
|
|
||||||
#endif
|
|
||||||
#include "MegaAvr20MHz.h"
|
|
||||||
#include "pins_arduino.h"
|
|
||||||
#include "../VirtualTimer.h"
|
|
||||||
|
|
||||||
#define TCB_RESOLUTION 65536UL // TCB is 16 bit
|
|
||||||
// CLOCK F_CPU DIV TICK OVERFLOW OVERFLOW/s
|
|
||||||
// CLKTCA 16MHz 64 4000 ns 262144us 3.8 Hz
|
|
||||||
// CLKDIV2 16MHz 2 125 ns 8192us 122 Hz
|
|
||||||
// CLKDIV1 16MHz 1 62.5ns 4096us 244 Hz
|
|
||||||
// CLKTCA 20MHz 64 3200 ns 209716us 4.8 Hz
|
|
||||||
// CLKDIV2 20MHz 2 100 ns 6554us 153 Hz
|
|
||||||
// CLKDIV1 20MHz 1 50 ns 3277us 305 Hz
|
|
||||||
|
|
||||||
class EveryTimerB : public VirtualTimer
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
// The AtMega Timer Control B clock sources selection:
|
|
||||||
// TCB_CLKSEL_CLKTCA_gc, // Timer Controller A, Arduino framework sets TCA to F_CPU/64 = 250kHz (4us) @ 16MHz or 312.5kHz (3.2us) @ 20MHz
|
|
||||||
// TCB_CLKSEL_CLKDIV2_gc, // CLK_PER/2 Peripheral Clock / 2: 8MHz @ 16Mhz or 10MHz @ 20MHz
|
|
||||||
// TCB_CLKSEL_CLKDIV1_gc // CLK_PER Peripheral Clock: 16MHz @ 16Mhz or 20MHz @ 20MHz
|
|
||||||
|
|
||||||
// intialize: sets the timer compare mode and the clock source
|
|
||||||
void initialize(TCB_t *timer_ = &TCB2, TCB_CLKSEL_t clockSource = EveryTimerB_CLOCMODE, unsigned long period = 1000000UL) /*__attribute__((always_inline))*/
|
|
||||||
{
|
|
||||||
timer = timer_;
|
|
||||||
#if defined(MegaAvr20MHzCorrected)
|
|
||||||
corrected20MHzInit(); // see commment in MegaAvr20MHz_h
|
|
||||||
#endif
|
|
||||||
stop();
|
|
||||||
timer->CTRLB = TCB_CNTMODE_INT_gc & ~TCB_CCMPEN_bm; // timer compare mode with output disabled
|
|
||||||
if (clockSource)
|
|
||||||
setClockSource(clockSource);
|
|
||||||
if (period)
|
|
||||||
setPeriod(period);
|
|
||||||
}
|
|
||||||
|
|
||||||
void initialize()
|
|
||||||
{
|
|
||||||
unsigned long period = 1000000UL;
|
|
||||||
timer = &TCB2;
|
|
||||||
#if defined(MegaAvr20MHzCorrected)
|
|
||||||
corrected20MHzInit(); // see commment in MegaAvr20MHz_h
|
|
||||||
#endif
|
|
||||||
stop();
|
|
||||||
timer->CTRLB = TCB_CNTMODE_INT_gc & ~TCB_CCMPEN_bm; // timer compare mode with output disabled
|
|
||||||
if (EveryTimerB_CLOCMODE)
|
|
||||||
setClockSource(EveryTimerB_CLOCMODE);
|
|
||||||
if (period)
|
|
||||||
setPeriod(period);
|
|
||||||
}
|
|
||||||
|
|
||||||
void setClockSource(TCB_CLKSEL_t clockSource) __attribute__((always_inline))
|
|
||||||
{
|
|
||||||
timer->CTRLA = clockSource; // this stops the clock as well ...
|
|
||||||
switch (clockSource)
|
|
||||||
{
|
|
||||||
#if F_CPU == 20000000UL
|
|
||||||
case TCB_CLKSEL_CLKTCA_gc:
|
|
||||||
maxTimeWithoutOverflow = 209715;
|
|
||||||
break; // (TCB_RESOLUTION * 64) / 20
|
|
||||||
case TCB_CLKSEL_CLKDIV2_gc:
|
|
||||||
maxTimeWithoutOverflow = 6553;
|
|
||||||
break; // (TCB_RESOLUTION * 2) / 20
|
|
||||||
case TCB_CLKSEL_CLKDIV1_gc:
|
|
||||||
maxTimeWithoutOverflow = 3276;
|
|
||||||
break; // (TCB_RESOLUTION * 1) / 20
|
|
||||||
#else
|
|
||||||
case TCB_CLKSEL_CLKTCA_gc:
|
|
||||||
maxTimeWithoutOverflow = 262144;
|
|
||||||
break;
|
|
||||||
case TCB_CLKSEL_CLKDIV2_gc:
|
|
||||||
maxTimeWithoutOverflow = 8192;
|
|
||||||
break;
|
|
||||||
case TCB_CLKSEL_CLKDIV1_gc:
|
|
||||||
maxTimeWithoutOverflow = 4096;
|
|
||||||
break;
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
TCB_CLKSEL_t getClockSource()
|
|
||||||
{
|
|
||||||
return (TCB_CLKSEL_t)(timer->CTRLA & (TCB_CLKSEL_CLKTCA_gc | TCB_CLKSEL_CLKDIV2_gc | TCB_CLKSEL_CLKDIV1_gc));
|
|
||||||
}
|
|
||||||
|
|
||||||
double getFrequencyOfClock(TCB_CLKSEL_t clock)
|
|
||||||
{
|
|
||||||
switch (clock)
|
|
||||||
{
|
|
||||||
// suppose nobody touched the default TCA configuration ...
|
|
||||||
case TCB_CLKSEL_CLKTCA_gc:
|
|
||||||
return double(F_CPU / 64);
|
|
||||||
break;
|
|
||||||
case TCB_CLKSEL_CLKDIV2_gc:
|
|
||||||
return double(F_CPU / 2);
|
|
||||||
break;
|
|
||||||
case TCB_CLKSEL_CLKDIV1_gc:
|
|
||||||
return double(F_CPU);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
return 0.0;
|
|
||||||
}
|
|
||||||
|
|
||||||
double getClockFrequency()
|
|
||||||
{
|
|
||||||
return getFrequencyOfClock(getClockSource());
|
|
||||||
}
|
|
||||||
|
|
||||||
// setPeriod: sets the period
|
|
||||||
// note: max and min values are different for each clock
|
|
||||||
// CLKTCA: conversion from us to ticks multiplies 'period' first with 10, so max value is MAX_ULONG/10 ~ 1 hr 11 minutes 34 seconds
|
|
||||||
// CLKDIV2: conversion from us to ticks is a *10 multiplication, so max value is 420M us (~ 7 minutes)
|
|
||||||
// CLKDIV1: conversion from us to ticks is a *20 multiplication, so max value is 210M us (~ 3.5 minutes)
|
|
||||||
void setPeriod(unsigned long period /* us */) /*__attribute__((always_inline))*/
|
|
||||||
{
|
|
||||||
timer->CTRLA &= ~TCB_ENABLE_bm;
|
|
||||||
// conversion from us to ticks depends on the clock
|
|
||||||
switch (timer->CTRLA & TCB_CLKSEL_gm)
|
|
||||||
{
|
|
||||||
case TCB_CLKSEL_CLKTCA_gc:
|
|
||||||
#if F_CPU == 20000000UL
|
|
||||||
period = (period * 10) / 32; // 20Mhz / 64x clock divider of TCA => 3.2 us / tick
|
|
||||||
#else // 16000000UL
|
|
||||||
period /= 4; // 16MHz / 64x clock divider of TCA => 4 us / tock
|
|
||||||
#endif
|
|
||||||
break;
|
|
||||||
case TCB_CLKSEL_CLKDIV2_gc:
|
|
||||||
#if F_CPU == 20000000UL
|
|
||||||
period *= 10; // 20MHz / 2x clock divider => 10 ticks / us
|
|
||||||
#else // 16000000UL
|
|
||||||
period *= 8; // 16MHz / 2x clock divider => 8 ticks / us
|
|
||||||
#endif
|
|
||||||
break;
|
|
||||||
case TCB_CLKSEL_CLKDIV1_gc:
|
|
||||||
#if F_CPU == 20000000UL
|
|
||||||
period *= 20; // 20MHz: 20 ticks / us
|
|
||||||
#else // 16000000UL
|
|
||||||
period *= 16; // 16MHz: 16 ticks / u3
|
|
||||||
#endif
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
// to support longer than TCB_RESOLUTION ticks,
|
|
||||||
// this class supports first waiting for N 'overflowCounts'
|
|
||||||
// and next program the timer the remaining 'remainder' ticks:
|
|
||||||
countsPerOverflow = TCB_RESOLUTION;
|
|
||||||
overflowCounts = period / TCB_RESOLUTION;
|
|
||||||
remainder = period % TCB_RESOLUTION;
|
|
||||||
|
|
||||||
// the timer period is always one tick longer than programmed,
|
|
||||||
// so a remainder of 1 is not possible. reduce the length of
|
|
||||||
// the 'overflow' cycles to get a remainder that is not 1
|
|
||||||
if (overflowCounts)
|
|
||||||
{
|
|
||||||
while (remainder == 1)
|
|
||||||
{
|
|
||||||
--countsPerOverflow;
|
|
||||||
overflowCounts = period / countsPerOverflow;
|
|
||||||
remainder = period % countsPerOverflow;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// the timer period is always one tick longer than programmed
|
|
||||||
--countsPerOverflow;
|
|
||||||
if (remainder)
|
|
||||||
--remainder;
|
|
||||||
|
|
||||||
// let's go
|
|
||||||
start();
|
|
||||||
}
|
|
||||||
|
|
||||||
void start() /*__attribute__((always_inline))*/
|
|
||||||
{
|
|
||||||
stop();
|
|
||||||
overflowCounter = overflowCounts;
|
|
||||||
timer->CCMP = overflowCounts ? countsPerOverflow : remainder;
|
|
||||||
timer->CNT = 0;
|
|
||||||
timer->CTRLA |= TCB_ENABLE_bm;
|
|
||||||
}
|
|
||||||
|
|
||||||
void stop() /*__attribute__((always_inline))*/
|
|
||||||
{
|
|
||||||
timer->CTRLA &= ~TCB_ENABLE_bm;
|
|
||||||
timer->INTFLAGS = TCB_CAPT_bm; // writing to the INTFLAGS register will clear the interrupt request flag
|
|
||||||
}
|
|
||||||
|
|
||||||
bool isEnabled(void) __attribute__((always_inline))
|
|
||||||
{
|
|
||||||
return timer->CTRLA & TCB_ENABLE_bm ? true : false;
|
|
||||||
}
|
|
||||||
|
|
||||||
void enable(void) __attribute__((always_inline))
|
|
||||||
{
|
|
||||||
timer->CTRLA |= TCB_ENABLE_bm;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool disable(void) __attribute__((always_inline))
|
|
||||||
{
|
|
||||||
timer->CTRLA &= ~TCB_ENABLE_bm;
|
|
||||||
}
|
|
||||||
|
|
||||||
void attachInterrupt(void (*isr)()) /*__attribute__((always_inline))*/
|
|
||||||
{
|
|
||||||
isrCallback = isr;
|
|
||||||
timer->INTFLAGS = TCB_CAPT_bm; // clear interrupt request flag
|
|
||||||
timer->INTCTRL = TCB_CAPT_bm; // Enable the interrupt
|
|
||||||
}
|
|
||||||
|
|
||||||
void attachInterrupt(void (*isr)(), unsigned long microseconds) __attribute__((always_inline))
|
|
||||||
{
|
|
||||||
if (microseconds > 0)
|
|
||||||
stop();
|
|
||||||
attachInterrupt(isr);
|
|
||||||
if (microseconds > 0)
|
|
||||||
setPeriod(microseconds);
|
|
||||||
}
|
|
||||||
|
|
||||||
void detachInterrupt() /*__attribute__((always_inline))*/
|
|
||||||
{
|
|
||||||
timer->INTCTRL &= ~TCB_CAPT_bm; // Disable the interrupt
|
|
||||||
isrCallback = isrDefaultUnused;
|
|
||||||
}
|
|
||||||
|
|
||||||
void enableInterrupt() __attribute__((always_inline))
|
|
||||||
{
|
|
||||||
timer->INTFLAGS = TCB_CAPT_bm; // clear interrupt request flag
|
|
||||||
timer->INTCTRL = TCB_CAPT_bm; // Enable the interrupt
|
|
||||||
}
|
|
||||||
|
|
||||||
void disableInterrupt() __attribute__((always_inline))
|
|
||||||
{
|
|
||||||
timer->INTCTRL &= ~TCB_CAPT_bm; // Enable the interrupt
|
|
||||||
}
|
|
||||||
|
|
||||||
TCB_CNTMODE_enum getMode() __attribute__((always_inline))
|
|
||||||
{
|
|
||||||
return (TCB_CNTMODE_enum)(timer->CTRLB & 0x7);
|
|
||||||
}
|
|
||||||
|
|
||||||
void setMode(TCB_CNTMODE_enum mode) __attribute__((always_inline))
|
|
||||||
{
|
|
||||||
timer->CTRLB = (timer->CTRLB & ~0x7) | mode;
|
|
||||||
}
|
|
||||||
|
|
||||||
uint8_t isOutputEnabled() __attribute__((always_inline))
|
|
||||||
{
|
|
||||||
return timer->CTRLB & TCB_CCMPEN_bm;
|
|
||||||
}
|
|
||||||
|
|
||||||
uint8_t enableOutput() __attribute__((always_inline))
|
|
||||||
{
|
|
||||||
timer->CTRLB |= TCB_CCMPEN_bm;
|
|
||||||
}
|
|
||||||
|
|
||||||
uint8_t disableOutput() __attribute__((always_inline))
|
|
||||||
{
|
|
||||||
timer->CTRLB &= ~TCB_CCMPEN_bm;
|
|
||||||
}
|
|
||||||
|
|
||||||
// this will start PWM on pin 6 (TCB0) or pin 3 (TCB1)
|
|
||||||
// set the pins to output with setMode(x,OUTPUT) before calling this function
|
|
||||||
// period determines the clock ticks in one cycle:
|
|
||||||
// 16MHz clock: slowest frequency at 255 = 62 kHz.
|
|
||||||
// 8MHz clock: slowest frequency at 255 = 31 kHz.
|
|
||||||
// 256kHz clock: slowest frequency at 255 = 1 kHz.
|
|
||||||
// compare determines the duty cycle.
|
|
||||||
// with a period of 255, set the compare to 128 to get 50% duty cycle.
|
|
||||||
void setPwmMode(byte period, byte compare)
|
|
||||||
{
|
|
||||||
disableInterrupt();
|
|
||||||
setMode(TCB_CNTMODE_PWM8_gc);
|
|
||||||
timer->CCMPL = period;
|
|
||||||
timer->CCMPH = compare;
|
|
||||||
enableOutput();
|
|
||||||
enable();
|
|
||||||
}
|
|
||||||
|
|
||||||
void getPwmMode(byte &period, byte &compare)
|
|
||||||
{
|
|
||||||
period = timer->CCMPL;
|
|
||||||
compare = timer->CCMPH;
|
|
||||||
}
|
|
||||||
|
|
||||||
void setPwm(double frequency, double dutyCycle)
|
|
||||||
{
|
|
||||||
TCB_CLKSEL_t clockSource = TCB_CLKSEL_CLKDIV1_gc;
|
|
||||||
double clockFrequency = getFrequencyOfClock(clockSource);
|
|
||||||
if (frequency < (clockFrequency / 256.))
|
|
||||||
{
|
|
||||||
clockSource = TCB_CLKSEL_CLKDIV2_gc;
|
|
||||||
clockFrequency = getFrequencyOfClock(clockSource);
|
|
||||||
}
|
|
||||||
if (frequency < (clockFrequency / 256.))
|
|
||||||
{
|
|
||||||
clockSource = TCB_CLKSEL_CLKTCA_gc;
|
|
||||||
clockFrequency = getFrequencyOfClock(clockSource);
|
|
||||||
}
|
|
||||||
double period = (clockFrequency / frequency) - 1.0 + 0.5;
|
|
||||||
if (period > 255.)
|
|
||||||
period = 255.;
|
|
||||||
if (period < 0.)
|
|
||||||
period = 0.0;
|
|
||||||
double compare = period * dutyCycle + 0.5;
|
|
||||||
if (compare < 0.0)
|
|
||||||
compare = 0.0;
|
|
||||||
if (compare > period)
|
|
||||||
compare = period;
|
|
||||||
setPwmMode((byte)(period), (byte)(compare));
|
|
||||||
}
|
|
||||||
|
|
||||||
void getPwm(double &frequency, double &dutyCycle)
|
|
||||||
{
|
|
||||||
byte period, compare;
|
|
||||||
getPwmMode(period, compare);
|
|
||||||
frequency = getClockFrequency() / (((double)period) + 1);
|
|
||||||
dutyCycle = (double)compare / (((double)period) + 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
void setTimerMode()
|
|
||||||
{
|
|
||||||
disable();
|
|
||||||
disableOutput();
|
|
||||||
setMode(TCB_CNTMODE_INT_gc);
|
|
||||||
if (isrCallback != isrDefaultUnused)
|
|
||||||
{
|
|
||||||
enableInterrupt();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
TCB_t *getTimer() { return timer; }
|
|
||||||
long getOverflowCounts() { return overflowCounts; }
|
|
||||||
long getRemainder() { return remainder; }
|
|
||||||
long getOverflowCounter() { return overflowCounter; }
|
|
||||||
long getOverflowTime() { return maxTimeWithoutOverflow; }
|
|
||||||
|
|
||||||
//protected:
|
|
||||||
// the next_tick function is called by the interrupt service routine TCB0_INT_vect
|
|
||||||
//friend extern "C" void TCB0_INT_vect(void);
|
|
||||||
void next_tick() __attribute__((always_inline))
|
|
||||||
{
|
|
||||||
--overflowCounter;
|
|
||||||
if (overflowCounter > 0)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (overflowCounter < 0)
|
|
||||||
{
|
|
||||||
// finished waiting for remainder
|
|
||||||
if (overflowCounts)
|
|
||||||
{
|
|
||||||
// restart with a max counter
|
|
||||||
overflowCounter = overflowCounts;
|
|
||||||
timer->CCMP = countsPerOverflow;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// overflowCounter == 0
|
|
||||||
// the overflow series has finished: to the remainder if any
|
|
||||||
if (remainder)
|
|
||||||
{
|
|
||||||
timer->CCMP = remainder;
|
|
||||||
if (timer->CNT < remainder)
|
|
||||||
return;
|
|
||||||
// remainder is so short: already passed !
|
|
||||||
timer->CCMP = countsPerOverflow;
|
|
||||||
}
|
|
||||||
// no remainder series: reset the overflow counter and do the callback
|
|
||||||
overflowCounter = overflowCounts;
|
|
||||||
}
|
|
||||||
(*isrCallback)();
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
TCB_t *timer = &TCB0;
|
|
||||||
long overflowCounts = 0;
|
|
||||||
long remainder = 10;
|
|
||||||
long overflowCounter = 0;
|
|
||||||
unsigned long countsPerOverflow = TCB_RESOLUTION - 1;
|
|
||||||
void (*isrCallback)();
|
|
||||||
static void isrDefaultUnused();
|
|
||||||
unsigned long maxTimeWithoutOverflow;
|
|
||||||
|
|
||||||
}; // EveryTimerB
|
|
||||||
|
|
||||||
extern EveryTimerB TimerA;
|
|
||||||
|
|
||||||
#endif // ARDUINO_ARCH_MEGAAVR
|
|
||||||
#endif // EveryTimerB_h_
|
|
@ -1,132 +0,0 @@
|
|||||||
#ifndef ATMEGA4809Timer_h
|
|
||||||
#define ATMEGA4809imer_h
|
|
||||||
|
|
||||||
#include "../VirtualTimer.h"
|
|
||||||
#include <Arduino.h>
|
|
||||||
|
|
||||||
// We only define behavior for timer 0 (TCA0), because TCB0 is very limited in functionality.
|
|
||||||
|
|
||||||
class Timer : public VirtualTimer {
|
|
||||||
private:
|
|
||||||
int pwmPeriod;
|
|
||||||
unsigned long timer_resolution;
|
|
||||||
unsigned char clockSelectBits;
|
|
||||||
int timer_num;
|
|
||||||
unsigned long lastMicroseconds;
|
|
||||||
public:
|
|
||||||
void (*isrCallback)();
|
|
||||||
Timer(int timer_num) {
|
|
||||||
switch (timer_num)
|
|
||||||
{
|
|
||||||
case 0:
|
|
||||||
timer_resolution = 65536;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
this->timer_num = timer_num;
|
|
||||||
lastMicroseconds = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
void initialize() {
|
|
||||||
switch (timer_num)
|
|
||||||
{
|
|
||||||
case 0:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void setPeriod(unsigned long microseconds) {
|
|
||||||
if(microseconds == lastMicroseconds)
|
|
||||||
return;
|
|
||||||
lastMicroseconds = microseconds;
|
|
||||||
const unsigned long cycles = (F_CPU / 1000000) * microseconds;
|
|
||||||
|
|
||||||
switch(timer_num) {
|
|
||||||
case 0:
|
|
||||||
if (cycles < timer_resolution) {
|
|
||||||
clockSelectBits = 0x0;
|
|
||||||
pwmPeriod = cycles;
|
|
||||||
} else
|
|
||||||
if (cycles < timer_resolution * 2) {
|
|
||||||
clockSelectBits = 0x1;
|
|
||||||
pwmPeriod = cycles / 8;
|
|
||||||
} else
|
|
||||||
if (cycles < timer_resolution * 4) {
|
|
||||||
clockSelectBits = 0x2;
|
|
||||||
pwmPeriod = cycles / 32;
|
|
||||||
} else
|
|
||||||
if (cycles < timer_resolution * 8) {
|
|
||||||
clockSelectBits = 0x3;
|
|
||||||
pwmPeriod = cycles / 64;
|
|
||||||
} else
|
|
||||||
if (cycles < timer_resolution * 64) {
|
|
||||||
clockSelectBits = 0x5;
|
|
||||||
pwmPeriod = cycles / 128;
|
|
||||||
} else
|
|
||||||
if (cycles < timer_resolution * 256) {
|
|
||||||
clockSelectBits = 0x6;
|
|
||||||
pwmPeriod = cycles / 256;
|
|
||||||
} else
|
|
||||||
if (cycles < timer_resolution * 1024) {
|
|
||||||
clockSelectBits = 0x7;
|
|
||||||
pwmPeriod = cycles / 1024;
|
|
||||||
} else {
|
|
||||||
clockSelectBits = 0x7;
|
|
||||||
pwmPeriod = timer_resolution - 1;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (timer_num)
|
|
||||||
{
|
|
||||||
case 0:
|
|
||||||
TCA0.SINGLE.PER = pwmPeriod;
|
|
||||||
TCA0.SINGLE.CTRLA = clockSelectBits << 1;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void start() {
|
|
||||||
switch (timer_num)
|
|
||||||
{
|
|
||||||
case 0:
|
|
||||||
bitSet(TCA0.SINGLE.CTRLA, 0);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
void stop() {
|
|
||||||
switch (timer_num)
|
|
||||||
{
|
|
||||||
case 0:
|
|
||||||
bitClear(TCA0.SINGLE.CTRLA, 0);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void attachInterrupt(void (*isr)()) {
|
|
||||||
isrCallback = isr;
|
|
||||||
|
|
||||||
switch (timer_num)
|
|
||||||
{
|
|
||||||
case 0:
|
|
||||||
TCA0.SINGLE.INTCTRL = 0x1;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void detachInterrupt() {
|
|
||||||
switch (timer_num)
|
|
||||||
{
|
|
||||||
case 0:
|
|
||||||
TCA0.SINGLE.INTCTRL = 0x0;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
extern Timer TimerA;
|
|
||||||
|
|
||||||
|
|
||||||
#endif
|
|
26
DCC.cpp
26
DCC.cpp
@ -232,14 +232,14 @@ FSH* DCC::getMotorShieldName() {
|
|||||||
return shieldName;
|
return shieldName;
|
||||||
}
|
}
|
||||||
|
|
||||||
const ackOp PROGMEM WRITE_BIT0_PROG[] = {
|
const ackOp FLASH WRITE_BIT0_PROG[] = {
|
||||||
BASELINE,
|
BASELINE,
|
||||||
W0,WACK,
|
W0,WACK,
|
||||||
V0, WACK, // validate bit is 0
|
V0, WACK, // validate bit is 0
|
||||||
ITC1, // if acked, callback(1)
|
ITC1, // if acked, callback(1)
|
||||||
FAIL // callback (-1)
|
FAIL // callback (-1)
|
||||||
};
|
};
|
||||||
const ackOp PROGMEM WRITE_BIT1_PROG[] = {
|
const ackOp FLASH WRITE_BIT1_PROG[] = {
|
||||||
BASELINE,
|
BASELINE,
|
||||||
W1,WACK,
|
W1,WACK,
|
||||||
V1, WACK, // validate bit is 1
|
V1, WACK, // validate bit is 1
|
||||||
@ -247,7 +247,7 @@ const ackOp PROGMEM WRITE_BIT1_PROG[] = {
|
|||||||
FAIL // callback (-1)
|
FAIL // callback (-1)
|
||||||
};
|
};
|
||||||
|
|
||||||
const ackOp PROGMEM VERIFY_BIT0_PROG[] = {
|
const ackOp FLASH VERIFY_BIT0_PROG[] = {
|
||||||
BASELINE,
|
BASELINE,
|
||||||
V0, WACK, // validate bit is 0
|
V0, WACK, // validate bit is 0
|
||||||
ITC0, // if acked, callback(0)
|
ITC0, // if acked, callback(0)
|
||||||
@ -255,7 +255,7 @@ const ackOp PROGMEM VERIFY_BIT0_PROG[] = {
|
|||||||
ITC1,
|
ITC1,
|
||||||
FAIL // callback (-1)
|
FAIL // callback (-1)
|
||||||
};
|
};
|
||||||
const ackOp PROGMEM VERIFY_BIT1_PROG[] = {
|
const ackOp FLASH VERIFY_BIT1_PROG[] = {
|
||||||
BASELINE,
|
BASELINE,
|
||||||
V1, WACK, // validate bit is 1
|
V1, WACK, // validate bit is 1
|
||||||
ITC1, // if acked, callback(1)
|
ITC1, // if acked, callback(1)
|
||||||
@ -264,7 +264,7 @@ const ackOp PROGMEM VERIFY_BIT1_PROG[] = {
|
|||||||
FAIL // callback (-1)
|
FAIL // callback (-1)
|
||||||
};
|
};
|
||||||
|
|
||||||
const ackOp PROGMEM READ_BIT_PROG[] = {
|
const ackOp FLASH READ_BIT_PROG[] = {
|
||||||
BASELINE,
|
BASELINE,
|
||||||
V1, WACK, // validate bit is 1
|
V1, WACK, // validate bit is 1
|
||||||
ITC1, // if acked, callback(1)
|
ITC1, // if acked, callback(1)
|
||||||
@ -273,7 +273,7 @@ const ackOp PROGMEM READ_BIT_PROG[] = {
|
|||||||
FAIL // bit not readable
|
FAIL // bit not readable
|
||||||
};
|
};
|
||||||
|
|
||||||
const ackOp PROGMEM WRITE_BYTE_PROG[] = {
|
const ackOp FLASH WRITE_BYTE_PROG[] = {
|
||||||
BASELINE,
|
BASELINE,
|
||||||
WB,WACK, // Write
|
WB,WACK, // Write
|
||||||
VB,WACK, // validate byte
|
VB,WACK, // validate byte
|
||||||
@ -281,7 +281,7 @@ const ackOp PROGMEM WRITE_BYTE_PROG[] = {
|
|||||||
FAIL // callback (-1)
|
FAIL // callback (-1)
|
||||||
};
|
};
|
||||||
|
|
||||||
const ackOp PROGMEM VERIFY_BYTE_PROG[] = {
|
const ackOp FLASH VERIFY_BYTE_PROG[] = {
|
||||||
BASELINE,
|
BASELINE,
|
||||||
VB,WACK, // validate byte
|
VB,WACK, // validate byte
|
||||||
ITCB, // if ok callback value
|
ITCB, // if ok callback value
|
||||||
@ -306,7 +306,7 @@ const ackOp PROGMEM VERIFY_BYTE_PROG[] = {
|
|||||||
FAIL };
|
FAIL };
|
||||||
|
|
||||||
|
|
||||||
const ackOp PROGMEM READ_CV_PROG[] = {
|
const ackOp FLASH READ_CV_PROG[] = {
|
||||||
BASELINE,
|
BASELINE,
|
||||||
STARTMERGE, //clear bit and byte values ready for merge pass
|
STARTMERGE, //clear bit and byte values ready for merge pass
|
||||||
// each bit is validated against 0 and the result inverted in MERGE
|
// each bit is validated against 0 and the result inverted in MERGE
|
||||||
@ -329,7 +329,7 @@ const ackOp PROGMEM READ_CV_PROG[] = {
|
|||||||
FAIL }; // verification failed
|
FAIL }; // verification failed
|
||||||
|
|
||||||
|
|
||||||
const ackOp PROGMEM LOCO_ID_PROG[] = {
|
const ackOp FLASH LOCO_ID_PROG[] = {
|
||||||
BASELINE,
|
BASELINE,
|
||||||
SETCV,(ackOp)29,
|
SETCV,(ackOp)29,
|
||||||
SETBIT,(ackOp)5,
|
SETBIT,(ackOp)5,
|
||||||
@ -581,7 +581,7 @@ bool DCC::checkResets(bool blocking, uint8_t numResets) {
|
|||||||
|
|
||||||
void DCC::ackManagerLoop(bool blocking) {
|
void DCC::ackManagerLoop(bool blocking) {
|
||||||
while (ackManagerProg) {
|
while (ackManagerProg) {
|
||||||
byte opcode=pgm_read_byte_near(ackManagerProg);
|
byte opcode=GETFLASH(ackManagerProg);
|
||||||
|
|
||||||
// breaks from this switch will step to next prog entry
|
// breaks from this switch will step to next prog entry
|
||||||
// returns from this switch will stay on same entry
|
// returns from this switch will stay on same entry
|
||||||
@ -700,12 +700,12 @@ void DCC::ackManagerLoop(bool blocking) {
|
|||||||
|
|
||||||
case SETBIT:
|
case SETBIT:
|
||||||
ackManagerProg++;
|
ackManagerProg++;
|
||||||
ackManagerBitNum=pgm_read_byte_near(ackManagerProg);
|
ackManagerBitNum=GETFLASH(ackManagerProg);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case SETCV:
|
case SETCV:
|
||||||
ackManagerProg++;
|
ackManagerProg++;
|
||||||
ackManagerCv=pgm_read_byte_near(ackManagerProg);
|
ackManagerCv=GETFLASH(ackManagerProg);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case STASHLOCOID:
|
case STASHLOCOID:
|
||||||
@ -723,7 +723,7 @@ void DCC::ackManagerLoop(bool blocking) {
|
|||||||
// SKIP opcodes until SKIPTARGET found
|
// SKIP opcodes until SKIPTARGET found
|
||||||
while (opcode!=SKIPTARGET) {
|
while (opcode!=SKIPTARGET) {
|
||||||
ackManagerProg++;
|
ackManagerProg++;
|
||||||
opcode=pgm_read_byte_near(ackManagerProg);
|
opcode=GETFLASH(ackManagerProg);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case SKIPTARGET:
|
case SKIPTARGET:
|
||||||
|
@ -21,7 +21,14 @@
|
|||||||
|
|
||||||
#include "DCCWaveform.h"
|
#include "DCCWaveform.h"
|
||||||
#include "DIAG.h"
|
#include "DIAG.h"
|
||||||
|
|
||||||
|
#ifdef ARDUINO_ARCH_MEGAAVR
|
||||||
|
#include "EveryTimerB.h"
|
||||||
|
#define Timer1 TimerB2 // use TimerB2 as a drop in replacement for Timer1
|
||||||
|
#else // assume architecture supported by TimerOne ....
|
||||||
|
#include "TimerOne.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
const int NORMAL_SIGNAL_TIME=58; // this is the 58uS DCC 1-bit waveform half-cycle
|
const int NORMAL_SIGNAL_TIME=58; // this is the 58uS DCC 1-bit waveform half-cycle
|
||||||
const int SLOW_SIGNAL_TIME=NORMAL_SIGNAL_TIME*512;
|
const int SLOW_SIGNAL_TIME=NORMAL_SIGNAL_TIME*512;
|
||||||
|
|
||||||
@ -31,7 +38,6 @@ DCCWaveform DCCWaveform::progTrack(PREAMBLE_BITS_PROG, false);
|
|||||||
|
|
||||||
bool DCCWaveform::progTrackSyncMain=false;
|
bool DCCWaveform::progTrackSyncMain=false;
|
||||||
bool DCCWaveform::progTrackBoosted=false;
|
bool DCCWaveform::progTrackBoosted=false;
|
||||||
VirtualTimer * DCCWaveform::interruptTimer=NULL;
|
|
||||||
|
|
||||||
void DCCWaveform::begin(MotorDriver * mainDriver, MotorDriver * progDriver, byte timerNumber) {
|
void DCCWaveform::begin(MotorDriver * mainDriver, MotorDriver * progDriver, byte timerNumber) {
|
||||||
mainTrack.motorDriver=mainDriver;
|
mainTrack.motorDriver=mainDriver;
|
||||||
@ -39,26 +45,15 @@ void DCCWaveform::begin(MotorDriver * mainDriver, MotorDriver * progDriver, byte
|
|||||||
|
|
||||||
mainTrack.setPowerMode(POWERMODE::OFF);
|
mainTrack.setPowerMode(POWERMODE::OFF);
|
||||||
progTrack.setPowerMode(POWERMODE::OFF);
|
progTrack.setPowerMode(POWERMODE::OFF);
|
||||||
switch (timerNumber) {
|
|
||||||
case 1: interruptTimer= &TimerA; break;
|
Timer1.initialize();
|
||||||
#ifndef ARDUINO_ARCH_MEGAAVR
|
Timer1.setPeriod(NORMAL_SIGNAL_TIME); // this is the 58uS DCC 1-bit waveform half-cycle
|
||||||
case 2: interruptTimer= &TimerB; break;
|
Timer1.attachInterrupt(interruptHandler);
|
||||||
#ifndef ARDUINO_AVR_UNO
|
Timer1.start();
|
||||||
case 3: interruptTimer= &TimerC; break;
|
|
||||||
#endif
|
|
||||||
#endif
|
|
||||||
default:
|
|
||||||
DIAG(F("\n\n *** Invalid Timer number %d requested. Only 1..3 valid. DCC will not work.*** \n\n"), timerNumber);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
interruptTimer->initialize();
|
|
||||||
interruptTimer->setPeriod(NORMAL_SIGNAL_TIME); // this is the 58uS DCC 1-bit waveform half-cycle
|
|
||||||
interruptTimer->attachInterrupt(interruptHandler);
|
|
||||||
interruptTimer->start();
|
|
||||||
}
|
}
|
||||||
void DCCWaveform::setDiagnosticSlowWave(bool slow) {
|
void DCCWaveform::setDiagnosticSlowWave(bool slow) {
|
||||||
interruptTimer->setPeriod(slow? SLOW_SIGNAL_TIME : NORMAL_SIGNAL_TIME);
|
Timer1.setPeriod(slow? SLOW_SIGNAL_TIME : NORMAL_SIGNAL_TIME);
|
||||||
interruptTimer->start();
|
Timer1.start();
|
||||||
DIAG(F("\nDCC SLOW WAVE %S\n"),slow?F("SET. DO NOT ADD LOCOS TO TRACK"):F("RESET"));
|
DIAG(F("\nDCC SLOW WAVE %S\n"),slow?F("SET. DO NOT ADD LOCOS TO TRACK"):F("RESET"));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -114,9 +109,6 @@ POWERMODE DCCWaveform::getPowerMode() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void DCCWaveform::setPowerMode(POWERMODE mode) {
|
void DCCWaveform::setPowerMode(POWERMODE mode) {
|
||||||
|
|
||||||
// Prevent power switch on with no timer... Otheruise track will get full power DC and locos will run away.
|
|
||||||
if (!interruptTimer) return;
|
|
||||||
|
|
||||||
powerMode = mode;
|
powerMode = mode;
|
||||||
bool ison = (mode == POWERMODE::ON);
|
bool ison = (mode == POWERMODE::ON);
|
||||||
|
@ -20,7 +20,7 @@
|
|||||||
#ifndef DCCWaveform_h
|
#ifndef DCCWaveform_h
|
||||||
#define DCCWaveform_h
|
#define DCCWaveform_h
|
||||||
#include "MotorDriver.h"
|
#include "MotorDriver.h"
|
||||||
#include "ArduinoTimers.h"
|
|
||||||
|
|
||||||
// Wait times for power management. Unit: milliseconds
|
// Wait times for power management. Unit: milliseconds
|
||||||
const int POWER_SAMPLE_ON_WAIT = 100;
|
const int POWER_SAMPLE_ON_WAIT = 100;
|
||||||
@ -88,7 +88,6 @@ class DCCWaveform {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
static VirtualTimer * interruptTimer;
|
|
||||||
static void interruptHandler();
|
static void interruptHandler();
|
||||||
bool interrupt1();
|
bool interrupt1();
|
||||||
void interrupt2();
|
void interrupt2();
|
||||||
|
@ -24,7 +24,6 @@
|
|||||||
#define EthernetInterface_h
|
#define EthernetInterface_h
|
||||||
|
|
||||||
#include "DCCEXParser.h"
|
#include "DCCEXParser.h"
|
||||||
#include "MemStream.h"
|
|
||||||
#include <Arduino.h>
|
#include <Arduino.h>
|
||||||
#include <avr/pgmspace.h>
|
#include <avr/pgmspace.h>
|
||||||
#include <Ethernet.h>
|
#include <Ethernet.h>
|
||||||
|
14
EveryTimerB.cpp
Normal file
14
EveryTimerB.cpp
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
#ifdef ARDUINO_ARCH_MEGAAVR
|
||||||
|
#include "EveryTimerB.h"
|
||||||
|
|
||||||
|
void EveryTimerB::isrDefaultUnused(void) {}
|
||||||
|
|
||||||
|
// code timer B2. For B0 and B1 copy this code and change the '2' to '0' and '1'
|
||||||
|
EveryTimerB TimerB2;
|
||||||
|
ISR(TCB2_INT_vect)
|
||||||
|
{
|
||||||
|
TimerB2.next_tick();
|
||||||
|
TCB2.INTFLAGS = TCB_CAPT_bm;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // ARDUINO_ARCH_MEGAAVR
|
390
EveryTimerB.h
Normal file
390
EveryTimerB.h
Normal file
@ -0,0 +1,390 @@
|
|||||||
|
// EveryTimerB library.
|
||||||
|
// by Kees van der Oord Kees.van.der.Oord@inter.nl.net
|
||||||
|
|
||||||
|
// Timer library for the TCB timer of the AtMega4809 processor.
|
||||||
|
// tested on the Arduino Nano Every (AtMega4809) and the Arduino 1.8.12 IDE
|
||||||
|
// support for the Every is the 'Arduino MegaAVR' boards module (Tools | Board | Boards Manager)
|
||||||
|
|
||||||
|
// usage:
|
||||||
|
/*
|
||||||
|
#ifdef ARDUINO_ARCH_MEGAAVR
|
||||||
|
#include "EveryTimerB.h"
|
||||||
|
#define Timer1 TimerB2 // use TimerB2 as a drop in replacement for Timer1
|
||||||
|
#else // assume architecture supported by TimerOne library ....
|
||||||
|
#include "TimerOne.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// code below will now work both on the MegaAVR and AVR processors
|
||||||
|
|
||||||
|
void setup() {
|
||||||
|
Timer1.initialize();
|
||||||
|
Timer1.attachInterrupt(myisr);
|
||||||
|
Timer1.setPeriod(1000000UL); // like the TimerOne library this will start the timer as well
|
||||||
|
}
|
||||||
|
|
||||||
|
void myisr() {
|
||||||
|
// do something useful every second
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
// clock source options:
|
||||||
|
// The TCB clock source is specified in the initialize() function with default value EveryTimerB_CLOCMODE.
|
||||||
|
// define this macro before including this file to use a different default clock mode
|
||||||
|
// e.g.:
|
||||||
|
// #define EveryTimerB_CLOCMODE TCB_CLKSEL_CLKTCA_gc // 250 kHz ~ 4 us
|
||||||
|
// #define EveryTimerB_CLOCMODE TCB_CLKSEL_CLKDIV2_gc // 8 MHz ~ 0.125 us
|
||||||
|
// #define EveryTimerB_CLOCMODE TCB_CLKSEL_CLKDIV_gc // 16 MHz ~ 0.0625 us
|
||||||
|
|
||||||
|
// timer options
|
||||||
|
// The 4809 has one A timer (TCA) and four B timers (TCB).
|
||||||
|
// TCA and TCB3 are used by the arduino core to generate the clock used by millis() and micros().
|
||||||
|
// TCB0 generates the PWM timing for pin D6, TCB1 for pin D3.
|
||||||
|
// By default Timer Control B2 is defined as TimerB2 in the EveryTimerB library.
|
||||||
|
// If you would like to use the TCB0 and TCB1 as well you have to copy the code
|
||||||
|
// from the EveryTimerB.cpp into your product file and adapt for B0 and B1 timers.
|
||||||
|
//
|
||||||
|
// for information on the 4809 TCA and TCB timers:
|
||||||
|
// http://ww1.microchip.com/downloads/en/AppNotes/TB3217-Getting-Started-with-TCA-90003217A.pdf
|
||||||
|
// http://ww1.microchip.com/downloads/en/Appnotes/TB3214-Getting-Started-with-TCB-90003214A.pdf
|
||||||
|
// %LOCALAPPDATA%\Arduino15\packages\arduino\hardware\megaavr\1.8.5\cores\arduino\wiring.c
|
||||||
|
// %LOCALAPPDATA%\Arduino15\packages\arduino\hardware\megaavr\1.8.5\variants\nona4809\variant.c
|
||||||
|
// %LOCALAPPDATA%\Arduino15\packages\arduino\tools\avr-gcc\7.3.0-atmel3.6.1-arduino5\avr\include\avr\iom4809.h
|
||||||
|
|
||||||
|
// 20 MHz system clock
|
||||||
|
// to run the Every at 20 MHz, add the lines below to the nona4809 section of the boards.txt file
|
||||||
|
// in %LOCALAPPDATA%\Arduino15\packages\arduino\hardware\megaavr\1.8.5.
|
||||||
|
// they add the sub menu 'Tools | Clock' to choose between 16MHz and 20MHz.
|
||||||
|
/*
|
||||||
|
menu.clock=Clock
|
||||||
|
nona4809.menu.clock.16internal=16MHz
|
||||||
|
nona4809.menu.clock.16internal.build.f_cpu=16000000L
|
||||||
|
nona4809.menu.clock.16internal.bootloader.OSCCFG=0x01
|
||||||
|
nona4809.menu.clock.20internal=20MHz
|
||||||
|
nona4809.menu.clock.20internal.build.f_cpu=20000000L
|
||||||
|
nona4809.menu.clock.20internal.bootloader.OSCCFG=0x02
|
||||||
|
*/
|
||||||
|
// On 20Mhz, the 1.8.12 IDE MegaAvr core library implementation
|
||||||
|
// of the millis() and micros() functions is not accurate.
|
||||||
|
// the file "MegaAvr20MHz.h" implements a quick hack to correct for this
|
||||||
|
//
|
||||||
|
// to do:
|
||||||
|
// there is no range check on the 'period' arguments of setPeriod ...
|
||||||
|
// check if it is necessary to set the CNT register to 0 in start()
|
||||||
|
|
||||||
|
#ifndef EveryTimerB_h_
|
||||||
|
#define EveryTimerB_h_
|
||||||
|
#ifdef ARDUINO_ARCH_MEGAAVR
|
||||||
|
|
||||||
|
#ifndef EveryTimerB_CLOCMODE
|
||||||
|
#define EveryTimerB_CLOCMODE TCB_CLKSEL_CLKTCA_gc
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined(ARDUINO) && ARDUINO >= 100
|
||||||
|
#include "Arduino.h"
|
||||||
|
#else
|
||||||
|
#include "WProgram.h"
|
||||||
|
#endif
|
||||||
|
#include "MegaAvr20MHz.h"
|
||||||
|
#include "pins_arduino.h"
|
||||||
|
|
||||||
|
#define TCB_RESOLUTION 65536UL // TCB is 16 bit
|
||||||
|
// CLOCK F_CPU DIV TICK OVERFLOW OVERFLOW/s
|
||||||
|
// CLKTCA 16MHz 64 4000 ns 262144us 3.8 Hz
|
||||||
|
// CLKDIV2 16MHz 2 125 ns 8192us 122 Hz
|
||||||
|
// CLKDIV1 16MHz 1 62.5ns 4096us 244 Hz
|
||||||
|
// CLKTCA 20MHz 64 3200 ns 209716us 4.8 Hz
|
||||||
|
// CLKDIV2 20MHz 2 100 ns 6554us 153 Hz
|
||||||
|
// CLKDIV1 20MHz 1 50 ns 3277us 305 Hz
|
||||||
|
|
||||||
|
class EveryTimerB
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
// The AtMega Timer Control B clock sources selection:
|
||||||
|
// TCB_CLKSEL_CLKTCA_gc, // Timer Controller A, Arduino framework sets TCA to F_CPU/64 = 250kHz (4us) @ 16MHz or 312.5kHz (3.2us) @ 20MHz
|
||||||
|
// TCB_CLKSEL_CLKDIV2_gc, // CLK_PER/2 Peripheral Clock / 2: 8MHz @ 16Mhz or 10MHz @ 20MHz
|
||||||
|
// TCB_CLKSEL_CLKDIV1_gc // CLK_PER Peripheral Clock: 16MHz @ 16Mhz or 20MHz @ 20MHz
|
||||||
|
|
||||||
|
// intialize: sets the timer compare mode and the clock source
|
||||||
|
void initialize(TCB_t * timer_ = &TCB2, TCB_CLKSEL_t clockSource = EveryTimerB_CLOCMODE, unsigned long period = 1000000UL) __attribute__((always_inline)) {
|
||||||
|
timer = timer_;
|
||||||
|
#if defined(MegaAvr20MHzCorrected)
|
||||||
|
corrected20MHzInit(); // see commment in MegaAvr20MHz_h
|
||||||
|
#endif
|
||||||
|
stop();
|
||||||
|
timer->CTRLB = TCB_CNTMODE_INT_gc & ~TCB_CCMPEN_bm; // timer compare mode with output disabled
|
||||||
|
if(clockSource) setClockSource(clockSource);
|
||||||
|
if(period) setPeriod(period);
|
||||||
|
}
|
||||||
|
|
||||||
|
void setClockSource(TCB_CLKSEL_t clockSource) __attribute__((always_inline)) {
|
||||||
|
timer->CTRLA = clockSource; // this stops the clock as well ...
|
||||||
|
switch(clockSource) {
|
||||||
|
#if F_CPU == 20000000UL
|
||||||
|
case TCB_CLKSEL_CLKTCA_gc: maxTimeWithoutOverflow = 209715; break; // (TCB_RESOLUTION * 64) / 20
|
||||||
|
case TCB_CLKSEL_CLKDIV2_gc: maxTimeWithoutOverflow = 6553; break; // (TCB_RESOLUTION * 2) / 20
|
||||||
|
case TCB_CLKSEL_CLKDIV1_gc: maxTimeWithoutOverflow = 3276; break; // (TCB_RESOLUTION * 1) / 20
|
||||||
|
#else
|
||||||
|
case TCB_CLKSEL_CLKTCA_gc: maxTimeWithoutOverflow = 262144; break;
|
||||||
|
case TCB_CLKSEL_CLKDIV2_gc: maxTimeWithoutOverflow = 8192; break;
|
||||||
|
case TCB_CLKSEL_CLKDIV1_gc: maxTimeWithoutOverflow = 4096; break;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TCB_CLKSEL_t getClockSource() {
|
||||||
|
return (TCB_CLKSEL_t)(timer->CTRLA & (TCB_CLKSEL_CLKTCA_gc|TCB_CLKSEL_CLKDIV2_gc|TCB_CLKSEL_CLKDIV1_gc));
|
||||||
|
}
|
||||||
|
|
||||||
|
double getFrequencyOfClock(TCB_CLKSEL_t clock) {
|
||||||
|
switch(clock) {
|
||||||
|
// suppose nobody touched the default TCA configuration ...
|
||||||
|
case TCB_CLKSEL_CLKTCA_gc: return double(F_CPU/64); break;
|
||||||
|
case TCB_CLKSEL_CLKDIV2_gc: return double(F_CPU/2); break;
|
||||||
|
case TCB_CLKSEL_CLKDIV1_gc: return double(F_CPU); break;
|
||||||
|
}
|
||||||
|
return 0.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
double getClockFrequency() {
|
||||||
|
return getFrequencyOfClock(getClockSource());
|
||||||
|
}
|
||||||
|
|
||||||
|
// setPeriod: sets the period
|
||||||
|
// note: max and min values are different for each clock
|
||||||
|
// CLKTCA: conversion from us to ticks multiplies 'period' first with 10, so max value is MAX_ULONG/10 ~ 1 hr 11 minutes 34 seconds
|
||||||
|
// CLKDIV2: conversion from us to ticks is a *10 multiplication, so max value is 420M us (~ 7 minutes)
|
||||||
|
// CLKDIV1: conversion from us to ticks is a *20 multiplication, so max value is 210M us (~ 3.5 minutes)
|
||||||
|
void setPeriod(unsigned long period /* us */) __attribute__((always_inline)) {
|
||||||
|
timer->CTRLA &= ~TCB_ENABLE_bm;
|
||||||
|
// conversion from us to ticks depends on the clock
|
||||||
|
switch(timer->CTRLA & TCB_CLKSEL_gm)
|
||||||
|
{
|
||||||
|
case TCB_CLKSEL_CLKTCA_gc:
|
||||||
|
#if F_CPU == 20000000UL
|
||||||
|
period = (period * 10) / 32; // 20Mhz / 64x clock divider of TCA => 3.2 us / tick
|
||||||
|
#else // 16000000UL
|
||||||
|
period /= 4; // 16MHz / 64x clock divider of TCA => 4 us / tock
|
||||||
|
#endif
|
||||||
|
break;
|
||||||
|
case TCB_CLKSEL_CLKDIV2_gc:
|
||||||
|
#if F_CPU == 20000000UL
|
||||||
|
period *= 10; // 20MHz / 2x clock divider => 10 ticks / us
|
||||||
|
#else // 16000000UL
|
||||||
|
period *= 8; // 16MHz / 2x clock divider => 8 ticks / us
|
||||||
|
#endif
|
||||||
|
break;
|
||||||
|
case TCB_CLKSEL_CLKDIV1_gc:
|
||||||
|
#if F_CPU == 20000000UL
|
||||||
|
period *= 20; // 20MHz: 20 ticks / us
|
||||||
|
#else // 16000000UL
|
||||||
|
period *= 16; // 16MHz: 16 ticks / u3
|
||||||
|
#endif
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// to support longer than TCB_RESOLUTION ticks,
|
||||||
|
// this class supports first waiting for N 'overflowCounts'
|
||||||
|
// and next program the timer the remaining 'remainder' ticks:
|
||||||
|
countsPerOverflow = TCB_RESOLUTION;
|
||||||
|
overflowCounts = period / TCB_RESOLUTION;
|
||||||
|
remainder = period % TCB_RESOLUTION;
|
||||||
|
|
||||||
|
// the timer period is always one tick longer than programmed,
|
||||||
|
// so a remainder of 1 is not possible. reduce the length of
|
||||||
|
// the 'overflow' cycles to get a remainder that is not 1
|
||||||
|
if(overflowCounts) {
|
||||||
|
while(remainder == 1) {
|
||||||
|
--countsPerOverflow;
|
||||||
|
overflowCounts = period / countsPerOverflow;
|
||||||
|
remainder = period % countsPerOverflow;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// the timer period is always one tick longer than programmed
|
||||||
|
--countsPerOverflow;
|
||||||
|
if(remainder) --remainder;
|
||||||
|
|
||||||
|
// let's go
|
||||||
|
start();
|
||||||
|
}
|
||||||
|
|
||||||
|
void start() __attribute__((always_inline)) {
|
||||||
|
stop();
|
||||||
|
overflowCounter = overflowCounts;
|
||||||
|
timer->CCMP = overflowCounts ? countsPerOverflow : remainder;
|
||||||
|
timer->CNT = 0;
|
||||||
|
timer->CTRLA |= TCB_ENABLE_bm;
|
||||||
|
}
|
||||||
|
|
||||||
|
void stop() __attribute__((always_inline)) {
|
||||||
|
timer->CTRLA &= ~TCB_ENABLE_bm;
|
||||||
|
timer->INTFLAGS = TCB_CAPT_bm; // writing to the INTFLAGS register will clear the interrupt request flag
|
||||||
|
}
|
||||||
|
|
||||||
|
bool isEnabled(void) __attribute__((always_inline)) {
|
||||||
|
return timer->CTRLA & TCB_ENABLE_bm ? true : false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void enable(void) __attribute__((always_inline)) {
|
||||||
|
timer->CTRLA |= TCB_ENABLE_bm;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool disable(void) __attribute__((always_inline)) {
|
||||||
|
timer->CTRLA &= ~TCB_ENABLE_bm;
|
||||||
|
}
|
||||||
|
|
||||||
|
void attachInterrupt(void (*isr)()) __attribute__((always_inline)) {
|
||||||
|
isrCallback = isr;
|
||||||
|
timer->INTFLAGS = TCB_CAPT_bm; // clear interrupt request flag
|
||||||
|
timer->INTCTRL = TCB_CAPT_bm; // Enable the interrupt
|
||||||
|
}
|
||||||
|
|
||||||
|
void attachInterrupt(void (*isr)(), unsigned long microseconds) __attribute__((always_inline)) {
|
||||||
|
if(microseconds > 0) stop();
|
||||||
|
attachInterrupt(isr);
|
||||||
|
if (microseconds > 0) setPeriod(microseconds);
|
||||||
|
}
|
||||||
|
|
||||||
|
void detachInterrupt() __attribute__((always_inline)) {
|
||||||
|
timer->INTCTRL &= ~TCB_CAPT_bm; // Disable the interrupt
|
||||||
|
isrCallback = isrDefaultUnused;
|
||||||
|
}
|
||||||
|
|
||||||
|
void enableInterrupt() __attribute__((always_inline)) {
|
||||||
|
timer->INTFLAGS = TCB_CAPT_bm; // clear interrupt request flag
|
||||||
|
timer->INTCTRL = TCB_CAPT_bm; // Enable the interrupt
|
||||||
|
}
|
||||||
|
|
||||||
|
void disableInterrupt() __attribute__((always_inline)) {
|
||||||
|
timer->INTCTRL &= ~TCB_CAPT_bm; // Enable the interrupt
|
||||||
|
}
|
||||||
|
|
||||||
|
TCB_CNTMODE_enum getMode() __attribute__((always_inline)) {
|
||||||
|
return (TCB_CNTMODE_enum) (timer->CTRLB & 0x7);
|
||||||
|
}
|
||||||
|
|
||||||
|
void setMode(TCB_CNTMODE_enum mode) __attribute__((always_inline)) {
|
||||||
|
timer->CTRLB = (timer->CTRLB & ~0x7) | mode;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t isOutputEnabled() __attribute__((always_inline)) {
|
||||||
|
return timer->CTRLB & TCB_CCMPEN_bm;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t enableOutput() __attribute__((always_inline)) {
|
||||||
|
timer->CTRLB |= TCB_CCMPEN_bm;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t disableOutput() __attribute__((always_inline)) {
|
||||||
|
timer->CTRLB &= ~TCB_CCMPEN_bm;
|
||||||
|
}
|
||||||
|
|
||||||
|
// this will start PWM on pin 6 (TCB0) or pin 3 (TCB1)
|
||||||
|
// set the pins to output with setMode(x,OUTPUT) before calling this function
|
||||||
|
// period determines the clock ticks in one cycle:
|
||||||
|
// 16MHz clock: slowest frequency at 255 = 62 kHz.
|
||||||
|
// 8MHz clock: slowest frequency at 255 = 31 kHz.
|
||||||
|
// 256kHz clock: slowest frequency at 255 = 1 kHz.
|
||||||
|
// compare determines the duty cycle.
|
||||||
|
// with a period of 255, set the compare to 128 to get 50% duty cycle.
|
||||||
|
void setPwmMode(byte period, byte compare) {
|
||||||
|
disableInterrupt();
|
||||||
|
setMode(TCB_CNTMODE_PWM8_gc);
|
||||||
|
timer->CCMPL = period;
|
||||||
|
timer->CCMPH = compare;
|
||||||
|
enableOutput();
|
||||||
|
enable();
|
||||||
|
}
|
||||||
|
|
||||||
|
void getPwmMode(byte & period, byte & compare) {
|
||||||
|
period = timer->CCMPL;
|
||||||
|
compare = timer->CCMPH;
|
||||||
|
}
|
||||||
|
|
||||||
|
void setPwm(double frequency, double dutyCycle) {
|
||||||
|
TCB_CLKSEL_t clockSource = TCB_CLKSEL_CLKDIV1_gc;
|
||||||
|
double clockFrequency = getFrequencyOfClock(clockSource);
|
||||||
|
if(frequency < (clockFrequency/256.)) {
|
||||||
|
clockSource = TCB_CLKSEL_CLKDIV2_gc;
|
||||||
|
clockFrequency = getFrequencyOfClock(clockSource);
|
||||||
|
}
|
||||||
|
if(frequency < (clockFrequency/256.)) {
|
||||||
|
clockSource = TCB_CLKSEL_CLKTCA_gc;
|
||||||
|
clockFrequency = getFrequencyOfClock(clockSource);
|
||||||
|
}
|
||||||
|
double period = (clockFrequency / frequency) - 1.0 + 0.5;
|
||||||
|
if(period > 255.) period = 255.;
|
||||||
|
if(period < 0.) period = 0.0;
|
||||||
|
double compare = period * dutyCycle + 0.5;
|
||||||
|
if(compare < 0.0) compare = 0.0;
|
||||||
|
if(compare > period) compare = period;
|
||||||
|
setPwmMode((byte)(period),(byte)(compare));
|
||||||
|
}
|
||||||
|
|
||||||
|
void getPwm(double & frequency, double & dutyCycle) {
|
||||||
|
byte period, compare;
|
||||||
|
getPwmMode(period,compare);
|
||||||
|
frequency = getClockFrequency() / (((double)period) + 1);
|
||||||
|
dutyCycle = (double) compare / (((double)period) + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
void setTimerMode() {
|
||||||
|
disable();
|
||||||
|
disableOutput();
|
||||||
|
setMode(TCB_CNTMODE_INT_gc);
|
||||||
|
if(isrCallback != isrDefaultUnused) {
|
||||||
|
enableInterrupt();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TCB_t * getTimer() { return timer; }
|
||||||
|
long getOverflowCounts() { return overflowCounts; }
|
||||||
|
long getRemainder() { return remainder; }
|
||||||
|
long getOverflowCounter() { return overflowCounter; }
|
||||||
|
long getOverflowTime() { return maxTimeWithoutOverflow; }
|
||||||
|
|
||||||
|
//protected:
|
||||||
|
// the next_tick function is called by the interrupt service routine TCB0_INT_vect
|
||||||
|
//friend extern "C" void TCB0_INT_vect(void);
|
||||||
|
void next_tick() __attribute__((always_inline)) {
|
||||||
|
--overflowCounter;
|
||||||
|
if(overflowCounter > 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if(overflowCounter < 0) {
|
||||||
|
// finished waiting for remainder
|
||||||
|
if (overflowCounts) {
|
||||||
|
// restart with a max counter
|
||||||
|
overflowCounter = overflowCounts;
|
||||||
|
timer->CCMP = countsPerOverflow;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// overflowCounter == 0
|
||||||
|
// the overflow series has finished: to the remainder if any
|
||||||
|
if(remainder) {
|
||||||
|
timer->CCMP = remainder;
|
||||||
|
if(timer->CNT < remainder) return;
|
||||||
|
// remainder is so short: already passed !
|
||||||
|
timer->CCMP = countsPerOverflow;
|
||||||
|
}
|
||||||
|
// no remainder series: reset the overflow counter and do the callback
|
||||||
|
overflowCounter = overflowCounts;
|
||||||
|
}
|
||||||
|
(*isrCallback)();
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
TCB_t * timer = &TCB0;
|
||||||
|
long overflowCounts = 0;
|
||||||
|
long remainder = 10;
|
||||||
|
long overflowCounter = 0;
|
||||||
|
unsigned long countsPerOverflow = TCB_RESOLUTION - 1;
|
||||||
|
void (*isrCallback)();
|
||||||
|
static void isrDefaultUnused();
|
||||||
|
unsigned long maxTimeWithoutOverflow;
|
||||||
|
|
||||||
|
}; // EveryTimerB
|
||||||
|
|
||||||
|
extern EveryTimerB TimerB2;
|
||||||
|
|
||||||
|
#endif // ARDUINO_ARCH_MEGAAVR
|
||||||
|
#endif // EveryTimerB_h_
|
4
FSH.h
4
FSH.h
@ -3,7 +3,11 @@
|
|||||||
#include <Arduino.h>
|
#include <Arduino.h>
|
||||||
#if defined(ARDUINO_ARCH_MEGAAVR)
|
#if defined(ARDUINO_ARCH_MEGAAVR)
|
||||||
typedef char FSH;
|
typedef char FSH;
|
||||||
|
#define GETFLASH(addr) (*(const unsigned char *)(addr))
|
||||||
|
#define FLASH
|
||||||
#else
|
#else
|
||||||
typedef __FlashStringHelper FSH;
|
typedef __FlashStringHelper FSH;
|
||||||
|
#define GETFLASH(addr) pgm_read_byte_near(addr)
|
||||||
|
#define FLASH PROGMEM
|
||||||
#endif
|
#endif
|
||||||
#endif
|
#endif
|
||||||
|
@ -1,98 +0,0 @@
|
|||||||
/*
|
|
||||||
|
|
||||||
(c) 2015 Ingo Fischer
|
|
||||||
buffer serial device
|
|
||||||
based on Arduino SoftwareSerial
|
|
||||||
|
|
||||||
Constructor warning messages fixed by Chris Harlow.
|
|
||||||
|
|
||||||
This library is free software; you can redistribute it and/or
|
|
||||||
modify it under the terms of the GNU Lesser General Public
|
|
||||||
License as published by the Free Software Foundation; either
|
|
||||||
version 2.1 of the License, or (at your option) any later version.
|
|
||||||
|
|
||||||
This library is distributed in the hope that it will be useful,
|
|
||||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
||||||
Lesser General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU Lesser General Public
|
|
||||||
License along with this library; if not, write to the Free Software
|
|
||||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
|
||||||
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "MemStream.h"
|
|
||||||
|
|
||||||
MemStream::MemStream(uint8_t *buffer, const uint16_t len, uint16_t content_len, bool allowWrite)
|
|
||||||
:_buffer(buffer),_len(len), _buffer_overflow(false), _pos_read(0), _allowWrite(allowWrite)
|
|
||||||
{
|
|
||||||
if (content_len==0) memset(_buffer, 0, _len);
|
|
||||||
_pos_write=(content_len>len)? len: content_len;
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t MemStream::write(uint8_t byte) {
|
|
||||||
if (! _allowWrite) return -1;
|
|
||||||
if (_pos_write >= _len) {
|
|
||||||
_buffer_overflow = true;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
_buffer[_pos_write] = byte;
|
|
||||||
++_pos_write;
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
void MemStream::flush() {
|
|
||||||
memset(_buffer, 0, _len);
|
|
||||||
_pos_write = 0;
|
|
||||||
_pos_read = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
int MemStream::read() {
|
|
||||||
if (_pos_read >= _len) {
|
|
||||||
_buffer_overflow = true;
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
if (_pos_read >= _pos_write) {
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
return _buffer[_pos_read++];
|
|
||||||
}
|
|
||||||
|
|
||||||
int MemStream::peek() {
|
|
||||||
if (_pos_read >= _len) {
|
|
||||||
_buffer_overflow = true;
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
if (_pos_read >= _pos_write) {
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
return _buffer[_pos_read+1];
|
|
||||||
}
|
|
||||||
|
|
||||||
int MemStream::available() {
|
|
||||||
int ret=_pos_write-_pos_read;
|
|
||||||
if (ret<0) ret=0;
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
void MemStream::setBufferContent(uint8_t *buffer, uint16_t content_len) {
|
|
||||||
memset(_buffer, 0, _len);
|
|
||||||
memcpy(_buffer, buffer, content_len);
|
|
||||||
_buffer_overflow=false;
|
|
||||||
_pos_write=content_len;
|
|
||||||
_pos_read=0;
|
|
||||||
}
|
|
||||||
|
|
||||||
void MemStream::setBufferContentFromProgmem(uint8_t *buffer, uint16_t content_len) {
|
|
||||||
memset(_buffer, 0, _len);
|
|
||||||
memcpy_P(_buffer, buffer, content_len);
|
|
||||||
_buffer_overflow=false;
|
|
||||||
_pos_write=content_len;
|
|
||||||
_pos_read=0;
|
|
||||||
}
|
|
||||||
|
|
||||||
void MemStream::setBufferContentPosition(uint16_t read_pos, uint16_t write_pos) {
|
|
||||||
_pos_write=write_pos;
|
|
||||||
_pos_read=read_pos;
|
|
||||||
}
|
|
78
MemStream.h
78
MemStream.h
@ -1,78 +0,0 @@
|
|||||||
/*
|
|
||||||
|
|
||||||
(c) 2015 Ingo FIscher
|
|
||||||
buffer serial device
|
|
||||||
based on Arduino SoftwareSerial
|
|
||||||
|
|
||||||
This library is free software; you can redistribute it and/or
|
|
||||||
modify it under the terms of the GNU Lesser General Public
|
|
||||||
License as published by the Free Software Foundation; either
|
|
||||||
version 2.1 of the License, or (at your option) any later version.
|
|
||||||
|
|
||||||
This library is distributed in the hope that it will be useful,
|
|
||||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
||||||
Lesser General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU Lesser General Public
|
|
||||||
License along with this library; if not, write to the Free Software
|
|
||||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
|
||||||
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef MemStream_h
|
|
||||||
#define MemStream_h
|
|
||||||
|
|
||||||
#include <inttypes.h>
|
|
||||||
#if defined(ARDUINO_ARCH_MEGAAVR)
|
|
||||||
#include <Arduino.h>
|
|
||||||
#else
|
|
||||||
#include <Stream.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include <avr/pgmspace.h>
|
|
||||||
|
|
||||||
class MemStream : public Stream
|
|
||||||
{
|
|
||||||
private:
|
|
||||||
uint8_t *_buffer;
|
|
||||||
const uint16_t _len;
|
|
||||||
bool _buffer_overflow;
|
|
||||||
uint16_t _pos_read;
|
|
||||||
uint16_t _pos_write;
|
|
||||||
bool _allowWrite;
|
|
||||||
|
|
||||||
public:
|
|
||||||
// public methods
|
|
||||||
MemStream(uint8_t *buffer, const uint16_t len, uint16_t content_len = 0, bool allowWrite = true);
|
|
||||||
~MemStream() {}
|
|
||||||
|
|
||||||
operator const uint8_t *() const { return _buffer; }
|
|
||||||
operator const char *() const { return (const char *)_buffer; }
|
|
||||||
|
|
||||||
uint16_t current_length() const { return _pos_write; }
|
|
||||||
|
|
||||||
bool listen() { return true; }
|
|
||||||
void end() {}
|
|
||||||
bool isListening() { return true; }
|
|
||||||
bool overflow()
|
|
||||||
{
|
|
||||||
bool ret = _buffer_overflow;
|
|
||||||
_buffer_overflow = false;
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
int peek();
|
|
||||||
|
|
||||||
virtual size_t write(uint8_t byte);
|
|
||||||
virtual int read();
|
|
||||||
virtual int available();
|
|
||||||
virtual void flush();
|
|
||||||
|
|
||||||
void setBufferContent(uint8_t *buffer, uint16_t content_len);
|
|
||||||
void setBufferContentFromProgmem(uint8_t *buffer, uint16_t content_len);
|
|
||||||
void setBufferContentPosition(uint16_t read_pos, uint16_t write_pos);
|
|
||||||
|
|
||||||
using Print::write;
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif
|
|
@ -28,7 +28,6 @@
|
|||||||
Print * StringFormatter::diagSerial= &Serial;
|
Print * StringFormatter::diagSerial= &Serial;
|
||||||
#elif defined(ARDUINO_ARCH_MEGAAVR)
|
#elif defined(ARDUINO_ARCH_MEGAAVR)
|
||||||
Print * StringFormatter::diagSerial=&Serial;
|
Print * StringFormatter::diagSerial=&Serial;
|
||||||
#define FSH char
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#include "LCDDisplay.h"
|
#include "LCDDisplay.h"
|
||||||
@ -45,6 +44,7 @@ void StringFormatter::diag( const FSH* input...) {
|
|||||||
va_list args;
|
va_list args;
|
||||||
va_start(args, input);
|
va_start(args, input);
|
||||||
send2(diagSerial,input,args);
|
send2(diagSerial,input,args);
|
||||||
|
diagSerial->flush();
|
||||||
}
|
}
|
||||||
|
|
||||||
void StringFormatter::lcd(byte row, const FSH* input...) {
|
void StringFormatter::lcd(byte row, const FSH* input...) {
|
||||||
@ -80,7 +80,7 @@ void StringFormatter::send2(Print * stream,const FSH* format, va_list args) {
|
|||||||
|
|
||||||
char* flash=(char*)format;
|
char* flash=(char*)format;
|
||||||
for(int i=0; ; ++i) {
|
for(int i=0; ; ++i) {
|
||||||
char c=pgm_read_byte_near(flash+i);
|
char c=GETFLASH(flash+i);
|
||||||
if (c=='\0') return;
|
if (c=='\0') return;
|
||||||
if(c!='%') { stream->print(c); continue; }
|
if(c!='%') { stream->print(c); continue; }
|
||||||
|
|
||||||
@ -91,7 +91,7 @@ void StringFormatter::send2(Print * stream,const FSH* format, va_list args) {
|
|||||||
|
|
||||||
formatContinues=false;
|
formatContinues=false;
|
||||||
i++;
|
i++;
|
||||||
c=pgm_read_byte_near(flash+i);
|
c=GETFLASH(flash+i);
|
||||||
switch(c) {
|
switch(c) {
|
||||||
case '%': stream->print('%'); break;
|
case '%': stream->print('%'); break;
|
||||||
case 'c': stream->print((char) va_arg(args, int)); break;
|
case 'c': stream->print((char) va_arg(args, int)); break;
|
||||||
@ -143,7 +143,7 @@ void StringFormatter::printEscapes(Print * stream, const FSH * input) {
|
|||||||
if (!stream) return;
|
if (!stream) return;
|
||||||
char* flash=(char*)input;
|
char* flash=(char*)input;
|
||||||
for(int i=0; ; ++i) {
|
for(int i=0; ; ++i) {
|
||||||
char c=pgm_read_byte_near(flash+i);
|
char c=GETFLASH(flash+i);
|
||||||
printEscape(stream,c);
|
printEscape(stream,c);
|
||||||
if (c=='\0') return;
|
if (c=='\0') return;
|
||||||
}
|
}
|
||||||
|
64
Timer.cpp
64
Timer.cpp
@ -1,64 +0,0 @@
|
|||||||
// This file is copied from https://github.com/davidcutting42/ArduinoTimers
|
|
||||||
// All Credit to David Cutting
|
|
||||||
|
|
||||||
#include <Arduino.h>
|
|
||||||
|
|
||||||
#if defined(ARDUINO_AVR_MEGA) || defined(ARDUINO_AVR_MEGA2560)
|
|
||||||
|
|
||||||
#include "ATMEGA2560/Timer.h"
|
|
||||||
|
|
||||||
Timer TimerA(1);
|
|
||||||
Timer TimerB(3);
|
|
||||||
Timer TimerC(4);
|
|
||||||
Timer TimerD(5);
|
|
||||||
|
|
||||||
ISR(TIMER1_OVF_vect)
|
|
||||||
{
|
|
||||||
TimerA.isrCallback();
|
|
||||||
}
|
|
||||||
|
|
||||||
ISR(TIMER3_OVF_vect)
|
|
||||||
{
|
|
||||||
TimerB.isrCallback();
|
|
||||||
}
|
|
||||||
|
|
||||||
ISR(TIMER4_OVF_vect)
|
|
||||||
{
|
|
||||||
TimerC.isrCallback();
|
|
||||||
}
|
|
||||||
|
|
||||||
ISR(TIMER5_OVF_vect)
|
|
||||||
{
|
|
||||||
TimerD.isrCallback();
|
|
||||||
}
|
|
||||||
|
|
||||||
#elif defined(ARDUINO_ARCH_MEGAAVR) // Todo: add other 328 boards for compatibility
|
|
||||||
|
|
||||||
#include "ATMEGA4809/Timer.h"
|
|
||||||
|
|
||||||
Timer TimerA(1);
|
|
||||||
|
|
||||||
|
|
||||||
ISR(TIMER1_OVF_vect)
|
|
||||||
{
|
|
||||||
TimerA.isrCallback();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
#elif defined(ARDUINO_AVR_UNO) // Todo: add other 328 boards for compatibility
|
|
||||||
|
|
||||||
#include "ATMEGA328/Timer.h"
|
|
||||||
|
|
||||||
Timer TimerA(1);
|
|
||||||
Timer TimerB(2);
|
|
||||||
|
|
||||||
ISR(TIMER1_OVF_vect)
|
|
||||||
{
|
|
||||||
TimerA.isrCallback();
|
|
||||||
}
|
|
||||||
|
|
||||||
ISR(TIMER2_OVF_vect)
|
|
||||||
{
|
|
||||||
TimerB.isrCallback();
|
|
||||||
}
|
|
||||||
#endif
|
|
@ -1,21 +0,0 @@
|
|||||||
// This file is copied from https://github.com/davidcutting42/ArduinoTimers
|
|
||||||
// All Credit to David Cutting
|
|
||||||
|
|
||||||
#ifndef VirtualTimer_h
|
|
||||||
#define VirtualTimer_h
|
|
||||||
|
|
||||||
class VirtualTimer
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
virtual void initialize() = 0;
|
|
||||||
virtual void setPeriod(unsigned long microseconds) = 0;
|
|
||||||
virtual void start() = 0;
|
|
||||||
virtual void stop() = 0;
|
|
||||||
|
|
||||||
virtual void attachInterrupt(void (*isr)()) = 0;
|
|
||||||
virtual void detachInterrupt() = 0;
|
|
||||||
private:
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif
|
|
@ -25,11 +25,11 @@
|
|||||||
|
|
||||||
#include "WifiInboundHandler.h"
|
#include "WifiInboundHandler.h"
|
||||||
|
|
||||||
const char PROGMEM READY_SEARCH[] = "\r\nready\r\n";
|
const char FLASH READY_SEARCH[] = "\r\nready\r\n";
|
||||||
const char PROGMEM OK_SEARCH[] = "\r\nOK\r\n";
|
const char FLASH OK_SEARCH[] = "\r\nOK\r\n";
|
||||||
const char PROGMEM END_DETAIL_SEARCH[] = "@ 1000";
|
const char FLASH END_DETAIL_SEARCH[] = "@ 1000";
|
||||||
const char PROGMEM SEND_OK_SEARCH[] = "\r\nSEND OK\r\n";
|
const char FLASH SEND_OK_SEARCH[] = "\r\nSEND OK\r\n";
|
||||||
const char PROGMEM IPD_SEARCH[] = "+IPD";
|
const char FLASH IPD_SEARCH[] = "+IPD";
|
||||||
const unsigned long LOOP_TIMEOUT = 2000;
|
const unsigned long LOOP_TIMEOUT = 2000;
|
||||||
bool WifiInterface::connected = false;
|
bool WifiInterface::connected = false;
|
||||||
Stream * WifiInterface::wifiStream;
|
Stream * WifiInterface::wifiStream;
|
||||||
@ -317,10 +317,10 @@ bool WifiInterface::checkForOK( const unsigned int timeout, const char * waitfor
|
|||||||
if (escapeEcho) StringFormatter::printEscape( ch); /// THIS IS A DIAG IN DISGUISE
|
if (escapeEcho) StringFormatter::printEscape( ch); /// THIS IS A DIAG IN DISGUISE
|
||||||
else DIAG(F("%c"), ch);
|
else DIAG(F("%c"), ch);
|
||||||
}
|
}
|
||||||
if (ch != pgm_read_byte_near(locator)) locator = waitfor;
|
if (ch != GETFLASH(locator)) locator = waitfor;
|
||||||
if (ch == pgm_read_byte_near(locator)) {
|
if (ch == GETFLASH(locator)) {
|
||||||
locator++;
|
locator++;
|
||||||
if (!pgm_read_byte_near(locator)) {
|
if (!GETFLASH(locator)) {
|
||||||
DIAG(F("\nFound in %dms"), millis() - startTime);
|
DIAG(F("\nFound in %dms"), millis() - startTime);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user