Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

In connection with #13, changes to setPeriod() #14

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
46 changes: 42 additions & 4 deletions TimerOne.h
Original file line number Diff line number Diff line change
Expand Up @@ -175,10 +175,33 @@ class TimerOne
// Configuration
//****************************
void initialize(unsigned long microseconds=1000000) __attribute__((always_inline)) {
FTM1_SC = 0; // moved from inside setPeriod to here!
FTM1_FMS = 0;
FTM1_MODE = FTM_MODE_FTMEN; // WPDIS set, all registers accessible
FTM1_SYNC |= FTM_SYNC_CNTMIN; // CNTMIN load point
// Enhanced PWM sync:
// SWWRBUF : Let Software event trigger update of MOD/CnV.
// Software event is nothing but "setting of FTM1_SYNC_SWSYNC"
// SWRSTCNT: Update MOD and CnV as soon as Software even occurs -- do not wait for
// "the next loading point"
FTM1_SYNCONF |= FTM_SYNCONF_SYNCMODE | FTM_SYNCONF_SWWRBUF | FTM_SYNCONF_SWRSTCNT;
setPeriod(microseconds);
}
void setPeriod(unsigned long microseconds) __attribute__((always_inline)) {
const unsigned long cycles = (F_TIMER / 2000000) * microseconds;
// @Paul
// I don't know what to call the second argument. It basically overrides CPWM if the user
// wants to setPeriod in each ISR (because there is no other way).
//
// Since CPWM cannot work alongside this use case, you might want to keep this "mode switch" inside
// `initialize()` Or we could think of 2 setPeriod functions, with better names ofcourse...
// Its upto you..
void setPeriod(unsigned long microseconds, uint8_t frequent_update=0) __attribute__((always_inline)) {
unsigned long factor;
if (frequent_update > 0)
factor = (F_TIMER / 1000000);
else
factor = (F_TIMER / 2000000);
const unsigned long cycles = factor * microseconds;

if (cycles < TIMER1_RESOLUTION) {
clockSelectBits = 0;
pwmPeriod = cycles;
Expand Down Expand Up @@ -215,9 +238,21 @@ class TimerOne
pwmPeriod = TIMER1_RESOLUTION - 1;
}
uint32_t sc = FTM1_SC;
FTM1_SC = 0;
// FTM1_SC = 0 not needed anymore!
FTM1_MOD = pwmPeriod;
FTM1_SC = FTM_SC_CLKS(1) | FTM_SC_CPWMS | clockSelectBits | (sc & FTM_SC_TOIE);
if (frequent_update)
FTM1_SC = FTM_SC_CLKS(1) | clockSelectBits | (sc & FTM_SC_TOIE);
else
FTM1_SC = FTM_SC_CLKS(1) | FTM_SC_CPWMS | clockSelectBits | (sc & FTM_SC_TOIE);
FTM1_SYNC |= FTM_SYNC_SWSYNC; // this bit is cleared by hardware.
// This might be negligibly faster than FTM1_SC = 0; FTM1_CNT = FTM1_CNT;
// Moreover the configuration can give you another library method:
// reset_pwm(){
// FTM1_SYNC |= FTM_SYNC_SWSYNC;
// }
// This configuration might help in powerful multi-channel PWM synchronisation. But it
// would require a lot more thinking on how to present all this power in a simple
// interface from TimerOne.
}

//****************************
Expand All @@ -236,6 +271,9 @@ class TimerOne
}
void resume() __attribute__((always_inline)) {
FTM1_SC = (FTM1_SC & (FTM_SC_TOIE | FTM_SC_PS(7))) | FTM_SC_CPWMS | FTM_SC_CLKS(1);
// Hmm, difficult to decide whether CPWM should be enabled at "every restart"
// It's probably better to keep that "mode switch" in `initialize()` to avoid pitfalls
// like this one...
}

//****************************
Expand Down
58 changes: 58 additions & 0 deletions examples/testing_setPeriod/testing_setPeriod.ino
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
// This program is for testing purposes.
// All setPeriods() have been commented out.
// By selectively uncommenting, and changing arguments you can test
// various scenarios.
//
// Contact @arrow- (ananya95@gmail.com or @jukedude on pjrc forum)

#include <TimerOne.h>

void setup(void)
{
pinMode(13, OUTPUT);
Timer1.initialize(20000);
Timer1.attachInterrupt(blinkLED);
Serial.begin(115200);
}

// The interrupt will blink the LED, and
// mainloop will print FTM1 configuration
unsigned long vv[] = {120000, 2000};

unsigned long last_event_micros = 0;
volatile uint8_t lstate = 0, wflag = 0;

void blinkLED(void)
{
lstate = !lstate;
wflag = 1;
digitalWrite(13, lstate);
/*
if (lstate)
FTM1_MOD = 65535;
else
FTM1_MOD = 324;
*/
//Timer1.setPeriod(vv[lstate], 1); // this is a frequent update
}

void loop(void)
{
if (wflag == 1){
Serial.print(micros() - last_event_micros); // slight errors (upto 10us) acceptable
last_event_micros = micros();
Serial.print(" ");
Serial.print(FTM1_MOD);
Serial.print(" ");
Serial.println(FTM1_SC & 0x7);
wflag = 0;

//Timer1.setPeriod(11650, 1); // this is a frequent update
}
/*
delay(4000);
Timer1.setPeriod(80000); // this is not a frequent update -- because of above delay.
// the printout value will be wrong but you can verify from
// MOD*2*PS/F_BUS (in sec) == setPeriod's argument (in usec)
*/
}