From cb0d2bcdc5734d3e01caa0eee5f9e52ec74ed940 Mon Sep 17 00:00:00 2001 From: Asbelos Date: Sun, 3 Jan 2021 09:11:11 +0000 Subject: [PATCH] Cleanup --- ATMEGA2560/Timer.h | 194 -------- ATMEGA328/Timer.h | 208 --------- ATMEGA4809/EveryTimerB.h | 474 -------------------- ATMEGA4809/Timer.h | 132 ------ DCC.cpp | 26 +- DCCWaveform.cpp | 38 +- DCCWaveform.h | 3 +- EthernetInterface.h | 1 - EveryTimerB.cpp | 14 + EveryTimerB.h | 390 ++++++++++++++++ FSH.h | 4 + ATMEGA4809/MegaAvr20Mhz.h => MegaAvr20Mhz.h | 0 MemStream.cpp | 98 ---- MemStream.h | 78 ---- StringFormatter.cpp | 8 +- Timer.cpp | 64 --- VirtualTimer.h | 21 - WifiInterface.cpp | 16 +- version.h | 2 +- 19 files changed, 450 insertions(+), 1321 deletions(-) delete mode 100644 ATMEGA2560/Timer.h delete mode 100644 ATMEGA328/Timer.h delete mode 100644 ATMEGA4809/EveryTimerB.h delete mode 100644 ATMEGA4809/Timer.h create mode 100644 EveryTimerB.cpp create mode 100644 EveryTimerB.h rename ATMEGA4809/MegaAvr20Mhz.h => MegaAvr20Mhz.h (100%) delete mode 100644 MemStream.cpp delete mode 100644 MemStream.h delete mode 100644 Timer.cpp delete mode 100644 VirtualTimer.h diff --git a/ATMEGA2560/Timer.h b/ATMEGA2560/Timer.h deleted file mode 100644 index 2568c9e..0000000 --- a/ATMEGA2560/Timer.h +++ /dev/null @@ -1,194 +0,0 @@ -#ifndef ATMEGA2560Timer_h -#define ATMEGA2560Timer_h - -#include "../VirtualTimer.h" -#include - -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 \ No newline at end of file diff --git a/ATMEGA328/Timer.h b/ATMEGA328/Timer.h deleted file mode 100644 index 6204953..0000000 --- a/ATMEGA328/Timer.h +++ /dev/null @@ -1,208 +0,0 @@ -#ifndef ATMEGA328Timer_h -#define ATMEGA328Timer_h - -#include "../VirtualTimer.h" -#include - -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 \ No newline at end of file diff --git a/ATMEGA4809/EveryTimerB.h b/ATMEGA4809/EveryTimerB.h deleted file mode 100644 index 6d9b481..0000000 --- a/ATMEGA4809/EveryTimerB.h +++ /dev/null @@ -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_ diff --git a/ATMEGA4809/Timer.h b/ATMEGA4809/Timer.h deleted file mode 100644 index dd44933..0000000 --- a/ATMEGA4809/Timer.h +++ /dev/null @@ -1,132 +0,0 @@ -#ifndef ATMEGA4809Timer_h -#define ATMEGA4809imer_h - -#include "../VirtualTimer.h" -#include - -// 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 \ No newline at end of file diff --git a/DCC.cpp b/DCC.cpp index 6eca2e2..0d1affe 100644 --- a/DCC.cpp +++ b/DCC.cpp @@ -232,14 +232,14 @@ FSH* DCC::getMotorShieldName() { return shieldName; } -const ackOp PROGMEM WRITE_BIT0_PROG[] = { +const ackOp FLASH WRITE_BIT0_PROG[] = { BASELINE, W0,WACK, V0, WACK, // validate bit is 0 ITC1, // if acked, callback(1) FAIL // callback (-1) }; -const ackOp PROGMEM WRITE_BIT1_PROG[] = { +const ackOp FLASH WRITE_BIT1_PROG[] = { BASELINE, W1,WACK, V1, WACK, // validate bit is 1 @@ -247,7 +247,7 @@ const ackOp PROGMEM WRITE_BIT1_PROG[] = { FAIL // callback (-1) }; -const ackOp PROGMEM VERIFY_BIT0_PROG[] = { +const ackOp FLASH VERIFY_BIT0_PROG[] = { BASELINE, V0, WACK, // validate bit is 0 ITC0, // if acked, callback(0) @@ -255,7 +255,7 @@ const ackOp PROGMEM VERIFY_BIT0_PROG[] = { ITC1, FAIL // callback (-1) }; -const ackOp PROGMEM VERIFY_BIT1_PROG[] = { +const ackOp FLASH VERIFY_BIT1_PROG[] = { BASELINE, V1, WACK, // validate bit is 1 ITC1, // if acked, callback(1) @@ -264,7 +264,7 @@ const ackOp PROGMEM VERIFY_BIT1_PROG[] = { FAIL // callback (-1) }; -const ackOp PROGMEM READ_BIT_PROG[] = { +const ackOp FLASH READ_BIT_PROG[] = { BASELINE, V1, WACK, // validate bit is 1 ITC1, // if acked, callback(1) @@ -273,7 +273,7 @@ const ackOp PROGMEM READ_BIT_PROG[] = { FAIL // bit not readable }; -const ackOp PROGMEM WRITE_BYTE_PROG[] = { +const ackOp FLASH WRITE_BYTE_PROG[] = { BASELINE, WB,WACK, // Write VB,WACK, // validate byte @@ -281,7 +281,7 @@ const ackOp PROGMEM WRITE_BYTE_PROG[] = { FAIL // callback (-1) }; -const ackOp PROGMEM VERIFY_BYTE_PROG[] = { +const ackOp FLASH VERIFY_BYTE_PROG[] = { BASELINE, VB,WACK, // validate byte ITCB, // if ok callback value @@ -306,7 +306,7 @@ const ackOp PROGMEM VERIFY_BYTE_PROG[] = { FAIL }; -const ackOp PROGMEM READ_CV_PROG[] = { +const ackOp FLASH READ_CV_PROG[] = { BASELINE, STARTMERGE, //clear bit and byte values ready for merge pass // 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 -const ackOp PROGMEM LOCO_ID_PROG[] = { +const ackOp FLASH LOCO_ID_PROG[] = { BASELINE, SETCV,(ackOp)29, SETBIT,(ackOp)5, @@ -581,7 +581,7 @@ bool DCC::checkResets(bool blocking, uint8_t numResets) { void DCC::ackManagerLoop(bool blocking) { while (ackManagerProg) { - byte opcode=pgm_read_byte_near(ackManagerProg); + byte opcode=GETFLASH(ackManagerProg); // breaks from this switch will step to next prog entry // returns from this switch will stay on same entry @@ -700,12 +700,12 @@ void DCC::ackManagerLoop(bool blocking) { case SETBIT: ackManagerProg++; - ackManagerBitNum=pgm_read_byte_near(ackManagerProg); + ackManagerBitNum=GETFLASH(ackManagerProg); break; case SETCV: ackManagerProg++; - ackManagerCv=pgm_read_byte_near(ackManagerProg); + ackManagerCv=GETFLASH(ackManagerProg); break; case STASHLOCOID: @@ -723,7 +723,7 @@ void DCC::ackManagerLoop(bool blocking) { // SKIP opcodes until SKIPTARGET found while (opcode!=SKIPTARGET) { ackManagerProg++; - opcode=pgm_read_byte_near(ackManagerProg); + opcode=GETFLASH(ackManagerProg); } break; case SKIPTARGET: diff --git a/DCCWaveform.cpp b/DCCWaveform.cpp index b33db9d..08f3c31 100644 --- a/DCCWaveform.cpp +++ b/DCCWaveform.cpp @@ -21,7 +21,14 @@ #include "DCCWaveform.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 SLOW_SIGNAL_TIME=NORMAL_SIGNAL_TIME*512; @@ -31,7 +38,6 @@ DCCWaveform DCCWaveform::progTrack(PREAMBLE_BITS_PROG, false); bool DCCWaveform::progTrackSyncMain=false; bool DCCWaveform::progTrackBoosted=false; -VirtualTimer * DCCWaveform::interruptTimer=NULL; void DCCWaveform::begin(MotorDriver * mainDriver, MotorDriver * progDriver, byte timerNumber) { mainTrack.motorDriver=mainDriver; @@ -39,26 +45,15 @@ void DCCWaveform::begin(MotorDriver * mainDriver, MotorDriver * progDriver, byte mainTrack.setPowerMode(POWERMODE::OFF); progTrack.setPowerMode(POWERMODE::OFF); - switch (timerNumber) { - case 1: interruptTimer= &TimerA; break; -#ifndef ARDUINO_ARCH_MEGAAVR - case 2: interruptTimer= &TimerB; break; -#ifndef ARDUINO_AVR_UNO - 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(); + + Timer1.initialize(); + Timer1.setPeriod(NORMAL_SIGNAL_TIME); // this is the 58uS DCC 1-bit waveform half-cycle + Timer1.attachInterrupt(interruptHandler); + Timer1.start(); } void DCCWaveform::setDiagnosticSlowWave(bool slow) { - interruptTimer->setPeriod(slow? SLOW_SIGNAL_TIME : NORMAL_SIGNAL_TIME); - interruptTimer->start(); + Timer1.setPeriod(slow? SLOW_SIGNAL_TIME : NORMAL_SIGNAL_TIME); + Timer1.start(); 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) { - - // Prevent power switch on with no timer... Otheruise track will get full power DC and locos will run away. - if (!interruptTimer) return; powerMode = mode; bool ison = (mode == POWERMODE::ON); diff --git a/DCCWaveform.h b/DCCWaveform.h index b51e79f..cc66607 100644 --- a/DCCWaveform.h +++ b/DCCWaveform.h @@ -20,7 +20,7 @@ #ifndef DCCWaveform_h #define DCCWaveform_h #include "MotorDriver.h" -#include "ArduinoTimers.h" + // Wait times for power management. Unit: milliseconds const int POWER_SAMPLE_ON_WAIT = 100; @@ -88,7 +88,6 @@ class DCCWaveform { } private: - static VirtualTimer * interruptTimer; static void interruptHandler(); bool interrupt1(); void interrupt2(); diff --git a/EthernetInterface.h b/EthernetInterface.h index 7b4c2f8..23cc6cd 100644 --- a/EthernetInterface.h +++ b/EthernetInterface.h @@ -24,7 +24,6 @@ #define EthernetInterface_h #include "DCCEXParser.h" -#include "MemStream.h" #include #include #include diff --git a/EveryTimerB.cpp b/EveryTimerB.cpp new file mode 100644 index 0000000..388d090 --- /dev/null +++ b/EveryTimerB.cpp @@ -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 diff --git a/EveryTimerB.h b/EveryTimerB.h new file mode 100644 index 0000000..4c371a9 --- /dev/null +++ b/EveryTimerB.h @@ -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_ diff --git a/FSH.h b/FSH.h index 04f2c9b..089216a 100644 --- a/FSH.h +++ b/FSH.h @@ -3,7 +3,11 @@ #include #if defined(ARDUINO_ARCH_MEGAAVR) typedef char FSH; +#define GETFLASH(addr) (*(const unsigned char *)(addr)) +#define FLASH #else typedef __FlashStringHelper FSH; +#define GETFLASH(addr) pgm_read_byte_near(addr) +#define FLASH PROGMEM #endif #endif diff --git a/ATMEGA4809/MegaAvr20Mhz.h b/MegaAvr20Mhz.h similarity index 100% rename from ATMEGA4809/MegaAvr20Mhz.h rename to MegaAvr20Mhz.h diff --git a/MemStream.cpp b/MemStream.cpp deleted file mode 100644 index bbb7695..0000000 --- a/MemStream.cpp +++ /dev/null @@ -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; -} diff --git a/MemStream.h b/MemStream.h deleted file mode 100644 index 61ef6bb..0000000 --- a/MemStream.h +++ /dev/null @@ -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 -#if defined(ARDUINO_ARCH_MEGAAVR) -#include -#else -#include -#endif - -#include - -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 diff --git a/StringFormatter.cpp b/StringFormatter.cpp index 32e1ee4..e1d7ec3 100644 --- a/StringFormatter.cpp +++ b/StringFormatter.cpp @@ -28,7 +28,6 @@ Print * StringFormatter::diagSerial= &Serial; #elif defined(ARDUINO_ARCH_MEGAAVR) Print * StringFormatter::diagSerial=&Serial; - #define FSH char #endif #include "LCDDisplay.h" @@ -45,6 +44,7 @@ void StringFormatter::diag( const FSH* input...) { va_list args; va_start(args, input); send2(diagSerial,input,args); + diagSerial->flush(); } 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; for(int i=0; ; ++i) { - char c=pgm_read_byte_near(flash+i); + char c=GETFLASH(flash+i); if (c=='\0') return; if(c!='%') { stream->print(c); continue; } @@ -91,7 +91,7 @@ void StringFormatter::send2(Print * stream,const FSH* format, va_list args) { formatContinues=false; i++; - c=pgm_read_byte_near(flash+i); + c=GETFLASH(flash+i); switch(c) { case '%': stream->print('%'); 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; char* flash=(char*)input; for(int i=0; ; ++i) { - char c=pgm_read_byte_near(flash+i); + char c=GETFLASH(flash+i); printEscape(stream,c); if (c=='\0') return; } diff --git a/Timer.cpp b/Timer.cpp deleted file mode 100644 index 2d23637..0000000 --- a/Timer.cpp +++ /dev/null @@ -1,64 +0,0 @@ -// This file is copied from https://github.com/davidcutting42/ArduinoTimers -// All Credit to David Cutting - -#include - -#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 \ No newline at end of file diff --git a/VirtualTimer.h b/VirtualTimer.h deleted file mode 100644 index 5e3832f..0000000 --- a/VirtualTimer.h +++ /dev/null @@ -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 diff --git a/WifiInterface.cpp b/WifiInterface.cpp index 6f67134..eb96885 100644 --- a/WifiInterface.cpp +++ b/WifiInterface.cpp @@ -25,11 +25,11 @@ #include "WifiInboundHandler.h" -const char PROGMEM READY_SEARCH[] = "\r\nready\r\n"; -const char PROGMEM OK_SEARCH[] = "\r\nOK\r\n"; -const char PROGMEM END_DETAIL_SEARCH[] = "@ 1000"; -const char PROGMEM SEND_OK_SEARCH[] = "\r\nSEND OK\r\n"; -const char PROGMEM IPD_SEARCH[] = "+IPD"; +const char FLASH READY_SEARCH[] = "\r\nready\r\n"; +const char FLASH OK_SEARCH[] = "\r\nOK\r\n"; +const char FLASH END_DETAIL_SEARCH[] = "@ 1000"; +const char FLASH SEND_OK_SEARCH[] = "\r\nSEND OK\r\n"; +const char FLASH IPD_SEARCH[] = "+IPD"; const unsigned long LOOP_TIMEOUT = 2000; bool WifiInterface::connected = false; 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 else DIAG(F("%c"), ch); } - if (ch != pgm_read_byte_near(locator)) locator = waitfor; - if (ch == pgm_read_byte_near(locator)) { + if (ch != GETFLASH(locator)) locator = waitfor; + if (ch == GETFLASH(locator)) { locator++; - if (!pgm_read_byte_near(locator)) { + if (!GETFLASH(locator)) { DIAG(F("\nFound in %dms"), millis() - startTime); return true; } diff --git a/version.h b/version.h index e60dd91..aa6a0bc 100644 --- a/version.h +++ b/version.h @@ -3,7 +3,7 @@ #include "StringFormatter.h" -// const char VERSION[] PROGMEM ="0.2.0"; +// const char VERSION[] FLASH ="0.2.0"; #define VERSION "3.0.1"