Kaynağa Gözat

✨ ESP32 - Hardware PWM for fan, cutter, servos (#23802)

John Robertson 3 yıl önce
ebeveyn
işleme
1f1571f726
No account linked to committer's email address

+ 1
- 1
Marlin/Configuration_adv.h Dosyayı Görüntüle

3464
   #define SPINDLE_LASER_USE_PWM                // Enable if your controller supports setting the speed/power
3464
   #define SPINDLE_LASER_USE_PWM                // Enable if your controller supports setting the speed/power
3465
   #if ENABLED(SPINDLE_LASER_USE_PWM)
3465
   #if ENABLED(SPINDLE_LASER_USE_PWM)
3466
     #define SPINDLE_LASER_PWM_INVERT    false  // Set to "true" if the speed/power goes up when you want it to go slower
3466
     #define SPINDLE_LASER_PWM_INVERT    false  // Set to "true" if the speed/power goes up when you want it to go slower
3467
-    #define SPINDLE_LASER_FREQUENCY     2500   // (Hz) Spindle/laser frequency (only on supported HALs: AVR and LPC)
3467
+    #define SPINDLE_LASER_FREQUENCY     2500   // (Hz) Spindle/laser frequency (only on supported HALs: AVR, ESP32 and LPC)
3468
   #endif
3468
   #endif
3469
 
3469
 
3470
   //#define AIR_EVACUATION                     // Cutter Vacuum / Laser Blower motor control with G-codes M10-M11
3470
   //#define AIR_EVACUATION                     // Cutter Vacuum / Laser Blower motor control with G-codes M10-M11

+ 82
- 19
Marlin/src/HAL/ESP32/HAL.cpp Dosyayı Görüntüle

73
 esp_adc_cal_characteristics_t characteristics[ADC_ATTEN_MAX];
73
 esp_adc_cal_characteristics_t characteristics[ADC_ATTEN_MAX];
74
 adc_atten_t attenuations[ADC1_CHANNEL_MAX] = {};
74
 adc_atten_t attenuations[ADC1_CHANNEL_MAX] = {};
75
 uint32_t thresholds[ADC_ATTEN_MAX];
75
 uint32_t thresholds[ADC_ATTEN_MAX];
76
-volatile int numPWMUsed = 0,
77
-             pwmPins[MAX_PWM_PINS],
78
-             pwmValues[MAX_PWM_PINS];
76
+
77
+volatile int numPWMUsed = 0;
78
+volatile struct { pin_t pin; int value; } pwmState[MAX_PWM_PINS];
79
+
80
+pin_t chan_pin[CHANNEL_MAX_NUM + 1] = { 0 }; // PWM capable IOpins - not 0 or >33 on ESP32
81
+
82
+struct {
83
+  uint32_t freq; // ledcReadFreq doesn't work if a duty hasn't been set yet!
84
+  uint16_t res;
85
+} pwmInfo[(CHANNEL_MAX_NUM + 1) / 2];
79
 
86
 
80
 // ------------------------
87
 // ------------------------
81
 // Public functions
88
 // Public functions
254
   adc1_set_attenuation(chan, atten);
261
   adc1_set_attenuation(chan, atten);
255
 }
262
 }
256
 
263
 
