PlatformIO package of the Teensy core framework compatible with GCC 10 & C++20
Du kannst nicht mehr als 25 Themen auswählen Themen müssen entweder mit einem Buchstaben oder einer Ziffer beginnen. Sie können Bindestriche („-“) enthalten und bis zu 35 Zeichen lang sein.

423 Zeilen
14KB

  1. /* PulsePosition Library for Teensy 3.x, LC, and 4.0
  2. * High resolution input and output of PPM encoded signals
  3. * http://www.pjrc.com/teensy/td_libs_PulsePosition.html
  4. * Copyright (c) 2014, Paul Stoffregen, paul@pjrc.com
  5. *
  6. * Development of this library was funded by PJRC.COM, LLC by sales of Teensy
  7. * boards. Please support PJRC's efforts to develop open source software by
  8. * purchasing Teensy or other PJRC products.
  9. *
  10. * Permission is hereby granted, free of charge, to any person obtaining a copy
  11. * of this software and associated documentation files (the "Software"), to deal
  12. * in the Software without restriction, including without limitation the rights
  13. * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  14. * copies of the Software, and to permit persons to whom the Software is
  15. * furnished to do so, subject to the following conditions:
  16. *
  17. * The above copyright notice, development funding notice, and this permission
  18. * notice shall be included in all copies or substantial portions of the Software.
  19. *
  20. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  21. * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  22. * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  23. * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  24. * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  25. * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  26. * THE SOFTWARE.
  27. */
  28. #if defined(__arm__) && defined(TEENSYDUINO) && (defined(__MKL26Z64__) || defined(__MK20DX128__) || defined(__MK20DX256__) || defined(__MK64FX512__) || defined(__MK66FX1M0__))
  29. #include "PulsePosition.h"
  30. // Timing parameters, in microseconds.
  31. // The shortest time allowed between any 2 rising edges. This should be at
  32. // least double TX_PULSE_WIDTH.
  33. #define TX_MINIMUM_SIGNAL 300.0
  34. // The longest time allowed between any 2 rising edges for a normal signal.
  35. #define TX_MAXIMUM_SIGNAL 2500.0
  36. // The default signal to send if nothing has been written.
  37. #define TX_DEFAULT_SIGNAL 1500.0
  38. // When transmitting with a single pin, the minimum space signal that marks
  39. // the end of a frame. Single wire receivers recognize the end of a frame
  40. // by looking for a gap longer than the maximum data size. When viewing the
  41. // waveform on an oscilloscope, set the trigger "holdoff" time to slightly
  42. // less than TX_MINIMUM_SPACE, for the most reliable display. This parameter
  43. // is not used when transmitting with 2 pins.
  44. #define TX_MINIMUM_SPACE 5000.0
  45. // The minimum total frame size. Some servo motors or other devices may not
  46. // work with pulses the repeat more often than 50 Hz. To allow transmission
  47. // as fast as possible, set this to the same as TX_MINIMUM_SIGNAL.
  48. #define TX_MINIMUM_FRAME 20000.0
  49. // The length of all transmitted pulses. This must be longer than the worst
  50. // case interrupt latency, which depends on how long any other library may
  51. // disable interrupts. This must also be no more than half TX_MINIMUM_SIGNAL.
  52. // Most libraries disable interrupts for no more than a few microseconds.
  53. // The OneWire library is a notable exception, so this may need to be lengthened
  54. // if a library that imposes unusual interrupt latency is in use.
  55. #define TX_PULSE_WIDTH 100.0
  56. // When receiving, any time between rising edges longer than this will be
  57. // treated as the end-of-frame marker.
  58. #define RX_MINIMUM_SPACE 3500.0
  59. // convert from microseconds to I/O clock ticks
  60. #if defined(KINETISK)
  61. #define CLOCKS_PER_MICROSECOND ((double)F_BUS / 1000000.0)
  62. #elif defined(KINETISL)
  63. #define CLOCKS_PER_MICROSECOND ((double)F_PLL / 2000000.0)
  64. #endif
  65. #define TX_MINIMUM_SIGNAL_CLOCKS (uint32_t)(TX_MINIMUM_SIGNAL * CLOCKS_PER_MICROSECOND)
  66. #define TX_MAXIMUM_SIGNAL_CLOCKS (uint32_t)(TX_MAXIMUM_SIGNAL * CLOCKS_PER_MICROSECOND)
  67. #define TX_DEFAULT_SIGNAL_CLOCKS (uint32_t)(TX_DEFAULT_SIGNAL * CLOCKS_PER_MICROSECOND)
  68. #define TX_MINIMUM_SPACE_CLOCKS (uint32_t)(TX_MINIMUM_SPACE * CLOCKS_PER_MICROSECOND)
  69. #define TX_MINIMUM_FRAME_CLOCKS (uint32_t)(TX_MINIMUM_FRAME * CLOCKS_PER_MICROSECOND)
  70. #define TX_PULSE_WIDTH_CLOCKS (uint32_t)(TX_PULSE_WIDTH * CLOCKS_PER_MICROSECOND)
  71. #define RX_MINIMUM_SPACE_CLOCKS (uint32_t)(RX_MINIMUM_SPACE * CLOCKS_PER_MICROSECOND)
  72. #define FTM0_SC_VALUE (FTM_SC_TOIE | FTM_SC_CLKS(1) | FTM_SC_PS(0))
  73. #if defined(KINETISK)
  74. #define CSC_CHANGE(reg, val) ((reg)->csc = (val))
  75. #define CSC_INTACK(reg, val) ((reg)->csc = (val))
  76. #define CSC_CHANGE_INTACK(reg, val) ((reg)->csc = (val))
  77. #define FRAME_PIN_SET() *framePinReg = 1
  78. #define FRAME_PIN_CLEAR() *framePinReg = 0
  79. #elif defined(KINETISL)
  80. #define CSC_CHANGE(reg, val) ({(reg)->csc = 0; while ((reg)->csc); (reg)->csc = (val);})
  81. #define CSC_INTACK(reg, val) ((reg)->csc = (val) | FTM_CSC_CHF)
  82. #define CSC_CHANGE_INTACK(reg, val) ({(reg)->csc = 0; while ((reg)->csc); (reg)->csc = (val) | FTM_CSC_CHF;})
  83. #define FRAME_PIN_SET() *(framePinReg + 4) = framePinMask
  84. #define FRAME_PIN_CLEAR() *(framePinReg + 8) = framePinMask
  85. #endif
  86. uint8_t PulsePositionOutput::channelmask = 0;
  87. PulsePositionOutput * PulsePositionOutput::list[8];
  88. PulsePositionOutput::PulsePositionOutput(void)
  89. {
  90. pulse_width[0] = TX_MINIMUM_FRAME_CLOCKS;
  91. for (int i=1; i <= PULSEPOSITION_MAXCHANNELS; i++) {
  92. pulse_width[i] = TX_DEFAULT_SIGNAL_CLOCKS;
  93. }
  94. cscSet = 0b01011100;
  95. cscClear = 0b01011000;
  96. }
  97. PulsePositionOutput::PulsePositionOutput(int polarity)
  98. {
  99. pulse_width[0] = TX_MINIMUM_FRAME_CLOCKS;
  100. for (int i=1; i <= PULSEPOSITION_MAXCHANNELS; i++) {
  101. pulse_width[i] = TX_DEFAULT_SIGNAL_CLOCKS;
  102. }
  103. if (polarity == FALLING) {
  104. cscSet = 0b01011000;
  105. cscClear = 0b01011100;
  106. } else {
  107. cscSet = 0b01011100;
  108. cscClear = 0b01011000;
  109. }
  110. }
  111. bool PulsePositionOutput::begin(uint8_t txPin)
  112. {
  113. return begin(txPin, 255);
  114. }
  115. bool PulsePositionOutput::begin(uint8_t txPin, uint8_t framePin)
  116. {
  117. uint32_t channel;
  118. volatile void *reg;
  119. if (FTM0_MOD != 0xFFFF || (FTM0_SC & 0x7F) != FTM0_SC_VALUE) {
  120. FTM0_SC = 0;
  121. FTM0_CNT = 0;
  122. FTM0_MOD = 0xFFFF;
  123. FTM0_SC = FTM0_SC_VALUE;
  124. #if defined(KINETISK)
  125. FTM0_MODE = 0;
  126. #endif
  127. }
  128. switch (txPin) {
  129. case 6: channel = 4; reg = &FTM0_C4SC; break;
  130. case 9: channel = 2; reg = &FTM0_C2SC; break;
  131. case 10: channel = 3; reg = &FTM0_C3SC; break;
  132. case 20: channel = 5; reg = &FTM0_C5SC; break;
  133. case 22: channel = 0; reg = &FTM0_C0SC; break;
  134. case 23: channel = 1; reg = &FTM0_C1SC; break;
  135. #if defined(KINETISK)
  136. case 5: channel = 7; reg = &FTM0_C7SC; break;
  137. case 21: channel = 6; reg = &FTM0_C6SC; break;
  138. #endif
  139. default:
  140. return false;
  141. }
  142. if (framePin < NUM_DIGITAL_PINS) {
  143. framePinReg = portOutputRegister(framePin);
  144. framePinMask = digitalPinToBitMask(framePin);
  145. pinMode(framePin, OUTPUT);
  146. FRAME_PIN_SET();
  147. } else {
  148. framePinReg = NULL;
  149. }
  150. state = 0;
  151. current_channel = 0;
  152. total_channels = 0;
  153. ftm = (struct ftm_channel_struct *)reg;
  154. ftm->cv = 200;
  155. CSC_CHANGE(ftm, cscSet); // set on compare match & interrupt
  156. list[channel] = this;
  157. channelmask |= (1<<channel);
  158. *portConfigRegister(txPin) = PORT_PCR_MUX(4) | PORT_PCR_DSE | PORT_PCR_SRE;
  159. NVIC_SET_PRIORITY(IRQ_FTM0, 32);
  160. NVIC_ENABLE_IRQ(IRQ_FTM0);
  161. return true;
  162. }
  163. bool PulsePositionOutput::write(uint8_t channel, float microseconds)
  164. {
  165. uint32_t i, sum, space, clocks, num_channels;
  166. if (channel < 1 || channel > PULSEPOSITION_MAXCHANNELS) return false;
  167. if (microseconds < TX_MINIMUM_SIGNAL || microseconds > TX_MAXIMUM_SIGNAL) return false;
  168. clocks = microseconds * CLOCKS_PER_MICROSECOND;
  169. num_channels = total_channels;
  170. if (channel > num_channels) num_channels = channel;
  171. sum = clocks;
  172. for (i=1; i < channel; i++) sum += pulse_width[i];
  173. for (i=channel+1; i <= num_channels; i++) sum += pulse_width[i];
  174. if (sum < TX_MINIMUM_FRAME_CLOCKS - TX_MINIMUM_SPACE_CLOCKS) {
  175. space = TX_MINIMUM_FRAME_CLOCKS - sum;
  176. } else {
  177. if (framePinReg) {
  178. space = TX_PULSE_WIDTH_CLOCKS;
  179. } else {
  180. space = TX_MINIMUM_SPACE_CLOCKS;
  181. }
  182. }
  183. __disable_irq();
  184. pulse_width[0] = space;
  185. pulse_width[channel] = clocks;
  186. total_channels = num_channels;
  187. __enable_irq();
  188. return true;
  189. }
  190. void PulsePositionOutput::isr(void)
  191. {
  192. #if defined(KINETISK)
  193. FTM0_MODE = 0;
  194. #endif
  195. if (state == 0) {
  196. // pin was just set high, schedule it to go low
  197. ftm->cv += TX_PULSE_WIDTH_CLOCKS;
  198. CSC_CHANGE_INTACK(ftm, cscClear); // clear on compare match & interrupt
  199. state = 1;
  200. } else {
  201. // pin just went low
  202. uint32_t width, channel;
  203. if (state == 1) {
  204. channel = current_channel;
  205. if (channel == 0) {
  206. total_channels_buffer = total_channels;
  207. for (uint32_t i=0; i <= total_channels_buffer; i++) {
  208. pulse_buffer[i] = pulse_width[i];
  209. }
  210. }
  211. width = pulse_buffer[channel] - TX_PULSE_WIDTH_CLOCKS;
  212. if (++channel > total_channels_buffer) {
  213. channel = 0;
  214. }
  215. if (framePinReg) {
  216. //if (channel == 0) {
  217. if (channel == 1) {
  218. FRAME_PIN_SET();
  219. } else {
  220. FRAME_PIN_CLEAR();
  221. }
  222. }
  223. current_channel = channel;
  224. } else {
  225. width = pulse_remaining;
  226. }
  227. if (width <= 60000) {
  228. ftm->cv += width;
  229. CSC_CHANGE_INTACK(ftm, cscSet); // set on compare match & interrupt
  230. state = 0;
  231. } else {
  232. ftm->cv += 58000;
  233. CSC_INTACK(ftm, cscClear); // clear on compare match & interrupt
  234. pulse_remaining = width - 58000;
  235. state = 2;
  236. }
  237. }
  238. }
  239. void ftm0_isr(void)
  240. {
  241. if (FTM0_SC & 0x80) {
  242. #if defined(KINETISK)
  243. FTM0_SC = FTM0_SC_VALUE;
  244. #elif defined(KINETISL)
  245. FTM0_SC = FTM0_SC_VALUE | FTM_SC_TOF;
  246. #endif
  247. PulsePositionInput::overflow_count++;
  248. PulsePositionInput::overflow_inc = true;
  249. }
  250. // TODO: this could be efficient by reading FTM0_STATUS
  251. uint8_t maskin = PulsePositionInput::channelmask;
  252. if ((maskin & 0x01) && (FTM0_C0SC & 0x80)) PulsePositionInput::list[0]->isr();
  253. if ((maskin & 0x02) && (FTM0_C1SC & 0x80)) PulsePositionInput::list[1]->isr();
  254. if ((maskin & 0x04) && (FTM0_C2SC & 0x80)) PulsePositionInput::list[2]->isr();
  255. if ((maskin & 0x08) && (FTM0_C3SC & 0x80)) PulsePositionInput::list[3]->isr();
  256. if ((maskin & 0x10) && (FTM0_C4SC & 0x80)) PulsePositionInput::list[4]->isr();
  257. if ((maskin & 0x20) && (FTM0_C5SC & 0x80)) PulsePositionInput::list[5]->isr();
  258. #if defined(KINETISK)
  259. if ((maskin & 0x40) && (FTM0_C6SC & 0x80)) PulsePositionInput::list[6]->isr();
  260. if ((maskin & 0x80) && (FTM0_C7SC & 0x80)) PulsePositionInput::list[7]->isr();
  261. #endif
  262. uint8_t maskout = PulsePositionOutput::channelmask;
  263. if ((maskout & 0x01) && (FTM0_C0SC & 0x80)) PulsePositionOutput::list[0]->isr();
  264. if ((maskout & 0x02) && (FTM0_C1SC & 0x80)) PulsePositionOutput::list[1]->isr();
  265. if ((maskout & 0x04) && (FTM0_C2SC & 0x80)) PulsePositionOutput::list[2]->isr();
  266. if ((maskout & 0x08) && (FTM0_C3SC & 0x80)) PulsePositionOutput::list[3]->isr();
  267. if ((maskout & 0x10) && (FTM0_C4SC & 0x80)) PulsePositionOutput::list[4]->isr();
  268. if ((maskout & 0x20) && (FTM0_C5SC & 0x80)) PulsePositionOutput::list[5]->isr();
  269. #if defined(KINETISK)
  270. if ((maskout & 0x40) && (FTM0_C6SC & 0x80)) PulsePositionOutput::list[6]->isr();
  271. if ((maskout & 0x80) && (FTM0_C7SC & 0x80)) PulsePositionOutput::list[7]->isr();
  272. #endif
  273. PulsePositionInput::overflow_inc = false;
  274. }
  275. // some explanation regarding this C to C++ trickery can be found here:
  276. // http://forum.pjrc.com/threads/25278-Low-Power-with-Event-based-software-architecture-brainstorm?p=43496&viewfull=1#post43496
  277. uint16_t PulsePositionInput::overflow_count = 0;
  278. bool PulsePositionInput::overflow_inc = false;
  279. uint8_t PulsePositionInput::channelmask = 0;
  280. PulsePositionInput * PulsePositionInput::list[8];
  281. PulsePositionInput::PulsePositionInput(void)
  282. {
  283. cscEdge = 0b01000100;
  284. }
  285. PulsePositionInput::PulsePositionInput(int polarity)
  286. {
  287. cscEdge = (polarity == FALLING) ? 0b01001000 : 0b01000100;
  288. }
  289. bool PulsePositionInput::begin(uint8_t pin)
  290. {
  291. uint32_t channel;
  292. volatile void *reg;
  293. if (FTM0_MOD != 0xFFFF || (FTM0_SC & 0x7F) != FTM0_SC_VALUE) {
  294. FTM0_SC = 0;
  295. FTM0_CNT = 0;
  296. FTM0_MOD = 0xFFFF;
  297. FTM0_SC = FTM0_SC_VALUE;
  298. #if defined(KINETISK)
  299. FTM0_MODE = 0;
  300. #endif
  301. }
  302. switch (pin) {
  303. case 6: channel = 4; reg = &FTM0_C4SC; break;
  304. case 9: channel = 2; reg = &FTM0_C2SC; break;
  305. case 10: channel = 3; reg = &FTM0_C3SC; break;
  306. case 20: channel = 5; reg = &FTM0_C5SC; break;
  307. case 22: channel = 0; reg = &FTM0_C0SC; break;
  308. case 23: channel = 1; reg = &FTM0_C1SC; break;
  309. #if defined(KINETISK)
  310. case 21: channel = 6; reg = &FTM0_C6SC; break;
  311. case 5: channel = 7; reg = &FTM0_C7SC; break;
  312. #endif
  313. default:
  314. return false;
  315. }
  316. prev = 0;
  317. write_index = 255;
  318. available_flag = false;
  319. ftm = (struct ftm_channel_struct *)reg;
  320. list[channel] = this;
  321. channelmask |= (1<<channel);
  322. *portConfigRegister(pin) = PORT_PCR_MUX(4);
  323. CSC_CHANGE(ftm, cscEdge); // input capture & interrupt on rising edge
  324. NVIC_SET_PRIORITY(IRQ_FTM0, 32);
  325. NVIC_ENABLE_IRQ(IRQ_FTM0);
  326. return true;
  327. }
  328. void PulsePositionInput::isr(void)
  329. {
  330. uint32_t val, count;
  331. val = ftm->cv;
  332. CSC_INTACK(ftm, cscEdge); // input capture & interrupt on rising edge
  333. count = overflow_count;
  334. if (val > 0xE000 && overflow_inc) count--;
  335. val |= (count << 16);
  336. count = val - prev;
  337. prev = val;
  338. //Serial.print(val, HEX);
  339. //Serial.print(" ");
  340. //Serial.println(count);
  341. if (count >= RX_MINIMUM_SPACE_CLOCKS) {
  342. if (write_index < 255) {
  343. for (int i=0; i < write_index; i++) {
  344. pulse_buffer[i] = pulse_width[i];
  345. }
  346. total_channels = write_index;
  347. available_flag = true;
  348. }
  349. write_index = 0;
  350. } else {
  351. if (write_index < PULSEPOSITION_MAXCHANNELS) {
  352. pulse_width[write_index++] = count;
  353. }
  354. }
  355. }
  356. int PulsePositionInput::available(void)
  357. {
  358. uint32_t total;
  359. bool flag;
  360. __disable_irq();
  361. flag = available_flag;
  362. total = total_channels;
  363. __enable_irq();
  364. if (flag) return total;
  365. return -1;
  366. }
  367. float PulsePositionInput::read(uint8_t channel)
  368. {
  369. uint32_t total, index, value=0;
  370. if (channel == 0) return 0.0;
  371. index = channel - 1;
  372. __disable_irq();
  373. total = total_channels;
  374. if (index < total) value = pulse_buffer[index];
  375. if (channel >= total) available_flag = false;
  376. __enable_irq();
  377. return (float)value / (float)CLOCKS_PER_MICROSECOND;
  378. }
  379. #endif