Просмотр исходного кода

Servo & PWM support for LPC1768 (#7500)

Bob-the-Kuhn 7 лет назад
Родитель
Сommit
97444391e0

+ 382
- 0
Marlin/src/HAL/HAL_LPC1768/LPC1768_PWM.h Просмотреть файл

@@ -0,0 +1,382 @@
1
+/**
2
+ * Marlin 3D Printer Firmware
3
+ * Copyright (C) 2017 MarlinFirmware [https://github.com/MarlinFirmware/Marlin]
4
+ *
5
+ * Based on Sprinter and grbl.
6
+ * Copyright (C) 2011 Camiel Gubbels / Erik van der Zalm
7
+ *
8
+ * This program is free software: you can redistribute it and/or modify
9
+ * it under the terms of the GNU General Public License as published by
10
+ * the Free Software Foundation, either version 3 of the License, or
11
+ * (at your option) any later version.
12
+ *
13
+ * This program is distributed in the hope that it will be useful,
14
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
15
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16
+ * GNU General Public License for more details.
17
+ *
18
+ * You should have received a copy of the GNU General Public License
19
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
20
+ *
21
+ */
22
+
23
+/**
24
+ * The class Servo uses the PWM class to implement it's functions
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
+ * All PWMs use the same repetition rate - 20mS because that's the normal servo rate
31
+ *
32
+ */
33
+
34
+/**
35
+ * 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.
37
+ *
38
+ * Two tables are used.  active_table is used by the ISR.  Changes to the table are
39
+ * are done by copying the active_table into the work_table, updating the work_table
40
+ * and then swapping the two tables.  Swapping is done by manipulating pointers.
41
+ *
42
+ * Immediately after the swap the ISR uses the work_table until the start of the
43
+ * next 20mS cycle. During this transition the "work_table" is actually the table
44
+ * that was being used before the swap.  The "active_table" contains the data that
45
+ * will start being used at the start of the next 20mS period.  This keeps the pins
46
+ * well behaved during the transition.
47
+ *
48
+ * The ISR's priority is set to the maximum otherwise other ISRs can cause considerable
49
+ * jitter in the PWM high time.
50
+ */
51
+
52
+
53
+#ifdef TARGET_LPC1768
54
+#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;
63
+    volatile uint32_t* set_register;
64
+    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;
72
+
73
+} PWM_map;
74
+
75
+#define MICRO_MAX 0xffffffff
76
+
77
+#define PWM_MAP_INIT_ROW {0, 0xff, 0, 0, 0, 0, MICRO_MAX, 0, 0, 0, 0, 0}
78
+#define PWM_MAP_INIT {PWM_MAP_INIT_ROW,\
79
+                      PWM_MAP_INIT_ROW,\
80
+                      PWM_MAP_INIT_ROW,\
81
+                      PWM_MAP_INIT_ROW,\
82
+                      PWM_MAP_INIT_ROW,\
83
+                      PWM_MAP_INIT_ROW,\
84
+                     };
85
+
86
+PWM_map PWM1_map_A[6] = PWM_MAP_INIT;
87
+PWM_map PWM1_map_B[6] = PWM_MAP_INIT;
88
+
89
+PWM_map *active_table = PWM1_map_A;
90
+PWM_map *work_table = PWM1_map_B;
91
+PWM_map *ISR_table;
92
+
93
+
94
+#define NUM_PWMS 6
95
+
96
+volatile uint8_t PWM1_ISR_index = 0;
97
+
98
+#define IR_BIT(p) (p >= 0 && p <= 3 ? p : p + 4 )
99
+#define COPY_ACTIVE_TABLE    for (uint8_t i = 0; i < 6 ; i++) work_table[i] = active_table[i]
100
+#define PIN_IS_INVERTED(p) 0  // place holder in case inverting PWM output is offered
101
+
102
+
103
+/**
104
+ *  Prescale register and MR0 register values
105
+ *
106
+ *               100MHz PCLK          50MHz PCLK          25MHz PCLK         12.5MHz PCLK
107
+ *             -----------------   -----------------   -----------------   -----------------
108
+ *  desired    prescale  MR0       prescale  MR0       prescale  MR0       prescale  MR0        resolution
109
+ *  prescale   register  register  register  register  register  register  register  register   in degrees
110
+ *  freq       value     value     value     value     value     value     value     value
111
+ *
112
+ *      8        11.5    159,999      5.25   159,999       2.13  159,999    0.5625   159,999    0.023
113
+ *      4        24       79,999     11.5     79,999       5.25   79,999    2.125     79,999    0.045
114
+ *      2        49       39,999     24       39,999      11.5    39,999    5.25      39,999    0.090
115
+ *      1        99       19,999     49       19,999      24      19,999   11.5       19,999    0.180
116
+ *    0.5       199        9,999     99        9,999      49       9,999   24          9,999    0.360
117
+ *   0.25       399        4,999    199        4,999      99       4,999   49          4,999    0.720
118
+ *  0.125       799        2,499    399        2,499     199       2,499   99          2,499    1.440
119
+ *
120
+ *  The desired prescale frequency comes from an input in the range of 544 - 2400 microseconds and the
121
+ *  desire to just shift the input left or right as needed.
122
+ *
123
+ *  A resolution of 0.2 degrees seems reasonable so a prescale frequency output of 1MHz is being used.
124
+ *  It also means we don't need to scale the input.
125
+ *
126
+ *  The PCLK is set to 25MHz because that's the slowest one that gives whole numbers for prescale and
127
+ *  MR0 registers.
128
+ *
129
+ *  Final settings:
130
+ *   PCLKSEL0: 0x0
131
+ *   PWM1PR:   0x018 (24)
132
+ *   PWM1MR0:  0x04E1F (19,999)
133
+ *
134
+ */
135
+
136
+#define LPC_PWM1_MR0 19999  // base repetition rate minus one count - 20mS
137
+#define LPC_PWM1_PR 24      // prescaler value - prescaler divide by 24 + 1  -  1 MHz output
138
+#define LPC_PWM1_PCLKSEL0 0x00   // select clock source for prescaler - defaults to 25MHz on power up
139
+                                 // 0: 25MHz, 1: 100MHz, 2: 50MHz, 3: 12.5MHZ to PWM1 prescaler
140
+#define MR0_MARGIN 200       // if channel value too close to MR0 the system locks up
141
+
142
+
143
+void LPC1768_PWM_init(void) {
144
+  #define SBIT_CNTEN     0  // PWM1 counter & pre-scaler enable/disable
145
+  #define SBIT_CNTRST    1  // reset counters to known state
146
+  #define SBIT_PWMEN     3  // 1 - PWM, 0 - timer
147
+  #define SBIT_PWMMR0R   1
148
+  #define PCPWM1         6
149
+  #define PCLK_PWM1      12
150
+
151
+  LPC_SC->PCONP |= (1 << PCPWM1);      // enable PWM1 controller (enabled on power up)
152
+  LPC_SC->PCLKSEL0 &= ~(0x3 << PCLK_PWM1);
153
+  LPC_SC->PCLKSEL0 |= (LPC_PWM1_PCLKSEL0 << PCLK_PWM1);
154
+  LPC_PWM1->MR0 = LPC_PWM1_MR0;                // TC resets every 19,999 + 1 cycles - sets PWM cycle(Ton+Toff) to 20 mS
155
+                                        // MR0 must be set before TCR enables the PWM
156
+  LPC_PWM1->TCR = _BV(SBIT_CNTEN) | _BV(SBIT_CNTRST)| _BV(SBIT_PWMEN);;  // enable counters, reset counters, set mode to PWM
157
+  LPC_PWM1->TCR &= ~(_BV(SBIT_CNTRST));  // take counters out of reset
158
+  LPC_PWM1->PR  =  LPC_PWM1_PR;
159
+  LPC_PWM1->MCR = (_BV(SBIT_PWMMR0R) | _BV(0));     // Reset TC if it matches MR0, disable all interrupts except for MR0
160
+  LPC_PWM1->CTCR = 0;                   // disable counter mode (enable PWM mode)
161
+
162
+  LPC_PWM1->LER = 0x07F;  // Set the latch Enable Bits to load the new Match Values for MR0 - MR6
163
+  // Set all PWMs to single edge
164
+  LPC_PWM1->PCR = 0;    // single edge mode for all channels, PWM1 control of outputs off
165
+
166
+  NVIC_EnableIRQ(PWM1_IRQn);     // Enable interrupt handler
167
+  //      NVIC_SetPriority(PWM1_IRQn, NVIC_EncodePriority(0, 10, 0));  // normal priority for PWM module
168
+  NVIC_SetPriority(PWM1_IRQn, NVIC_EncodePriority(0, 0, 0));  // minimizes jitter due to higher priority ISRs
169
+}
170
+
171
+
172
+bool PWM_table_swap;  // flag to tell the ISR that the tables have been swapped
173
+
174
+
175
+
176
+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) {
177
+  COPY_ACTIVE_TABLE;  // copy active table into work table
178
+  uint8_t slot = 0;
179
+  for (uint8_t i = 0; i < NUM_PWMS ; i++)         // see if already in table
180
+    if (work_table[i].logical_pin == pin) return 1;
181
+
182
+  for (uint8_t i = 1; (i < NUM_PWMS + 1) && !slot; i++)         // find empty slot
183
+    if ( !(work_table[i - 1].set_register)) slot = i;  // any item that can't be zero when active or just attached is OK
184
+  if (!slot) return 0;
185
+  slot--;  // turn it into array index
186
+
187
+  work_table[slot].logical_pin = pin;  // init slot
188
+  work_table[slot].PWM_mask = 0;  // real value set by PWM_write
189
+  work_table[slot].set_register = PIN_IS_INVERTED(pin) ? &LPC_GPIO(pin_map[pin].port)->FIOCLR : &LPC_GPIO(pin_map[pin].port)->FIOSET;
190
+  work_table[slot].clr_register = PIN_IS_INVERTED(pin) ? &LPC_GPIO(pin_map[pin].port)->FIOSET : &LPC_GPIO(pin_map[pin].port)->FIOCLR;
191
+  work_table[slot].write_mask = LPC_PIN(pin_map[pin].pin);
192
+  work_table[slot].microseconds = MICRO_MAX;
193
+  work_table[slot].min = min;
194
+  work_table[slot].max = MIN(max, LPC_PWM1_MR0 - MR0_MARGIN);
195
+  work_table[slot].servo_index = servo_index;
196
+  work_table[slot].active_flag = false;
197
+
198
+  //swap tables
199
+  NVIC_DisableIRQ(PWM1_IRQn);
200
+  PWM_map *pointer_swap = active_table;
201
+  active_table = work_table;
202
+  work_table = pointer_swap;
203
+  PWM_table_swap = true;  // tell the ISR that the tables have been swapped
204
+  NVIC_EnableIRQ(PWM1_IRQn);  // re-enable PWM interrupts
205
+
206
+  return 1;
207
+}
208
+
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
+
221
+  for (uint8_t i = NUM_PWMS; --i;) {  // (bubble) sort table by microseconds
222
+    bool didSwap = false;
223
+    PWM_map temp;
224
+    for (uint16_t j = 0; j < i; ++j) {
225
+      if (work_table[j].microseconds > work_table[j + 1].microseconds) {
226
+        temp = work_table[j + 1];
227
+        work_table[j + 1] = work_table[j];
228
+        work_table[j] = temp;
229
+        didSwap = true;
230
+      }
231
+    }
232
+    if (!didSwap) break;
233
+  }
234
+
235
+  for (uint8_t i = 0; i < NUM_PWMS; i++)                             // set the index & PWM_mask
236
+    if (work_table[i].active_flag == true) {
237
+      work_table[i].sequence = i + 1;
238
+      work_table[i].PWM_mask = _BV(IR_BIT(i + 1));
239
+    }
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
270
+  NVIC_DisableIRQ(PWM1_IRQn);
271
+  LPC_PWM1->LER = 0x07E;  // Set the latch Enable Bits to load the new Match Values for MR1 - MR6
272
+  PWM_map *pointer_swap = active_table;
273
+  active_table = work_table;
274
+  work_table = pointer_swap;
275
+  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
+  NVIC_EnableIRQ(PWM1_IRQn);  // re-enable PWM interrupts
278
+
279
+  return 1;
280
+}
281
+
282
+
283
+
284
+bool LPC1768_PWM_detach_pin(uint8_t pin) {
285
+  COPY_ACTIVE_TABLE;  // copy active table into work table
286
+  uint8_t slot = 0xFF;
287
+  for (uint8_t i = 0; i < NUM_PWMS; i++)         // find slot
288
+    if (work_table[i].logical_pin == pin) slot = i;
289
+  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
+
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
+  }
306
+
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;
313
+
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
+  }
339
+
340
+  interrupt_mask |= _BV(0);  // add in MR0 interrupt
341
+
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
351
+
352
+  return 1;
353
+}
354
+
355
+////////////////////////////////////////////////////////////////////////////////
356
+
357
+#define HAL_PWM_LPC1768_ISR  extern "C" void PWM1_IRQHandler(void)
358
+
359
+HAL_PWM_LPC1768_ISR {
360
+  if (PWM_table_swap) ISR_table = work_table;   // use old table if a swap was just done
361
+  else ISR_table = active_table;
362
+
363
+  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
368
+    LPC_PWM1->IR = 0x01;                                             // clear the MR0 interrupt flag bit
369
+    PWM1_ISR_index = 0;
370
+  }
371
+  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
377
+  }
378
+
379
+return;
380
+}
381
+
382
+#endif

