PlatformIO package of the Teensy core framework compatible with GCC 10 & C++20
Nie możesz wybrać więcej, niż 25 tematów Tematy muszą się zaczynać od litery lub cyfry, mogą zawierać myślniki ('-') i mogą mieć do 35 znaków.

223 lines
6.0KB

  1. #include "Arduino.h"
  2. #include "PWMServo.h"
  3. /*
  4. PWMServo.cpp - Hardware Servo Timer Library
  5. http://arduiniana.org/libraries/pwmservo/
  6. Author: Jim Studt, jim@federated.com
  7. Copyright (c) 2007 David A. Mellis. All right reserved.
  8. renamed to PWMServo by Mikal Hart
  9. ported to other chips by Paul Stoffregen
  10. This library is free software; you can redistribute it and/or
  11. modify it under the terms of the GNU Lesser General Public
  12. License as published by the Free Software Foundation; either
  13. version 2.1 of the License, or (at your option) any later version.
  14. This library is distributed in the hope that it will be useful,
  15. but WITHOUT ANY WARRANTY; without even the implied warranty of
  16. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  17. Lesser General Public License for more details.
  18. You should have received a copy of the GNU Lesser General Public
  19. License along with this library; if not, write to the Free Software
  20. Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
  21. */
  22. #define NO_ANGLE (0xff)
  23. #if defined(__AVR__)
  24. #include <avr/interrupt.h>
  25. uint8_t PWMServo::attachedA = 0;
  26. uint8_t PWMServo::attachedB = 0;
  27. #ifdef SERVO_PIN_C
  28. uint8_t PWMServo::attachedC = 0;
  29. #endif
  30. void PWMServo::seizeTimer1()
  31. {
  32. uint8_t oldSREG = SREG;
  33. cli();
  34. TCCR1A = _BV(WGM11); /* Fast PWM, ICR1 is top */
  35. TCCR1B = _BV(WGM13) | _BV(WGM12) /* Fast PWM, ICR1 is top */
  36. | _BV(CS11) /* div 8 clock prescaler */
  37. ;
  38. OCR1A = 3000;
  39. OCR1B = 3000;
  40. ICR1 = clockCyclesPerMicrosecond()*(20000L/8); // 20000 uS is a bit fast for the refresh, 20ms, but
  41. // it keeps us from overflowing ICR1 at 20MHz clocks
  42. // That "/8" at the end is the prescaler.
  43. #if defined(__AVR_ATmega8__)
  44. TIMSK &= ~(_BV(TICIE1) | _BV(OCIE1A) | _BV(OCIE1B) | _BV(TOIE1) );
  45. #else
  46. TIMSK1 &= ~(_BV(OCIE1A) | _BV(OCIE1B) | _BV(TOIE1) );
  47. #endif
  48. SREG = oldSREG; // undo cli()
  49. }
  50. void PWMServo::releaseTimer1() {}
  51. PWMServo::PWMServo() : pin(0), angle(NO_ANGLE) {}
  52. uint8_t PWMServo::attach(int pinArg, int min, int max)
  53. {
  54. #ifdef SERVO_PIN_C
  55. if (pinArg != SERVO_PIN_A && pinArg != SERVO_PIN_B && pinArg != SERVO_PIN_C) return 0;
  56. #else
  57. if (pinArg != SERVO_PIN_A && pinArg != SERVO_PIN_B) return 0;
  58. #endif
  59. min16 = min / 16;
  60. max16 = max / 16;
  61. pin = pinArg;
  62. angle = NO_ANGLE;
  63. digitalWrite(pin, LOW);
  64. pinMode(pin, OUTPUT);
  65. #ifdef SERVO_PIN_C
  66. if (!attachedA && !attachedB && !attachedC) seizeTimer1();
  67. #else
  68. if (!attachedA && !attachedB) seizeTimer1();
  69. #endif
  70. if (pin == SERVO_PIN_A) {
  71. attachedA = 1;
  72. TCCR1A = (TCCR1A & ~_BV(COM1A0)) | _BV(COM1A1);
  73. }
  74. if (pin == SERVO_PIN_B) {
  75. attachedB = 1;
  76. TCCR1A = (TCCR1A & ~_BV(COM1B0)) | _BV(COM1B1);
  77. }
  78. #ifdef SERVO_PIN_C
  79. if (pin == SERVO_PIN_C) {
  80. attachedC = 1;
  81. TCCR1A = (TCCR1A & ~_BV(COM1C0)) | _BV(COM1C1);
  82. }
  83. #endif
  84. return 1;
  85. }
  86. void PWMServo::detach()
  87. {
  88. // muck with timer flags
  89. if (pin == SERVO_PIN_A) {
  90. attachedA = 0;
  91. TCCR1A = TCCR1A & ~_BV(COM1A0) & ~_BV(COM1A1);
  92. pinMode(pin, INPUT);
  93. }
  94. if (pin == SERVO_PIN_B) {
  95. attachedB = 0;
  96. TCCR1A = TCCR1A & ~_BV(COM1B0) & ~_BV(COM1B1);
  97. pinMode(pin, INPUT);
  98. }
  99. #ifdef SERVO_PIN_C
  100. if (pin == SERVO_PIN_C) {
  101. attachedC = 0;
  102. TCCR1A = TCCR1A & ~_BV(COM1C0) & ~_BV(COM1C1);
  103. pinMode(pin, INPUT);
  104. }
  105. #endif
  106. #ifdef SERVO_PIN_C
  107. if (!attachedA && !attachedB && !attachedC) releaseTimer1();
  108. #else
  109. if (!attachedA && !attachedB) releaseTimer1();
  110. #endif
  111. }
  112. void PWMServo::write(int angleArg)
  113. {
  114. uint16_t p;
  115. if (angleArg < 0) angleArg = 0;
  116. if (angleArg > 180) angleArg = 180;
  117. angle = angleArg;
  118. // bleh, have to use longs to prevent overflow, could be tricky if always a 16MHz clock, but not true
  119. // That 8L on the end is the TCNT1 prescaler, it will need to change if the clock's prescaler changes,
  120. // but then there will likely be an overflow problem, so it will have to be handled by a human.
  121. p = (min16*16L*clockCyclesPerMicrosecond() + (max16-min16)*(16L*clockCyclesPerMicrosecond())*angle/180L)/8L;
  122. if (pin == SERVO_PIN_A) OCR1A = p;
  123. if (pin == SERVO_PIN_B) OCR1B = p;
  124. #ifdef SERVO_PIN_C
  125. if (pin == SERVO_PIN_C) OCR1C = p;
  126. #endif
  127. }
  128. uint8_t PWMServo::attached()
  129. {
  130. if (pin == SERVO_PIN_A && attachedA) return 1;
  131. if (pin == SERVO_PIN_B && attachedB) return 1;
  132. #ifdef SERVO_PIN_C
  133. if (pin == SERVO_PIN_C && attachedC) return 1;
  134. #endif
  135. return 0;
  136. }
  137. #elif defined(__arm__) && defined(TEENSYDUINO)
  138. uint32_t PWMServo::attachedpins[(NUM_DIGITAL_PINS+31)/32]; // 1 bit per digital pin
  139. PWMServo::PWMServo() : pin(255), angle(NO_ANGLE) {}
  140. uint8_t PWMServo::attach(int pinArg, int min, int max)
  141. {
  142. //Serial.printf("attach, pin=%d, min=%d, max=%d\n", pinArg, min, max);
  143. if (pinArg < 0 || pinArg >= NUM_DIGITAL_PINS) return 0;
  144. if (!digitalPinHasPWM(pinArg)) return 0;
  145. pin = pinArg;
  146. analogWriteFrequency(pin, 50);
  147. min16 = min >> 4;
  148. max16 = max >> 4;
  149. angle = NO_ANGLE;
  150. digitalWrite(pin, LOW);
  151. pinMode(pin, OUTPUT);
  152. attachedpins[pin >> 5] |= (1 << (pin & 31));
  153. return 1;
  154. }
  155. void PWMServo::write(int angleArg)
  156. {
  157. //Serial.printf("write, pin=%d, angle=%d\n", pin, angleArg);
  158. if (pin >= NUM_DIGITAL_PINS) return;
  159. if (angleArg < 0) angleArg = 0;
  160. if (angleArg > 180) angleArg = 180;
  161. angle = angleArg;
  162. uint32_t us = (((max16 - min16) * 46603 * angle) >> 11) + (min16 << 12); // us*256
  163. uint32_t duty = (us * 3355) >> 22;
  164. //float usec = (float)((max16 - min16)<<4) * ((float)angle / 180.0f) + (float)(min16<<4);
  165. //uint32_t duty = (int)(usec / 20000.0f * 4096.0f);
  166. //Serial.printf("angle=%d, usec=%.2f, us=%.2f, duty=%d, min=%d, max=%d\n",
  167. //angle, usec, (float)us / 256.0f, duty, min16<<4, max16<<4);
  168. #if TEENSYDUINO >= 137
  169. noInterrupts();
  170. uint32_t oldres = analogWriteResolution(12);
  171. analogWrite(pin, duty);
  172. analogWriteResolution(oldres);
  173. interrupts();
  174. #else
  175. analogWriteResolution(12);
  176. analogWrite(pin, duty);
  177. #endif
  178. }
  179. uint8_t PWMServo::attached()
  180. {
  181. if (pin >= NUM_DIGITAL_PINS) return 0;
  182. return (attachedpins[pin >> 5] & (1 << (pin & 31))) ? 1 : 0;
  183. }
  184. #endif