257
-void analogWrite(pin_t pin, int value) {
258
-  // Use ledc hardware for internal pins
259
-  if (pin < 34) {
260
-    static int cnt_channel = 1, pin_to_channel[40] = { 0 };
261
-    if (pin_to_channel[pin] == 0) {
262
-      ledcAttachPin(pin, cnt_channel);
263
-      ledcSetup(cnt_channel, 490, 8);
264
-      ledcWrite(cnt_channel, value);
265
-      pin_to_channel[pin] = cnt_channel++;
264
+// ------------------------
265
+// PWM
266
+// ------------------------
267
+
268
+int8_t channel_for_pin(const uint8_t pin) {
269
+  for (int i = 0; i <= CHANNEL_MAX_NUM; i++)
270
+    if (chan_pin[i] == pin) return i;
271
+  return -1;
272
+}
273
+
274
+// get PWM channel for pin - if none then attach a new one
275
+// return -1 if fail or invalid pin#, channel # (0-15) if success
276
+int8_t get_pwm_channel(const pin_t pin, const uint32_t freq, const uint16_t res) {
277
+  if (!WITHIN(pin, 1, MAX_PWM_IOPIN)) return -1; // Not a hardware PWM pin!
278
+  int8_t cid = channel_for_pin(pin);
279
+  if (cid >= 0) return cid;
280
+
281
+  // Find an empty adjacent channel (same timer & freq/res)
282
+  for (int i = 0; i <= CHANNEL_MAX_NUM; i++) {
283
+    if (chan_pin[i] == 0) {
284
+      if (chan_pin[i ^ 0x1] != 0) {
285
+        if (pwmInfo[i / 2].freq == freq && pwmInfo[i / 2].res == res) {
286
+          chan_pin[i] = pin; // Allocate PWM to this channel
287
+          ledcAttachPin(pin, i);
288
+          return i;
289
+        }
290
+      }
291
+      else if (cid == -1)    // Pair of empty channels?
292
+        cid = i & 0xFE;      // Save lower channel number
266
     }
293
     }
267
-    ledcWrite(pin_to_channel[pin], value);
294
+  }
295
+  // not attached, is an empty timer slot avail?
296
+  if (cid >= 0) {
297
+    chan_pin[cid] = pin;
298
+    pwmInfo[cid / 2].freq = freq;
299
+    pwmInfo[cid / 2].res = res;
300
+    ledcSetup(cid, freq, res);
301
+    ledcAttachPin(pin, cid);
302
+  }
303
+  return cid; // -1 if no channel avail
304
+}
305
+
306
+void MarlinHAL::set_pwm_duty(const pin_t pin, const uint16_t v, const uint16_t v_size/*=_BV(PWM_RESOLUTION)-1*/, const bool invert/*=false*/) {
307
+  const int8_t cid = get_pwm_channel(pin, PWM_FREQUENCY, PWM_RESOLUTION);
308
+  if (cid >= 0) {
309
+    uint32_t duty = map(invert ? v_size - v : v, 0, v_size, 0, _BV(PWM_RESOLUTION)-1);
310
+    ledcWrite(cid, duty);
311
+  }
312
+}
313
+
314
+int8_t MarlinHAL::set_pwm_frequency(const pin_t pin, const uint32_t f_desired) {
315
+  const int8_t cid = channel_for_pin(pin);
316
+  if (cid >= 0) {
317
+    if (f_desired == ledcReadFreq(cid)) return cid; // no freq change
318
+    ledcDetachPin(chan_pin[cid]);
319
+    chan_pin[cid] = 0;              // remove old freq channel
320
+  }
321
+  return get_pwm_channel(pin, f_desired, PWM_RESOLUTION); // try for new one
322
+}
323
+
324
+// use hardware PWM if avail, if not then ISR
325
+void analogWrite(const pin_t pin, const uint16_t value, const uint32_t freq/*=PWM_FREQUENCY*/, const uint16_t res/*=8*/) { // always 8 bit resolution!
326
+  // Use ledc hardware for internal pins
327
+  const int8_t cid = get_pwm_channel(pin, freq, res);
328
+  if (cid >= 0) {
329
+    ledcWrite(cid, value); // set duty value
268
     return;
330
     return;
269
   }
331
   }
270
 
332
 
333
+  // not a hardware PWM pin OR no PWM channels available
271
   int idx = -1;
334
   int idx = -1;
272
 
335
 
273
   // Search Pin
336
   // Search Pin
274
   for (int i = 0; i < numPWMUsed; ++i)
337
   for (int i = 0; i < numPWMUsed; ++i)
275
-    if (pwmPins[i] == pin) { idx = i; break; }
338
+    if (pwmState[i].pin == pin) { idx = i; break; }
276
 
339
 
277
   // not found ?
340
   // not found ?
278
   if (idx < 0) {
341
   if (idx < 0) {
281
 
344
 
282
     // Take new slot for pin
345
     // Take new slot for pin
283
     idx = numPWMUsed;
346
     idx = numPWMUsed;
284
-    pwmPins[idx] = pin;
347
+    pwmState[idx].pin = pin;
285
     // Start timer on first use
348
     // Start timer on first use
286
     if (idx == 0) HAL_timer_start(MF_TIMER_PWM, PWM_TIMER_FREQUENCY);
349
     if (idx == 0) HAL_timer_start(MF_TIMER_PWM, PWM_TIMER_FREQUENCY);
287
 
350
 
289
   }
352
   }
290
 
353
 
291
   // Use 7bit internal value - add 1 to have 100% high at 255
354
   // Use 7bit internal value - add 1 to have 100% high at 255
292
-  pwmValues[idx] = (value + 1) / 2;
355
+  pwmState[idx].value = (value + 1) / 2;
293
 }
356
 }
294
 
357
 
295
 // Handle PWM timer interrupt
358
 // Handle PWM timer interrupt
300
 
363
 
