Teensy 4.1 core updated for C++20
Du kan inte välja fler än 25 ämnen Ämnen måste starta med en bokstav eller siffra, kan innehålla bindestreck ('-') och vara max 35 tecken långa.

205 lines
6.5KB

  1. /* Copyright (c) 2013 Daniel Gilbert, loglow@gmail.com
  2. Permission is hereby granted, free of charge, to any person obtaining a copy of
  3. this software and associated documentation files (the "Software"), to deal in the
  4. Software without restriction, including without limitation the rights to use, copy,
  5. modify, merge, publish, distribute, sublicense, and/or sell copies of the Software,
  6. and to permit persons to whom the Software is furnished to do so, subject to the
  7. following conditions:
  8. The above copyright notice and this permission notice shall be included in all
  9. copies or substantial portions of the Software.
  10. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
  11. INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
  12. PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
  13. HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
  14. OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
  15. SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
  16. #include "IntervalTimer.h"
  17. // ------------------------------------------------------------
  18. // static class variables need to be reiterated here before use
  19. // ------------------------------------------------------------
  20. bool IntervalTimer::PIT_enabled;
  21. bool IntervalTimer::PIT_used[];
  22. IntervalTimer::ISR IntervalTimer::PIT_ISR[];
  23. // ------------------------------------------------------------
  24. // these are the ISRs (Interrupt Service Routines) that get
  25. // called by each PIT timer when it fires. they're defined here
  26. // so that they can auto-clear themselves and so the user can
  27. // specify a custom ISR and reassign it as needed
  28. // ------------------------------------------------------------
  29. #if defined(KINETISK)
  30. void pit0_isr() { PIT_TFLG0 = 1; IntervalTimer::PIT_ISR[0](); }
  31. void pit1_isr() { PIT_TFLG1 = 1; IntervalTimer::PIT_ISR[1](); }
  32. void pit2_isr() { PIT_TFLG2 = 1; IntervalTimer::PIT_ISR[2](); }
  33. void pit3_isr() { PIT_TFLG3 = 1; IntervalTimer::PIT_ISR[3](); }
  34. #elif defined(KINETISL)
  35. void pit_isr() {
  36. if (PIT_TFLG0) { PIT_TFLG0 = 1; IntervalTimer::PIT_ISR[0](); }
  37. if (!IntervalTimer::PIT_enabled) return;
  38. if (PIT_TFLG1) { PIT_TFLG1 = 1; IntervalTimer::PIT_ISR[1](); }
  39. }
  40. #endif
  41. // ------------------------------------------------------------
  42. // this function inits and starts the timer, using the specified
  43. // function as a callback and the period provided. must be passed
  44. // the name of a function taking no arguments and returning void.
  45. // make sure this function can complete within the time allowed.
  46. // attempts to allocate a timer using available resources,
  47. // returning true on success or false in case of failure.
  48. // period is specified as number of bus cycles
  49. // ------------------------------------------------------------
  50. bool IntervalTimer::beginCycles(ISR newISR, uint32_t newValue) {
  51. // if this interval timer is already running, stop it
  52. if (status == TIMER_PIT) {
  53. stop_PIT();
  54. status = TIMER_OFF;
  55. }
  56. // store callback pointer
  57. myISR = newISR;
  58. // attempt to allocate this timer
  59. if (allocate_PIT(newValue)) status = TIMER_PIT;
  60. else status = TIMER_OFF;
  61. // check for success and return
  62. if (status != TIMER_OFF) return true;
  63. return false;
  64. }
  65. // ------------------------------------------------------------
  66. // stop the timer if it's currently running, using its status
  67. // to determine what hardware resources the timer may be using
  68. // ------------------------------------------------------------
  69. void IntervalTimer::end() {
  70. if (status == TIMER_PIT) stop_PIT();
  71. status = TIMER_OFF;
  72. }
  73. // ------------------------------------------------------------
  74. // enables the PIT clock bit, the master PIT reg, and sets flag
  75. // ------------------------------------------------------------
  76. void IntervalTimer::enable_PIT() {
  77. SIM_SCGC6 |= SIM_SCGC6_PIT;
  78. PIT_MCR = 0;
  79. PIT_enabled = true;
  80. }
  81. // ------------------------------------------------------------
  82. // disables the master PIT reg, the PIT clock bit, and unsets flag
  83. // ------------------------------------------------------------
  84. void IntervalTimer::disable_PIT() {
  85. PIT_MCR = 1;
  86. SIM_SCGC6 &= ~SIM_SCGC6_PIT;
  87. PIT_enabled = false;
  88. }
  89. // ------------------------------------------------------------
  90. // enables the PIT clock if not already enabled, then checks to
  91. // see if any PITs are available for use. if one is available,
  92. // it's initialized and started with the specified value, and
  93. // the function returns true, otherwise it returns false
  94. // ------------------------------------------------------------
  95. bool IntervalTimer::allocate_PIT(uint32_t newValue) {
  96. // enable clock to the PIT module if necessary
  97. if (!PIT_enabled) enable_PIT();
  98. // check for an available PIT, and if so, start it
  99. for (uint8_t id = 0; id < NUM_PIT; id++) {
  100. if (!PIT_used[id]) {
  101. PIT_id = id;
  102. start_PIT(newValue);
  103. PIT_used[id] = true;
  104. return true;
  105. }
  106. }
  107. // no PIT available
  108. return false;
  109. }
  110. // ------------------------------------------------------------
  111. // configuters a PIT's registers, function pointer, and enables
  112. // interrupts, effectively starting the timer upon completion
  113. // ------------------------------------------------------------
  114. void IntervalTimer::start_PIT(uint32_t newValue) {
  115. // point to the correct registers
  116. PIT_LDVAL = &PIT_LDVAL0 + PIT_id * 4;
  117. PIT_TCTRL = &PIT_TCTRL0 + PIT_id * 4;
  118. // point to the correct PIT ISR
  119. PIT_ISR[PIT_id] = myISR;
  120. // write value to register and enable interrupt
  121. *PIT_TCTRL = 0;
  122. *PIT_LDVAL = newValue;
  123. *PIT_TCTRL = 3;
  124. #if defined(KINETISK)
  125. IRQ_PIT_CH = IRQ_PIT_CH0 + PIT_id;
  126. NVIC_SET_PRIORITY(IRQ_PIT_CH, nvic_priority);
  127. NVIC_ENABLE_IRQ(IRQ_PIT_CH);
  128. #elif defined(KINETISL)
  129. NVIC_SET_PRIORITY(IRQ_PIT, nvic_priority); // TODO: use the higher of both channels, shared irq
  130. NVIC_ENABLE_IRQ(IRQ_PIT);
  131. #endif
  132. }
  133. // ------------------------------------------------------------
  134. // stops an active PIT by disabling its interrupt, writing to
  135. // its control register, and freeing up its state for future use.
  136. // also, if no PITs remain in use, disables the core PIT clock
  137. // ------------------------------------------------------------
  138. void IntervalTimer::stop_PIT() {
  139. // disable interrupt and PIT
  140. *PIT_TCTRL = 0;
  141. #if defined(KINETISK)
  142. NVIC_DISABLE_IRQ(IRQ_PIT_CH);
  143. #elif defined(KINETISL)
  144. NVIC_DISABLE_IRQ(IRQ_PIT);
  145. #endif
  146. // free PIT for future use
  147. PIT_used[PIT_id] = false;
  148. // check if we're still using any PIT
  149. for (uint8_t id = 0; id < NUM_PIT; id++) {
  150. if (PIT_used[id]) return;
  151. }
  152. // none used, disable PIT clock
  153. disable_PIT();
  154. }