Переглянути джерело

hybrid PWM system

Uses PWM1 to directly control pins 4, 6 & 11 (servo 0, 1 & 3) and PWM1
generated interrupts to control other pins.

Interupt control of the servo pins had too much jitter so switched all
that we could to PWM1 direct control.  The PWM1 direct control pins have
less than 1 microsecond pulse width jitter while the interrupt
controlled ones can have 20+ microseconds of jitter.

Also added insurance to the servo code in the "disable servo after move"
section.
Bob-the-Kuhn 6 роки тому
джерело
коміт
01fb45b4f8

+ 267
- 146
Marlin/src/HAL/HAL_LPC1768/LPC1768_PWM.h Переглянути файл

@@ -23,17 +23,26 @@
23 23
 /**
24 24
  * The class Servo uses the PWM class to implement it's functions
25 25
  *
26
- * The PWM1 module is only used to generate interrups at specified times. It
27
- * is NOT used to directly toggle pins. The ISR writes to the pin assigned to
28
- * that interrupt
29
- *
30 26
  * All PWMs use the same repetition rate - 20mS because that's the normal servo rate
27
+*/
28
+
29
+/**
30
+ * This is a hybrid system.  
31
+ *
32
+ * The PWM1 module is used to directly control the Servo 0, 1 & 3 pins.  This keeps
33
+ * the pulse width jitter to under a microsecond.
31 34
  *
35
+ * For all other pins the PWM1 module is used to generate interrupts.  The ISR 
36
+ * routine does the actual setting/clearing of pins.  The upside is that any pin can
37
+ * have a PWM channel assigned to it.  The downside is that there is more pulse width
38
+ * jitter. The jitter depends on what else is happening in the system and what ISRs
39
+ * prempt the PWM ISR.  Writing to the SD card can add 20 microseconds to the pulse
40
+ * width.
32 41
  */
33
-
42
+ 
34 43
 /**
35 44
  * The data structures are setup to minimize the computation done by the ISR which
36
- * minimizes ISR execution time.  Execution times are 1.7 to 1.9 microseconds.
45
+ * minimizes ISR execution time.  Execution times are 2.2 - 3.7 microseconds.
37 46
  *
38 47
  * Two tables are used.  active_table is used by the ISR.  Changes to the table are
39 48
  * are done by copying the active_table into the work_table, updating the work_table
@@ -47,34 +56,39 @@
47 56
  *
48 57
  * The ISR's priority is set to the maximum otherwise other ISRs can cause considerable
49 58
  * jitter in the PWM high time.
59
+ *
60
+ * See the end of this file for details on the hardware/firmware interaction
50 61
  */
51 62
 
52 63
 
53 64
 #ifdef TARGET_LPC1768
54 65
 #include <lpc17xx_pinsel.h>