301
   for (int i = 0; i < numPWMUsed; ++i) {
364
   for (int i = 0; i < numPWMUsed; ++i) {
302
     if (count == 0)                   // Start of interval
365
     if (count == 0)                   // Start of interval
303
-      WRITE(pwmPins[i], pwmValues[i] ? HIGH : LOW);
304
-    else if (pwmValues[i] == count)   // End of duration
305
-      WRITE(pwmPins[i], LOW);
366
+      digitalWrite(pwmState[i].pin, pwmState[i].value ? HIGH : LOW);
367
+    else if (pwmState[i].value == count)   // End of duration
368
+      digitalWrite(pwmState[i].pin, LOW);
306
   }
369
   }
307
 
370
 
308
   // 128 for 7 Bit resolution
371
   // 128 for 7 Bit resolution

+ 18
- 7
Marlin/src/HAL/ESP32/HAL.h Dosyayı Görüntüle

64
 #define CRITICAL_SECTION_START() portENTER_CRITICAL(&spinlock)
64
 #define CRITICAL_SECTION_START() portENTER_CRITICAL(&spinlock)
65
 #define CRITICAL_SECTION_END()   portEXIT_CRITICAL(&spinlock)
65
 #define CRITICAL_SECTION_END()   portEXIT_CRITICAL(&spinlock)
66
 
66
 
67
+#define HAL_CAN_SET_PWM_FREQ   // This HAL supports PWM Frequency adjustment
68
+#define PWM_FREQUENCY  1000u   // Default PWM frequency when set_pwm_duty() is called without set_pwm_frequency()
69
+#define PWM_RESOLUTION   10u   // Default PWM bit resolution
70
+#define CHANNEL_MAX_NUM  15u   // max PWM channel # to allocate (7 to only use low speed, 15 to use low & high)
71
+#define MAX_PWM_IOPIN    33u   // hardware pwm pins < 34
72
+
67
 // ------------------------
73
 // ------------------------
68
 // Types
74
 // Types
69
 // ------------------------
75
 // ------------------------
83
 void tone(const pin_t _pin, const unsigned int frequency, const unsigned long duration=0);
89
 void tone(const pin_t _pin, const unsigned int frequency, const unsigned long duration=0);
84
 void noTone(const pin_t _pin);
90
 void noTone(const pin_t _pin);
85
 
91
 
86
-void analogWrite(pin_t pin, int value);
92
+void analogWrite(const pin_t pin, const uint16_t value, const uint32_t freq=PWM_FREQUENCY, const uint16_t res=8);
87
 
93
 
88
 //
94
 //
89
 // Pin Mapping for M42, M43, M226
95
 // Pin Mapping for M42, M43, M226
209
   static uint16_t adc_value() { return adc_result; }
215
   static uint16_t adc_value() { return adc_result; }
210
 
216
 
211
   /**
217
   /**
212
-   * Set the PWM duty cycle for the pin to the given value.
213
-   * No inverting the duty cycle in this HAL.
214
-   * No changing the maximum size of the provided value to enable finer PWM duty control in this HAL.
218
+   * If not already allocated, allocate a hardware PWM channel
219
+   * to the pin and set the duty cycle..
220
+   * Optionally invert the duty cycle [default = false]
221
+   * Optionally change the scale of the provided value to enable finer PWM duty control [default = 255]
215
    */
222
    */
216
-  static void set_pwm_duty(const pin_t pin, const uint16_t v, const uint16_t=255, const bool=false) {
217
-    analogWrite(pin, v);
218
-  }
223
+  static void set_pwm_duty(const pin_t pin, const uint16_t v, const uint16_t v_size=255, const bool invert=false);
224
+
225
+  /**
226
+   * Allocate and set the frequency of a hardware PWM pin
227
+   * Returns -1 if no pin available.
228
+   */
229
+  static int8_t set_pwm_frequency(const pin_t pin, const uint32_t f_desired);
219
 
230
 
220
 };
231
 };

+ 8
- 10
Marlin/src/HAL/ESP32/Servo.cpp Dosyayı Görüntüle

31
 // so we only allocate servo channels up high to avoid side effects with regards to analogWrite (fans, leds, laser pwm etc.)
31
 // so we only allocate servo channels up high to avoid side effects with regards to analogWrite (fans, leds, laser pwm etc.)
32
 int Servo::channel_next_free = 12;
32
 int Servo::channel_next_free = 12;
33
 
33
 
34
-Servo::Servo() {
35
-  channel = channel_next_free++;
36
-}
34
+Servo::Servo() {}
37
 
35
 
