parent
5f92dd7844
commit
eb8e483801
@ -1,69 +0,0 @@ |
|||||||
#include <Audio.h> |
|
||||||
#include "synth_dexed.h" |
|
||||||
|
|
||||||
uint8_t fmpiano_sysex[156] = { |
|
||||||
95, 29, 20, 50, 99, 95, 00, 00, 41, 00, 19, 00, 00, 03, 00, 06, 79, 00, 01, 00, 14, // OP6 eg_rate_1-4, level_1-4, kbd_lev_scl_brk_pt, kbd_lev_scl_lft_depth, kbd_lev_scl_rht_depth, kbd_lev_scl_lft_curve, kbd_lev_scl_rht_curve, kbd_rate_scaling, amp_mod_sensitivity, key_vel_sensitivity, operator_output_level, osc_mode, osc_freq_coarse, osc_freq_fine, osc_detune
|
|
||||||
95, 20, 20, 50, 99, 95, 00, 00, 00, 00, 00, 00, 00, 03, 00, 00, 99, 00, 01, 00, 00, // OP5
|
|
||||||
95, 29, 20, 50, 99, 95, 00, 00, 00, 00, 00, 00, 00, 03, 00, 06, 89, 00, 01, 00, 07, // OP4
|
|
||||||
95, 20, 20, 50, 99, 95, 00, 00, 00, 00, 00, 00, 00, 03, 00, 02, 99, 00, 01, 00, 07, // OP3
|
|
||||||
95, 50, 35, 78, 99, 75, 00, 00, 00, 00, 00, 00, 00, 03, 00, 07, 58, 00, 14, 00, 07, // OP2
|
|
||||||
96, 25, 25, 67, 99, 75, 00, 00, 00, 00, 00, 00, 00, 03, 00, 02, 99, 00, 01, 00, 10, // OP1
|
|
||||||
94, 67, 95, 60, 50, 50, 50, 50, // 4 * pitch EG rates, 4 * pitch EG level
|
|
||||||
04, 06, 00, // algorithm, feedback, osc sync
|
|
||||||
34, 33, 00, 00, 00, 04, // lfo speed, lfo delay, lfo pitch_mod_depth, lfo_amp_mod_depth, lfo_sync, lfo_waveform
|
|
||||||
03, 24, // pitch_mod_sensitivity, transpose
|
|
||||||
70, 77, 45, 80, 73, 65, 78, 79, 00, 00 // 10 * char for name ("DEFAULT ")
|
|
||||||
}; // FM-Piano
|
|
||||||
|
|
||||||
AudioSynthDexed dexed(4,SAMPLE_RATE); // 4 voices max
|
|
||||||
AudioOutputI2S i2s1; |
|
||||||
AudioControlSGTL5000 sgtl5000_1; |
|
||||||
AudioConnection patchCord1(dexed, 0, i2s1, 0); |
|
||||||
AudioConnection patchCord2(dexed, 0, i2s1, 1); |
|
||||||
|
|
||||||
void setup() |
|
||||||
{ |
|
||||||
AudioMemory(32); |
|
||||||
|
|
||||||
sgtl5000_1.enable(); |
|
||||||
sgtl5000_1.lineOutLevel(29); |
|
||||||
sgtl5000_1.dacVolumeRamp(); |
|
||||||
sgtl5000_1.dacVolume(1.0); |
|
||||||
sgtl5000_1.unmuteHeadphone(); |
|
||||||
sgtl5000_1.unmuteLineout(); |
|
||||||
sgtl5000_1.volume(0.8, 0.8); // Headphone volume
|
|
||||||
} |
|
||||||
|
|
||||||
void loop() |
|
||||||
{ |
|
||||||
static uint8_t count; |
|
||||||
|
|
||||||
if (count % 2 == 0) |
|
||||||
{ |
|
||||||
dexed.loadInitVoice(); |
|
||||||
} |
|
||||||
else |
|
||||||
{ |
|
||||||
dexed.loadVoiceParameters(fmpiano_sysex); |
|
||||||
dexed.setTranspose(36); |
|
||||||
} |
|
||||||
|
|
||||||
Serial.println("Key-Down"); |
|
||||||
dexed.keydown(48, 100); |
|
||||||
delay(100); |
|
||||||
dexed.keydown(52, 100); |
|
||||||
delay(100); |
|
||||||
dexed.keydown(55, 100); |
|
||||||
delay(100); |
|
||||||
dexed.keydown(60, 100); |
|
||||||
delay(2000); |
|
||||||
|
|
||||||
Serial.println("Key-Up"); |
|
||||||
dexed.keyup(48); |
|
||||||
dexed.keyup(52); |
|
||||||
dexed.keyup(55); |
|
||||||
dexed.keyup(60); |
|
||||||
delay(2000); |
|
||||||
|
|
||||||
count++; |
|
||||||
} |
|
@ -0,0 +1,165 @@ |
|||||||
|
####################################### |
||||||
|
# Syntax Coloring Map For Synth_Dexed |
||||||
|
####################################### |
||||||
|
|
||||||
|
####################################### |
||||||
|
# Datatypes (KEYWORD1) |
||||||
|
####################################### |
||||||
|
|
||||||
|
Dexed KEYWORD1 |
||||||
|
AudioSynthDexed KEYWORD1 |
||||||
|
|
||||||
|
####################################### |
||||||
|
# Methods and Functions (KEYWORD2) |
||||||
|
####################################### |
||||||
|
|
||||||
|
# Global methods |
||||||
|
activate KEYWORD2 |
||||||
|
deactivate KEYWORD2 |
||||||
|
getMonoMode KEYWORD2 |
||||||
|
setMonoMode KEYWORD2 |
||||||
|
setRefreshMode KEYWORD2 |
||||||
|
setMaxNotes KEYWORD2 |
||||||
|
getMaxNotes KEYWORD2 |
||||||
|
doRefreshVoice KEYWORD2 |
||||||
|
setOPAll KEYWORD2 |
||||||
|
decodeVoice KEYWORD2 |
||||||
|
encodeVoice KEYWORD2 |
||||||
|
getVoiceData KEYWORD2 |
||||||
|
setVoiceDataElement KEYWORD2 |
||||||
|
getVoiceDataElement KEYWORD2 |
||||||
|
loadInitVoice KEYWORD2 |
||||||
|
loadVoiceParameters KEYWORD2 |
||||||
|
getNumNotesPlaying KEYWORD2 |
||||||
|
uint32_t getXRun KEYWORD2 |
||||||
|
uint16_t getRenderTimeMax KEYWORD2 |
||||||
|
resetRenderTimeMax KEYWORD2 |
||||||
|
ControllersRefresh KEYWORD2 |
||||||
|
|
||||||
|
# Sound methods KEYWORD2 |
||||||
|
keyup KEYWORD2 |
||||||
|
keydown KEYWORD2 |
||||||
|
setSustain KEYWORD2 |
||||||
|
getSustain KEYWORD2 |
||||||
|
panic KEYWORD2 |
||||||
|
notesOff KEYWORD2 |
||||||
|
resetControllers KEYWORD2 |
||||||
|
setMasterTune KEYWORD2 |
||||||
|
int8_t getMasterTune KEYWORD2 |
||||||
|
setPortamentoMode KEYWORD2 |
||||||
|
setPBController KEYWORD2 |
||||||
|
setMWController KEYWORD2 |
||||||
|
setFCController KEYWORD2 |
||||||
|
setBCController KEYWORD2 |
||||||
|
setATController KEYWORD2 |
||||||
|
setModWheel KEYWORD2 |
||||||
|
getModWheel KEYWORD2 |
||||||
|
setBreathController KEYWORD2 |
||||||
|
getBreathController KEYWORD2 |
||||||
|
setFootController KEYWORD2 |
||||||
|
getFootController KEYWORD2 |
||||||
|
setAftertouch KEYWORD2 |
||||||
|
getAftertouch KEYWORD2 |
||||||
|
setPitchbend KEYWORD2 |
||||||
|
getPitchbend KEYWORD2 |
||||||
|
setPitchbendRange KEYWORD2 |
||||||
|
getPitchbendRange KEYWORD2 |
||||||
|
setPitchbendStep KEYWORD2 |
||||||
|
getPitchbendStep KEYWORD2 |
||||||
|
setModWheelRange KEYWORD2 |
||||||
|
getModWheelRange KEYWORD2 |
||||||
|
setModWheelTarget KEYWORD2 |
||||||
|
getModWheelTarget KEYWORD2 |
||||||
|
setFootControllerRange KEYWORD2 |
||||||
|
getFootControllerRange KEYWORD2 |
||||||
|
setFootControllerTarget KEYWORD2 |
||||||
|
getFootControllerTarget KEYWORD2 |
||||||
|
setBreathControllerRange KEYWORD2 |
||||||
|
getBreathControllerRange KEYWORD2 |
||||||
|
setBreathControllerTarget KEYWORD2 |
||||||
|
getBreathControllerTarget KEYWORD2 |
||||||
|
setAftertouchRange KEYWORD2 |
||||||
|
getAftertouchRange KEYWORD2 |
||||||
|
setAftertouchTarget KEYWORD2 |
||||||
|
getAftertouchTarget KEYWORD2 |
||||||
|
setFilterCutoff KEYWORD2 |
||||||
|
float getFilterCutoff KEYWORD2 |
||||||
|
setFilterResonance KEYWORD2 |
||||||
|
float getFilterResonance KEYWORD2 |
||||||
|
setGain KEYWORD2 |
||||||
|
float getGain KEYWORD2 |
||||||
|
|
||||||
|
# Voice configuration methods KEYWORD2 |
||||||
|
setOPRateAll KEYWORD2 |
||||||
|
setOPLevelAll KEYWORD2 |
||||||
|
setOPRateAllCarrier KEYWORD2 |
||||||
|
setOPLevelAllCarrier KEYWORD2 |
||||||
|
setOPRateAllModulator KEYWORD2 |
||||||
|
setOPLevelAllModulator KEYWORD2 |
||||||
|
setOPRate KEYWORD2 |
||||||
|
getOPRate KEYWORD2 |
||||||
|
setOPLevel KEYWORD2 |
||||||
|
getOPLevel KEYWORD2 |
||||||
|
setOPKeyboardLevelScalingBreakPoint KEYWORD2 |
||||||
|
getOPKeyboardLevelScalingBreakPoint KEYWORD2 |
||||||
|
setOPKeyboardLevelScalingDepthLeft KEYWORD2 |
||||||
|
getOPKeyboardLevelScalingDepthLeft KEYWORD2 |
||||||
|
setOPKeyboardLevelScalingDepthRight KEYWORD2 |
||||||
|
getOPKeyboardLevelScalingDepthRight KEYWORD2 |
||||||
|
setOPKeyboardLevelScalingCurveLeft KEYWORD2 |
||||||
|
getOPKeyboardLevelScalingCurveLeft KEYWORD2 |
||||||
|
setOPKeyboardLevelScalingCurveRight KEYWORD2 |
||||||
|
getOPKeyboardLevelScalingCurveRight KEYWORD2 |
||||||
|
setOPKeyboardRateScale KEYWORD2 |
||||||
|
getOPKeyboardRateScale KEYWORD2 |
||||||
|
setOPAmpModulationSensity KEYWORD2 |
||||||
|
getOPAmpModulationSensity KEYWORD2 |
||||||
|
setOPKeyboardVelocitySensity KEYWORD2 |
||||||
|
getOPKeyboardVelocitySensity KEYWORD2 |
||||||
|
setOPOutputLevel KEYWORD2 |
||||||
|
getOPOutputLevel KEYWORD2 |
||||||
|
setOPMode KEYWORD2 |
||||||
|
getOPMode KEYWORD2 |
||||||
|
setOPFrequencyCoarse KEYWORD2 |
||||||
|
getOPFrequencyCoarse KEYWORD2 |
||||||
|
setOPFrequencyFine KEYWORD2 |
||||||
|
getOPFrequencyFine KEYWORD2 |
||||||
|
setOPDetune KEYWORD2 |
||||||
|
getOPDetune KEYWORD2 |
||||||
|
setPitchRate KEYWORD2 |
||||||
|
getPitchRate KEYWORD2 |
||||||
|
setPitchLevel KEYWORD2 |
||||||
|
getPitchLevel KEYWORD2 |
||||||
|
setAlgorithm KEYWORD2 |
||||||
|
getAlgorithm KEYWORD2 |
||||||
|
setFeedback KEYWORD2 |
||||||
|
getFeedback KEYWORD2 |
||||||
|
setOscillatorSync KEYWORD2 |
||||||
|
getOscillatorSync KEYWORD2 |
||||||
|
setLFOSpeed KEYWORD2 |
||||||
|
getLFOSpeed KEYWORD2 |
||||||
|
setLFODelay KEYWORD2 |
||||||
|
getLFODelay KEYWORD2 |
||||||
|
setLFOPitchModulationDepth KEYWORD2 |
||||||
|
getLFOPitchModulationDepth KEYWORD2 |
||||||
|
setLFOAmpModulationDepth KEYWORD2 |
||||||
|
getLFOAmpModulationDepth KEYWORD2 |
||||||
|
setLFOSync KEYWORD2 |
||||||
|
getLFOSync KEYWORD2 |
||||||
|
setLFOWaveform KEYWORD2 |
||||||
|
getLFOWaveform KEYWORD2 |
||||||
|
setLFOPitchModulationSensitivity KEYWORD2 |
||||||
|
getLFOPitchModulationSensitivity KEYWORD2 |
||||||
|
setTranspose KEYWORD2 |
||||||
|
getTranspose KEYWORD2 |
||||||
|
setName KEYWORD2 |
||||||
|
getName KEYWORD2 |
||||||
|
|
||||||
|
####################################### |
||||||
|
# Instances (KEYWORD2) |
||||||
|
####################################### |
||||||
|
|
||||||
|
####################################### |
||||||
|
# Constants (LITERAL1) |
||||||
|
####################################### |
||||||
|
|
@ -0,0 +1,33 @@ |
|||||||
|
// Uses the built in Real Time Clock (RTC) as timebase.
|
||||||
|
// The sketch calculates the relative drift of the main 24MHz crystal relative to the 32.768kHz RTC crystal.
|
||||||
|
// Requires a T4.x board
|
||||||
|
|
||||||
|
#include "Arduino.h" |
||||||
|
#include "TeensyTimerTool.h" |
||||||
|
using namespace TeensyTimerTool; |
||||||
|
|
||||||
|
PeriodicTimer t1(TCK_RTC); |
||||||
|
|
||||||
|
constexpr uint32_t period = 500'000; //µs
|
||||||
|
|
||||||
|
void callback() |
||||||
|
{ |
||||||
|
static uint32_t start = micros(); |
||||||
|
static uint32_t idx = 0; |
||||||
|
|
||||||
|
uint32_t now = micros() - start; |
||||||
|
uint32_t expected = idx++ * period; |
||||||
|
int32_t delta = now - expected; |
||||||
|
float drift = 1E6 * delta / expected; // ppm
|
||||||
|
|
||||||
|
Serial.printf("t: %d µs, rel. drift: %.2f ppm\n", now, drift); |
||||||
|
} |
||||||
|
|
||||||
|
void setup() |
||||||
|
{ |
||||||
|
t1.begin(callback, period); |
||||||
|
} |
||||||
|
|
||||||
|
void loop() |
||||||
|
{ |
||||||
|
} |
@ -0,0 +1,10 @@ |
|||||||
|
#include "timer.h" |
||||||
|
#include "config.h" |
||||||
|
|
||||||
|
namespace TeensyTimerTool |
||||||
|
{ |
||||||
|
Timer::Timer(TimerGenerator *generator) |
||||||
|
: BaseTimer(generator, true) |
||||||
|
{ |
||||||
|
} |
||||||
|
} // namespace TeensyTimerTool
|
@ -0,0 +1,30 @@ |
|||||||
|
#include "baseTimer.h" |
||||||
|
//#include "Arduino.h"
|
||||||
|
#include "types.h" |
||||||
|
|
||||||
|
namespace TeensyTimerTool |
||||||
|
{ |
||||||
|
|
||||||
|
BaseTimer::BaseTimer(TimerGenerator *generator, bool periodic) |
||||||
|
: timerGenerator(generator) |
||||||
|
{ |
||||||
|
this->timerGenerator = generator; |
||||||
|
this->timerChannel = nullptr; |
||||||
|
this->isPeriodic = periodic; |
||||||
|
} |
||||||
|
|
||||||
|
BaseTimer::~BaseTimer() |
||||||
|
{ |
||||||
|
if (timerChannel != nullptr) |
||||||
|
{ |
||||||
|
delete timerChannel; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
errorCode BaseTimer::setPrescaler(int psc) |
||||||
|
{ |
||||||
|
this->prescaler = psc; |
||||||
|
return errorCode::OK; |
||||||
|
} |
||||||
|
|
||||||
|
} // namespace TeensyTimerTool
|
@ -0,0 +1,57 @@ |
|||||||
|
#pragma once |
||||||
|
|
||||||
|
#include "ErrorHandling/error_codes.h" |
||||||
|
#include "baseTimer.h" |
||||||
|
#include "type_traits" |
||||||
|
|
||||||
|
namespace TeensyTimerTool |
||||||
|
{ |
||||||
|
class OneShotTimer : public BaseTimer |
||||||
|
{ |
||||||
|
public: |
||||||
|
inline OneShotTimer(TimerGenerator *generator = nullptr); |
||||||
|
|
||||||
|
inline errorCode begin(callback_t cb); |
||||||
|
template <typename T> errorCode trigger(T delay); |
||||||
|
template <typename T> errorCode triggerDirect(T reload); |
||||||
|
template <typename T> errorCode getTriggerReload(float delay, T *reload); |
||||||
|
}; |
||||||
|
|
||||||
|
// Implementation ================================================
|
||||||
|
|
||||||
|
OneShotTimer::OneShotTimer(TimerGenerator *generator) |
||||||
|
: BaseTimer(generator, false) |
||||||
|
{} |
||||||
|
|
||||||
|
errorCode OneShotTimer::begin(callback_t callback) |
||||||
|
{ |
||||||
|
return BaseTimer::begin(callback, 0, false); |
||||||
|
} |
||||||
|
|
||||||
|
template <typename period_t> |
||||||
|
errorCode OneShotTimer::trigger(period_t delay) |
||||||
|
{ |
||||||
|
if (timerChannel) |
||||||
|
return postError(timerChannel->trigger(period2us(delay))); |
||||||
|
|
||||||
|
return postError(errorCode::notInitialized); |
||||||
|
} |
||||||
|
|
||||||
|
template <typename T> |
||||||
|
errorCode OneShotTimer::triggerDirect(T reload) |
||||||
|
{ |
||||||
|
if (timerChannel) |
||||||
|
return postError(timerChannel->triggerDirect(reload)); |
||||||
|
|
||||||
|
return postError(errorCode::notInitialized); |
||||||
|
} |
||||||
|
|
||||||
|
template <typename T> |
||||||
|
errorCode OneShotTimer::getTriggerReload(float delay, T *reload) |
||||||
|
{ |
||||||
|
if (timerChannel) |
||||||
|
return postError(timerChannel->getTriggerReload(delay, reload)); |
||||||
|
|
||||||
|
return postError(errorCode::notInitialized); |
||||||
|
} |
||||||
|
} // namespace TeensyTimerTool
|
@ -0,0 +1,21 @@ |
|||||||
|
#pragma once |
||||||
|
|
||||||
|
#include "baseTimer.h" |
||||||
|
|
||||||
|
namespace TeensyTimerTool |
||||||
|
{ |
||||||
|
class PeriodicTimer : public BaseTimer |
||||||
|
{ |
||||||
|
public: |
||||||
|
PeriodicTimer(TimerGenerator *generator = nullptr) : BaseTimer(generator, true) {} |
||||||
|
|
||||||
|
template <typename period_t> |
||||||
|
inline errorCode setPeriod(period_t p) { return postError(timerChannel->setPeriod(period2us(p))); }; |
||||||
|
|
||||||
|
template <typename period_t> |
||||||
|
inline errorCode setNextPeriod(period_t p) { return postError(timerChannel->setNextPeriod(period2us(p))); }; |
||||||
|
}; |
||||||
|
|
||||||
|
// IMPLEMENTATION =====================================================================
|
||||||
|
|
||||||
|
} // namespace TeensyTimerTool
|
@ -1,17 +0,0 @@ |
|||||||
#ifdef ESP32 |
|
||||||
|
|
||||||
#include "../../TeensyTimerTool.h" |
|
||||||
|
|
||||||
#if defined(HAS_TCK) and defined(ESP32) |
|
||||||
|
|
||||||
namespace TeensyTimerTool |
|
||||||
{ |
|
||||||
bool TCK_t::isInitialized = false; |
|
||||||
TckChannel* TCK_t::channels[maxTckChannels]; |
|
||||||
} |
|
||||||
|
|
||||||
void yield() { TeensyTimerTool::TCK_t::tick(); } |
|
||||||
|
|
||||||
#endif |
|
||||||
|
|
||||||
#endif |
|
@ -1,79 +0,0 @@ |
|||||||
#pragma once |
|
||||||
|
|
||||||
#include "TckChannel.h" |
|
||||||
#include <Arduino.h> |
|
||||||
#include <cstdint> |
|
||||||
|
|
||||||
namespace TeensyTimerTool |
|
||||||
{ |
|
||||||
constexpr unsigned maxTckChannels = 20; |
|
||||||
|
|
||||||
class TCK_t |
|
||||||
{ |
|
||||||
public: |
|
||||||
static inline ITimerChannel* getTimer(); |
|
||||||
static inline void removeTimer(TckChannel*); |
|
||||||
static inline void tick(); |
|
||||||
|
|
||||||
protected: |
|
||||||
static bool isInitialized;
|
|
||||||
static TckChannel* channels[maxTckChannels]; |
|
||||||
}; |
|
||||||
|
|
||||||
// IMPLEMENTATION ==================================================================
|
|
||||||
|
|
||||||
ITimerChannel* TCK_t::getTimer() |
|
||||||
{ |
|
||||||
Serial.printf("TCK getTimer()\n"); |
|
||||||
|
|
||||||
if (!isInitialized) |
|
||||||
{ |
|
||||||
for (unsigned chNr = 0; chNr < maxTckChannels; chNr++) |
|
||||||
{ |
|
||||||
channels[chNr] = nullptr; |
|
||||||
} |
|
||||||
isInitialized = true; |
|
||||||
} |
|
||||||
|
|
||||||
for (unsigned chNr = 0; chNr < maxTckChannels; chNr++) |
|
||||||
{ |
|
||||||
if (channels[chNr] == nullptr) |
|
||||||
{ |
|
||||||
channels[chNr] = new TckChannel(); |
|
||||||
return channels[chNr]; |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
return nullptr; |
|
||||||
} |
|
||||||
|
|
||||||
void TCK_t::removeTimer(TckChannel* channel) |
|
||||||
{ |
|
||||||
for (unsigned chNr = 0; chNr < maxTckChannels; chNr++) |
|
||||||
{ |
|
||||||
if (channels[chNr] == channel) |
|
||||||
{ |
|
||||||
channels[chNr] = nullptr; |
|
||||||
delete channel; |
|
||||||
break; |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
void TCK_t::tick() |
|
||||||
{ |
|
||||||
digitalWriteFast(12,HIGH); |
|
||||||
for(unsigned i = 0; i < maxTckChannels; i++) |
|
||||||
{ |
|
||||||
if (channels[i] != nullptr ) |
|
||||||
{ |
|
||||||
channels[i]->tick(); |
|
||||||
} |
|
||||||
} |
|
||||||
digitalWriteFast(12,LOW); |
|
||||||
} |
|
||||||
|
|
||||||
|
|
||||||
constexpr TimerGenerator* TCK = TCK_t::getTimer; |
|
||||||
|
|
||||||
} // namespace TeensyTimerTool
|
|
@ -1,89 +0,0 @@ |
|||||||
#pragma once |
|
||||||
|
|
||||||
#include "../ITimerChannel.h" |
|
||||||
#include "../types.h" |
|
||||||
#include "Arduino.h" |
|
||||||
|
|
||||||
namespace TeensyTimerTool |
|
||||||
{ |
|
||||||
class TCK_t; |
|
||||||
|
|
||||||
class TckChannel : public ITimerChannel |
|
||||||
{ |
|
||||||
public: |
|
||||||
inline TckChannel() { triggered = false; } |
|
||||||
inline virtual ~TckChannel(){}; |
|
||||||
|
|
||||||
inline void begin(callback_t cb, unsigned period, bool periodic) |
|
||||||
{ |
|
||||||
Serial.println("begin"); |
|
||||||
|
|
||||||
triggered = false; |
|
||||||
this->periodic = periodic; |
|
||||||
this->period = period * (F_CPU / 1'000'000); |
|
||||||
this->callback = cb; |
|
||||||
|
|
||||||
startCNT = ARM_DWT_CYCCNT;
|
|
||||||
} |
|
||||||
|
|
||||||
inline void start() |
|
||||||
{ |
|
||||||
Serial.println("start"); |
|
||||||
|
|
||||||
this->startCNT = ARM_DWT_CYCCNT; |
|
||||||
this->triggered = true; |
|
||||||
} |
|
||||||
|
|
||||||
inline void stop() |
|
||||||
{ |
|
||||||
this->triggered = false; |
|
||||||
} |
|
||||||
|
|
||||||
// inline void setPeriod(uint32_t microSeconds);
|
|
||||||
inline uint32_t getPeriod(void); |
|
||||||
|
|
||||||
inline void trigger(uint32_t delay) // µs
|
|
||||||
{ |
|
||||||
this->startCNT = ARM_DWT_CYCCNT; |
|
||||||
this->period = delay * (F_CPU / 1'000'000) - 68; |
|
||||||
this->triggered = true; |
|
||||||
} |
|
||||||
|
|
||||||
protected: |
|
||||||
uint32_t startCNT, period; |
|
||||||
callback_t callback; |
|
||||||
bool triggered; |
|
||||||
bool periodic; |
|
||||||
|
|
||||||
inline void tick(); |
|
||||||
bool block = false; |
|
||||||
|
|
||||||
friend TCK_t; |
|
||||||
}; |
|
||||||
|
|
||||||
// IMPLEMENTATION ==============================================
|
|
||||||
|
|
||||||
void TckChannel::tick() |
|
||||||
{ |
|
||||||
static bool lock = false;
|
|
||||||
|
|
||||||
if (!lock && period != 0 && triggered && (ARM_DWT_CYCCNT - startCNT) >= period) |
|
||||||
{ |
|
||||||
lock = true; |
|
||||||
startCNT = ARM_DWT_CYCCNT; |
|
||||||
triggered = periodic; // i.e., stays triggerd if periodic, stops if oneShot
|
|
||||||
callback(); |
|
||||||
lock = false; |
|
||||||
}
|
|
||||||
} |
|
||||||
|
|
||||||
// void TckChannel::setPeriod(uint32_t microSeconds)
|
|
||||||
// {
|
|
||||||
// period = microSeconds * (F_CPU / 1'000'000) ;
|
|
||||||
// }
|
|
||||||
uint32_t TckChannel::getPeriod() |
|
||||||
{ |
|
||||||
return period * (1'000'000.0f / F_CPU); |
|
||||||
} |
|
||||||
|
|
||||||
} // namespace TeensyTimerTool
|
|
@ -1,110 +0,0 @@ |
|||||||
#pragma once |
|
||||||
|
|
||||||
#include "boardDef.h" |
|
||||||
#include <algorithm> |
|
||||||
|
|
||||||
// All information in this header is calculated at compile time.
|
|
||||||
// FTM_Info will not generate any code
|
|
||||||
|
|
||||||
namespace TeensyTimerTool |
|
||||||
{ |
|
||||||
typedef struct // FTM & TPM Channels
|
|
||||||
{ |
|
||||||
volatile uint32_t SC; |
|
||||||
volatile uint32_t CV; |
|
||||||
} FTM_CH_t; |
|
||||||
|
|
||||||
typedef struct // FTM register block (this layout is compatible to a TPM register block)
|
|
||||||
{ |
|
||||||
volatile uint32_t SC; |
|
||||||
volatile uint32_t CNT; |
|
||||||
volatile uint32_t MOD; |
|
||||||
FTM_CH_t CH[8]; |
|
||||||
volatile uint32_t CNTIN; |
|
||||||
volatile uint32_t STATUS; |
|
||||||
volatile uint32_t MODE; |
|
||||||
volatile uint32_t SYNC; |
|
||||||
volatile uint32_t OUTINIT; |
|
||||||
volatile uint32_t OUTMASK; |
|
||||||
volatile uint32_t COMBINE; |
|
||||||
volatile uint32_t DEADTIME; |
|
||||||
volatile uint32_t EXTTRIG; |
|
||||||
volatile uint32_t POL; |
|
||||||
volatile uint32_t FMS; |
|
||||||
volatile uint32_t FILTER; |
|
||||||
volatile uint32_t FLTCTRL; |
|
||||||
volatile uint32_t QDCTRL; |
|
||||||
volatile uint32_t CONF; |
|
||||||
volatile uint32_t FLTPOL; |
|
||||||
volatile uint32_t SYNCONF; |
|
||||||
volatile uint32_t INVCTRL; |
|
||||||
volatile uint32_t SWOCTRL; |
|
||||||
volatile uint32_t PWMLOAD; |
|
||||||
} FTM_r_t; |
|
||||||
|
|
||||||
//=======================================================================
|
|
||||||
// using a static class instead of namespace here to reduce namespace pollution
|
|
||||||
// (anonymous namespace doesn't make sense for header only class)
|
|
||||||
|
|
||||||
template<unsigned module> |
|
||||||
class FTM_Info |
|
||||||
{ |
|
||||||
private: |
|
||||||
#if defined(ARDUINO_TEENSYLC) |
|
||||||
static constexpr unsigned boardNr = 0; |
|
||||||
#elif defined(ARDUINO_TEENSY30) |
|
||||||
static constexpr unsigned boardNr = 1; |
|
||||||
#elif defined(ARDUINO_TEENSY31) || defined(ARDUINO_TEENSY32) |
|
||||||
static constexpr unsigned boardNr = 2; |
|
||||||
#elif defined(ARDUINO_TEENSY35) |
|
||||||
static constexpr unsigned boardNr = 3; |
|
||||||
#elif defined(ARDUINO_TEENSY36) |
|
||||||
static constexpr unsigned boardNr = 4; |
|
||||||
#else |
|
||||||
#error Board not valid |
|
||||||
#endif |
|
||||||
|
|
||||||
static constexpr int IRQ_Numbers[][4] |
|
||||||
{ //FTM0 FTM1 FTM2 FTM3
|
|
||||||
{ 0, 0, 0, 0 }, // Teensy LC
|
|
||||||
{ 25, 26, 0, 0 }, // Teensy 3.0
|
|
||||||
{ 62, 63, 64, 0 }, // Teensy 3.1/3.2
|
|
||||||
{ 42, 43, 44, 71 }, // Teensy 3.5
|
|
||||||
{ 42, 43, 44, 71 }, // Teensy 3.6
|
|
||||||
}; |
|
||||||
|
|
||||||
static constexpr int FTM_NrOfChannels[] |
|
||||||
{ |
|
||||||
8, // FTM0
|
|
||||||
2, // FTM1
|
|
||||||
2, // FTM2
|
|
||||||
8, // FTM3
|
|
||||||
}; |
|
||||||
|
|
||||||
static constexpr uintptr_t FTM_BaseAdr[] // can't use defines from kinetis.h since, depending on board, not all are always defined.
|
|
||||||
{ |
|
||||||
0x4003'8000, // FTM0
|
|
||||||
0x4003'9000, // FTM1
|
|
||||||
0x400B'8000, // FTM2
|
|
||||||
0x400B'9000, // FTM3
|
|
||||||
}; |
|
||||||
|
|
||||||
static constexpr unsigned FTM_Prescale = |
|
||||||
FTM_DEFAULT_PSC[module] < 0 || FTM_DEFAULT_PSC[module] > 7 ? // prescale value to roughly get 2 ticks per µs
|
|
||||||
( |
|
||||||
F_BUS > 120'000'000 ? 0b111 : |
|
||||||
F_BUS > 60'000'000 ? 0b110 : |
|
||||||
F_BUS > 30'000'000 ? 0b101 : |
|
||||||
F_BUS > 15'000'000 ? 0b100 : |
|
||||||
F_BUS > 8'000'000 ? 0b011 : |
|
||||||
F_BUS > 4'000'000 ? 0b010 : |
|
||||||
F_BUS > 2'000'000 ? 0b001 : 0b000 |
|
||||||
):FTM_DEFAULT_PSC[module]; |
|
||||||
|
|
||||||
public: |
|
||||||
static constexpr uintptr_t baseAdr = FTM_BaseAdr[module]; |
|
||||||
static constexpr IRQ_NUMBER_t irqNumber = (IRQ_NUMBER_t)IRQ_Numbers[boardNr][module]; |
|
||||||
static constexpr unsigned nrOfChannels = FTM_NrOfChannels[module]; |
|
||||||
static constexpr unsigned prescale = FTM_Prescale; |
|
||||||
}; |
|
||||||
} |
|
@ -1,32 +0,0 @@ |
|||||||
#pragma once |
|
||||||
|
|
||||||
#include <cstdint> |
|
||||||
#include <imxrt.h> |
|
||||||
|
|
||||||
namespace TeensyTimerTool |
|
||||||
{ |
|
||||||
// struct IMXRT_GPT_t
|
|
||||||
// {
|
|
||||||
// //51.7.1 GPT Control Register
|
|
||||||
// volatile uint32_t CR;
|
|
||||||
// //51.7.2 GPT Prescaler Register(GPTx_PR)
|
|
||||||
// volatile uint32_t PR;
|
|
||||||
// //51.7.3 GPT Status Register(GPTx_SR)
|
|
||||||
// volatile uint32_t SR;
|
|
||||||
// //51.7.4 GPT Interrupt Register(GPTx_IR)
|
|
||||||
// volatile uint32_t IR;
|
|
||||||
// //51.7.5 GPT Output Compare Register (GPTx_OCR1)
|
|
||||||
// volatile uint32_t OCR1;
|
|
||||||
// //51.7.6 GPT Output Compare Register (GPTx_OCR2)
|
|
||||||
// volatile uint32_t OCR2;
|
|
||||||
// //51.7.7 GPT Output Compare Register (GPTx_OCR3)
|
|
||||||
// volatile uint32_t OCR3;
|
|
||||||
// //51.7.8 GPT Input Capture Register 1 (GPTx_ICR1)
|
|
||||||
// volatile uint32_t ICR1;
|
|
||||||
// //51.7.9 GPT Input Capture Register 1 (GPTx_ICR2)
|
|
||||||
// volatile uint32_t ICR2;
|
|
||||||
// //51.7.10 GPT Counter Register (GPTx_CNT)
|
|
||||||
// volatile uint32_t CNT;
|
|
||||||
// };
|
|
||||||
|
|
||||||
} // namespace TeensyTimerTool
|
|
@ -1,41 +0,0 @@ |
|||||||
#include "../../config.h" |
|
||||||
|
|
||||||
#if defined(TEENSYDUINO) |
|
||||||
|
|
||||||
#include "TCK.h" |
|
||||||
namespace TeensyTimerTool |
|
||||||
{ |
|
||||||
bool TCK_t::isInitialized = false; |
|
||||||
TckChannelBase* TCK_t::channels[NR_OF_TCK_TIMERS]; |
|
||||||
} |
|
||||||
|
|
||||||
//----------------------------------------------------------------------
|
|
||||||
#if YIELD_TYPE == YIELD_OPTIMIZED |
|
||||||
|
|
||||||
void yield() |
|
||||||
{ |
|
||||||
TeensyTimerTool::TCK_t::tick(); |
|
||||||
} |
|
||||||
|
|
||||||
//----------------------------------------------------------------------
|
|
||||||
#elif YIELD_TYPE == YIELD_STANDARD |
|
||||||
|
|
||||||
#include "EventResponder.h" |
|
||||||
|
|
||||||
namespace TeensyTimerTool |
|
||||||
{ |
|
||||||
static EventResponder er; |
|
||||||
|
|
||||||
void initYieldHook() |
|
||||||
{ |
|
||||||
er.attach([](EventResponderRef r) |
|
||||||
{ |
|
||||||
TeensyTimerTool::TCK_t::tick(); |
|
||||||
r.triggerEvent(); |
|
||||||
}); |
|
||||||
er.triggerEvent(); |
|
||||||
} |
|
||||||
} |
|
||||||
#endif |
|
||||||
#endif |
|
||||||
//#endif
|
|
@ -1,336 +0,0 @@ |
|||||||
#pragma once |
|
||||||
|
|
||||||
//#include "Arduino.h"
|
|
||||||
#include "ErrorHandling/error_codes.h" |
|
||||||
#include "TckChannelBase.h" |
|
||||||
#include "core_pins.h" |
|
||||||
|
|
||||||
namespace TeensyTimerTool |
|
||||||
{ |
|
||||||
class TCK_t; |
|
||||||
|
|
||||||
#if !defined(ARDUINO_TEENSYLC) // T-LC doesn't have a cycle counter, nees special treatment
|
|
||||||
|
|
||||||
template <typename CounterType> |
|
||||||
class TckChannel : public TckChannelBase |
|
||||||
{ |
|
||||||
public: |
|
||||||
TckChannel(); |
|
||||||
virtual ~TckChannel(){}; //TBD
|
|
||||||
|
|
||||||
inline errorCode begin(callback_t cb, float period, bool periodic) override; |
|
||||||
inline errorCode start() override; |
|
||||||
inline errorCode stop() override; |
|
||||||
|
|
||||||
inline errorCode trigger(float delay_us) override; |
|
||||||
inline errorCode triggerDirect(CounterType reload) override; |
|
||||||
inline errorCode getTriggerReload(float delay, CounterType* reload) override |
|
||||||
{ |
|
||||||
*reload = microsecondToCycles(delay); |
|
||||||
return errorCode::OK; |
|
||||||
} |
|
||||||
|
|
||||||
float getMaxPeriod() const override { return getMaxMicros() / 1E6; } // seconds
|
|
||||||
|
|
||||||
CounterType getCycleCounter() { postError(errorCode::wrongType); } |
|
||||||
|
|
||||||
// inline errorCode setPeriod(uint32_t microSeconds) override;
|
|
||||||
// inline errorCode setPeriod(uint32_t microSeconds) override;
|
|
||||||
// inline errorCode setNextPeriod(uint32_t microSeconds) override;
|
|
||||||
|
|
||||||
protected: |
|
||||||
inline CounterType microsecondToCycles(const float microSecond) const; |
|
||||||
float getMaxMicros() const; |
|
||||||
|
|
||||||
inline bool tick(); |
|
||||||
callback_t callback; |
|
||||||
|
|
||||||
bool periodic; |
|
||||||
bool triggered; |
|
||||||
|
|
||||||
// bool block = false;
|
|
||||||
|
|
||||||
CounterType startCnt, currentPeriod, nextPeriod; |
|
||||||
|
|
||||||
uint32_t lastCyCnt; |
|
||||||
uint32_t curHIGH; |
|
||||||
|
|
||||||
float clock; |
|
||||||
|
|
||||||
friend TCK_t; |
|
||||||
|
|
||||||
// inline uint32_t CPUCyclesToMicroseond(const uint32_t cpuCycles);
|
|
||||||
// inline errorCode _setCurrentPeriod(const uint32_t period);
|
|
||||||
// inline void _setNextPeriod(const uint32_t period);
|
|
||||||
}; |
|
||||||
|
|
||||||
// IMPLEMENTATION ==============================================
|
|
||||||
|
|
||||||
template <typename T> |
|
||||||
TckChannel<T>::TckChannel() |
|
||||||
{ |
|
||||||
triggered = false; |
|
||||||
clock = F_CPU / 1'000'000.0f; |
|
||||||
} |
|
||||||
|
|
||||||
template <typename T> |
|
||||||
errorCode TckChannel<T>::begin(callback_t cb, float period, bool periodic) |
|
||||||
{ |
|
||||||
this->triggered = false; |
|
||||||
|
|
||||||
this->periodic = periodic; |
|
||||||
if (periodic) |
|
||||||
{ |
|
||||||
this->currentPeriod = microsecondToCycles(period); |
|
||||||
this->nextPeriod = this->currentPeriod; |
|
||||||
} |
|
||||||
this->callback = cb; |
|
||||||
|
|
||||||
return errorCode::OK; |
|
||||||
} |
|
||||||
|
|
||||||
template <typename T> |
|
||||||
errorCode TckChannel<T>::start() |
|
||||||
{ |
|
||||||
this->startCnt = getCycleCounter(); |
|
||||||
this->triggered = true; |
|
||||||
return errorCode::OK; |
|
||||||
} |
|
||||||
|
|
||||||
template <typename T> |
|
||||||
errorCode TckChannel<T>::stop() |
|
||||||
{ |
|
||||||
this->triggered = false; |
|
||||||
return errorCode::OK; |
|
||||||
} |
|
||||||
|
|
||||||
template <typename CounterType> |
|
||||||
errorCode TckChannel<CounterType>::triggerDirect(CounterType reload) |
|
||||||
{ |
|
||||||
this->startCnt = getCycleCounter(); |
|
||||||
this->nextPeriod = reload; |
|
||||||
this->currentPeriod = this->nextPeriod; |
|
||||||
this->triggered = true; |
|
||||||
return errorCode::OK; |
|
||||||
} |
|
||||||
|
|
||||||
template <typename T> |
|
||||||
errorCode TckChannel<T>::trigger(float delay) // µs
|
|
||||||
{ |
|
||||||
return triggerDirect(microsecondToCycles(delay)); |
|
||||||
} |
|
||||||
|
|
||||||
template <typename counter_t> |
|
||||||
bool TckChannel<counter_t>::tick() |
|
||||||
{ |
|
||||||
static bool lock = false; |
|
||||||
counter_t now = getCycleCounter(); |
|
||||||
if (!lock && this->currentPeriod != 0 && this->triggered && (now - this->startCnt) >= this->currentPeriod) |
|
||||||
{ |
|
||||||
lock = true; |
|
||||||
//this->startCnt = now;
|
|
||||||
this->startCnt += currentPeriod; |
|
||||||
this->triggered = this->periodic; // i.e., stays triggerd if periodic, stops if oneShot
|
|
||||||
callback(); |
|
||||||
lock = false; |
|
||||||
return true; |
|
||||||
} else |
|
||||||
{ |
|
||||||
return false; |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
template <typename CounterType> |
|
||||||
CounterType TckChannel<CounterType>::microsecondToCycles(float microSecond) const |
|
||||||
{ |
|
||||||
if (microSecond > getMaxMicros()) |
|
||||||
{ |
|
||||||
microSecond = getMaxMicros(); |
|
||||||
postError(errorCode::periodOverflow); |
|
||||||
} |
|
||||||
return (CounterType)(microSecond * clock); |
|
||||||
} |
|
||||||
|
|
||||||
// SPECIALIZATIONS =================================================================================
|
|
||||||
// 32bit Counter -------------------------------------------------------------------------
|
|
||||||
|
|
||||||
template <> |
|
||||||
inline uint32_t TckChannel<uint32_t>::getCycleCounter() |
|
||||||
{ |
|
||||||
return ARM_DWT_CYCCNT; //directly use the cycle counter for uint32_t
|
|
||||||
} |
|
||||||
|
|
||||||
template <> |
|
||||||
inline float TckChannel<uint32_t>::getMaxMicros() const |
|
||||||
{ |
|
||||||
return 0xF000'0000 / clock; // don't use full range otherwise tick might miss the turnover for large periods
|
|
||||||
} |
|
||||||
|
|
||||||
// 64bit Counter -------------------------------------------------------------------------
|
|
||||||
|
|
||||||
template <> |
|
||||||
inline uint64_t TckChannel<uint64_t>::getCycleCounter() |
|
||||||
{ |
|
||||||
uint32_t now = ARM_DWT_CYCCNT; // (extend the cycle counter to 64 bit)
|
|
||||||
if (now < lastCyCnt) |
|
||||||
{ |
|
||||||
curHIGH++; |
|
||||||
} |
|
||||||
lastCyCnt = now; |
|
||||||
return (((uint64_t)curHIGH << 32) | now); |
|
||||||
} |
|
||||||
|
|
||||||
template <> |
|
||||||
inline float TckChannel<uint64_t>::getMaxMicros() const |
|
||||||
{ |
|
||||||
return 0xFFFF'FFFF; // currently limited to 2^32 µs (71.6h). Could be extended to 2^64 but would require change of interface
|
|
||||||
} |
|
||||||
|
|
||||||
// template <typename ct>
|
|
||||||
// uint32_t TckChannel<ct>::CPUCyclesToMicroseond(const uint32_t cpuCycles)
|
|
||||||
// {
|
|
||||||
// return (1'000'000.0f / F_CPU) * cpuCycles;
|
|
||||||
// }
|
|
||||||
|
|
||||||
// template <typename ct>
|
|
||||||
// void TckChannel<ct>::_setNextPeriod(const uint32_t period)
|
|
||||||
// {
|
|
||||||
// this->nextPeriod = period;
|
|
||||||
// }
|
|
||||||
|
|
||||||
// template <typename ct>
|
|
||||||
// errorCode TckChannel<ct>::_setCurrentPeriod(const uint32_t period)
|
|
||||||
// {
|
|
||||||
// this->currentPeriod = period;
|
|
||||||
// const bool hasTicked = this->tick();
|
|
||||||
|
|
||||||
// if (hasTicked)
|
|
||||||
// {
|
|
||||||
// return errorCode::triggeredLate;
|
|
||||||
// } else
|
|
||||||
// {
|
|
||||||
// return errorCode::OK;
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
// template <typename ct>
|
|
||||||
// errorCode TckChannel<ct>::setPeriod(uint32_t microSeconds)
|
|
||||||
// {
|
|
||||||
// const uint32_t period = microsecondToCPUCycles(microSeconds);
|
|
||||||
|
|
||||||
// this->_setNextPeriod(period);
|
|
||||||
// return this->_setCurrentPeriod(period);
|
|
||||||
// }
|
|
||||||
|
|
||||||
// template <typename ct>
|
|
||||||
// errorCode TckChannel<ct>::setPeriod(uint32_t microSeconds)
|
|
||||||
// {
|
|
||||||
// const uint32_t period = microsecondToCPUCycles(microSeconds);
|
|
||||||
// return this->_setCurrentPeriod(period);
|
|
||||||
// }
|
|
||||||
|
|
||||||
// template <typename ct>
|
|
||||||
// errorCode TckChannel<ct>::setNextPeriod(uint32_t microSeconds)
|
|
||||||
// {
|
|
||||||
// const uint32_t period = microsecondToCPUCycles(microSeconds);
|
|
||||||
// this->_setNextPeriod(period);
|
|
||||||
// return errorCode::OK;
|
|
||||||
// }
|
|
||||||
|
|
||||||
#else // TeensyLC
|
|
||||||
|
|
||||||
// Quick hack only, needs to be improved
|
|
||||||
|
|
||||||
template <typename uint32_64_t> |
|
||||||
class TckChannel : public TckChannelBase |
|
||||||
{ |
|
||||||
public: |
|
||||||
TckChannel() { triggered = false; } |
|
||||||
virtual ~TckChannel(){}; |
|
||||||
|
|
||||||
errorCode begin(callback_t cb, float period, bool periodic) override |
|
||||||
{ |
|
||||||
triggered = false; |
|
||||||
this->periodic = periodic; |
|
||||||
this->period = (uint32_t)period; |
|
||||||
this->callback = cb; |
|
||||||
|
|
||||||
return errorCode::OK; |
|
||||||
} |
|
||||||
|
|
||||||
errorCode start() |
|
||||||
{ |
|
||||||
this->startCNT = micros(); |
|
||||||
this->triggered = true; |
|
||||||
return errorCode::OK; |
|
||||||
} |
|
||||||
|
|
||||||
errorCode stop() |
|
||||||
{ |
|
||||||
this->triggered = false; |
|
||||||
return errorCode::OK; |
|
||||||
} |
|
||||||
|
|
||||||
inline errorCode setPeriod(uint32_t microSeconds) override; |
|
||||||
inline uint32_t getPeriod(void); |
|
||||||
|
|
||||||
inline errorCode trigger(float delay) // µs
|
|
||||||
{ |
|
||||||
this->startCNT = micros(); |
|
||||||
this->period = (uint32_t)delay; |
|
||||||
this->triggered = true; |
|
||||||
return errorCode::OK; |
|
||||||
} |
|
||||||
|
|
||||||
float getMaxPeriod() const override { return getMaxMicros() / 1E6; } // seconds
|
|
||||||
|
|
||||||
protected: |
|
||||||
static constexpr float clock = F_CPU / 1'000'000.0f; |
|
||||||
float getMaxMicros() const { return 0xF000'0000 / clock; } // don't use full range otherwise tick might miss the turnover for large periods
|
|
||||||
|
|
||||||
uint32_t startCNT, period; |
|
||||||
callback_t callback; |
|
||||||
bool triggered; |
|
||||||
bool periodic; |
|
||||||
|
|
||||||
inline bool tick(); |
|
||||||
bool block = false; |
|
||||||
|
|
||||||
friend TCK_t; |
|
||||||
}; |
|
||||||
|
|
||||||
// IMPLEMENTATION ==============================================
|
|
||||||
|
|
||||||
template <typename ct> |
|
||||||
bool TckChannel<ct>::tick() |
|
||||||
{ |
|
||||||
static bool lock = false; |
|
||||||
|
|
||||||
if (!lock && period != 0 && triggered && (micros() - startCNT) >= period) |
|
||||||
{ |
|
||||||
lock = true; |
|
||||||
startCNT = micros(); |
|
||||||
triggered = periodic; // i.e., stays triggerd if periodic, stops if oneShot
|
|
||||||
callback(); |
|
||||||
lock = false; |
|
||||||
return true; |
|
||||||
} |
|
||||||
return false; |
|
||||||
} |
|
||||||
|
|
||||||
template <typename ct> |
|
||||||
errorCode TckChannel<ct>::setPeriod(uint32_t microSeconds) |
|
||||||
{ |
|
||||||
period = microSeconds; |
|
||||||
return errorCode::OK; |
|
||||||
} |
|
||||||
|
|
||||||
template <typename ct> |
|
||||||
uint32_t TckChannel<ct>::getPeriod() |
|
||||||
{ |
|
||||||
return period; |
|
||||||
} |
|
||||||
|
|
||||||
#endif |
|
||||||
|
|
||||||
} // namespace TeensyTimerTool
|
|
@ -1,9 +1,9 @@ |
|||||||
#pragma once |
#pragma once |
||||||
|
|
||||||
#include "config.h" |
#include "API/oneShotTimer.h" |
||||||
#include "timer.h" |
#include "API/periodicTimer.h" |
||||||
#include "periodicTimer.h" |
#include "API/timer.h" |
||||||
#include "oneShotTimer.h" |
|
||||||
#include "ErrorHandling/error_handler.h" |
#include "ErrorHandling/error_handler.h" |
||||||
|
#include "config.h" |
||||||
|
|
||||||
static_assert(TEENSYDUINO >= 150, "This library requires Teensyduino > 1.5"); |
static_assert(TEENSYDUINO >= 150, "This library requires Teensyduino > 1.5"); |
@ -1,10 +0,0 @@ |
|||||||
#include "timer.h" |
|
||||||
#include "config.h" |
|
||||||
|
|
||||||
namespace TeensyTimerTool |
|
||||||
{ |
|
||||||
Timer::Timer(TimerGenerator* generator) |
|
||||||
:BaseTimer(generator, true) |
|
||||||
{ |
|
||||||
} |
|
||||||
} |
|
@ -1,9 +1,9 @@ |
|||||||
#pragma once |
#pragma once |
||||||
|
|
||||||
#include "../../ITimerChannel.h" |
|
||||||
#include "Arduino.h" |
#include "Arduino.h" |
||||||
#include "FTM_ChannelInfo.h" |
#include "FTM_ChannelInfo.h" |
||||||
#include "FTM_Info.h" |
#include "FTM_Info.h" |
||||||
|
#include "ITimerChannel.h" |
||||||
|
|
||||||
namespace TeensyTimerTool |
namespace TeensyTimerTool |
||||||
{ |
{ |
@ -0,0 +1,109 @@ |
|||||||
|
#pragma once |
||||||
|
|
||||||
|
#include "boardDef.h" |
||||||
|
#include <algorithm> |
||||||
|
|
||||||
|
// All information in this header is calculated at compile time.
|
||||||
|
// FTM_Info will not generate any code
|
||||||
|
|
||||||
|
namespace TeensyTimerTool |
||||||
|
{ |
||||||
|
typedef struct // FTM & TPM Channels
|
||||||
|
{ |
||||||
|
volatile uint32_t SC; |
||||||
|
volatile uint32_t CV; |
||||||
|
} FTM_CH_t; |
||||||
|
|
||||||
|
typedef struct // FTM register block (this layout is compatible to a TPM register block)
|
||||||
|
{ |
||||||
|
volatile uint32_t SC; |
||||||
|
volatile uint32_t CNT; |
||||||
|
volatile uint32_t MOD; |
||||||
|
FTM_CH_t CH[8]; |
||||||
|
volatile uint32_t CNTIN; |
||||||
|
volatile uint32_t STATUS; |
||||||
|
volatile uint32_t MODE; |
||||||
|
volatile uint32_t SYNC; |
||||||
|
volatile uint32_t OUTINIT; |
||||||
|
volatile uint32_t OUTMASK; |
||||||
|
volatile uint32_t COMBINE; |
||||||
|
volatile uint32_t DEADTIME; |
||||||
|
volatile uint32_t EXTTRIG; |
||||||
|
volatile uint32_t POL; |
||||||
|
volatile uint32_t FMS; |
||||||
|
volatile uint32_t FILTER; |
||||||
|
volatile uint32_t FLTCTRL; |
||||||
|
volatile uint32_t QDCTRL; |
||||||
|
volatile uint32_t CONF; |
||||||
|
volatile uint32_t FLTPOL; |
||||||
|
volatile uint32_t SYNCONF; |
||||||
|
volatile uint32_t INVCTRL; |
||||||
|
volatile uint32_t SWOCTRL; |
||||||
|
volatile uint32_t PWMLOAD; |
||||||
|
} FTM_r_t; |
||||||
|
|
||||||
|
//=======================================================================
|
||||||
|
// using a static class instead of namespace here to reduce namespace pollution
|
||||||
|
// (anonymous namespace doesn't make sense for header only class)
|
||||||
|
|
||||||
|
template <unsigned module> |
||||||
|
class FTM_Info |
||||||
|
{ |
||||||
|
private: |
||||||
|
#if defined(ARDUINO_TEENSYLC) |
||||||
|
static constexpr unsigned boardNr = 0; |
||||||
|
#elif defined(ARDUINO_TEENSY30) |
||||||
|
static constexpr unsigned boardNr = 1; |
||||||
|
#elif defined(ARDUINO_TEENSY31) || defined(ARDUINO_TEENSY32) |
||||||
|
static constexpr unsigned boardNr = 2; |
||||||
|
#elif defined(ARDUINO_TEENSY35) |
||||||
|
static constexpr unsigned boardNr = 3; |
||||||
|
#elif defined(ARDUINO_TEENSY36) |
||||||
|
static constexpr unsigned boardNr = 4; |
||||||
|
#else |
||||||
|
#error Board not valid |
||||||
|
#endif |
||||||
|
|
||||||
|
static constexpr int IRQ_Numbers[][4]{ |
||||||
|
//FTM0 FTM1 FTM2 FTM3
|
||||||
|
{0, 0, 0, 0}, // Teensy LC
|
||||||
|
{25, 26, 0, 0}, // Teensy 3.0
|
||||||
|
{62, 63, 64, 0}, // Teensy 3.1/3.2
|
||||||
|
{42, 43, 44, 71}, // Teensy 3.5
|
||||||
|
{42, 43, 44, 71}, // Teensy 3.6
|
||||||
|
}; |
||||||
|
|
||||||
|
static constexpr int FTM_NrOfChannels[]{ |
||||||
|
8, // FTM0
|
||||||
|
2, // FTM1
|
||||||
|
2, // FTM2
|
||||||
|
8, // FTM3
|
||||||
|
}; |
||||||
|
|
||||||
|
static constexpr uintptr_t FTM_BaseAdr[] // can't use defines from kinetis.h since, depending on board, not all are always defined.
|
||||||
|
{ |
||||||
|
0x4003'8000, // FTM0
|
||||||
|
0x4003'9000, // FTM1
|
||||||
|
0x400B'8000, // FTM2
|
||||||
|
0x400B'9000, // FTM3
|
||||||
|
}; |
||||||
|
|
||||||
|
static constexpr unsigned FTM_Prescale = |
||||||
|
FTM_DEFAULT_PSC[module] < 0 || FTM_DEFAULT_PSC[module] > 7 ? // prescale value to roughly get 2 ticks per µs
|
||||||
|
( |
||||||
|
F_BUS > 120'000'000 ? 0b111 : F_BUS > 60'000'000 ? 0b110 |
||||||
|
: F_BUS > 30'000'000 ? 0b101 |
||||||
|
: F_BUS > 15'000'000 ? 0b100 |
||||||
|
: F_BUS > 8'000'000 ? 0b011 |
||||||
|
: F_BUS > 4'000'000 ? 0b010 |
||||||
|
: F_BUS > 2'000'000 ? 0b001 |
||||||
|
: 0b000) |
||||||
|
: FTM_DEFAULT_PSC[module]; |
||||||
|
|
||||||
|
public: |
||||||
|
static constexpr uintptr_t baseAdr = FTM_BaseAdr[module]; |
||||||
|
static constexpr IRQ_NUMBER_t irqNumber = (IRQ_NUMBER_t)IRQ_Numbers[boardNr][module]; |
||||||
|
static constexpr unsigned nrOfChannels = FTM_NrOfChannels[module]; |
||||||
|
static constexpr unsigned prescale = FTM_Prescale; |
||||||
|
}; |
||||||
|
} // namespace TeensyTimerTool
|
@ -0,0 +1,24 @@ |
|||||||
|
#if defined(ARDUINO_TEENSY40) || defined(ARDUINO_TEENSY41) || defined(ARDUINO_TEENSY_MICROMOD) |
||||||
|
|
||||||
|
#include "GPTChannel.h" |
||||||
|
#include "GPT.h" |
||||||
|
//#include "Arduino.h"
|
||||||
|
|
||||||
|
namespace TeensyTimerTool |
||||||
|
{ |
||||||
|
GptChannel::~GptChannel() |
||||||
|
{ |
||||||
|
stop(); |
||||||
|
setCallback(nullptr); |
||||||
|
|
||||||
|
if (regs == (IMXRT_GPT_t *)&IMXRT_GPT1) |
||||||
|
GPT_t<0>::end(); |
||||||
|
|
||||||
|
else if (regs == (IMXRT_GPT_t *)&IMXRT_GPT2) |
||||||
|
GPT_t<1>::end(); |
||||||
|
else |
||||||
|
postError(errorCode::GTP_err); |
||||||
|
} |
||||||
|
} // namespace TeensyTimerTool
|
||||||
|
|
||||||
|
#endif |
@ -1,7 +1,7 @@ |
|||||||
#pragma once |
#pragma once |
||||||
|
|
||||||
|
#include "imxrt.h" |
||||||
#include <cstdint> |
#include <cstdint> |
||||||
#include <imxrt.h> |
|
||||||
|
|
||||||
namespace TeensyTimerTool |
namespace TeensyTimerTool |
||||||
{ |
{ |
@ -1,8 +1,7 @@ |
|||||||
#pragma once |
#pragma once |
||||||
|
|
||||||
#include "../../ITimerChannel.h" |
|
||||||
#include "Arduino.h" |
#include "Arduino.h" |
||||||
#include "PITMap.h" |
#include "ITimerChannel.h" |
||||||
|
|
||||||
namespace TeensyTimerTool |
namespace TeensyTimerTool |
||||||
{ |
{ |
@ -0,0 +1,40 @@ |
|||||||
|
#include "config.h" |
||||||
|
|
||||||
|
#if defined(TEENSYDUINO) |
||||||
|
|
||||||
|
#include "TCK.h" |
||||||
|
|
||||||
|
namespace TeensyTimerTool |
||||||
|
{ |
||||||
|
bool TCK_t::isInitialized = false; |
||||||
|
TckChannelBase *TCK_t::channels[NR_OF_TCK_TIMERS]; |
||||||
|
} // namespace TeensyTimerTool
|
||||||
|
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
#if YIELD_TYPE == YIELD_OPTIMIZED |
||||||
|
|
||||||
|
void yield() |
||||||
|
{ |
||||||
|
TeensyTimerTool::TCK_t::tick(); |
||||||
|
} |
||||||
|
|
||||||
|
//----------------------------------------------------------------------
|
||||||
|
#elif YIELD_TYPE == YIELD_STANDARD |
||||||
|
|
||||||
|
#include "EventResponder.h" |
||||||
|
|
||||||
|
namespace TeensyTimerTool |
||||||
|
{ |
||||||
|
static EventResponder er; |
||||||
|
|
||||||
|
void initYieldHook() |
||||||
|
{ |
||||||
|
er.attach([](EventResponderRef r) { |
||||||
|
TeensyTimerTool::TCK_t::tick(); |
||||||
|
r.triggerEvent(); |
||||||
|
}); |
||||||
|
er.triggerEvent(); |
||||||
|
} |
||||||
|
} // namespace TeensyTimerTool
|
||||||
|
#endif |
||||||
|
#endif |
@ -0,0 +1,144 @@ |
|||||||
|
#pragma once |
||||||
|
|
||||||
|
//#include "Arduino.h"
|
||||||
|
#include "ErrorHandling/error_codes.h" |
||||||
|
#include "TckChannelBase.h" |
||||||
|
|
||||||
|
namespace TeensyTimerTool |
||||||
|
{ |
||||||
|
template <typename tckCounter> // tckCounter is the underlying counter, e.g. 32bit cycle counter, RTC counter, micros counter etc
|
||||||
|
class TckChannel : public TckChannelBase |
||||||
|
{ |
||||||
|
using counter_t = typename tckCounter::counter_t; // use the counter type (uint32_t, unit64_t...) of the used tckCounter class
|
||||||
|
|
||||||
|
public: |
||||||
|
TckChannel(); |
||||||
|
|
||||||
|
inline errorCode begin(callback_t cb, float period, bool periodic) override; |
||||||
|
inline errorCode start() override; |
||||||
|
inline errorCode stop() override; |
||||||
|
|
||||||
|
inline errorCode trigger(float delay_us) override; |
||||||
|
inline errorCode triggerDirect(counter_t reload) override; |
||||||
|
inline errorCode getTriggerReload(float delay, counter_t *reload) override; |
||||||
|
|
||||||
|
inline errorCode setNextPeriod(float us) override; |
||||||
|
inline errorCode setPeriod(float us) override; |
||||||
|
|
||||||
|
float getMaxPeriod() const override { return tckCounter::getMaxMicros() / 1E6f; } // seconds
|
||||||
|
|
||||||
|
protected: |
||||||
|
inline bool tick(); |
||||||
|
callback_t callback; |
||||||
|
counter_t startCnt, currentPeriod, nextPeriod; |
||||||
|
|
||||||
|
bool periodic; |
||||||
|
bool triggered; |
||||||
|
}; |
||||||
|
|
||||||
|
// IMPLEMENTATION ==============================================
|
||||||
|
|
||||||
|
template <typename tckCounter> |
||||||
|
TckChannel<tckCounter>::TckChannel() |
||||||
|
{ |
||||||
|
triggered = false; |
||||||
|
} |
||||||
|
|
||||||
|
template <typename tckCounter> |
||||||
|
errorCode TckChannel<tckCounter>::begin(callback_t cb, float period, bool periodic) |
||||||
|
{ |
||||||
|
this->triggered = false; |
||||||
|
|
||||||
|
this->periodic = periodic; |
||||||
|
if (periodic) |
||||||
|
{ |
||||||
|
this->currentPeriod = tckCounter::us2ticks(period); |
||||||
|
this->nextPeriod = this->currentPeriod; |
||||||
|
} |
||||||
|
this->callback = cb; |
||||||
|
|
||||||
|
return errorCode::OK; |
||||||
|
} |
||||||
|
|
||||||
|
template <typename tckCounter> |
||||||
|
errorCode TckChannel<tckCounter>::start() |
||||||
|
{ |
||||||
|
this->startCnt = tckCounter::getCount(); |
||||||
|
this->triggered = true; |
||||||
|
return errorCode::OK; |
||||||
|
} |
||||||
|
|
||||||
|
template <typename tckCounter> |
||||||
|
errorCode TckChannel<tckCounter>::stop() |
||||||
|
{ |
||||||
|
this->triggered = false; |
||||||
|
return errorCode::OK; |
||||||
|
} |
||||||
|
|
||||||
|
template <typename tckCounter> |
||||||
|
errorCode TckChannel<tckCounter>::triggerDirect(counter_t reload) |
||||||
|
{ |
||||||
|
this->startCnt = tckCounter::getCount(); |
||||||
|
this->nextPeriod = reload; |
||||||
|
this->currentPeriod = this->nextPeriod; |
||||||
|
this->triggered = true; |
||||||
|
return errorCode::OK; |
||||||
|
} |
||||||
|
|
||||||
|
template <typename tckCounter> |
||||||
|
errorCode TckChannel<tckCounter>::trigger(float delay) // µs
|
||||||
|
{ |
||||||
|
return triggerDirect(tckCounter::us2ticks(delay)); |
||||||
|
} |
||||||
|
|
||||||
|
template <typename tckCounter> |
||||||
|
errorCode TckChannel<tckCounter>::getTriggerReload(float delay, counter_t *reload) |
||||||
|
{ |
||||||
|
*reload = tckCounter::us2ticks(delay); |
||||||
|
return errorCode::OK; |
||||||
|
} |
||||||
|
|
||||||
|
template <typename tckCounter> |
||||||
|
errorCode TckChannel<tckCounter>::setNextPeriod(float us) |
||||||
|
{ |
||||||
|
nextPeriod = tckCounter::us2ticks(us); |
||||||
|
return errorCode::OK; |
||||||
|
} |
||||||
|
|
||||||
|
template <typename tckCounter> |
||||||
|
errorCode TckChannel<tckCounter>::setPeriod(float us) |
||||||
|
{ |
||||||
|
counter_t newPeriod = tckCounter::us2ticks(us); |
||||||
|
counter_t now = tckCounter::getCount(); |
||||||
|
|
||||||
|
if (now - startCnt >= newPeriod) // new period already expired
|
||||||
|
{ //
|
||||||
|
startCnt = now; // -> restart cycle but,
|
||||||
|
callback(); // since expired, invoke callback now
|
||||||
|
} //
|
||||||
|
currentPeriod = newPeriod; // in any case next callback will
|
||||||
|
nextPeriod = newPeriod; // be invoked after newly set period
|
||||||
|
return errorCode::OK; |
||||||
|
} |
||||||
|
|
||||||
|
template <typename tckCounter> |
||||||
|
bool TckChannel<tckCounter>::tick() |
||||||
|
{ |
||||||
|
static bool lock = false; |
||||||
|
counter_t now = tckCounter::getCount(); |
||||||
|
if (!lock && this->currentPeriod != 0 && this->triggered && (now - this->startCnt) >= this->currentPeriod) |
||||||
|
{ |
||||||
|
lock = true; |
||||||
|
this->startCnt += currentPeriod; |
||||||
|
this->currentPeriod = nextPeriod; // update period if it was changed during current cycle.
|
||||||
|
this->triggered = this->periodic; // i.e., stays triggerd if periodic, stops if oneShot
|
||||||
|
callback(); |
||||||
|
lock = false; |
||||||
|
return true; |
||||||
|
} else |
||||||
|
{ |
||||||
|
return false; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
} // namespace TeensyTimerTool
|
@ -0,0 +1,15 @@ |
|||||||
|
#if defined(TEENSYDUINO) |
||||||
|
|
||||||
|
#include "TckChannelBase.h" |
||||||
|
#include "Arduino.h" |
||||||
|
#include "TCK.h" |
||||||
|
|
||||||
|
namespace TeensyTimerTool |
||||||
|
{ |
||||||
|
TckChannelBase::~TckChannelBase() |
||||||
|
{ |
||||||
|
TCK_t::removeTimer(this); |
||||||
|
} |
||||||
|
} // namespace TeensyTimerTool
|
||||||
|
|
||||||
|
#endif |
@ -0,0 +1,108 @@ |
|||||||
|
|
||||||
|
#include "tickCounters.h" |
||||||
|
#include "Arduino.h" |
||||||
|
#include "boardDef.h" |
||||||
|
|
||||||
|
#if defined(TTT_TEENSY4X) |
||||||
|
#define TTT_F_CPU F_CPU_ACTUAL |
||||||
|
#else |
||||||
|
#define TTT_F_CPU F_CPU |
||||||
|
#endif |
||||||
|
|
||||||
|
namespace TeensyTimerTool |
||||||
|
{ |
||||||
|
//---------------------------------------------------------------------
|
||||||
|
// CycleCounter32
|
||||||
|
|
||||||
|
uint32_t CycleCounter32::getCount() |
||||||
|
{ |
||||||
|
return ARM_DWT_CYCCNT; |
||||||
|
} |
||||||
|
|
||||||
|
uint32_t CycleCounter32::us2ticks(float us) |
||||||
|
{ |
||||||
|
return us * (TTT_F_CPU / 1E6f); |
||||||
|
} |
||||||
|
|
||||||
|
float CycleCounter32::getMaxMicros() |
||||||
|
{ |
||||||
|
return 5E6; //Can't use full 2^32 *1.66ns =7.2s range since we need some headroom to detect reaching target. This works if we call tick() at least once per 2 seconds
|
||||||
|
} |
||||||
|
|
||||||
|
//---------------------------------------------------------------------
|
||||||
|
// CycleCounter64
|
||||||
|
|
||||||
|
uint64_t CycleCounter64::getCount() |
||||||
|
{ |
||||||
|
static uint32_t lastCyCnt = ARM_DWT_CYCCNT; |
||||||
|
static uint32_t curHIGH = 0; |
||||||
|
|
||||||
|
uint32_t now = ARM_DWT_CYCCNT; |
||||||
|
if (now < lastCyCnt) |
||||||
|
{ |
||||||
|
curHIGH++; |
||||||
|
} |
||||||
|
lastCyCnt = now; |
||||||
|
return ((uint64_t)curHIGH << 32) | now; |
||||||
|
} |
||||||
|
|
||||||
|
uint64_t CycleCounter64::us2ticks(float us) |
||||||
|
{ |
||||||
|
return us * (TTT_F_CPU / 1E6f); |
||||||
|
} |
||||||
|
|
||||||
|
float CycleCounter64::getMaxMicros() |
||||||
|
{ |
||||||
|
return 2E16f; //~630 years can't use full 2^64 *1.66ns range since we need some headroom to detect reaching target.
|
||||||
|
} |
||||||
|
|
||||||
|
//------------------------------------------------------------------
|
||||||
|
// MicrosCounter
|
||||||
|
|
||||||
|
uint32_t MicrosCounter::getCount() |
||||||
|
{ |
||||||
|
return micros(); |
||||||
|
} |
||||||
|
|
||||||
|
uint32_t MicrosCounter::us2ticks(float us) |
||||||
|
{ |
||||||
|
return us; |
||||||
|
} |
||||||
|
|
||||||
|
float MicrosCounter::getMaxMicros() |
||||||
|
{ |
||||||
|
return 70 * 60 * 1E6; // 70min, can't use full 2^32 µs = 72min range since we need some headroom to detect reaching target. This works if we call tick() at least once per 2min
|
||||||
|
} |
||||||
|
|
||||||
|
#if defined(TTT_TEENSY4X) |
||||||
|
//------------------------------------------------------------------
|
||||||
|
// RtcCounter
|
||||||
|
|
||||||
|
uint64_t RtcCounter::getCount() |
||||||
|
{ |
||||||
|
uint32_t hi1 = SNVS_HPRTCMR, lo1 = SNVS_HPRTCLR; |
||||||
|
while (true) |
||||||
|
{ |
||||||
|
uint32_t hi2 = SNVS_HPRTCMR, lo2 = SNVS_HPRTCLR; |
||||||
|
if (lo1 == lo2 && hi1 == hi2) |
||||||
|
{ |
||||||
|
return (uint64_t)hi2 << 32 | lo2; |
||||||
|
} |
||||||
|
hi1 = hi2; |
||||||
|
lo1 = lo2; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
uint64_t RtcCounter::us2ticks(float us) |
||||||
|
{ |
||||||
|
return us * (32'768 / 1E6f); |
||||||
|
} |
||||||
|
|
||||||
|
float RtcCounter::getMaxMicros() |
||||||
|
{ |
||||||
|
return 2E16f; // ~630 years
|
||||||
|
} |
||||||
|
|
||||||
|
#endif |
||||||
|
|
||||||
|
} // namespace TeensyTimerTool
|
@ -0,0 +1,68 @@ |
|||||||
|
#pragma once |
||||||
|
|
||||||
|
#include <cstdint> |
||||||
|
|
||||||
|
namespace TeensyTimerTool |
||||||
|
{ |
||||||
|
class CycleCounterBase |
||||||
|
{ |
||||||
|
protected: |
||||||
|
CycleCounterBase(); |
||||||
|
}; |
||||||
|
|
||||||
|
//---------------------------------------------------------------------
|
||||||
|
// Software counter based on the 32bit ARM cycle counter
|
||||||
|
// Resolution 1/FCPU_ACTUAL (1.66ns @600MHz)
|
||||||
|
//
|
||||||
|
struct CycleCounter32 : CycleCounterBase |
||||||
|
{ |
||||||
|
using counter_t = uint32_t; |
||||||
|
|
||||||
|
static counter_t getCount(); |
||||||
|
static uint32_t us2ticks(float us); |
||||||
|
static float getMaxMicros(); |
||||||
|
}; |
||||||
|
|
||||||
|
//---------------------------------------------------------------------
|
||||||
|
// Software counter based on a 64bit extension of
|
||||||
|
// the ARM cycle counter
|
||||||
|
// Resolution 1/FCPU_ACTUAL (6.66ns @600MHz)
|
||||||
|
//
|
||||||
|
struct CycleCounter64 : CycleCounterBase |
||||||
|
{ |
||||||
|
using counter_t = uint64_t; |
||||||
|
|
||||||
|
static uint64_t getCount(); |
||||||
|
static uint64_t us2ticks(float us); |
||||||
|
static float getMaxMicros(); |
||||||
|
}; |
||||||
|
|
||||||
|
//------------------------------------------------------------------
|
||||||
|
// Software counter based on the 64bit period counter
|
||||||
|
// of the built in real time clock (RTC).
|
||||||
|
// Resolution: 1/32768s (30.5 µs)
|
||||||
|
//
|
||||||
|
struct RtcCounter |
||||||
|
{ |
||||||
|
using counter_t = uint64_t; |
||||||
|
|
||||||
|
static counter_t getCount(); |
||||||
|
static counter_t us2ticks(float us); |
||||||
|
static float getMaxMicros(); |
||||||
|
}; |
||||||
|
|
||||||
|
//------------------------------------------------------------------
|
||||||
|
// Fallback counter for boards without cycle counter
|
||||||
|
// E.g. Teensy LC
|
||||||
|
// Resolution: 1 µs
|
||||||
|
//
|
||||||
|
struct MicrosCounter |
||||||
|
{ |
||||||
|
using counter_t = uint32_t; |
||||||
|
|
||||||
|
static counter_t getCount(); |
||||||
|
static counter_t us2ticks(float us); |
||||||
|
static float getMaxMicros(); |
||||||
|
}; |
||||||
|
|
||||||
|
} // namespace TeensyTimerTool
|
@ -1,14 +0,0 @@ |
|||||||
//#include "../../TeensyTimerTool.h"
|
|
||||||
#include "../../config.h" |
|
||||||
|
|
||||||
#if defined(HAS_TCK) and defined(UNO) |
|
||||||
|
|
||||||
namespace TeensyTimerTool |
|
||||||
{ |
|
||||||
bool TCK_t::isInitialized = false; |
|
||||||
TckChannel* TCK_t::channels[maxTckChannels]; |
|
||||||
} |
|
||||||
|
|
||||||
void yield() { TeensyTimerTool::TCK_t::tick(); } |
|
||||||
|
|
||||||
#endif |
|
@ -1,79 +0,0 @@ |
|||||||
#pragma once |
|
||||||
|
|
||||||
#include "TckChannel.h" |
|
||||||
#include <Arduino.h> |
|
||||||
#include <cstdint> |
|
||||||
|
|
||||||
namespace TeensyTimerTool |
|
||||||
{ |
|
||||||
constexpr unsigned maxTckChannels = 20; |
|
||||||
|
|
||||||
class TCK_t |
|
||||||
{ |
|
||||||
public: |
|
||||||
static inline ITimerChannel* getTimer(); |
|
||||||
static inline void removeTimer(TckChannel*); |
|
||||||
static inline void tick(); |
|
||||||
|
|
||||||
protected: |
|
||||||
static bool isInitialized;
|
|
||||||
static TckChannel* channels[maxTckChannels]; |
|
||||||
}; |
|
||||||
|
|
||||||
// IMPLEMENTATION ==================================================================
|
|
||||||
|
|
||||||
ITimerChannel* TCK_t::getTimer() |
|
||||||
{ |
|
||||||
Serial.printf("TCK getTimer()\n"); |
|
||||||
|
|
||||||
if (!isInitialized) |
|
||||||
{ |
|
||||||
for (unsigned chNr = 0; chNr < maxTckChannels; chNr++) |
|
||||||
{ |
|
||||||
channels[chNr] = nullptr; |
|
||||||
} |
|
||||||
isInitialized = true; |
|
||||||
} |
|
||||||
|
|
||||||
for (unsigned chNr = 0; chNr < maxTckChannels; chNr++) |
|
||||||
{ |
|
||||||
if (channels[chNr] == nullptr) |
|
||||||
{ |
|
||||||
channels[chNr] = new TckChannel(); |
|
||||||
return channels[chNr]; |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
return nullptr; |
|
||||||
} |
|
||||||
|
|
||||||
void TCK_t::removeTimer(TckChannel* channel) |
|
||||||
{ |
|
||||||
for (unsigned chNr = 0; chNr < maxTckChannels; chNr++) |
|
||||||
{ |
|
||||||
if (channels[chNr] == channel) |
|
||||||
{ |
|
||||||
channels[chNr] = nullptr; |
|
||||||
delete channel; |
|
||||||
break; |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
void TCK_t::tick() |
|
||||||
{ |
|
||||||
digitalWriteFast(12,HIGH); |
|
||||||
for(unsigned i = 0; i < maxTckChannels; i++) |
|
||||||
{ |
|
||||||
if (channels[i] != nullptr ) |
|
||||||
{ |
|
||||||
channels[i]->tick(); |
|
||||||
} |
|
||||||
} |
|
||||||
digitalWriteFast(12,LOW); |
|
||||||
} |
|
||||||
|
|
||||||
|
|
||||||
//constexpr TimerGenerator* TCK = TCK_t::getTimer;
|
|
||||||
|
|
||||||
} // namespace TeensyTimerTool
|
|
@ -1,89 +0,0 @@ |
|||||||
#pragma once |
|
||||||
|
|
||||||
#include "../ITimerChannel.h" |
|
||||||
#include "../types.h" |
|
||||||
#include "Arduino.h" |
|
||||||
|
|
||||||
namespace TeensyTimerTool |
|
||||||
{ |
|
||||||
class TCK_t; |
|
||||||
|
|
||||||
class TckChannel : public ITimerChannel |
|
||||||
{ |
|
||||||
public: |
|
||||||
inline TckChannel() { triggered = false; } |
|
||||||
inline virtual ~TckChannel(){}; |
|
||||||
|
|
||||||
inline void begin(callback_t cb, unsigned period, bool periodic) |
|
||||||
{ |
|
||||||
Serial.println("begin"); |
|
||||||
|
|
||||||
triggered = false; |
|
||||||
this->periodic = periodic; |
|
||||||
this->period = period * (F_CPU / 1'000'000); |
|
||||||
this->callback = cb; |
|
||||||
|
|
||||||
startCNT = ARM_DWT_CYCCNT;
|
|
||||||
} |
|
||||||
|
|
||||||
inline void start() |
|
||||||
{ |
|
||||||
Serial.println("start"); |
|
||||||
|
|
||||||
this->startCNT = ARM_DWT_CYCCNT; |
|
||||||
this->triggered = true; |
|
||||||
} |
|
||||||
|
|
||||||
inline void stop() |
|
||||||
{ |
|
||||||
this->triggered = false; |
|
||||||
} |
|
||||||
|
|
||||||
// inline void setPeriod(uint32_t microSeconds);
|
|
||||||
inline uint32_t getPeriod(void); |
|
||||||
|
|
||||||
inline void trigger(uint32_t delay) // µs
|
|
||||||
{ |
|
||||||
this->startCNT = ARM_DWT_CYCCNT; |
|
||||||
this->period = delay * (F_CPU / 1'000'000) - 68; |
|
||||||
this->triggered = true; |
|
||||||
} |
|
||||||
|
|
||||||
protected: |
|
||||||
uint32_t startCNT, period; |
|
||||||
callback_t callback; |
|
||||||
bool triggered; |
|
||||||
bool periodic; |
|
||||||
|
|
||||||
inline void tick(); |
|
||||||
bool block = false; |
|
||||||
|
|
||||||
friend TCK_t; |
|
||||||
}; |
|
||||||
|
|
||||||
// IMPLEMENTATION ==============================================
|
|
||||||
|
|
||||||
void TckChannel::tick() |
|
||||||
{ |
|
||||||
static bool lock = false;
|
|
||||||
|
|
||||||
if (!lock && period != 0 && triggered && (ARM_DWT_CYCCNT - startCNT) >= period) |
|
||||||
{ |
|
||||||
lock = true; |
|
||||||
startCNT = ARM_DWT_CYCCNT; |
|
||||||
triggered = periodic; // i.e., stays triggerd if periodic, stops if oneShot
|
|
||||||
callback(); |
|
||||||
lock = false; |
|
||||||
}
|
|
||||||
} |
|
||||||
|
|
||||||
// void TckChannel::setPeriod(uint32_t microSeconds)
|
|
||||||
// {
|
|
||||||
// period = microSeconds * (F_CPU / 1'000'000) ;
|
|
||||||
// }
|
|
||||||
uint32_t TckChannel::getPeriod() |
|
||||||
{ |
|
||||||
return period * (1'000'000.0f / F_CPU); |
|
||||||
} |
|
||||||
|
|
||||||
} // namespace TeensyTimerTool
|
|
@ -1,22 +0,0 @@ |
|||||||
#include "baseTimer.h" |
|
||||||
#include "Arduino.h" |
|
||||||
#include "types.h" |
|
||||||
|
|
||||||
namespace TeensyTimerTool |
|
||||||
{ |
|
||||||
|
|
||||||
BaseTimer::BaseTimer(TimerGenerator* generator, bool periodic) |
|
||||||
: timerGenerator(generator) |
|
||||||
{ |
|
||||||
this->timerGenerator = generator; |
|
||||||
this->timerChannel = nullptr; |
|
||||||
this->isPeriodic = periodic; |
|
||||||
} |
|
||||||
|
|
||||||
errorCode BaseTimer::setPrescaler(int psc) |
|
||||||
{ |
|
||||||
this->prescaler = psc; |
|
||||||
return errorCode::OK; |
|
||||||
} |
|
||||||
|
|
||||||
} |
|
@ -0,0 +1,41 @@ |
|||||||
|
#pragma once |
||||||
|
|
||||||
|
#include "config.h" |
||||||
|
#include <type_traits> |
||||||
|
|
||||||
|
#if defined(USE_TIME_LITERALS) |
||||||
|
#include "frequency.h" |
||||||
|
#include <chrono> |
||||||
|
#endif |
||||||
|
|
||||||
|
namespace TeensyTimerTool |
||||||
|
{ |
||||||
|
//--------------------------------------------------------------
|
||||||
|
// Transform arithmetic, period and frequency values to float
|
||||||
|
// For constant inputs the transformations are calculated at compile time
|
||||||
|
|
||||||
|
// Arithmetic types
|
||||||
|
template <class period_t, std::enable_if_t<std::is_arithmetic<period_t>::value, int> * = nullptr> |
||||||
|
float constexpr period2us(period_t v) |
||||||
|
{ |
||||||
|
return (float)v; |
||||||
|
} |
||||||
|
|
||||||
|
#if defined(USE_TIME_LITERALS) |
||||||
|
|
||||||
|
// Duration types (s, ms, ns...)
|
||||||
|
template <class period_t, std::enable_if_t<std::chrono::__is_duration<period_t>::value, int> * = nullptr> |
||||||
|
float constexpr period2us(period_t v) |
||||||
|
{ |
||||||
|
return (duration_cast<duration<float, std::micro>>(v).count()); |
||||||
|
} |
||||||
|
|
||||||
|
// Frequency types (Hz, MHz...)
|
||||||
|
template <class period_t, std::enable_if_t<TeensyTimerTool::__is_frequency<period_t>::value, int> * = nullptr> |
||||||
|
float constexpr period2us(period_t v) |
||||||
|
{ |
||||||
|
return 1'000'000 / duration_cast<hertz>(v).count(); |
||||||
|
} |
||||||
|
#endif |
||||||
|
|
||||||
|
} // namespace TeensyTimerTool
|
@ -1,66 +0,0 @@ |
|||||||
#pragma once |
|
||||||
|
|
||||||
#include "ErrorHandling/error_codes.h" |
|
||||||
#include "baseTimer.h" |
|
||||||
#include "type_traits" |
|
||||||
|
|
||||||
namespace TeensyTimerTool |
|
||||||
{ |
|
||||||
class OneShotTimer : public BaseTimer |
|
||||||
{ |
|
||||||
public: |
|
||||||
inline OneShotTimer(TimerGenerator* generator = nullptr); |
|
||||||
|
|
||||||
inline errorCode begin(callback_t cb); |
|
||||||
template <typename T> errorCode trigger(T delay); |
|
||||||
template <typename T> errorCode triggerDirect(T reload); |
|
||||||
template <typename T> errorCode getTriggerReload(float delay, T* reload); |
|
||||||
|
|
||||||
#if defined(USE_TIME_LITERALS) |
|
||||||
template <typename T, typename ratio> |
|
||||||
errorCode trigger(duration<T, ratio> _delay) |
|
||||||
{ |
|
||||||
T delay = duration_cast<microseconds>(_delay).count(); |
|
||||||
return trigger(delay); |
|
||||||
} |
|
||||||
#endif |
|
||||||
}; |
|
||||||
|
|
||||||
// Implementation ================================================
|
|
||||||
|
|
||||||
OneShotTimer::OneShotTimer(TimerGenerator* generator) |
|
||||||
: BaseTimer(generator, false) |
|
||||||
{} |
|
||||||
|
|
||||||
errorCode OneShotTimer::begin(callback_t callback) |
|
||||||
{ |
|
||||||
return BaseTimer::begin(callback, 0, false); |
|
||||||
} |
|
||||||
|
|
||||||
template <typename T> |
|
||||||
errorCode OneShotTimer::trigger(T delay) |
|
||||||
{ |
|
||||||
static_assert(std::is_integral<T>() || std::is_floating_point<T>(), "Only floating point or integral types allowed"); |
|
||||||
|
|
||||||
errorCode result; |
|
||||||
|
|
||||||
if (std::is_floating_point<T>()) |
|
||||||
result = timerChannel->trigger((float)delay); |
|
||||||
else |
|
||||||
result = timerChannel->trigger((uint32_t)delay); |
|
||||||
|
|
||||||
return result; |
|
||||||
} |
|
||||||
|
|
||||||
template <typename T> |
|
||||||
errorCode OneShotTimer::triggerDirect(T reload) |
|
||||||
{ |
|
||||||
return timerChannel->triggerDirect(reload); |
|
||||||
} |
|
||||||
|
|
||||||
template <typename T> |
|
||||||
errorCode OneShotTimer::getTriggerReload(float delay, T* reload) |
|
||||||
{ |
|
||||||
return timerChannel->getTriggerReload(delay, reload); |
|
||||||
} |
|
||||||
} |
|
@ -1,22 +0,0 @@ |
|||||||
#pragma once |
|
||||||
|
|
||||||
#include "baseTimer.h" |
|
||||||
|
|
||||||
namespace TeensyTimerTool |
|
||||||
{ |
|
||||||
class PeriodicTimer : public BaseTimer |
|
||||||
{ |
|
||||||
public: |
|
||||||
PeriodicTimer(TimerGenerator* generator = nullptr) |
|
||||||
: BaseTimer(generator, true) {} |
|
||||||
|
|
||||||
template <class T, std::enable_if_t<std::is_arithmetic<T>::value, int>* = nullptr> |
|
||||||
errorCode setPeriod(T p) { return postError(timerChannel->setPeriod((float)p)); } |
|
||||||
|
|
||||||
template <class T, std::enable_if_t<std::is_arithmetic<T>::value, int>* = nullptr> |
|
||||||
errorCode setNextPeriod(T p) { return postError(timerChannel->setNextPeriod((float)p)); } |
|
||||||
}; |
|
||||||
|
|
||||||
// IMPLEMENTATION =====================================================================
|
|
||||||
|
|
||||||
} |
|
Loading…
Reference in new issue