55
-//#include "../HAL.h"
56
-//#include "../../../macros.h"
57
-#include "serial.h"
58
-
59
-typedef struct {       // holds all data needed to control the 6 PWM channels
60
-    uint8_t sequence;  // 0: available slot, 1 - 6: PWM channel assigned to that slot
61
-    uint8_t logical_pin;
62
-    uint16_t PWM_mask;
66
+
67
+#define NUM_PWMS 6
68
+
69
+typedef struct {            // holds all data needed to control/init one of the PWM channels
70
+    uint8_t  sequence;       // 0: available slot, 1 - 6: PWM channel assigned to that slot
71
+    uint8_t  logical_pin;
72
+    uint16_t PWM_mask;       // MASK TO CHECK/WRITE THE IR REGISTER
63 73
     volatile uint32_t* set_register;
64 74
     volatile uint32_t* clr_register;
65
-    uint32_t write_mask;
66
-    uint32_t microseconds;
67
-    uint32_t min;
68
-    uint32_t max;
69
-    bool    PWM_flag;     //
70
-    uint8_t servo_index;  // 0 - MAX_SERVO -1 : servo index,  0xFF : PWM channel
71
-    bool    active_flag;
75
+    uint32_t write_mask;     // USED BY SET/CLEAR COMMANDS 
76
+    uint32_t microseconds;   // value written to MR register
77
+    uint32_t min;            // lower value limit checked by WRITE routine before writing to the MR register
78
+    uint32_t max;            // upper value limit checked by WRITE routine before writing to the MR register
79
+    bool     PWM_flag;       // 0 - USED BY sERVO, 1 - USED BY ANALOGWRITE
80
+    uint8_t  servo_index;    // 0 - MAX_SERVO -1 : servo index,  0xFF : PWM channel
81
+    bool     active_flag;    // THIS TABLE ENTRY IS ACTIVELY TOGGLING A PIN
82
+    uint8_t  assigned_MR;    // Which MR (1-6) is used by this logical channel
83
+    uint32_t PCR_bit;        // PCR register bit to enable PWM1 control of this pin
84
+    uint32_t PINSEL3_bits;   // PINSEL3 register bits to set pin mode to PWM1 control
72 85
 
73 86
 } PWM_map;
74 87
 
88
+
75 89
 #define MICRO_MAX 0xffffffff
76 90
 
77
-#define PWM_MAP_INIT_ROW {0, 0xff, 0, 0, 0, 0, MICRO_MAX, 0, 0, 0, 0, 0}
91
+#define PWM_MAP_INIT_ROW {0, 0xff, 0, 0, 0, 0, MICRO_MAX, 0, 0, 0, 0, 0, 0, 0, 0}
78 92
 #define PWM_MAP_INIT {PWM_MAP_INIT_ROW,\
79 93
                       PWM_MAP_INIT_ROW,\
80 94
                       PWM_MAP_INIT_ROW,\
@@ -83,18 +97,14 @@ typedef struct {       // holds all data needed to control the 6 PWM channels
83 97
                       PWM_MAP_INIT_ROW,\
84 98
                      };
85 99
 
86
-PWM_map PWM1_map_A[6] = PWM_MAP_INIT;
87
-PWM_map PWM1_map_B[6] = PWM_MAP_INIT;
100
+PWM_map PWM1_map_A[NUM_PWMS] = PWM_MAP_INIT;
101
+PWM_map PWM1_map_B[NUM_PWMS] = PWM_MAP_INIT;
88 102
 
89 103
 PWM_map *active_table = PWM1_map_A;
90 104
 PWM_map *work_table = PWM1_map_B;
91 105
 PWM_map *ISR_table;
92 106
 
93 107
 
94
-#define NUM_PWMS 6
95
-
96
-volatile uint8_t PWM1_ISR_index = 0;
97
-
98 108
 #define IR_BIT(p) (p >= 0 && p <= 3 ? p : p + 4 )
99 109
 #define COPY_ACTIVE_TABLE    for (uint8_t i = 0; i < 6 ; i++) work_table[i] = active_table[i]
100 110
 #define PIN_IS_INVERTED(p) 0  // place holder in case inverting PWM output is offered
@@ -169,11 +179,12 @@ void LPC1768_PWM_init(void) {
169 179
 }
170 180
 
171 181
 
172
-bool PWM_table_swap;  // flag to tell the ISR that the tables have been swapped
173
-
182
+bool PWM_table_swap = false;  // flag to tell the ISR that the tables have been swapped
183
+bool PWM_MR0_wait = false;  // flag to ensure don't delay MR0 interrupt 
174 184
 
175 185
 
176 186
 bool LPC1768_PWM_attach_pin(uint8_t pin, uint32_t min = 1, uint32_t max = (LPC_PWM1_MR0 - MR0_MARGIN), uint8_t servo_index = 0xff) {
187
+  while (PWM_table_swap) delay(5);  // don't do anything until the previous change has been implemented by the ISR
177 188
   COPY_ACTIVE_TABLE;  // copy active table into work table
178 189
   uint8_t slot = 0;
179 190
   for (uint8_t i = 0; i < NUM_PWMS ; i++)         // see if already in table
@@ -196,6 +207,9 @@ bool LPC1768_PWM_attach_pin(uint8_t pin, uint32_t min = 1, uint32_t max = (LPC_P
196 207
   work_table[slot].active_flag = false;
197 208
 
198 209
   //swap tables
210
+  PWM_MR0_wait = true;
211
+  while (PWM_MR0_wait) delay(5);  //wait until MR0 interrupt has happend so don't delay it.
212
+  
199 213
   NVIC_DisableIRQ(PWM1_IRQn);
200 214
   PWM_map *pointer_swap = active_table;
201 215
   active_table = work_table;
@@ -206,18 +220,36 @@ bool LPC1768_PWM_attach_pin(uint8_t pin, uint32_t min = 1, uint32_t max = (LPC_P
206 220
   return 1;
207 221
 }
208 222
 
209
-
210
-
211
-bool LPC1768_PWM_write(uint8_t pin, uint32_t value) {
212
-  COPY_ACTIVE_TABLE;  // copy active table into work table
213
-  uint8_t slot = 0xFF;
214
-  for (uint8_t i = 0; i < NUM_PWMS; i++)         // find slot
215
-    if (work_table[i].logical_pin == pin) slot = i;
216
-  if (slot == 0xFF) return false;    // return error if pin not found
217
-  digitalWrite(pin, 0);  // set pin to output & set it low
218
-  work_table[slot].microseconds = MAX(MIN(value, work_table[slot].max), work_table[slot].min);
219
-  work_table[slot].active_flag = true;
220
-
223
+#define pin_11_PWM_channel 2
224
+#define pin_6_PWM_channel  3
225
+#define pin_4_PWM_channel  1
226
+
227
+// used to keep track of which Match Registers have been used and if they will be used by the
228
+// PWM1 module to directly control the pin or will be used to generate an interrupt
229
+typedef struct {                        // status of PWM1 channel
230
+    uint8_t map_used;                   // 0 - this MR register not used/assigned
231
+    uint8_t map_PWM_INT;                // 0 - available for interrupts, 1 - in use by PWM
232
+    uint8_t map_PWM_PIN;                // logical pin number for this PwM1 controlled pin / port
233
+    volatile uint32_t* MR_register;     // address of the MR register for this PWM1 channel
234
+    uint32_t PCR_bit;                   // PCR register bit to enable PWM1 control of this pin
235
+    uint32_t PINSEL3_bits;              // PINSEL3 register bits to set pin mode to PWM1 control
236
+} MR_map;
237
+
238
+MR_map map_MR[NUM_PWMS];  
239
+ 
240
+void LPC1768_PWM_update_map_MR(void) {
241
+  map_MR[0] = {0, (uint8_t) (LPC_PWM1->PCR & _BV(8 + pin_4_PWM_channel)  ? 1 : 0),  4, &LPC_PWM1->MR1, 0, 0};
242
+  map_MR[1] = {0, (uint8_t) (LPC_PWM1->PCR & _BV(8 + pin_11_PWM_channel) ? 1 : 0), 11, &LPC_PWM1->MR2, 0, 0};
243
+  map_MR[2] = {0, (uint8_t) (LPC_PWM1->PCR & _BV(8 + pin_6_PWM_channel)  ? 1 : 0),  6, &LPC_PWM1->MR3, 0, 0};
244
+  map_MR[3] = {0, 0,  0, &LPC_PWM1->MR4, 0, 0};
245
+  map_MR[4] = {0, 0,  0, &LPC_PWM1->MR5, 0, 0};
246
+  map_MR[5] = {0, 0,  0, &LPC_PWM1->MR6, 0, 0};
247
+}  
248
+
249
+
250
+uint32_t LPC1768_PWM_interrupt_mask = 1;
251
+
252
+void LPC1768_PWM_update(void) {
221 253
   for (uint8_t i = NUM_PWMS; --i;) {  // (bubble) sort table by microseconds
222 254
     bool didSwap = false;
223 255
     PWM_map temp;
@@ -232,122 +264,137 @@ bool LPC1768_PWM_write(uint8_t pin, uint32_t value) {
232 264
     if (!didSwap) break;
233 265
   }
234 266
 
235
-  for (uint8_t i = 0; i < NUM_PWMS; i++)                             // set the index & PWM_mask
267
+  LPC1768_PWM_interrupt_mask = 0;                          // set match registers to new values, build IRQ mask
268
+  for (uint8_t i = 0; i < NUM_PWMS; i++) { 
236 269
     if (work_table[i].active_flag == true) {
237 270
       work_table[i].sequence = i + 1;
238
-      work_table[i].PWM_mask = _BV(IR_BIT(i + 1));
271
+      
272
+      // first see if there is a PWM1 controlled pin for this entry
273
+      bool found = false;
274
+      for (uint8_t j = 0; (j < NUM_PWMS) && !found; j++) {      
275
+        if ( (map_MR[j].map_PWM_PIN == work_table[i].logical_pin) && map_MR[j].map_PWM_INT ) {
276
+          *map_MR[j].MR_register = work_table[i].microseconds;  // found one of the PWM pins
277
+          work_table[i].PWM_mask = 0;
278
+          work_table[i].PCR_bit = map_MR[j].PCR_bit;            // PCR register bit to enable PWM1 control of this pin
279
+          work_table[i].PINSEL3_bits = map_MR[j].PINSEL3_bits;  // PINSEL3 register bits to set pin mode to PWM1 control} MR_map;
280
+          map_MR[j].map_used = 2;
281
+          work_table[i].assigned_MR = j +1;                    // only used to help in debugging                                                                  
282
+          found = true;
283
+        }
284
+      } 
285
+      
286
+      // didn't find a PWM1 pin so get an interrupt
287
+      for (uint8_t k = 0; (k < NUM_PWMS) && !found; k++) {     
288
+        if ( !(map_MR[k].map_PWM_INT || map_MR[k].map_used)) {
289
+          *map_MR[k].MR_register = work_table[i].microseconds;  // found one for an interrupt pin
290
+          map_MR[k].map_used = 1;
291
+          LPC1768_PWM_interrupt_mask |= _BV(3 * (k + 1));  // set bit in the MCR to enable this MR to generate an interrupt
292
+          work_table[i].PWM_mask = _BV(IR_BIT(k + 1));  // bit in the IR that will go active when this MR generates an interrupt
293
+          work_table[i].assigned_MR = k +1;                // only used to help in debugging 
294
+          found = true;
295
+        }  
296
+      }
239 297
     }
240
-    else work_table[i].sequence = 0;
241
-
242
-  uint32_t interrupt_mask = 0;                          // set match registers to new values, build IRQ mask
243
-  if (work_table[0].active_flag == true) {
244
-    LPC_PWM1->MR1 = work_table[0].microseconds;
245
-    interrupt_mask |= _BV(3);
246
-  }
247
-  if (work_table[1].active_flag == true) {
248
-    LPC_PWM1->MR2 = work_table[1].microseconds;
249
-    interrupt_mask |= _BV(6);
250
-  }
251
-  if (work_table[2].active_flag == true) {
252
-    LPC_PWM1->MR3 = work_table[2].microseconds;
253
-    interrupt_mask |= _BV(9);
254
-  }
255
-  if (work_table[3].active_flag == true) {
256
-    LPC_PWM1->MR4 = work_table[3].microseconds;
257
-    interrupt_mask |= _BV(12);
258
-  }
259
-  if (work_table[4].active_flag == true) {
260
-    LPC_PWM1->MR5 = work_table[4].microseconds;
261
-    interrupt_mask |= _BV(15);
262
-  }
263
-  if (work_table[5].active_flag == true) {
264
-    LPC_PWM1->MR6 = work_table[5].microseconds;
265
-    interrupt_mask |= _BV(18);
266
-  }
267
-  interrupt_mask |= _BV(0);  // add in MR0 interrupt
268
-
269
-  // swap tables
298
+    else
299
+      work_table[i].sequence = 0;
300
+  }      
301
+  LPC1768_PWM_interrupt_mask |= (uint32_t) _BV(0);  // add in MR0 interrupt
302
+
303
+   // swap tables
304
+   
305
+  PWM_MR0_wait = true;
306
+  while (PWM_MR0_wait) delay(5);  //wait until MR0 interrupt has happend so don't delay it.
307
+  
270 308
   NVIC_DisableIRQ(PWM1_IRQn);
271 309
   LPC_PWM1->LER = 0x07E;  // Set the latch Enable Bits to load the new Match Values for MR1 - MR6
272 310
   PWM_map *pointer_swap = active_table;
273 311
   active_table = work_table;
274 312
   work_table = pointer_swap;
275 313
   PWM_table_swap = true;  // tell the ISR that the tables have been swapped
276
-  LPC_PWM1->MCR = interrupt_mask;          // enable new PWM individual channel interrupts
277 314
   NVIC_EnableIRQ(PWM1_IRQn);  // re-enable PWM interrupts
278
-
279
-  return 1;
280 315
 }
281 316
 
282 317
 
283
-
284
-bool LPC1768_PWM_detach_pin(uint8_t pin) {
318
+bool LPC1768_PWM_write(uint8_t pin, uint32_t value) {
319
+  while (PWM_table_swap) delay(5);  // don't do anything until the previous change has been implemented by the ISR
285 320
   COPY_ACTIVE_TABLE;  // copy active table into work table
286 321
   uint8_t slot = 0xFF;
287 322
   for (uint8_t i = 0; i < NUM_PWMS; i++)         // find slot
288 323
     if (work_table[i].logical_pin == pin) slot = i;
289 324
   if (slot == 0xFF) return false;    // return error if pin not found
290
-  pinMode(pin, INPUT_PULLUP);  // set pin to input with pullup
291
-  work_table[slot] = PWM_MAP_INIT_ROW;
292 325
 
293
-  for (uint8_t i = NUM_PWMS; --i;) {  // (bubble) sort table by microseconds
294
-    bool didSwap = false;
295
-    PWM_map temp;
296
-    for (uint16_t j = 0; j < i; ++j) {
297
-      if (work_table[j].microseconds > work_table[j + 1].microseconds) {
298
-        temp = work_table[j + 1];
299
-        work_table[j + 1] = work_table[j];
300
-        work_table[j] = temp;
301
-        didSwap = true;
302
-      }
303
-    }
304
-    if (!didSwap) break;
305
-  }
326
+  LPC1768_PWM_update_map_MR();
327
+  
328
+  switch(pin) {
329
+    case 11:                        // Servo 0, PWM1 channel 2 (Pin 11  P1.20 PWM1.2)
330
+      map_MR[pin_11_PWM_channel - 1].PCR_bit = _BV(8 + pin_11_PWM_channel);  // enable PWM1 module control of this pin
331
+      map_MR[pin_11_PWM_channel - 1].map_PWM_INT = 1;               // 0 - available for interrupts, 1 - in use by PWM
332
+      map_MR[pin_11_PWM_channel - 1].PINSEL3_bits = 0x2 <<  8;      // ISR must do this AFTER setting PCR
333
+      break;
334
+    case  6:                        // Servo 1, PWM1 channel 3 (Pin 6  P1.21 PWM1.3)
335
+      map_MR[pin_6_PWM_channel - 1].PCR_bit = _BV(8 + pin_6_PWM_channel);                  // enable PWM1 module control of this pin
336
+      map_MR[pin_6_PWM_channel - 1].map_PWM_INT = 1;                // 0 - available for interrupts, 1 - in use by PWM
337
+      map_MR[pin_6_PWM_channel - 1].PINSEL3_bits = 0x2 << 10;       // ISR must do this AFTER setting PCR
338
+      break;
339
+    case  4:                        // Servo 3, PWM1 channel 1 (Pin 4  P1.18 PWM1.1)
340
+      map_MR[pin_4_PWM_channel - 1].PCR_bit = _BV(8 + pin_4_PWM_channel);                  // enable PWM1 module control of this pin 
341
+      map_MR[pin_4_PWM_channel - 1].map_PWM_INT = 1;                // 0 - available for interrupts, 1 - in use by PWM
342
+      map_MR[pin_4_PWM_channel - 1].PINSEL3_bits =  0x2 <<  4;       // ISR must do this AFTER setting PCR
343
+      break;
344
+    default:                                                        // ISR pins 
345
+      pinMode(pin, OUTPUT);  // set pin to output but don't write anything in case it's already in use
346
+      break;
347
+  }      
348
+    
349
+  work_table[slot].microseconds = MAX(MIN(value, work_table[slot].max), work_table[slot].min);
350
+  work_table[slot].active_flag = true;
306 351
 
307
-  for (uint8_t i = 0; i < NUM_PWMS; i++)                             // set the index & PWM_mask
308
-    if (work_table[i].active_flag == true) {
309
-      work_table[i].sequence = i + 1;
310
-      work_table[i].PWM_mask = _BV(IR_BIT(i + 1));
311
-    }
312
-    else work_table[i].sequence = 0;
352
+  LPC1768_PWM_update();
313 353
 
314
-  uint32_t interrupt_mask = 0;                        // set match registers to new values, build IRQ mask
315
-  if (work_table[0].active_flag == true) {
316
-    LPC_PWM1->MR1 = work_table[0].microseconds;
317
-    interrupt_mask |= _BV(3);
318
-  }
319
-  if (work_table[1].active_flag == true) {
320
-    LPC_PWM1->MR2 = work_table[1].microseconds;
321
-    interrupt_mask |= _BV(6);
322
-  }
323
-  if (work_table[2].active_flag == true) {
324
-    LPC_PWM1->MR3 = work_table[2].microseconds;
325
-    interrupt_mask |= _BV(9);
326
-  }
327
-  if (work_table[3].active_flag == true) {
328
-    LPC_PWM1->MR4 = work_table[3].microseconds;
329
-    interrupt_mask |= _BV(12);
330
-  }
331
-  if (work_table[4].active_flag == true) {
332
-    LPC_PWM1->MR5 = work_table[4].microseconds;
333
-    interrupt_mask |= _BV(15);
334
-  }
335
-  if (work_table[5].active_flag == true) {
336
-    LPC_PWM1->MR6 = work_table[5].microseconds;
337
-    interrupt_mask |= _BV(18);
338
-  }
354
+  return 1;
355
+}  
339 356
 
340
-  interrupt_mask |= _BV(0);  // add in MR0 interrupt
341 357
 
342
-  // swap tables
343
-  NVIC_DisableIRQ(PWM1_IRQn);
344
-  LPC_PWM1->LER = 0x07E;  // Set the latch Enable Bits to load the new Match Values for MR1 - MR6
345
-  PWM_map *pointer_swap = active_table;
346
-  active_table = work_table;
347
-  work_table = pointer_swap;
348
-  PWM_table_swap = true;  // tell the ISR that the tables have been swapped
349
-  LPC_PWM1->MCR = interrupt_mask;          // enable remaining PWM individual channel interrupts
350
-  NVIC_EnableIRQ(PWM1_IRQn);  // re-enable PWM interrupts
358
+bool LPC1768_PWM_detach_pin(uint8_t pin) {
359
+  while (PWM_table_swap) delay(5);  // don't do anything until the previous change has been implemented by the ISR
360
+  COPY_ACTIVE_TABLE;  // copy active table into work table
361
+  uint8_t slot = 0xFF;
362
+  for (uint8_t i = 0; i < NUM_PWMS; i++)         // find slot
363
+    if (work_table[i].logical_pin == pin) slot = i;
364
+  if (slot == 0xFF) return false;    // return error if pin not found
365
+
366
+  LPC1768_PWM_update_map_MR();
367
+
368
+  // OK to make these changes before the MR0 interrupt
369
+  switch(pin) {
370
+    case 11:                        // Servo 0, PWM1 channel 2  (Pin 11  P1.20 PWM1.2)
371
+      LPC_PWM1->PCR &= ~(_BV(8 + pin_11_PWM_channel));                 // disable PWM1 module control of this pin
372
+      map_MR[pin_11_PWM_channel - 1].PCR_bit = 0;
373
+      LPC_PINCON->PINSEL3 &= ~(0x3 <<  8);    // return pin to general purpose I/O
374
+      map_MR[pin_11_PWM_channel - 1].PINSEL3_bits = 0;
375
+      map_MR[pin_11_PWM_channel - 1].map_PWM_INT = 0;               // 0 - available for interrupts, 1 - in use by PWM
376
+      break;
377
+    case  6:                        // Servo 1, PWM1 channel 3  (Pin 6  P1.21 PWM1.3)
378
+      LPC_PWM1->PCR &= ~(_BV(8 + pin_6_PWM_channel));                  // disable PWM1 module control of this pin
379
+      map_MR[pin_6_PWM_channel - 1].PCR_bit = 0;
380
+      LPC_PINCON->PINSEL3 &= ~(0x3 << 10);  // return pin to general purpose I/O
381
+      map_MR[pin_6_PWM_channel - 1].PINSEL3_bits = 0;
382
+      map_MR[pin_6_PWM_channel - 1].map_PWM_INT = 0;                // 0 - available for interrupts, 1 - in use by PWM
383
+      break;
384
+    case  4:                        // Servo 3, PWM1 channel 1 (Pin 4  P1.18 PWM1.1)
385
+      LPC_PWM1->PCR &= ~(_BV(8 + pin_4_PWM_channel));                  // disable PWM1 module control of this pin 
386
+      map_MR[pin_4_PWM_channel - 1].PCR_bit =  0;
387
+      LPC_PINCON->PINSEL3 &= ~(0x3 <<  4);  // return pin to general purpose I/O
388
+      map_MR[pin_4_PWM_channel - 1].PINSEL3_bits =  0;
389
+      map_MR[pin_4_PWM_channel - 1].map_PWM_INT = 0;                // 0 - available for interrupts, 1 - in use by PWM
390
+      break;
391
+  } 
392
+  
393
+  pinMode(pin, INPUT);
394
+  
395
+  work_table[slot] = PWM_MAP_INIT_ROW;
396
+
397
+  LPC1768_PWM_update();
351 398
 
352 399
   return 1;
353 400
 }
@@ -356,27 +403,101 @@ bool LPC1768_PWM_detach_pin(uint8_t pin) {
356 403
 
357 404
 #define HAL_PWM_LPC1768_ISR  extern "C" void PWM1_IRQHandler(void)
358 405
 
406
+
407
+// Both loops could be terminated when the last active channel is found but that would
408
+// result in variations ISR run time which results in variations in pulse width
409
+
410
+/**
411
+ * Changes to PINSEL3, PCR and MCR are only done during the MR0 interrupt otherwise
412
+ * the wrong pin may be toggled or even have the system hang.
413
+ */
414
+  
415
+  
359 416
 HAL_PWM_LPC1768_ISR {
360 417
   if (PWM_table_swap) ISR_table = work_table;   // use old table if a swap was just done
361 418
   else ISR_table = active_table;
362 419
 
363 420
   if (LPC_PWM1->IR & 0x1) {                                      // MR0 interrupt
364
-    PWM_table_swap = false;                                         // MR0 means new values could have been
365
-    ISR_table = active_table;                                       // loaded so set everything to normal operation
366
-    for (uint8_t i = 0; (i < NUM_PWMS) && ISR_table[i].active_flag ; i++)
367
-      *ISR_table[i].set_register = ISR_table[i].write_mask;     // set all enabled channels active
421
+    ISR_table = active_table;                    // MR0 means new values could have been loaded so set everything
422
+    if (PWM_table_swap) LPC_PWM1->MCR = LPC1768_PWM_interrupt_mask; // enable new PWM individual channel interrupts
423
+
424
+    for (uint8_t i = 0; (i < NUM_PWMS) ; i++) {
425
+      if(ISR_table[i].active_flag && !((ISR_table[i].logical_pin ==  11) || 
426
+                                       (ISR_table[i].logical_pin ==  4) || 
427
+                                       (ISR_table[i].logical_pin ==  6))) 
428
+        *ISR_table[i].set_register = ISR_table[i].write_mask;       // set pins for all enabled interrupt channels active
429
+      if (PWM_table_swap && ISR_table[i].PCR_bit) {
430
+        LPC_PWM1->PCR |= ISR_table[i].PCR_bit;              // enable PWM1 module control of this pin
431
+        LPC_PINCON->PINSEL3 |= ISR_table[i].PINSEL3_bits;   // set pin mode to PWM1 control - must be done after PCR 
432
+      }
433
+    }
434
+    PWM_table_swap = false;
435
+    PWM_MR0_wait = false;
368 436
     LPC_PWM1->IR = 0x01;                                             // clear the MR0 interrupt flag bit
369
-    PWM1_ISR_index = 0;
370 437
   }
371 438
   else {
372
-    if (ISR_table[PWM1_ISR_index].active_flag && (LPC_PWM1->IR & ISR_table[PWM1_ISR_index].PWM_mask)) {
373
-      LPC_PWM1->IR = ISR_table[PWM1_ISR_index].PWM_mask;       // clear the interrupt flag bit
374
-      *ISR_table[PWM1_ISR_index].clr_register = ISR_table[PWM1_ISR_index].write_mask;   // set channel to inactive
375
-    }
376
-    PWM1_ISR_index++;                                           // should be the index for the next interrupt
439
+    for (uint8_t i = 0; i < NUM_PWMS ; i++)
440
+      if (ISR_table[i].active_flag && (LPC_PWM1->IR & ISR_table[i].PWM_mask) ){
441
+        LPC_PWM1->IR = ISR_table[i].PWM_mask;       // clear the interrupt flag bits for expected interrupts
442
+        *ISR_table[i].clr_register = ISR_table[i].write_mask;   // set channel to inactive
443
+      }
377 444
   }
445
+ 
446
+  LPC_PWM1->IR = 0x70F;  // guarantees all interrupt flags are cleared which, if there is an unexpected
447
+                           // PWM interrupt, will keep the ISR from hanging which will crash the controller
378 448
 
379 449
 return;
380 450
 }
381
-
382 451
 #endif
452
+
453
+/////////////////////////////////////////////////////////////////
454
+/////////////////  HARDWARE FIRMWARE INTERACTION ////////////////
455
+/////////////////////////////////////////////////////////////////
456
+
457
+/**
458
+ *  Almost all changes to the hardware registers must be coordinated with the Match Register 0 (MR0)
459
+ *  interrupt.  The only exception is detaching pins.  It doesn't matter when they go
460
+ *  tristate.  
461
+ *
462
+ *  The LPC1768_PWM_init routine kicks off the MR0 interrupt.  This interrupt is never disabled or 
463
+ *  delayed. 
464
+ *
465
+ *  The PWM_table_swap flag is set when the firmware has swapped in an updated table.  It is
466
+ *  cleared by the ISR during the MR0 interrupt as it completes the swap and accompanying updates.
467
+ *  It serves two purposes:
468
+ *    1) Tells the ISR that the tables have been swapped
469
+ *    2) Keeps the firmware from starting a new update until the previous one has been completed. 
470
+ *
471
+ *  The PWM_MR0_wait flag is set when the firmware is ready to swap in an updated table and cleared by 
472
+ *  the ISR during the MR0 interrupt.  It is used to avoid delaying the MR0 interrupt when swapping in
473
+ *  an updated table.  This avoids glitches in pulse width and/or repetition rate. 
474
+ *
475
+ *  The sequence of events during a write to a PWM channel is:
476
+ *    1) Waits until PWM_table_swap flag is false before starting
477
+ *    2) Copies the active table into the work table
478
+ *    3) Updates the work table
479
+ *         NOTES - MR1-MR6 are updated at this time.  The updates aren't put into use until the first
480
+ *                 MR0 after the LER register has been written.  The LER register is written during the
481
+ *                 table swap process.
482
+ *               - The MCR mask is created at this time.  It is not used until the ISR writes the MCR
483
+ *                 during the MR0 interrupt in the table swap process.
484
+ *    4) Sets the PWM_MR0_wait flag
485
+ *    5) ISR clears the PWM_MR0_wait flag during the next MR0 interrupt
486
+ *    6) Once the PWM_MR0_wait flag is cleared then the firmware:
487
+ *          disables the ISR interrupt
488
+ *          swaps the pointers to the tables
489
+ *          writes to the LER register
490
+ *          sets the PWM_table_swap flag active
491
+ *          re-enables the ISR
492
+ *     7) On the next interrupt the ISR changes it's pointer to the work table which is now the old, 
493
+ *        unmodified, active table.
494
+ *     8) On the next MR0 interrupt the ISR:
495
+ *          switches over to the active table
496
+ *          clears the PWM_table_swap and PWM_MR0_wait flags
497
+ *          updates the MCR register with the possibly new interrupt sources/assignments
498
+ *          writes to the PCR register to enable the direct control of the Servo 0, 1 & 3 pins by the PWM1 module
499
+ *          sets the PINSEL3 register to function/mode 0x2 for the Servo 0, 1 & 3 pins
500
+ *             NOTE - PCR must be set before PINSEL
501
+ *          sets the pins controlled by the ISR to their active states
502
+ */
503
+ 

+ 1
- 0
Marlin/src/HAL/HAL_LPC1768/LPC1768_Servo.cpp Переглянути файл

@@ -159,6 +159,7 @@
159 159
       #if ENABLED(DEACTIVATE_SERVOS_AFTER_MOVE)
160 160
         this->detach();
161 161
         LPC1768_PWM_detach_pin(servo_info[this->servoIndex].Pin.nbr);  // shut down the PWM signal
162
+        LPC1768_PWM_attach_pin(servo_info[this->servoIndex].Pin.nbr, MIN_PULSE_WIDTH, MAX_PULSE_WIDTH, this->servoIndex);  // make sure no one else steals the slot
162 163
       #endif
163 164
     }
164 165
   }

Завантаження…
Відмінити
Зберегти