+ 169
- 0
Marlin/src/HAL/HAL_LPC1768/LPC1768_Servo.cpp Просмотреть файл

@@ -0,0 +1,169 @@
1
+/**
2
+  * Marlin 3D Printer Firmware
3
+  * Copyright (C) 2016, 2017 MarlinFirmware [https://github.com/MarlinFirmware/Marlin]
4
+  *
5
+  * Based on Sprinter and grbl.
6
+  * Copyright (C) 2011 Camiel Gubbels / Erik van der Zalm
7
+  *
8
+  * This program is free software: you can redistribute it and/or modify
9
+  * it under the terms of the GNU General Public License as published by
10
+  * the Free Software Foundation, either version 3 of the License, or
11
+  * (at your option) any later version.
12
+  *
13
+  * This program is distributed in the hope that it will be useful,
14
+  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15
+  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16
+  * GNU General Public License for more details.
17
+  *
18
+  * You should have received a copy of the GNU General Public License
19
+  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
20
+  *
21
+*/
22
+
23
+/**
24
+  * Based on servo.cpp - Interrupt driven Servo library for Arduino using 16 bit
25
+  * timers- Version 2  Copyright (c) 2009 Michael Margolis.  All right reserved.
26
+*/
27
+
28
+/**
29
+  * A servo is activated by creating an instance of the Servo class passing the desired pin to the attach() method.
30
+  * The servos are pulsed in the background using the value most recently written using the write() method
31
+  *
32
+  * Note that analogWrite of PWM on pins associated with the timer are disabled when the first servo is attached.
33
+  * Timers are seized as needed in groups of 12 servos - 24 servos use two timers, 48 servos will use four.
34
+  *
35
+  * The methods are:
36
+  *
37
+  * Servo - Class for manipulating servo motors connected to Arduino pins.
38
+  *
39
+  * attach(pin)           - Attach a servo motor to an i/o pin.
40
+  * attach(pin, min, max) - Attach to a pin, setting min and max values in microseconds
41
+  *                         Default min is 544, max is 2400
42
+  *
43
+  * write()               - Set the servo angle in degrees. (Invalid angles —over MIN_PULSE_WIDTH— are treated as µs.)
44
+  * writeMicroseconds()   - Set the servo pulse width in microseconds.
45
+  * move(pin, angle)      - Sequence of attach(pin), write(angle), delay(SERVO_DELAY).
46
+  *                         With DEACTIVATE_SERVOS_AFTER_MOVE it detaches after SERVO_DELAY.
47
+  * read()                - Get the last-written servo pulse width as an angle between 0 and 180.
48
+  * readMicroseconds()    - Get the last-written servo pulse width in microseconds.
49
+  * attached()            - Return true if a servo is attached.
50
+  * detach()              - Stop an attached servo from pulsing its i/o pin.
51
+  *
52
+*/
53
+
54
+/**
55
+ *  The only time that this library wants physical movement is when a WRITE
56
+ *  command is issued.  Before that all the attach & detach activity is solely
57
+ *  within the data base.
58
+ *
59
+ *  The PWM output is inactive until the first WRITE.  After that it stays active
60
+ *  unless DEACTIVATE_SERVOS_AFTER_MOVE is enabled and a MOVE command was issued.
61
+ *
62
+ */
63
+
64
+
65
+#if HAS_SERVOS
66
+
67
+
68
+#include "LPC1768_Servo.h"
69
+#include "servo_private.h"
70
+
71
+
72
+  extern bool LPC1768_PWM_attach_pin(uint8_t, uint32_t, uint32_t, uint8_t);
73
+  extern bool LPC1768_PWM_write(uint8_t, uint32_t);
74
+  extern bool LPC1768_PWM_detach_pin(uint8_t);
75
+
76
+
77
+
78
+  ServoInfo_t servo_info[MAX_SERVOS];                  // static array of servo info structures
79
+  uint8_t ServoCount = 0;                              // the total number of attached servos
80
+
81
+
82
+  #define US_TO_PULSE_WIDTH(p) p
83
+  #define PULSE_WIDTH_TO_US(p) p
84
+  #define TRIM_DURATION 0
85
+  #define SERVO_MIN() MIN_PULSE_WIDTH  // minimum value in uS for this servo
86
+  #define SERVO_MAX() MAX_PULSE_WIDTH  // maximum value in uS for this servo
87
+
88
+  Servo::Servo() {
89
+    if (ServoCount < MAX_SERVOS) {
90
+      this->servoIndex = ServoCount++;                    // assign a servo index to this instance
91
+      servo_info[this->servoIndex].pulse_width = US_TO_PULSE_WIDTH(DEFAULT_PULSE_WIDTH);   // store default values  - 12 Aug 2009
92
+    }
93
+    else
94
+    this->servoIndex = INVALID_SERVO;  // too many servos
95
+  }
96
+
97
+  int8_t Servo::attach(int pin) {
98
+    return this->attach(pin, MIN_PULSE_WIDTH, MAX_PULSE_WIDTH);
99
+  }
100
+
101
+  int8_t Servo::attach(int pin, int min, int max) {
102
+
103
+    if (this->servoIndex >= MAX_SERVOS) return -1;
104
+
105
+    if (pin > 0) servo_info[this->servoIndex].Pin.nbr = pin;  // only assign a pin value if the pin info is
106
+                                                              // greater than zero. This way the init routine can
107
+                                                              // assign the pin and the MOVE command only needs the value.
108
+
109
+
110
+    this->min = MIN_PULSE_WIDTH; //resolution of min/max is 1 uS
111
+    this->max = MAX_PULSE_WIDTH;
112
+
113
+    servo_info[this->servoIndex].Pin.isActive = true;
114
+
115
+    return this->servoIndex;
116
+  }
117
+
118
+  void Servo::detach() {
119
+    servo_info[this->servoIndex].Pin.isActive = false;
120
+  }
121
+
122
+  void Servo::write(int value) {
123
+    if (value < MIN_PULSE_WIDTH) { // treat values less than 544 as angles in degrees (valid values in microseconds are handled as microseconds)
124
+      value = map(constrain(value, 0, 180), 0, 180, SERVO_MIN(), SERVO_MAX());
125
+        // odd - this sets zero degrees to 544 and 180 degrees to 2400 microseconds but the literature says
126
+        //          zero degrees should be 500 microseconds and 180 should be 2500
127
+    }
128
+    this->writeMicroseconds(value);
129
+  }
130
+
131
+  void Servo::writeMicroseconds(int value) {
132
+    // calculate and store the values for the given channel
133
+    byte channel = this->servoIndex;
134
+    if (channel < MAX_SERVOS) {  // ensure channel is valid
135
+      // ensure pulse width is valid
136
+      value = constrain(value, SERVO_MIN(), SERVO_MAX()) - (TRIM_DURATION);
137
+      value = US_TO_PULSE_WIDTH(value);  // convert to pulse_width after compensating for interrupt overhead - 12 Aug 2009
138
+
139
+      servo_info[channel].pulse_width = value;
140
+      LPC1768_PWM_attach_pin(servo_info[this->servoIndex].Pin.nbr, MIN_PULSE_WIDTH, MAX_PULSE_WIDTH, this->servoIndex);
141
+      LPC1768_PWM_write(servo_info[this->servoIndex].Pin.nbr, value);
142
+
143
+    }
144
+  }
145
+
146
+  // return the value as degrees
147
+  int Servo::read() { return map(this->readMicroseconds() + 1, SERVO_MIN(), SERVO_MAX(), 0, 180); }
148
+
149
+  int Servo::readMicroseconds() {
150
+    return (this->servoIndex == INVALID_SERVO) ? 0 : PULSE_WIDTH_TO_US(servo_info[this->servoIndex].pulse_width) + TRIM_DURATION;
151
+  }
152
+
153
+  bool Servo::attached() { return servo_info[this->servoIndex].Pin.isActive; }
154
+
155
+  void Servo::move(int value) {
156
+    if (this->attach(0) >= 0) {    // notice the pin number is zero here
157
+      this->write(value);
158
+      delay(SERVO_DELAY);
159
+      #if ENABLED(DEACTIVATE_SERVOS_AFTER_MOVE)
160
+        this->detach();
161
+        LPC1768_PWM_detach_pin(servo_info[this->servoIndex].Pin.nbr);  // shut down the PWM signal
162
+      #endif
163
+    }
164
+  }
165
+
166
+#endif // HAS_SERVOS
167
+
168
+
169
+

