diff --git a/TimerOne.h b/TimerOne.h index c3f6f2f..3376feb 100644 --- a/TimerOne.h +++ b/TimerOne.h @@ -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; @@ -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. } //**************************** @@ -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... } //**************************** diff --git a/examples/testing_setPeriod/testing_setPeriod.ino b/examples/testing_setPeriod/testing_setPeriod.ino new file mode 100644 index 0000000..5d46477 --- /dev/null +++ b/examples/testing_setPeriod/testing_setPeriod.ino @@ -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 + +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) + */ +}