My Marlin configs for Fabrikator Mini and CTC i3 Pro B
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.

HAL_Servo_STM32F1.cpp 6.8KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232
  1. /**
  2. * Marlin 3D Printer Firmware
  3. * Copyright (c) 2019 MarlinFirmware [https://github.com/MarlinFirmware/Marlin]
  4. *
  5. * Based on Sprinter and grbl.
  6. * Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm
  7. * Copyright (c) 2017 Victor Perez
  8. *
  9. * This program is free software: you can redistribute it and/or modify
  10. * it under the terms of the GNU General Public License as published by
  11. * the Free Software Foundation, either version 3 of the License, or
  12. * (at your option) any later version.
  13. *
  14. * This program 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
  17. * GNU General Public License for more details.
  18. *
  19. * You should have received a copy of the GNU General Public License
  20. * along with this program. If not, see <http://www.gnu.org/licenses/>.
  21. *
  22. */
  23. #ifdef __STM32F1__
  24. #include "../../inc/MarlinConfig.h"
  25. #if HAS_SERVOS
  26. uint8_t ServoCount = 0;
  27. #include "HAL_Servo_STM32F1.h"
  28. #include "HAL_timers_STM32F1.h"
  29. //#include "Servo.h"
  30. #include <boards.h>
  31. #include <io.h>
  32. #include <pwm.h>
  33. #include <wirish_math.h>
  34. /**
  35. * 20 millisecond period config. For a 1-based prescaler,
  36. *
  37. * (prescaler * overflow / CYC_MSEC) msec = 1 timer cycle = 20 msec
  38. * => prescaler * overflow = 20 * CYC_MSEC
  39. *
  40. * This uses the smallest prescaler that allows an overflow < 2^16.
  41. */
  42. #define MAX_OVERFLOW UINT16_MAX //((1 << 16) - 1)
  43. #define CYC_MSEC (1000 * CYCLES_PER_MICROSECOND)
  44. #define TAU_MSEC 20
  45. #define TAU_USEC (TAU_MSEC * 1000)
  46. #define TAU_CYC (TAU_MSEC * CYC_MSEC)
  47. #define SERVO_PRESCALER (TAU_CYC / MAX_OVERFLOW + 1)
  48. #define SERVO_OVERFLOW ((uint16_t)round((double)TAU_CYC / SERVO_PRESCALER))
  49. // Unit conversions
  50. #define US_TO_COMPARE(us) ((uint16_t)map((us), 0, TAU_USEC, 0, SERVO_OVERFLOW))
  51. #define COMPARE_TO_US(c) ((uint32_t)map((c), 0, SERVO_OVERFLOW, 0, TAU_USEC))
  52. #define ANGLE_TO_US(a) ((uint16_t)(map((a), this->minAngle, this->maxAngle, \
  53. SERVO_DEFAULT_MIN_PW, SERVO_DEFAULT_MAX_PW)))
  54. #define US_TO_ANGLE(us) ((int16_t)(map((us), SERVO_DEFAULT_MIN_PW, SERVO_DEFAULT_MAX_PW, \
  55. this->minAngle, this->maxAngle)))
  56. void libServo::servoWrite(uint8_t pin, uint16_t duty_cycle) {
  57. #ifdef SERVO0_TIMER_NUM
  58. if (this->servoIndex == 0) {
  59. this->pwmSetDuty(duty_cycle);
  60. return;
  61. }
  62. #endif
  63. timer_dev *tdev = PIN_MAP[pin].timer_device;
  64. uint8_t tchan = PIN_MAP[pin].timer_channel;
  65. if (tdev) timer_set_compare(tdev, tchan, duty_cycle);
  66. }
  67. libServo::libServo() {
  68. this->servoIndex = ServoCount < MAX_SERVOS ? ServoCount++ : INVALID_SERVO;
  69. }
  70. bool libServo::attach(const int32_t pin, const int32_t minAngle, const int32_t maxAngle) {
  71. if (this->servoIndex >= MAX_SERVOS) return false;
  72. if (pin >= BOARD_NR_GPIO_PINS) return false;
  73. this->minAngle = minAngle;
  74. this->maxAngle = maxAngle;
  75. this->angle = -1;
  76. #ifdef SERVO0_TIMER_NUM
  77. if (this->servoIndex == 0 && this->setupSoftPWM(pin)) {
  78. this->pin = pin; // set attached()
  79. return true;
  80. }
  81. #endif
  82. if (!PWM_PIN(pin)) return false;
  83. timer_dev *tdev = PIN_MAP[pin].timer_device;
  84. //uint8_t tchan = PIN_MAP[pin].timer_channel;
  85. SET_PWM(pin);
  86. servoWrite(pin, 0);
  87. timer_pause(tdev);
  88. timer_set_prescaler(tdev, SERVO_PRESCALER - 1); // prescaler is 1-based
  89. timer_set_reload(tdev, SERVO_OVERFLOW);
  90. timer_generate_update(tdev);
  91. timer_resume(tdev);
  92. this->pin = pin; // set attached()
  93. return true;
  94. }
  95. bool libServo::detach() {
  96. if (!this->attached()) return false;
  97. this->angle = -1;
  98. servoWrite(this->pin, 0);
  99. return true;
  100. }
  101. int32_t libServo::read() const {
  102. if (this->attached()) {
  103. #ifdef SERVO0_TIMER_NUM
  104. if (this->servoIndex == 0) return this->angle;
  105. #endif
  106. timer_dev *tdev = PIN_MAP[this->pin].timer_device;
  107. uint8_t tchan = PIN_MAP[this->pin].timer_channel;
  108. return US_TO_ANGLE(COMPARE_TO_US(timer_get_compare(tdev, tchan)));
  109. }
  110. return 0;
  111. }
  112. void libServo::move(const int32_t value) {
  113. constexpr uint16_t servo_delay[] = SERVO_DELAY;
  114. static_assert(COUNT(servo_delay) == NUM_SERVOS, "SERVO_DELAY must be an array NUM_SERVOS long.");
  115. if (this->attached()) {
  116. this->angle = constrain(value, this->minAngle, this->maxAngle);
  117. servoWrite(this->pin, US_TO_COMPARE(ANGLE_TO_US(this->angle)));
  118. safe_delay(servo_delay[this->servoIndex]);
  119. #if ENABLED(DEACTIVATE_SERVOS_AFTER_MOVE)
  120. this->detach();
  121. #endif
  122. }
  123. }
  124. #ifdef SERVO0_TIMER_NUM
  125. extern "C" void Servo_IRQHandler(void) {
  126. static timer_dev *tdev = get_timer_dev(SERVO0_TIMER_NUM);
  127. uint16_t SR = timer_get_status(tdev);
  128. if (SR & TIMER_SR_CC1IF) { // channel 1 off
  129. #ifdef SERVO0_PWM_OD
  130. OUT_WRITE_OD(SERVO0_PIN, 1); // off
  131. #else
  132. OUT_WRITE(SERVO0_PIN, 0);
  133. #endif
  134. timer_reset_status_bit(tdev, TIMER_SR_CC1IF_BIT);
  135. }
  136. if (SR & TIMER_SR_CC2IF) { // channel 2 resume
  137. #ifdef SERVO0_PWM_OD
  138. OUT_WRITE_OD(SERVO0_PIN, 0); // on
  139. #else
  140. OUT_WRITE(SERVO0_PIN, 1);
  141. #endif
  142. timer_reset_status_bit(tdev, TIMER_SR_CC2IF_BIT);
  143. }
  144. }
  145. bool libServo::setupSoftPWM(const int32_t pin) {
  146. timer_dev *tdev = get_timer_dev(SERVO0_TIMER_NUM);
  147. if (!tdev) return false;
  148. #ifdef SERVO0_PWM_OD
  149. OUT_WRITE_OD(pin, 1);
  150. #else
  151. OUT_WRITE(pin, 0);
  152. #endif
  153. timer_pause(tdev);
  154. timer_set_mode(tdev, 1, TIMER_OUTPUT_COMPARE); // counter with isr
  155. timer_oc_set_mode(tdev, 1, TIMER_OC_MODE_FROZEN, 0); // no pin output change
  156. timer_oc_set_mode(tdev, 2, TIMER_OC_MODE_FROZEN, 0); // no pin output change
  157. timer_set_prescaler(tdev, SERVO_PRESCALER - 1); // prescaler is 1-based
  158. timer_set_reload(tdev, SERVO_OVERFLOW);
  159. timer_set_compare(tdev, 1, SERVO_OVERFLOW);
  160. timer_set_compare(tdev, 2, SERVO_OVERFLOW);
  161. timer_attach_interrupt(tdev, 1, Servo_IRQHandler);
  162. timer_attach_interrupt(tdev, 2, Servo_IRQHandler);
  163. timer_generate_update(tdev);
  164. timer_resume(tdev);
  165. return true;
  166. }
  167. void libServo::pwmSetDuty(const uint16_t duty_cycle) {
  168. timer_dev *tdev = get_timer_dev(SERVO0_TIMER_NUM);
  169. timer_set_compare(tdev, 1, duty_cycle);
  170. timer_generate_update(tdev);
  171. if (duty_cycle) {
  172. timer_enable_irq(tdev, 1);
  173. timer_enable_irq(tdev, 2);
  174. }
  175. else {
  176. timer_disable_irq(tdev, 1);
  177. timer_disable_irq(tdev, 2);
  178. #ifdef SERVO0_PWM_OD
  179. OUT_WRITE_OD(this->pin, 1); // off
  180. #else
  181. OUT_WRITE(this->pin, 0);
  182. #endif
  183. }
  184. }
  185. void libServo::pauseSoftPWM() { // detach
  186. timer_dev *tdev = get_timer_dev(SERVO0_TIMER_NUM);
  187. timer_pause(tdev);
  188. pwmSetDuty(0);
  189. }
  190. #else
  191. bool libServo::setupSoftPWM(const int32_t pin) { return false; }
  192. void libServo::pwmSetDuty(const uint16_t duty_cycle) {}
  193. void libServo::pauseSoftPWM() {}
  194. #endif
  195. #endif // HAS_SERVOS
  196. #endif // __STM32F1__