+ 69
- 0
Marlin/src/HAL/HAL_LPC1768/LPC1768_Servo.h Просмотреть файл

@@ -0,0 +1,69 @@
1
+/**
2
+ * Marlin 3D Printer Firmware
3
+ * Copyright (C) 2017 MarlinFirmware [https://github.com/MarlinFirmware/Marlin]
4
+ *
5
+ * Based on Sprinter and grbl.
6
+ * Copyright (C) 2011 Camiel Gubbels / Erik van der Zalm
7
+ *
8
+ * This program is free software: you can redistribute it and/or modify
9
+ * it under the terms of the GNU General Public License as published by
10
+ * the Free Software Foundation, either version 3 of the License, or
11
+ * (at your option) any later version.
12
+ *
13
+ * This program is distributed in the hope that it will be useful,
14
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
15
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16
+ * GNU General Public License for more details.
17
+ *
18
+ * You should have received a copy of the GNU General Public License
19
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
20
+ *
21
+ */
22
+
23
+/**
24
+ * The class Servo uses the PWM class to implement it's functions
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
+ * All PWMs use the same repetition rate - 20mS because that's the normal servo rate
31
+ *
32
+ */
33
+
34
+#ifndef LPC1768_SERVO_h
35
+  #define LPC1768_SERVO_h
36
+
37
+  #ifdef TARGET_LPC1768
38
+    #include <inttypes.h>
39
+
40
+
41
+    class Servo {
42
+      public:
43
+        Servo();
44
+        int8_t attach(int pin);            // attach the given pin to the next free channel, set pinMode, return channel number (-1 on fail)
45
+        int8_t attach(int pin, int min, int max); // as above but also sets min and max values for writes.
46
+        void detach();
47
+        void write(int value);             // if value is < 200 it is treated as an angle, otherwise as pulse width in microseconds
48
+        void writeMicroseconds(int value); // write pulse width in microseconds
49
+        void move(int value);              // attach the servo, then move to value
50
+                                           // if value is < 200 it is treated as an angle, otherwise as pulse width in microseconds
51
+                                           // if DEACTIVATE_SERVOS_AFTER_MOVE wait SERVO_DELAY, then detach
52
+        int read();                        // returns current pulse width as an angle between 0 and 180 degrees
53
+        int readMicroseconds();            // returns current pulse width in microseconds for this servo (was read_us() in first release)
54
+        bool attached();                   // return true if this servo is attached, otherwise false
55
+
56
+      private:
57
+        uint8_t servoIndex;               // index into the channel data for this servo
58
+        int min;
59
+        int max;
60
+    };
61
+
62
+
63
+    #define HAL_SERVO_LIB Servo
64
+
65
+  #endif
66
+#endif
67
+
68
+
69
+

