PlatformIO package of the Teensy core framework compatible with GCC 10 & C++20
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

479 line
14KB

  1. /*
  2. * Interrupt and PWM utilities for 16 bit Timer3 on ATmega168/328
  3. * Original code by Jesse Tane for http://labs.ideo.com August 2008
  4. * Modified March 2009 by Jérôme Despatis and Jesse Tane for ATmega328 support
  5. * Modified June 2009 by Michael Polli and Jesse Tane to fix a bug in setPeriod() which caused the timer to stop
  6. * Modified April 2012 by Paul Stoffregen - portable to other AVR chips, use inline functions
  7. * Modified again, June 2014 by Paul Stoffregen - support Teensy 3.1 & even more AVR chips
  8. *
  9. *
  10. * This is free software. You can redistribute it and/or modify it under
  11. * the terms of Creative Commons Attribution 3.0 United States License.
  12. * To view a copy of this license, visit http://creativecommons.org/licenses/by/3.0/us/
  13. * or send a letter to Creative Commons, 171 Second Street, Suite 300, San Francisco, California, 94105, USA.
  14. *
  15. */
  16. #ifndef TimerThree_h_
  17. #define TimerThree_h_
  18. #if defined(ARDUINO) && ARDUINO >= 100
  19. #include "Arduino.h"
  20. #else
  21. #include "WProgram.h"
  22. #endif
  23. #include "config/known_16bit_timers.h"
  24. #define TIMER3_RESOLUTION 65536UL // Timer3 is 16 bit
  25. // Placing nearly all the code in this .h file allows the functions to be
  26. // inlined by the compiler. In the very common case with constant values
  27. // the compiler will perform all calculations and simply write constants
  28. // to the hardware registers (for example, setPeriod).
  29. class TimerThree
  30. {
  31. #if defined(__AVR__)
  32. public:
  33. //****************************
  34. // Configuration
  35. //****************************
  36. void initialize(unsigned long microseconds=1000000) __attribute__((always_inline)) {
  37. TCCR3B = _BV(WGM33); // set mode as phase and frequency correct pwm, stop the timer
  38. TCCR3A = 0; // clear control register A
  39. setPeriod(microseconds);
  40. }
  41. void setPeriod(unsigned long microseconds) __attribute__((always_inline)) {
  42. const unsigned long cycles = (F_CPU / 2000000) * microseconds;
  43. if (cycles < TIMER3_RESOLUTION) {
  44. clockSelectBits = _BV(CS30);
  45. pwmPeriod = cycles;
  46. } else
  47. if (cycles < TIMER3_RESOLUTION * 8) {
  48. clockSelectBits = _BV(CS31);
  49. pwmPeriod = cycles / 8;
  50. } else
  51. if (cycles < TIMER3_RESOLUTION * 64) {
  52. clockSelectBits = _BV(CS31) | _BV(CS30);
  53. pwmPeriod = cycles / 64;
  54. } else
  55. if (cycles < TIMER3_RESOLUTION * 256) {
  56. clockSelectBits = _BV(CS32);
  57. pwmPeriod = cycles / 256;
  58. } else
  59. if (cycles < TIMER3_RESOLUTION * 1024) {
  60. clockSelectBits = _BV(CS32) | _BV(CS30);
  61. pwmPeriod = cycles / 1024;
  62. } else {
  63. clockSelectBits = _BV(CS32) | _BV(CS30);
  64. pwmPeriod = TIMER3_RESOLUTION - 1;
  65. }
  66. ICR3 = pwmPeriod;
  67. TCCR3B = _BV(WGM33) | clockSelectBits;
  68. }
  69. //****************************
  70. // Run Control
  71. //****************************
  72. void start() __attribute__((always_inline)) {
  73. TCCR3B = 0;
  74. TCNT3 = 0; // TODO: does this cause an undesired interrupt?
  75. resume();
  76. }
  77. void stop() __attribute__((always_inline)) {
  78. TCCR3B = _BV(WGM33);
  79. }
  80. void restart() __attribute__((always_inline)) {
  81. start();
  82. }
  83. void resume() __attribute__((always_inline)) {
  84. TCCR3B = _BV(WGM33) | clockSelectBits;
  85. }
  86. //****************************
  87. // PWM outputs
  88. //****************************
  89. void setPwmDuty(char pin, unsigned int duty) __attribute__((always_inline)) {
  90. unsigned long dutyCycle = pwmPeriod;
  91. dutyCycle *= duty;
  92. dutyCycle >>= 10;
  93. if (pin == TIMER3_A_PIN) OCR3A = dutyCycle;
  94. #ifdef TIMER3_B_PIN
  95. else if (pin == TIMER3_B_PIN) OCR3B = dutyCycle;
  96. #endif
  97. #ifdef TIMER3_C_PIN
  98. else if (pin == TIMER3_C_PIN) OCR3C = dutyCycle;
  99. #endif
  100. }
  101. void pwm(char pin, unsigned int duty) __attribute__((always_inline)) {
  102. if (pin == TIMER3_A_PIN) { pinMode(TIMER3_A_PIN, OUTPUT); TCCR3A |= _BV(COM3A1); }
  103. #ifdef TIMER3_B_PIN
  104. else if (pin == TIMER3_B_PIN) { pinMode(TIMER3_B_PIN, OUTPUT); TCCR3A |= _BV(COM3B1); }
  105. #endif
  106. #ifdef TIMER3_C_PIN
  107. else if (pin == TIMER3_C_PIN) { pinMode(TIMER3_C_PIN, OUTPUT); TCCR3A |= _BV(COM3C1); }
  108. #endif
  109. setPwmDuty(pin, duty);
  110. TCCR3B = _BV(WGM33) | clockSelectBits;
  111. }
  112. void pwm(char pin, unsigned int duty, unsigned long microseconds) __attribute__((always_inline)) {
  113. if (microseconds > 0) setPeriod(microseconds);
  114. pwm(pin, duty);
  115. }
  116. void disablePwm(char pin) __attribute__((always_inline)) {
  117. if (pin == TIMER3_A_PIN) TCCR3A &= ~_BV(COM3A1);
  118. #ifdef TIMER3_B_PIN
  119. else if (pin == TIMER3_B_PIN) TCCR3A &= ~_BV(COM3B1);
  120. #endif
  121. #ifdef TIMER3_C_PIN
  122. else if (pin == TIMER3_C_PIN) TCCR3A &= ~_BV(COM3C1);
  123. #endif
  124. }
  125. //****************************
  126. // Interrupt Function
  127. //****************************
  128. void attachInterrupt(void (*isr)()) __attribute__((always_inline)) {
  129. isrCallback = isr;
  130. TIMSK3 = _BV(TOIE3);
  131. }
  132. void attachInterrupt(void (*isr)(), unsigned long microseconds) __attribute__((always_inline)) {
  133. if(microseconds > 0) setPeriod(microseconds);
  134. attachInterrupt(isr);
  135. }
  136. void detachInterrupt() __attribute__((always_inline)) {
  137. TIMSK3 = 0;
  138. }
  139. static void (*isrCallback)();
  140. static void isrDefaultUnused();
  141. private:
  142. // properties
  143. static unsigned short pwmPeriod;
  144. static unsigned char clockSelectBits;
  145. #elif defined(__arm__) && defined(TEENSYDUINO) && (defined(KINETISK) || defined(KINETISL))
  146. #if defined(KINETISK)
  147. #define F_TIMER F_BUS
  148. #elif defined(KINETISL)
  149. #define F_TIMER (F_PLL/2)
  150. #endif
  151. // Use only 15 bit resolution. From K66 reference manual, 45.5.7 page 1200:
  152. // The CPWM pulse width (duty cycle) is determined by 2 x (CnV - CNTIN) and the
  153. // period is determined by 2 x (MOD - CNTIN). See the following figure. MOD must be
  154. // kept in the range of 0x0001 to 0x7FFF because values outside this range can produce
  155. // ambiguous results.
  156. #undef TIMER3_RESOLUTION
  157. #define TIMER3_RESOLUTION 32768
  158. public:
  159. //****************************
  160. // Configuration
  161. //****************************
  162. void initialize(unsigned long microseconds=1000000) __attribute__((always_inline)) {
  163. setPeriod(microseconds);
  164. }
  165. void setPeriod(unsigned long microseconds) __attribute__((always_inline)) {
  166. const unsigned long cycles = (F_TIMER / 2000000) * microseconds;
  167. /*
  168. // This code does not work properly in all cases :(
  169. // https://github.com/PaulStoffregen/TimerOne/issues/17
  170. if (cycles < TIMER3_RESOLUTION * 16) {
  171. if (cycles < TIMER3_RESOLUTION * 4) {
  172. if (cycles < TIMER3_RESOLUTION) {
  173. clockSelectBits = 0;
  174. pwmPeriod = cycles;
  175. }else{
  176. clockSelectBits = 1;
  177. pwmPeriod = cycles >> 1;
  178. }
  179. }else{
  180. if (cycles < TIMER3_RESOLUTION * 8) {
  181. clockSelectBits = 3;
  182. pwmPeriod = cycles >> 3;
  183. }else{
  184. clockSelectBits = 4;
  185. pwmPeriod = cycles >> 4;
  186. }
  187. }
  188. }else{
  189. if (cycles > TIMER3_RESOLUTION * 64) {
  190. if (cycles > TIMER3_RESOLUTION * 128) {
  191. clockSelectBits = 7;
  192. pwmPeriod = TIMER3_RESOLUTION - 1;
  193. }else{
  194. clockSelectBits = 7;
  195. pwmPeriod = cycles >> 7;
  196. }
  197. }else{
  198. if (cycles > TIMER3_RESOLUTION * 32) {
  199. clockSelectBits = 6;
  200. pwmPeriod = cycles >> 6;
  201. }else{
  202. clockSelectBits = 5;
  203. pwmPeriod = cycles >> 5;
  204. }
  205. }
  206. }
  207. */
  208. if (cycles < TIMER3_RESOLUTION) {
  209. clockSelectBits = 0;
  210. pwmPeriod = cycles;
  211. } else
  212. if (cycles < TIMER3_RESOLUTION * 2) {
  213. clockSelectBits = 1;
  214. pwmPeriod = cycles >> 1;
  215. } else
  216. if (cycles < TIMER3_RESOLUTION * 4) {
  217. clockSelectBits = 2;
  218. pwmPeriod = cycles >> 2;
  219. } else
  220. if (cycles < TIMER3_RESOLUTION * 8) {
  221. clockSelectBits = 3;
  222. pwmPeriod = cycles >> 3;
  223. } else
  224. if (cycles < TIMER3_RESOLUTION * 16) {
  225. clockSelectBits = 4;
  226. pwmPeriod = cycles >> 4;
  227. } else
  228. if (cycles < TIMER3_RESOLUTION * 32) {
  229. clockSelectBits = 5;
  230. pwmPeriod = cycles >> 5;
  231. } else
  232. if (cycles < TIMER3_RESOLUTION * 64) {
  233. clockSelectBits = 6;
  234. pwmPeriod = cycles >> 6;
  235. } else
  236. if (cycles < TIMER3_RESOLUTION * 128) {
  237. clockSelectBits = 7;
  238. pwmPeriod = cycles >> 7;
  239. } else {
  240. clockSelectBits = 7;
  241. pwmPeriod = TIMER3_RESOLUTION - 1;
  242. }
  243. uint32_t sc = FTM2_SC;
  244. FTM2_SC = 0;
  245. FTM2_MOD = pwmPeriod;
  246. FTM2_SC = FTM_SC_CLKS(1) | FTM_SC_CPWMS | clockSelectBits | (sc & FTM_SC_TOIE);
  247. }
  248. //****************************
  249. // Run Control
  250. //****************************
  251. void start() __attribute__((always_inline)) {
  252. stop();
  253. FTM2_CNT = 0;
  254. resume();
  255. }
  256. void stop() __attribute__((always_inline)) {
  257. FTM2_SC = FTM2_SC & (FTM_SC_TOIE | FTM_SC_CPWMS | FTM_SC_PS(7));
  258. }
  259. void restart() __attribute__((always_inline)) {
  260. start();
  261. }
  262. void resume() __attribute__((always_inline)) {
  263. FTM2_SC = (FTM2_SC & (FTM_SC_TOIE | FTM_SC_PS(7))) | FTM_SC_CPWMS | FTM_SC_CLKS(1);
  264. }
  265. //****************************
  266. // PWM outputs
  267. //****************************
  268. void setPwmDuty(char pin, unsigned int duty) __attribute__((always_inline)) {
  269. unsigned long dutyCycle = pwmPeriod;
  270. dutyCycle *= duty;
  271. dutyCycle >>= 10;
  272. if (pin == TIMER3_A_PIN) {
  273. FTM2_C0V = dutyCycle;
  274. } else if (pin == TIMER3_B_PIN) {
  275. FTM2_C1V = dutyCycle;
  276. }
  277. }
  278. void pwm(char pin, unsigned int duty) __attribute__((always_inline)) {
  279. setPwmDuty(pin, duty);
  280. if (pin == TIMER3_A_PIN) {
  281. *portConfigRegister(TIMER3_A_PIN) = PORT_PCR_MUX(3) | PORT_PCR_DSE | PORT_PCR_SRE;
  282. } else if (pin == TIMER3_B_PIN) {
  283. *portConfigRegister(TIMER3_B_PIN) = PORT_PCR_MUX(3) | PORT_PCR_DSE | PORT_PCR_SRE;
  284. }
  285. }
  286. void pwm(char pin, unsigned int duty, unsigned long microseconds) __attribute__((always_inline)) {
  287. if (microseconds > 0) setPeriod(microseconds);
  288. pwm(pin, duty);
  289. }
  290. void disablePwm(char pin) __attribute__((always_inline)) {
  291. if (pin == TIMER3_A_PIN) {
  292. *portConfigRegister(TIMER3_A_PIN) = 0;
  293. } else if (pin == TIMER3_B_PIN) {
  294. *portConfigRegister(TIMER3_B_PIN) = 0;
  295. }
  296. }
  297. //****************************
  298. // Interrupt Function
  299. //****************************
  300. void attachInterrupt(void (*isr)()) __attribute__((always_inline)) {
  301. isrCallback = isr;
  302. FTM2_SC |= FTM_SC_TOIE;
  303. NVIC_ENABLE_IRQ(IRQ_FTM2);
  304. }
  305. void attachInterrupt(void (*isr)(), unsigned long microseconds) __attribute__((always_inline)) {
  306. if(microseconds > 0) setPeriod(microseconds);
  307. attachInterrupt(isr);
  308. }
  309. void detachInterrupt() __attribute__((always_inline)) {
  310. FTM2_SC &= ~FTM_SC_TOIE;
  311. NVIC_DISABLE_IRQ(IRQ_FTM2);
  312. }
  313. static void (*isrCallback)();
  314. static void isrDefaultUnused();
  315. private:
  316. // properties
  317. static unsigned short pwmPeriod;
  318. static unsigned char clockSelectBits;
  319. #undef F_TIMER
  320. #elif defined(__arm__) && defined(TEENSYDUINO) && defined(__IMXRT1062__)
  321. public:
  322. //****************************
  323. // Configuration
  324. //****************************
  325. void initialize(unsigned long microseconds=1000000) __attribute__((always_inline)) {
  326. setPeriod(microseconds);
  327. }
  328. void setPeriod(unsigned long microseconds) __attribute__((always_inline)) {
  329. uint32_t period = (float)F_BUS_ACTUAL * (float)microseconds * 0.0000005f;
  330. uint32_t prescale = 0;
  331. while (period > 32767) {
  332. period = period >> 1;
  333. if (++prescale > 7) {
  334. prescale = 7; // when F_BUS is 150 MHz, longest
  335. period = 32767; // period is 55922 us (~17.9 Hz)
  336. break;
  337. }
  338. }
  339. //Serial.printf("setPeriod, period=%u, prescale=%u\n", period, prescale);
  340. FLEXPWM2_FCTRL0 |= FLEXPWM_FCTRL0_FLVL(4); // logic high = fault
  341. FLEXPWM2_FSTS0 = 0x0008; // clear fault status
  342. FLEXPWM2_MCTRL |= FLEXPWM_MCTRL_CLDOK(4);
  343. FLEXPWM2_SM2CTRL2 = FLEXPWM_SMCTRL2_INDEP;
  344. FLEXPWM2_SM2CTRL = FLEXPWM_SMCTRL_HALF | FLEXPWM_SMCTRL_PRSC(prescale);
  345. FLEXPWM2_SM2INIT = -period;
  346. FLEXPWM2_SM2VAL0 = 0;
  347. FLEXPWM2_SM2VAL1 = period;
  348. FLEXPWM2_SM2VAL2 = 0;
  349. FLEXPWM2_SM2VAL3 = 0;
  350. FLEXPWM2_SM2VAL4 = 0;
  351. FLEXPWM2_SM2VAL5 = 0;
  352. FLEXPWM2_MCTRL |= FLEXPWM_MCTRL_LDOK(4) | FLEXPWM_MCTRL_RUN(4);
  353. pwmPeriod = period;
  354. }
  355. //****************************
  356. // Run Control
  357. //****************************
  358. void start() __attribute__((always_inline)) {
  359. stop();
  360. // TODO: how to force counter back to zero?
  361. resume();
  362. }
  363. void stop() __attribute__((always_inline)) {
  364. FLEXPWM2_MCTRL &= ~FLEXPWM_MCTRL_RUN(4);
  365. }
  366. void restart() __attribute__((always_inline)) {
  367. start();
  368. }
  369. void resume() __attribute__((always_inline)) {
  370. FLEXPWM2_MCTRL |= FLEXPWM_MCTRL_RUN(4);
  371. }
  372. //****************************
  373. // PWM outputs
  374. //****************************
  375. void setPwmDuty(char pin, unsigned int duty) __attribute__((always_inline)) {
  376. if (duty > 1023) duty = 1023;
  377. int dutyCycle = (pwmPeriod * duty) >> 10;
  378. //Serial.printf("setPwmDuty, period=%u\n", dutyCycle);
  379. if (pin == TIMER3_A_PIN) {
  380. FLEXPWM2_MCTRL |= FLEXPWM_MCTRL_CLDOK(4);
  381. FLEXPWM2_SM2VAL5 = dutyCycle;
  382. FLEXPWM2_SM2VAL4 = -dutyCycle;
  383. FLEXPWM2_MCTRL |= FLEXPWM_MCTRL_LDOK(4);
  384. } else if (pin == TIMER3_B_PIN) {
  385. FLEXPWM2_MCTRL |= FLEXPWM_MCTRL_CLDOK(4);
  386. FLEXPWM2_SM2VAL3 = dutyCycle;
  387. FLEXPWM2_SM2VAL2 = -dutyCycle;
  388. FLEXPWM2_MCTRL |= FLEXPWM_MCTRL_LDOK(4);
  389. }
  390. }
  391. void pwm(char pin, unsigned int duty) __attribute__((always_inline)) {
  392. setPwmDuty(pin, duty);
  393. if (pin == TIMER3_A_PIN) {
  394. FLEXPWM2_OUTEN |= FLEXPWM_OUTEN_PWMB_EN(4);
  395. IOMUXC_SW_MUX_CTL_PAD_GPIO_B0_11 = 2; // pin 9 FLEXPWM2_PWM2_B
  396. } else if (pin == TIMER3_B_PIN) {
  397. FLEXPWM2_OUTEN |= FLEXPWM_OUTEN_PWMA_EN(4);
  398. IOMUXC_SW_MUX_CTL_PAD_GPIO_B0_10 = 2; // pin 6 FLEXPWM2_PWM2_A
  399. }
  400. }
  401. void pwm(char pin, unsigned int duty, unsigned long microseconds) __attribute__((always_inline)) {
  402. if (microseconds > 0) setPeriod(microseconds);
  403. pwm(pin, duty);
  404. }
  405. void disablePwm(char pin) __attribute__((always_inline)) {
  406. if (pin == TIMER3_A_PIN) {
  407. IOMUXC_SW_MUX_CTL_PAD_GPIO_B0_11 = 5; // pin 9 FLEXPWM2_PWM2_B
  408. FLEXPWM2_OUTEN &= ~FLEXPWM_OUTEN_PWMB_EN(4);
  409. } else if (pin == TIMER3_B_PIN) {
  410. IOMUXC_SW_MUX_CTL_PAD_GPIO_B0_10 = 5; // pin 6 FLEXPWM2_PWM2_A
  411. FLEXPWM2_OUTEN &= ~FLEXPWM_OUTEN_PWMA_EN(4);
  412. }
  413. }
  414. //****************************
  415. // Interrupt Function
  416. //****************************
  417. void attachInterrupt(void (*f)()) __attribute__((always_inline)) {
  418. isrCallback = f;
  419. attachInterruptVector(IRQ_FLEXPWM2_2, &isr);
  420. FLEXPWM2_SM2STS = FLEXPWM_SMSTS_RF;
  421. FLEXPWM2_SM2INTEN = FLEXPWM_SMINTEN_RIE;
  422. NVIC_ENABLE_IRQ(IRQ_FLEXPWM2_2);
  423. }
  424. void attachInterrupt(void (*f)(), unsigned long microseconds) __attribute__((always_inline)) {
  425. if(microseconds > 0) setPeriod(microseconds);
  426. attachInterrupt(f);
  427. }
  428. void detachInterrupt() __attribute__((always_inline)) {
  429. NVIC_DISABLE_IRQ(IRQ_FLEXPWM2_2);
  430. FLEXPWM2_SM2INTEN = 0;
  431. }
  432. static void isr(void);
  433. static void (*isrCallback)();
  434. static void isrDefaultUnused();
  435. private:
  436. // properties
  437. static unsigned short pwmPeriod;
  438. static unsigned char clockSelectBits;
  439. #endif
  440. };
  441. extern TimerThree Timer3;
  442. #endif