38
 int8_t Servo::attach(const int inPin) {
36
 int8_t Servo::attach(const int inPin) {
39
-  if (channel >= CHANNEL_MAX_NUM) return -1;
40
   if (inPin > 0) pin = inPin;
37
   if (inPin > 0) pin = inPin;
41
-
42
-  ledcSetup(channel, 50, 16); // channel X, 50 Hz, 16-bit depth
43
-  ledcAttachPin(pin, channel);
44
-  return true;
38
+  channel = get_pwm_channel(pin, 50u, 16u);
39
+  return channel; // -1 if no PWM avail.
45
 }
40
 }
46
 
41
 
47
-void Servo::detach() { ledcDetachPin(pin); }
42
+// leave channel connected to servo - set duty to zero
43
+void Servo::detach() {
44
+  if (channel >= 0) ledcWrite(channel, 0);
45
+}
48
 
46
 
49
 int Servo::read() { return degrees; }
47
 int Servo::read() { return degrees; }
50
 
48
 
52
   degrees = constrain(inDegrees, MIN_ANGLE, MAX_ANGLE);
50
   degrees = constrain(inDegrees, MIN_ANGLE, MAX_ANGLE);
53
   int us = map(degrees, MIN_ANGLE, MAX_ANGLE, MIN_PULSE_WIDTH, MAX_PULSE_WIDTH);
51
   int us = map(degrees, MIN_ANGLE, MAX_ANGLE, MIN_PULSE_WIDTH, MAX_PULSE_WIDTH);
54
   int duty = map(us, 0, TAU_USEC, 0, MAX_COMPARE);
52
   int duty = map(us, 0, TAU_USEC, 0, MAX_COMPARE);
55
-  ledcWrite(channel, duty);
53
+  if (channel >= 0) ledcWrite(channel, duty); // don't save duty for servos!
56
 }
54
 }
57
 
55
 
58
 void Servo::move(const int value) {
56
 void Servo::move(const int value) {

+ 1
- 2
Marlin/src/HAL/ESP32/Servo.h Dosyayı Görüntüle

30
                    MAX_PULSE_WIDTH = 2400,  // Longest pulse sent to a servo
30
                    MAX_PULSE_WIDTH = 2400,  // Longest pulse sent to a servo
31
                    TAU_MSEC = 20,
31
                    TAU_MSEC = 20,
32
                    TAU_USEC = (TAU_MSEC * 1000),
32
                    TAU_USEC = (TAU_MSEC * 1000),
33
-                   MAX_COMPARE = _BV(16) - 1, // 65535
34
-                   CHANNEL_MAX_NUM = 16;
33
+                   MAX_COMPARE = _BV(16) - 1; // 65535
35
 
34
 
36
 public:
35
 public:
37
   Servo();
36
   Servo();

+ 6
- 2
Marlin/src/HAL/ESP32/inc/SanityCheck.h Dosyayı Görüntüle

25
   #error "EMERGENCY_PARSER is not yet implemented for ESP32. Disable EMERGENCY_PARSER to continue."
25
   #error "EMERGENCY_PARSER is not yet implemented for ESP32. Disable EMERGENCY_PARSER to continue."
26
 #endif
26
 #endif
27
 
27
 
28
-#if ENABLED(FAST_PWM_FAN) || SPINDLE_LASER_FREQUENCY
29
-  #error "Features requiring Hardware PWM (FAST_PWM_FAN, SPINDLE_LASER_FREQUENCY) are not yet supported on ESP32."
28
+#if (ENABLED(SPINDLE_LASER_USE_PWM) && SPINDLE_LASER_FREQUENCY > 78125) || (ENABLED(FAST_PWM_FAN_FREQUENCY) && FAST_PWM_FAN_FREQUENCY > 78125)
29
+  #error "SPINDLE_LASER_FREQUENCY and FAST_PWM_FREQUENCY maximum value is 78125Hz for ESP32."
30
 #endif
30
 #endif
31
 
31
 
32
 #if HAS_TMC_SW_SERIAL
32
 #if HAS_TMC_SW_SERIAL
40
 #if ENABLED(POSTMORTEM_DEBUGGING)
40
 #if ENABLED(POSTMORTEM_DEBUGGING)
41
   #error "POSTMORTEM_DEBUGGING is not yet supported on ESP32."
41
   #error "POSTMORTEM_DEBUGGING is not yet supported on ESP32."
42
 #endif
42
 #endif
43
+
44
+#if MB(MKS_TINYBEE) && ENABLED(FAST_PWM_FAN)
45
+  #error "FAST_PWM_FAN is not available on TinyBee."
46
+#endif

Loading…
İptal
Kaydet