+ 0
- 44
Marlin/src/HAL/HAL_LPC1768/Servo.cpp Просмотреть файл

@@ -1,44 +0,0 @@
1
-/**
2
- * Marlin 3D Printer Firmware
3
- * Copyright (C) 2016 MarlinFirmware [https://github.com/MarlinFirmware/Marlin]
4
- *
5
- * Based on Sprinter and grbl.
6
- * Copyright (C) 2011 Camiel Gubbels / Erik van der Zalm
7
- *
8
- * This program is free software: you can redistribute it and/or modify
9
- * it under the terms of the GNU General Public License as published by
10
- * the Free Software Foundation, either version 3 of the License, or
11
- * (at your option) any later version.
12
- *
13
- * This program is distributed in the hope that it will be useful,
14
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
15
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16
- * GNU General Public License for more details.
17
- *
18
- * You should have received a copy of the GNU General Public License
19
- * along with this program.  If not, see <http://www.gnu.org/licenses/>.
20
- *
21
- */
22
-
23
-/*
24
-  Copyright (c) 2013 Arduino LLC. All right reserved.
25
-
26
-  This library is free software; you can redistribute it and/or
27
-  modify it under the terms of the GNU Lesser General Public
28
-  License as published by the Free Software Foundation; either
29
-  version 2.1 of the License, or (at your option) any later version.
30
-
31
-  This library is distributed in the hope that it will be useful,
32
-  but WITHOUT ANY WARRANTY; without even the implied warranty of
33
-  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
34
-  Lesser General Public License for more details.
35
-
36
-  You should have received a copy of the GNU Lesser General Public
37
-  License along with this library; if not, write to the Free Software
38
-  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
39
-*/
40
-
41
-#ifdef TARGET_LPC1768
42
-
43
-
44
-#endif // ARDUINO_ARCH_SAM

