ESP32Servo.cpp 7.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236
  1. /*
  2. Copyright (c) 2017 John K. Bennett. All right reserved.
  3. This library is free software; you can redistribute it and/or
  4. modify it under the terms of the GNU Lesser General Public
  5. License as published by the Free Software Foundation; either
  6. version 2.1 of the License, or (at your option) any later version.
  7. This library is distributed in the hope that it will be useful,
  8. but WITHOUT ANY WARRANTY; without even the implied warranty of
  9. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  10. Lesser General Public License for more details.
  11. You should have received a copy of the GNU Lesser General Public
  12. License along with this library; if not, write to the Free Software
  13. Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
  14. * Notes on the implementation:
  15. * The ESP32 supports 16 hardware LED PWM channels that are intended
  16. * to be used for LED brightness control. The low level ESP32 code
  17. * (esp32-hal-ledc.*) allows us to set the PWM frequency and bit-depth,
  18. * and then manipulate them by setting bits in the relevant control
  19. * registers.
  20. *
  21. * Different servos require different pulse widths to vary servo angle, but the range is
  22. * an approximately 500-2500 microsecond pulse every 20ms (50Hz). In general, hobbyist servos
  23. * sweep 180 degrees, so the lowest number in the published range for a particular servo
  24. * represents an angle of 0 degrees, the middle of the range represents 90 degrees, and the top
  25. * of the range represents 180 degrees. So for example, if the range is 1000us to 2000us,
  26. * 1000us would equal an angle of 0, 1500us would equal 90 degrees, and 2000us would equal 180
  27. * degrees. We vary pulse width (recall that the pulse period is already set to 20ms) as follows:
  28. *
  29. * The ESP32 PWM timers allow us to set the timer width (max 20 bits). Thus
  30. * the timer "tick" length is (pulse_period/2**timer_width), and the equation for pulse_high_width
  31. * (the portion of the 20ms cycle that the signal is high) becomes:
  32. *
  33. * pulse_high_width = count * tick_length
  34. * = count * (pulse_period/2**timer_width)
  35. *
  36. * and count = (pulse_high_width / (pulse_period/2**timer_width))
  37. *
  38. * So, for example, if I want a 1500us pulse_high_width, I set pulse_period to 20ms (20000us)
  39. * (this value is set in the ledcSetup call), and count (used in the ledcWrite call) to
  40. * 1500/(20000/65536), or 4924. This is the value we write to the timer in the ledcWrite call.
  41. * If we increase the timer_width, the timer_count values need to be adjusted.
  42. *
  43. * The servo signal pins connect to any available GPIO pins on the ESP32, but not all pins are
  44. * GPIO pins.
  45. *
  46. * The ESP32 is a 32 bit processor that includes FP support; this code reflects that fact.
  47. */
  48. #include <ESP32Servo.h>
  49. #include "Arduino.h"
  50. //
  51. Servo::Servo()
  52. { // initialize this channel with plausible values, except pin # (we set pin # when attached)
  53. REFRESH_CPS = 50;
  54. this->ticks = DEFAULT_PULSE_WIDTH_TICKS;
  55. this->timer_width = DEFAULT_TIMER_WIDTH;
  56. this->pinNumber = -1; // make it clear that we haven't attached a pin to this channel
  57. this->min = DEFAULT_uS_LOW;
  58. this->max = DEFAULT_uS_HIGH;
  59. this->timer_width_ticks = pow(2,this->timer_width);
  60. }
  61. ESP32PWM * Servo::getPwm(){
  62. return &pwm;
  63. }
  64. int Servo::attach(int pin)
  65. {
  66. return (this->attach(pin, DEFAULT_uS_LOW, DEFAULT_uS_HIGH));
  67. }
  68. int Servo::attach(int pin, int min, int max)
  69. {
  70. #ifdef ENFORCE_PINS
  71. // Recommend only the following pins 2,4,12-19,21-23,25-27,32-33
  72. if (pwm.hasPwm(pin))
  73. {
  74. #endif
  75. // OK to proceed; first check for new/reuse
  76. if (this->pinNumber < 0) // we are attaching to a new or previously detached pin; we need to initialize/reinitialize
  77. {
  78. this->ticks = DEFAULT_PULSE_WIDTH_TICKS;
  79. this->timer_width = DEFAULT_TIMER_WIDTH;
  80. this->timer_width_ticks = pow(2,this->timer_width);
  81. }
  82. this->pinNumber = pin;
  83. #ifdef ENFORCE_PINS
  84. }
  85. else
  86. {
  87. Serial.println("This pin can not be a servo: "+String(pin)+"\r\nServo availible on: 2,4,5,12-19,21-23,25-27,32-33");
  88. return 0;
  89. }
  90. #endif
  91. // min/max checks
  92. if (min < MIN_PULSE_WIDTH) // ensure pulse width is valid
  93. min = MIN_PULSE_WIDTH;
  94. if (max > MAX_PULSE_WIDTH)
  95. max = MAX_PULSE_WIDTH;
  96. this->min = min; //store this value in uS
  97. this->max = max; //store this value in uS
  98. // Set up this channel
  99. // if you want anything other than default timer width, you must call setTimerWidth() before attach
  100. pwm.attachPin(this->pinNumber,REFRESH_CPS, this->timer_width ); // GPIO pin assigned to channel
  101. //Serial.println("Attaching servo : "+String(pin)+" on PWM "+String(pwm.getChannel()));
  102. return 1;
  103. }
  104. void Servo::detach()
  105. {
  106. if (this->attached())
  107. {
  108. //keep track of detached servos channels so we can reuse them if needed
  109. pwm.detachPin(this->pinNumber);
  110. this->pinNumber = -1;
  111. }
  112. }
  113. void Servo::write(int value)
  114. {
  115. // treat values less than MIN_PULSE_WIDTH (500) as angles in degrees (valid values in microseconds are handled as microseconds)
  116. if (value < MIN_PULSE_WIDTH)
  117. {
  118. if (value < 0)
  119. value = 0;
  120. else if (value > 180)
  121. value = 180;
  122. value = map(value, 0, 180, this->min, this->max);
  123. }
  124. this->writeMicroseconds(value);
  125. }
  126. void Servo::writeMicroseconds(int value)
  127. {
  128. // calculate and store the values for the given channel
  129. if (this->attached()) // ensure channel is valid
  130. {
  131. if (value < this->min) // ensure pulse width is valid
  132. value = this->min;
  133. else if (value > this->max)
  134. value = this->max;
  135. value = usToTicks(value); // convert to ticks
  136. this->ticks = value;
  137. // do the actual write
  138. pwm.write( this->ticks);
  139. }
  140. }
  141. int Servo::read() // return the value as degrees
  142. {
  143. return (map(readMicroseconds()+1, this->min, this->max, 0, 180));
  144. }
  145. int Servo::readMicroseconds()
  146. {
  147. int pulsewidthUsec;
  148. if (this->attached())
  149. {
  150. pulsewidthUsec = ticksToUs(this->ticks);
  151. }
  152. else
  153. {
  154. pulsewidthUsec = 0;
  155. }
  156. return (pulsewidthUsec);
  157. }
  158. bool Servo::attached()
  159. {
  160. return (pwm.attached());
  161. }
  162. void Servo::setTimerWidth(int value)
  163. {
  164. // only allow values between 16 and 20
  165. if (value < 16)
  166. value = 16;
  167. else if (value > 20)
  168. value = 20;
  169. // Fix the current ticks value after timer width change
  170. // The user can reset the tick value with a write() or writeUs()
  171. int widthDifference = this->timer_width - value;
  172. // if positive multiply by diff; if neg, divide
  173. if (widthDifference > 0)
  174. {
  175. this->ticks = widthDifference * this->ticks;
  176. }
  177. else if (widthDifference < 0)
  178. {
  179. this->ticks = this->ticks/-widthDifference;
  180. }
  181. this->timer_width = value;
  182. this->timer_width_ticks = pow(2,this->timer_width);
  183. // If this is an attached servo, clean up
  184. if (this->attached())
  185. {
  186. // detach, setup and attach again to reflect new timer width
  187. pwm.detachPin(this->pinNumber);
  188. pwm.attachPin(this->pinNumber, REFRESH_CPS, this->timer_width);
  189. }
  190. }
  191. int Servo::readTimerWidth()
  192. {
  193. return (this->timer_width);
  194. }
  195. int Servo::usToTicks(int usec)
  196. {
  197. return (int)((float)usec / ((float)REFRESH_USEC / (float)this->timer_width_ticks)*(((float)REFRESH_CPS)/50.0));
  198. }
  199. int Servo::ticksToUs(int ticks)
  200. {
  201. return (int)((float)ticks * ((float)REFRESH_USEC / (float)this->timer_width_ticks)/(((float)REFRESH_CPS)/50.0));
  202. }