+ 23
- 16
Marlin/src/HAL/HAL_LPC1768/arduino.cpp Просмотреть файл

@@ -140,27 +140,34 @@ bool digitalRead(int pin) {
140 140
   return LPC_GPIO(pin_map[pin].port)->FIOPIN & LPC_PIN(pin_map[pin].pin) ? 1 : 0;
141 141
 }
142 142
 
143
-void analogWrite(int pin, int pwm_value) {
144
-/*
143
+
144
+
145
+void analogWrite(int pin, int pwm_value) {  // 1 - 254: pwm_value, 0: LOW, 255: HIGH
146
+
147
+  extern bool LPC1768_PWM_attach_pin(uint8_t, uint32_t, uint32_t, uint8_t);
148
+  extern bool LPC1768_PWM_write(uint8_t, uint32_t);
149
+  extern bool LPC1768_PWM_detach_pin(uint8_t);
150
+  #define MR0_MARGIN 200       // if channel value too close to MR0 the system locks up
151
+
152
+  static bool out_of_PWM_slots = false;
153
+
145 154
   if (!WITHIN(pin, 0, NUM_DIGITAL_PINS - 1) || pin_map[pin].port == 0xFF)
146 155
     return;
147 156
 
148
-  int old_pin = pin;
149
-  int old_value = pwm_value;
150
-
151
-  if(old_value != 0) {
152
-    for(uint16_t x = 0; x <= 5000; x++) {
153
-      LPC_GPIO(pin_map[pin].port)->FIOSET = LPC_PIN(pin_map[pin].pin);
154
-      //digitalWrite(old_pin, HIGH);
155
-      delayMicroseconds(old_value);
156
-      LPC_GPIO(pin_map[pin].port)->FIOCLR = LPC_PIN(pin_map[pin].pin);
157
-      //pinMode(pin, OUTPUT);
158
-      //digitalWrite(old_pin, LOW);
159
-      delayMicroseconds(255 - old_value);
157
+  uint value = MAX(MIN(pwm_value, 255), 0);
158
+  if (value == 0 || value == 255) {  // treat as digital pin
159
+    LPC1768_PWM_detach_pin(pin);    // turn off PWM
160
+    digitalWrite(pin, value);
161
+  }
162
+  else {
163
+    if (LPC1768_PWM_attach_pin(pin, 1, (LPC_PWM1->MR0 - MR0_MARGIN),  0xff))   // locks up if get too close to MR0 value
164
+      LPC1768_PWM_write(pin, map(value, 1, 254, 1, (LPC_PWM1->MR0 - MR0_MARGIN)));  // map 1-254 onto PWM range
165
+    else {                                                                 // out of PWM channels
166
+      if (!out_of_PWM_slots) usb_serial.printf(".\nWARNING - OUT OF PWM CHANNELS\n.\n");  //only warn once
167
+      out_of_PWM_slots = true;
168
+      digitalWrite(pin, value);  // treat as a digital pin if out of channels
160 169
     }
161 170
   }
162
-*/
163
-
164 171
 }
165 172
 
166 173
 extern bool HAL_adc_finished();

+ 4
- 0
Marlin/src/HAL/HAL_LPC1768/main.cpp Просмотреть файл

@@ -30,6 +30,7 @@ extern "C" {
30 30
 #include <stdarg.h>
31 31
 #include "arduino.h"
32 32
 #include "serial.h"
33
+#include "LPC1768_PWM.h"
33 34
 
34 35
 static __INLINE uint32_t SysTick_Config(uint32_t ticks) {
35 36
   if (ticks > SysTick_LOAD_RELOAD_Msk)
@@ -86,6 +87,9 @@ int main(void) {
86 87
 
87 88
   HAL_timer_init();
88 89
 
90
+  extern void LPC1768_PWM_init();
91
+  LPC1768_PWM_init();
92
+
89 93
   setup();
90 94
   while (true) {
91 95
     loop();

+ 87
- 0
Marlin/src/HAL/HAL_LPC1768/servo_private.h Просмотреть файл

@@ -0,0 +1,87 @@
1
+/**
2
+ * Marlin 3D Printer Firmware
3
+ * Copyright (C) 2016, 2017 MarlinFirmware [https://github.com/MarlinFirmware/Marlin]
4
+ *
5
+ * Based on Sprinter and grbl.
6
+ * Copyright (C) 2011 Camiel Gubbels / Erik van der Zalm
7
+ *
8
+ * This program is free software: you can redistribute it and/or modify
9
+ * it under the terms of the GNU General Public License as published by
10
+ * the Free Software Foundation, either version 3 of the License, or
11
+ * (at your option) any later version.
12
+ *
13
+ * This program is distributed in the hope that it will be useful,
14
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
15
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16
+ * GNU General Public License for more details.
17
+ *
18
+ * You should have received a copy of the GNU General Public License
19
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
20
+ *
21
+ */
22
+
23
+/**
24
+  servo.h - Interrupt driven Servo library for Arduino using 16 bit timers- Version 2
25
+  Copyright (c) 2009 Michael Margolis.  All right reserved.
26
+
27
+  This library is free software; you can redistribute it and/or
28
+  modify it under the terms of the GNU Lesser General Public
29
+  License as published by the Free Software Foundation; either
30
+  version 2.1 of the License, or (at your option) any later version.
31
+
32
+  This library is distributed in the hope that it will be useful,
33
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
34
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
35
+  Lesser General Public License for more details.
36
+
37
+  You should have received a copy of the GNU Lesser General Public
38
+  License along with this library; if not, write to the Free Software
39
+  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
40
+*/
41
+
42
+/**
43
+ * Based on "servo.h - Interrupt driven Servo library for Arduino using 16 bit timers -
44
+ *           Version 2 Copyright (c) 2009 Michael Margolis.  All right reserved.
45
+ *
46
+ * The only modification was to update/delete macros to match the LPC176x.
47
+ *
48
+ */
49
+
50
+
51
+#ifndef servo_private_h
52
+#define servo_private_h
53
+
54
+#include <inttypes.h>
55
+
56
+
57
+// Macros
58
+//values in microseconds
59
+#define MIN_PULSE_WIDTH       544     // the shortest pulse sent to a servo
60
+#define MAX_PULSE_WIDTH      2400     // the longest pulse sent to a servo
61
+#define DEFAULT_PULSE_WIDTH  1500     // default pulse width when servo is attached
62
+#define REFRESH_INTERVAL    20000     // minimum time to refresh servos in microseconds
63
+
64
+#define MAX_SERVOS             4
65
+
66
+#define INVALID_SERVO         255     // flag indicating an invalid servo index
67
+
68
+
69
+// Types
70
+
71
+typedef struct {
72
+  uint8_t nbr        : 8 ;            // a pin number from 0 to 254 (255 signals invalid pin)
73
+  uint8_t isActive   : 1 ;            // true if this channel is enabled, pin not pulsed if false
74
+} ServoPin_t;
75
+
76
+typedef struct {
77
+  ServoPin_t Pin;
78
+  unsigned int pulse_width;           // pulse width in microseconds
79
+} ServoInfo_t;
80
+
81
+// Global variables
82
+
83
+extern uint8_t ServoCount;
84
+extern ServoInfo_t servo_info[MAX_SERVOS];
85
+
86
+
87
+#endif

+ 0
- 25
Marlin/src/HAL/HAL_LPC1768/servotimers.h Просмотреть файл

@@ -1,25 +0,0 @@
1
-/*
2
-  Copyright (c) 2013 Arduino LLC. All right reserved.
3
-
4
-  This library is free software; you can redistribute it and/or
5
-  modify it under the terms of the GNU Lesser General Public
6
-  License as published by the Free Software Foundation; either
7
-  version 2.1 of the License, or (at your option) any later version.
8
-
9
-  This library is distributed in the hope that it will be useful,
10
-  but WITHOUT ANY WARRANTY; without even the implied warranty of
11
-  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12
-  Lesser General Public License for more details.
13
-
14
-  You should have received a copy of the GNU Lesser General Public
15
-  License along with this library; if not, write to the Free Software
16
-  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
17
-*/
18
-
19
-/**
20
- * Defines for 16 bit timers used with  Servo library
21
- *
22
- * If _useTimerX is defined then TimerX is a 32 bit timer on the current board
23
- * timer16_Sequence_t enumerates the sequence that the timers should be allocated
24
- * _Nbr_16timers indicates how many timers are available.
25
- */

+ 1
- 1
Marlin/src/HAL/servo.cpp Просмотреть файл

@@ -56,7 +56,7 @@
56 56
 
57 57
 #include "HAL.h"
58 58
 
59
-#if HAS_SERVOS && !IS_32BIT_TEENSY
59
+#if HAS_SERVOS && !(IS_32BIT_TEENSY || defined(TARGET_LPC1768))
60 60
 
61 61
 //#include <Arduino.h>
62 62
 

+ 4
- 0
Marlin/src/HAL/servo.h Просмотреть файл

@@ -71,6 +71,10 @@
71 71
 
72 72
 #if IS_32BIT_TEENSY
73 73
   #include "HAL_TEENSY35_36/HAL_Servo_Teensy.h" // Teensy HAL uses an inherited library
74
+
75
+#elif defined(TARGET_LPC1768)
76
+  #include "HAL_LPC1768/LPC1768_Servo.cpp"
77
+
74 78
 #else
75 79
 
76 80
 #include <inttypes.h>

Загрузка…
Отмена
Сохранить