Browse Source

[New Feature] I2C position encoder support (#6946)

* [New Feature] I2C position encoder support

I plan to continue improving/cleaning this up, as there areas that need work.

* let the cleanups begin.

* progress

* more progress

* comments, rename files, etc.

* clean

* Cleanups per thinkyhead

* a few more cleanups

* cleanups, bugfixes, etc.

* remove unnecessary passes_test(), additional cleanups/optimizations

* cleanups

* misc.

* Fix up I2CPEM.init() and a few other things.

* organize, fix, rename, etc.

* more optimization

* a few more tweaks
bgort 8 years ago
parent
commit
2f55870edb
8 changed files with 1684 additions and 17 deletions
  1. 83
    0
      Marlin/Configuration_adv.h
  2. 1101
    0
      Marlin/I2CPositionEncoder.cpp
  3. 356
    0
      Marlin/I2CPositionEncoder.h
  4. 88
    1
      Marlin/Marlin_main.cpp
  5. 14
    1
      Marlin/SanityCheck.h
  6. 21
    15
      Marlin/enum.h
  7. 19
    0
      Marlin/gcode.h
  8. 2
    0
      Marlin/macros.h

+ 83
- 0
Marlin/Configuration_adv.h View File

1261
   #define USER_GCODE_5 "G28\nM503"
1261
   #define USER_GCODE_5 "G28\nM503"
1262
 #endif
1262
 #endif
1263
 
1263
 
1264
+//===========================================================================
1265
+//============================ I2C Encoder Settings =========================
1266
+//===========================================================================
1267
+/**
1268
+ *  I2C position encoders for closed loop control.
1269
+ *  Developed by Chris Barr at Aus3D.
1270
+ *
1271
+ *  Wiki: http://wiki.aus3d.com.au/Magnetic_Encoder
1272
+ *  Github: https://github.com/Aus3D/MagneticEncoder
1273
+ *
1274
+ *  Supplier: http://aus3d.com.au/magnetic-encoder-module
1275
+ *  Alternative Supplier: http://reliabuild3d.com/
1276
+ *
1277
+ *  Reilabuild encoders have been modified to improve reliability.
1278
+ */
1279
+
1280
+//#define I2C_POSITION_ENCODERS
1281
+#if ENABLED(I2C_POSITION_ENCODERS)
1282
+
1283
+  #define I2CPE_ENCODER_CNT         1                       // The number of encoders installed; max of 5
1284
+                                                            // encoders supported currently.
1285
+
1286
+  #define I2CPE_ENC_1_ADDR          I2CPE_PRESET_ADDR_X     // I2C address of the encoder. 30-200.
1287
+  #define I2CPE_ENC_1_AXIS          X_AXIS                  // Axis the encoder module is installed on.  <X|Y|Z|E>_AXIS.
1288
+  #define I2CPE_ENC_1_TYPE          I2CPE_ENC_TYPE_LINEAR   // Type of encoder:  I2CPE_ENC_TYPE_LINEAR -or-
1289
+                                                            // I2CPE_ENC_TYPE_ROTARY.
1290
+  #define I2CPE_ENC_1_TICKS_UNIT    2048                    // 1024 for magnetic strips with 2mm poles; 2048 for
1291
+                                                            // 1mm poles. For linear encoders this is ticks / mm,
1292
+                                                            // for rotary encoders this is ticks / revolution.
1293
+  //#define I2CPE_ENC_1_TICKS_REV     (16 * 200)            // Only needed for rotary encoders; number of stepper
1294
+                                                            // steps per full revolution (motor steps/rev * microstepping)
1295
+  //#define I2CPE_ENC_1_INVERT                              // Invert the direction of axis travel.
1296
+  #define I2CPE_ENC_1_EC_METHOD     I2CPE_ECM_NONE          // Type of error error correction.
1297
+  #define I2CPE_ENC_1_EC_THRESH     0.10                    // Threshold size for error (in mm) above which the
1298
+                                                            // printer will attempt to correct the error; errors
1299
+                                                            // smaller than this are ignored to minimize effects of
1300
+                                                            // measurement noise / latency (filter).
1301
+
1302
+  #define I2CPE_ENC_2_ADDR          I2CPE_PRESET_ADDR_Y     // Same as above, but for encoder 2.
1303
+  #define I2CPE_ENC_2_AXIS          Y_AXIS
1304
+  #define I2CPE_ENC_2_TYPE          I2CPE_ENC_TYPE_LINEAR
1305
+  #define I2CPE_ENC_2_TICKS_UNIT    2048
1306
+  //#define I2CPE_ENC_2_TICKS_REV   (16 * 200)
1307
+  //#define I2CPE_ENC_2_INVERT
1308
+  #define I2CPE_ENC_2_EC_METHOD     I2CPE_ECM_NONE
1309
+  #define I2CPE_ENC_2_EC_THRESH     0.10
1310
+
1311
+  #define I2CPE_ENC_3_ADDR          I2CPE_PRESET_ADDR_Z     // Encoder 3.  Add additional configuration options
1312
+  #define I2CPE_ENC_3_AXIS          Z_AXIS                  // as above, or use defaults below.
1313
+
1314
+  #define I2CPE_ENC_4_ADDR          I2CPE_PRESET_ADDR_E     // Encoder 4.
1315
+  #define I2CPE_ENC_4_AXIS          E_AXIS
1316
+
1317
+  #define I2CPE_ENC_5_ADDR          34                      // Encoder 5.
1318
+  #define I2CPE_ENC_5_AXIS          E_AXIS
1319
+
1320
+  // Default settings for encoders which are enabled, but without settings configured above.
1321
+  #define I2CPE_DEF_TYPE            I2CPE_ENC_TYPE_LINEAR
1322
+  #define I2CPE_DEF_ENC_TICKS_UNIT  2048
1323
+  #define I2CPE_DEF_TICKS_REV       (16 * 200)
1324
+  #define I2CPE_DEF_EC_METHOD       I2CPE_ECM_NONE
1325
+  #define I2CPE_DEF_EC_THRESH       0.1
1326
+
1327
+  //#define I2CPE_ERR_THRESH_ABORT  100.0                   // Threshold size for error (in mm) error on any given
1328
+                                                            // axis after which the printer will abort. Comment out to
1329
+                                                            // disable abort behaviour.
1330
+
1331
+  #define I2CPE_TIME_TRUSTED        10000                   // After an encoder fault, there must be no further fault
1332
+                                                            // for this amount of time (in ms) before the encoder
1333
+                                                            // is trusted again.
1334
+
1335
+  /**
1336
+   * Position is checked every time a new command is executed from the buffer but during long moves,
1337
+   * this setting determines the minimum update time between checks. A value of 100 works well with
1338
+   * error rolling average when attempting to correct only for skips and not for vibration.
1339
+   */
1340
+  #define I2CPE_MIN_UPD_TIME_MS     100                     // Minimum time in miliseconds between encoder checks.
1341
+
1342
+  // Use a rolling average to identify persistant errors that indicate skips, as opposed to vibration and noise.
1343
+  #define I2CPE_ERR_ROLLING_AVERAGE
1344
+
1345
+#endif
1346
+
1264
 #endif // CONFIGURATION_ADV_H
1347
 #endif // CONFIGURATION_ADV_H

+ 1101
- 0
Marlin/I2CPositionEncoder.cpp
File diff suppressed because it is too large
View File


+ 356
- 0
Marlin/I2CPositionEncoder.h View File

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
+#ifndef I2CPOSENC_H
24
+#define I2CPOSENC_H
25
+
26
+#include "MarlinConfig.h"
27
+
28
+#if ENABLED(I2C_POSITION_ENCODERS)
29
+
30
+  #include "enum.h"
31
+  #include "macros.h"
32
+  #include "types.h"
33
+  #include <Wire.h>
34
+
35
+  //=========== Advanced / Less-Common Encoder Configuration Settings ==========
36
+
37
+  #define I2CPE_EC_THRESH_PROPORTIONAL                    // if enabled adjusts the error correction threshold
38
+                                                          // proportional to the current speed of the axis allows
39
+                                                          // for very small error margin at low speeds without
40
+                                                          // stuttering due to reading latency at high speeds
41
+
42
+  #define I2CPE_DEBUG                                     // enable encoder-related debug serial echos
43
+
44
+  #define I2CPE_REBOOT_TIME             5000              // time we wait for an encoder module to reboot
45
+                                                          // after changing address.
46
+
47
+  #define I2CPE_MAG_SIG_GOOD            0
48
+  #define I2CPE_MAG_SIG_MID             1
49
+  #define I2CPE_MAG_SIG_BAD             2
50
+  #define I2CPE_MAG_SIG_NF              255
51
+
52
+  #define I2CPE_REQ_REPORT              0
53
+  #define I2CPE_RESET_COUNT             1
54
+  #define I2CPE_SET_ADDR                2
55
+  #define I2CPE_SET_REPORT_MODE         3
56
+  #define I2CPE_CLEAR_EEPROM            4
57
+
58
+  #define I2CPE_LED_PAR_MODE            10
59
+  #define I2CPE_LED_PAR_BRT             11
60
+  #define I2CPE_LED_PAR_RATE            14
61
+
62
+  #define I2CPE_REPORT_DISTANCE         0
63
+  #define I2CPE_REPORT_STRENGTH         1
64
+  #define I2CPE_REPORT_VERSION          2
65
+
66
+  // Default I2C addresses
67
+  #define I2CPE_PRESET_ADDR_X           30
68
+  #define I2CPE_PRESET_ADDR_Y           31
69
+  #define I2CPE_PRESET_ADDR_Z           32
70
+  #define I2CPE_PRESET_ADDR_E           33
71
+
72
+  #define I2CPE_DEF_AXIS                X_AXIS
73
+  #define I2CPE_DEF_ADDR                I2CPE_PRESET_ADDR_X
74
+
75
+  // Error event counter; tracks how many times there is an error exceeding a certain threshold
76
+  #define I2CPE_ERR_CNT_THRESH          3.00
77
+  #define I2CPE_ERR_CNT_DEBOUNCE_MS     2000
78
+
79
+  #if ENABLED(I2CPE_ERR_ROLLING_AVERAGE)
80
+    #define I2CPE_ERR_ARRAY_SIZE        32
81
+  #endif
82
+
83
+  // Error Correction Methods
84
+  #define I2CPE_ECM_NONE                0
85
+  #define I2CPE_ECM_MICROSTEP           1
86
+  #define I2CPE_ECM_PLANNER             2
87
+  #define I2CPE_ECM_STALLDETECT         3
88
+
89
+  // Encoder types
90
+  #define I2CPE_ENC_TYPE_ROTARY         0
91
+  #define I2CPE_ENC_TYPE_LINEAR         1
92
+
93
+  // Parser
94
+  #define I2CPE_PARSE_ERR               1
95
+  #define I2CPE_PARSE_OK                0
96
+
97
+  #define LOOP_PE(VAR) LOOP_L_N(VAR, I2CPE_ENCODER_CNT)
98
+  #define CHECK_IDX if (!WITHIN(idx, 0, I2CPE_ENCODER_CNT - 1)) return;
99
+
100
+  extern const char axis_codes[XYZE];
101
+
102
+  typedef union {
103
+    volatile long   val = 0;
104
+    uint8_t         bval[4];
105
+  } i2cLong;
106
+
107
+  class I2CPositionEncoder {
108
+  private:
109
+    AxisEnum        encoderAxis             = I2CPE_DEF_AXIS;
110
+
111
+    uint8_t         i2cAddress              = I2CPE_DEF_ADDR,
112
+                    ecMethod                = I2CPE_DEF_EC_METHOD,
113
+                    type                    = I2CPE_DEF_TYPE,
114
+                    H                       = I2CPE_MAG_SIG_NF;    // Magnetic field strength
115
+
116
+    int             encoderTicksPerUnit     = I2CPE_DEF_ENC_TICKS_UNIT,
117
+                    stepperTicks            = I2CPE_DEF_TICKS_REV;
118
+
119
+    float           ecThreshold             = I2CPE_DEF_EC_THRESH;
120
+
121
+    bool            homed                   = false,
122
+                    trusted                 = false,
123
+                    initialised             = false,
124
+                    active                  = false,
125
+                    invert                  = false,
126
+                    ec                      = true;
127
+
128
+    int             errorCount              = 0,
129
+                    errorPrev               = 0;
130
+
131
+    float           axisOffset              = 0;
132
+
133
+    long            axisOffsetTicks         = 0,
134
+                    zeroOffset              = 0,
135
+                    lastPosition            = 0,
136
+                    position;
137
+
138
+    unsigned long   lastPositionTime        = 0,
139
+                    lastErrorCountTime      = 0,
140
+                    lastErrorTime;
141
+
142
+    //double        positionMm; //calculate
143
+
144
+    #if ENABLED(I2CPE_ERR_ROLLING_AVERAGE)
145
+      uint8_t       errIdx = 0;
146
+      int           err[I2CPE_ERR_ARRAY_SIZE] = {0};
147
+    #endif
148
+
149
+  public:
150
+    void init(uint8_t address, AxisEnum axis);
151
+    void reset();
152
+
153
+    void update();
154
+
155
+    void set_homed();
156
+
157
+    long get_raw_count();
158
+
159
+    FORCE_INLINE double mm_from_count(long count) {
160
+      if (type == I2CPE_ENC_TYPE_LINEAR) return count / encoderTicksPerUnit;
161
+      else if (type == I2CPE_ENC_TYPE_ROTARY)
162
+        return (count * stepperTicks) / (encoderTicksPerUnit * planner.axis_steps_per_mm[encoderAxis]);
163
+      return -1;
164
+    }
165
+
166
+    FORCE_INLINE double get_position_mm() { return mm_from_count(get_position()); }
167
+    FORCE_INLINE long get_position() { return get_raw_count() - zeroOffset - axisOffsetTicks; }
168
+
169
+    long get_axis_error_steps(bool report);
170
+    double get_axis_error_mm(bool report);
171
+
172
+    void calibrate_steps_mm(int iter);
173
+
174
+    bool passes_test(bool report);
175
+
176
+    bool test_axis(void);
177
+
178
+    FORCE_INLINE int get_error_count(void) { return errorCount; }
179
+    FORCE_INLINE void set_error_count(int newCount) { errorCount = newCount; }
180
+
181
+    FORCE_INLINE uint8_t get_address() { return i2cAddress; }
182
+    FORCE_INLINE void set_address(uint8_t addr) { i2cAddress = addr; }
183
+
184
+    FORCE_INLINE bool get_active(void) { return active; }
185
+    FORCE_INLINE void set_active(bool a) { active = a; }
186
+
187
+    FORCE_INLINE void set_inverted(bool i) { invert = i; }
188
+
189
+    FORCE_INLINE AxisEnum get_axis() { return encoderAxis; }
190
+
191
+    FORCE_INLINE bool get_ec_enabled() { return ec; }
192
+    FORCE_INLINE void set_ec_enabled(bool enabled) { ec = enabled; }
193
+
194
+    FORCE_INLINE uint8_t get_ec_method() { return ecMethod; }
195
+    FORCE_INLINE void set_ec_method(byte method) { ecMethod = method; }
196
+
197
+    FORCE_INLINE float get_ec_threshold() { return ecThreshold; }
198
+    FORCE_INLINE void set_ec_threshold(float newThreshold) { ecThreshold = newThreshold; }
199
+
200
+    FORCE_INLINE int get_encoder_ticks_mm() {
201
+      if (type == I2CPE_ENC_TYPE_LINEAR) return encoderTicksPerUnit;
202
+      else if (type == I2CPE_ENC_TYPE_ROTARY)
203
+        return (int)((encoderTicksPerUnit / stepperTicks) * planner.axis_steps_per_mm[encoderAxis]);
204
+      return 0;
205
+    }
206
+
207
+    FORCE_INLINE int get_ticks_unit() { return encoderTicksPerUnit; }
208
+    FORCE_INLINE void set_ticks_unit(int ticks) { encoderTicksPerUnit = ticks; }
209
+
210
+    FORCE_INLINE uint8_t get_type() { return type; }
211
+    FORCE_INLINE void set_type(byte newType) { type = newType; }
212
+
213
+    FORCE_INLINE int get_stepper_ticks() { return stepperTicks; }
214
+    FORCE_INLINE void set_stepper_ticks(int ticks) { stepperTicks = ticks; }
215
+
216
+    FORCE_INLINE float get_axis_offset() { return axisOffset; }
217
+    FORCE_INLINE void set_axis_offset(float newOffset) {
218
+      axisOffset = newOffset;
219
+      axisOffsetTicks = (long)(axisOffset * get_encoder_ticks_mm());
220
+    }
221
+
222
+    FORCE_INLINE void set_current_position(float newPositionMm) {
223
+      set_axis_offset(get_position_mm() - newPositionMm + axisOffset);
224
+    }
225
+  };
226
+
227
+  class I2CPositionEncodersMgr {
228
+  private:
229
+    bool I2CPE_anyaxis;
230
+    uint8_t I2CPE_addr;
231
+    int8_t I2CPE_idx;
232
+
233
+  public:
234
+    void init(void);
235
+
236
+    // consider only updating one endoder per call / tick if encoders become too time intensive
237
+    void update(void) { LOOP_PE(i) encoders[i].update(); }
238
+
239
+    void homed(AxisEnum axis) {
240
+      LOOP_PE(i)
241
+        if (encoders[i].get_axis() == axis) encoders[i].set_homed();
242
+    }
243
+
244
+    void report_position(uint8_t idx, bool units, bool noOffset);
245
+
246
+    void report_status(uint8_t idx) {
247
+      CHECK_IDX
248
+      SERIAL_ECHOPAIR("Encoder ",idx);
249
+      SERIAL_ECHOPGM(": ");
250
+      encoders[idx].get_raw_count();
251
+      encoders[idx].passes_test(true);
252
+    }
253
+
254
+    void report_error(uint8_t idx) {
255
+      CHECK_IDX
256
+      encoders[idx].get_axis_error_steps(true);
257
+    }
258
+
259
+    void test_axis(uint8_t idx) {
260
+      CHECK_IDX
261
+      encoders[idx].test_axis();
262
+    }
263
+
264
+    void calibrate_steps_mm(uint8_t idx, int iterations) {
265
+      CHECK_IDX
266
+      encoders[idx].calibrate_steps_mm(iterations);
267
+    }
268
+
269
+    void change_module_address(uint8_t oldaddr, uint8_t newaddr);
270
+    void report_module_firmware(uint8_t address);
271
+
272
+    void report_error_count(uint8_t idx, AxisEnum axis) {
273
+      CHECK_IDX
274
+      SERIAL_ECHOPAIR("Error count on ", axis_codes[axis]);
275
+      SERIAL_ECHOLNPAIR(" axis is ", encoders[idx].get_error_count());
276
+    }
277
+
278
+    void reset_error_count(uint8_t idx, AxisEnum axis) {
279
+      CHECK_IDX
280
+      encoders[idx].set_error_count(0);
281
+      SERIAL_ECHOPAIR("Error count on ", axis_codes[axis]);
282
+      SERIAL_ECHOLNPGM(" axis has been reset.");
283
+    }
284
+
285
+    void enable_ec(uint8_t idx, bool enabled, AxisEnum axis) {
286
+      CHECK_IDX
287
+      encoders[idx].set_ec_enabled(enabled);
288
+      SERIAL_ECHOPAIR("Error correction on ", axis_codes[axis]);
289
+      SERIAL_ECHOPGM(" axis is ");
290
+      serialprintPGM(encoders[idx].get_ec_enabled() ? PSTR("en") : PSTR("dis"));
291
+      SERIAL_ECHOLNPGM("abled.");
292
+    }
293
+
294
+    void set_ec_threshold(uint8_t idx, float newThreshold, AxisEnum axis) {
295
+      CHECK_IDX
296
+      encoders[idx].set_ec_threshold(newThreshold);
297
+      SERIAL_ECHOPAIR("Error correct threshold for ", axis_codes[axis]);
298
+      SERIAL_ECHOPAIR_F(" axis set to ", newThreshold);
299
+      SERIAL_ECHOLNPGM("mm.");
300
+    }
301
+
302
+    void get_ec_threshold(uint8_t idx, AxisEnum axis) {
303
+      CHECK_IDX
304
+      float threshold = encoders[idx].get_ec_threshold();
305
+      SERIAL_ECHOPAIR("Error correct threshold for ", axis_codes[axis]);
306
+      SERIAL_ECHOPAIR_F(" axis is ", threshold);
307
+      SERIAL_ECHOLNPGM("mm.");
308
+    }
309
+
310
+    int8_t idx_from_axis(AxisEnum axis) {
311
+      LOOP_PE(i)
312
+        if (encoders[i].get_axis() == axis) return i;
313
+
314
+      return -1;
315
+    }
316
+
317
+    int8_t idx_from_addr(uint8_t addr) {
318
+      LOOP_PE(i)
319
+        if (encoders[i].get_address() == addr) return i;
320
+
321
+      return -1;
322
+    }
323
+
324
+    int8_t parse();
325
+
326
+    void M860();
327
+    void M861();
328
+    void M862();
329
+    void M863();
330
+    void M864();
331
+    void M865();
332
+    void M866();
333
+    void M867();
334
+    void M868();
335
+    void M869();
336
+
337
+    I2CPositionEncoder encoders[I2CPE_ENCODER_CNT];
338
+  };
339
+
340
+  extern I2CPositionEncodersMgr I2CPEM;
341
+
342
+  FORCE_INLINE void gcode_M860() { I2CPEM.M860(); }
343
+  FORCE_INLINE void gcode_M861() { I2CPEM.M861(); }
344
+  FORCE_INLINE void gcode_M862() { I2CPEM.M862(); }
345
+  FORCE_INLINE void gcode_M863() { I2CPEM.M863(); }
346
+  FORCE_INLINE void gcode_M864() { I2CPEM.M864(); }
347
+  FORCE_INLINE void gcode_M865() { I2CPEM.M865(); }
348
+  FORCE_INLINE void gcode_M866() { I2CPEM.M866(); }
349
+  FORCE_INLINE void gcode_M867() { I2CPEM.M867(); }
350
+  FORCE_INLINE void gcode_M868() { I2CPEM.M868(); }
351
+  FORCE_INLINE void gcode_M869() { I2CPEM.M869(); }
352
+
353
+#endif //I2C_POSITION_ENCODERS
354
+#endif //I2CPOSENC_H
355
+
356
+

+ 88
- 1
Marlin/Marlin_main.cpp View File

200
  * M666 - Set delta endstop adjustment. (Requires DELTA)
200
  * M666 - Set delta endstop adjustment. (Requires DELTA)
201
  * M605 - Set dual x-carriage movement mode: "M605 S<mode> [X<x_offset>] [R<temp_offset>]". (Requires DUAL_X_CARRIAGE)
201
  * M605 - Set dual x-carriage movement mode: "M605 S<mode> [X<x_offset>] [R<temp_offset>]". (Requires DUAL_X_CARRIAGE)
202
  * M851 - Set Z probe's Z offset in current units. (Negative = below the nozzle.)
202
  * M851 - Set Z probe's Z offset in current units. (Negative = below the nozzle.)
203
+ * M860 - Report the position of position encoder modules.
204
+ * M861 - Report the status of position encoder modules.
205
+ * M862 - Perform an axis continuity test for position encoder modules.
206
+ * M863 - Perform steps-per-mm calibration for position encoder modules.
207
+ * M864 - Change position encoder module I2C address.
208
+ * M865 - Check position encoder module firmware version.
209
+ * M866 - Report or reset position encoder module error count.
210
+ * M867 - Enable/disable or toggle error correction for position encoder modules.
211
+ * M868 - Report or set position encoder module error correction threshold.
212
+ * M869 - Report position encoder module error.
203
  * M900 - Get and/or Set advance K factor and WH/D ratio. (Requires LIN_ADVANCE)
213
  * M900 - Get and/or Set advance K factor and WH/D ratio. (Requires LIN_ADVANCE)
204
  * M906 - Set or get motor current in milliamps using axis codes X, Y, Z, E. Report values if no axis codes given. (Requires HAVE_TMC2130)
214
  * M906 - Set or get motor current in milliamps using axis codes X, Y, Z, E. Report values if no axis codes given. (Requires HAVE_TMC2130)
205
  * M907 - Set digital trimpot motor current using axis codes. (Requires a board with digital trimpots)
215
  * M907 - Set digital trimpot motor current using axis codes. (Requires a board with digital trimpots)
286
   #include "twibus.h"
296
   #include "twibus.h"
287
 #endif
297
 #endif
288
 
298
 
299
+#if ENABLED(I2C_POSITION_ENCODERS)
300
+  #include "I2CPositionEncoder.h"
301
+#endif
302
+
289
 #if ENABLED(ENDSTOP_INTERRUPTS_FEATURE)
303
 #if ENABLED(ENDSTOP_INTERRUPTS_FEATURE)
290
   #include "endstop_interrupts.h"
304
   #include "endstop_interrupts.h"
291
 #endif
305
 #endif
662
   #define host_keepalive() NOOP
676
   #define host_keepalive() NOOP
663
 #endif
677
 #endif
664
 
678
 
679
+#if ENABLED(I2C_POSITION_ENCODERS)
680
+  I2CPositionEncodersMgr I2CPEM;
681
+  uint8_t blockBufferIndexRef = 0;
682
+  millis_t lastUpdateMillis;
683
+#endif
684
+
665
 FORCE_INLINE float pgm_read_any(const float *p) { return pgm_read_float_near(p); }
685
 FORCE_INLINE float pgm_read_any(const float *p) { return pgm_read_float_near(p); }
666
 FORCE_INLINE signed char pgm_read_any(const signed char *p) { return pgm_read_byte_near(p); }
686
 FORCE_INLINE signed char pgm_read_any(const signed char *p) { return pgm_read_byte_near(p); }
667
 
687
 
1493
       SERIAL_EOL;
1513
       SERIAL_EOL;
1494
     }
1514
     }
1495
   #endif
1515
   #endif
1516
+
1517
+  #if ENABLED(I2C_POSITION_ENCODERS)
1518
+    I2CPEM.homed(axis);
1519
+  #endif
1496
 }
1520
 }
1497
 
1521
 
1498
 /**
1522
 /**
5609
           #if HAS_POSITION_SHIFT
5633
           #if HAS_POSITION_SHIFT
5610
             position_shift[i] += v - p; // Offset the coordinate space
5634
             position_shift[i] += v - p; // Offset the coordinate space
5611
             update_software_endstops((AxisEnum)i);
5635
             update_software_endstops((AxisEnum)i);
5636
+
5637
+            #if ENABLED(I2C_POSITION_ENCODERS)
5638
+              I2CPEM.encoders[I2CPEM.idx_from_axis((AxisEnum) i)].set_axis_offset(position_shift[i]);
5639
+            #endif
5640
+
5612
           #endif
5641
           #endif
5613
         }
5642
         }
5614
       #endif
5643
       #endif
10904
           break;
10933
           break;
10905
       #endif
10934
       #endif
10906
 
10935
 
10936
+      #if ENABLED(I2C_POSITION_ENCODERS)
10937
+
10938
+        case 860: // M860 Report encoder module position
10939
+          gcode_M860();
10940
+          break;
10941
+
10942
+        case 861: // M861 Report encoder module status
10943
+          gcode_M861();
10944
+          break;
10945
+
10946
+        case 862: // M862 Perform axis test
10947
+          gcode_M862();
10948
+          break;
10949
+
10950
+        case 863: // M863 Calibrate steps/mm
10951
+          gcode_M863();
10952
+          break;
10953
+
10954
+        case 864: // M864 Change module address
10955
+          gcode_M864();
10956
+          break;
10957
+
10958
+        case 865: // M865 Check module firmware version
10959
+          gcode_M865();
10960
+          break;
10961
+
10962
+        case 866: // M866 Report axis error count
10963
+          gcode_M866();
10964
+          break;
10965
+
10966
+        case 867: // M867 Toggle error correction
10967
+          gcode_M867();
10968
+          break;
10969
+
10970
+        case 868: // M868 Set error correction threshold
10971
+          gcode_M868();
10972
+          break;
10973
+
10974
+        case 869: // M869 Report axis error
10975
+          gcode_M869();
10976
+          break;
10977
+
10978
+      #endif // I2C_POSITION_ENCODERS
10979
+
10907
       case 999: // M999: Restart after being Stopped
10980
       case 999: // M999: Restart after being Stopped
10908
         gcode_M999();
10981
         gcode_M999();
10909
         break;
10982
         break;
12200
       const bool has_days = (elapsed.value > 60*60*24L);
12273
       const bool has_days = (elapsed.value > 60*60*24L);
12201
       (void)elapsed.toDigital(timestamp, has_days);
12274
       (void)elapsed.toDigital(timestamp, has_days);
12202
       SERIAL_ECHO(timestamp);
12275
       SERIAL_ECHO(timestamp);
12203
-      SERIAL_ECHO(": ");
12276
+      SERIAL_ECHOPGM(": ");
12204
       SERIAL_ECHO(axisID);
12277
       SERIAL_ECHO(axisID);
12205
       SERIAL_ECHOLNPGM(" driver overtemperature warning!");
12278
       SERIAL_ECHOLNPGM(" driver overtemperature warning!");
12206
     }
12279
     }
12495
   #if HAS_BUZZER && DISABLED(LCD_USE_I2C_BUZZER)
12568
   #if HAS_BUZZER && DISABLED(LCD_USE_I2C_BUZZER)
12496
     buzzer.tick();
12569
     buzzer.tick();
12497
   #endif
12570
   #endif
12571
+
12572
+  #if ENABLED(I2C_POSITION_ENCODERS)
12573
+    if (planner.blocks_queued() &&
12574
+        ( (blockBufferIndexRef != planner.block_buffer_head) ||
12575
+          ((lastUpdateMillis + I2CPE_MIN_UPD_TIME_MS) < millis())) ) {
12576
+      blockBufferIndexRef = planner.block_buffer_head;
12577
+      I2CPEM.update();
12578
+      lastUpdateMillis = millis();
12579
+    }
12580
+  #endif
12498
 }
12581
 }
12499
 
12582
 
12500
 /**
12583
 /**
12739
     set_bltouch_deployed(false);
12822
     set_bltouch_deployed(false);
12740
   #endif
12823
   #endif
12741
 
12824
 
12825
+  #if ENABLED(I2C_POSITION_ENCODERS)
12826
+    I2CPEM.init();
12827
+  #endif
12828
+
12742
   #if ENABLED(EXPERIMENTAL_I2CBUS) && I2C_SLAVE_ADDRESS > 0
12829
   #if ENABLED(EXPERIMENTAL_I2CBUS) && I2C_SLAVE_ADDRESS > 0
12743
     i2c.onReceive(i2c_on_receive);
12830
     i2c.onReceive(i2c_on_receive);
12744
     i2c.onRequest(i2c_on_request);
12831
     i2c.onRequest(i2c_on_request);

+ 14
- 1
Marlin/SanityCheck.h View File

271
 #endif
271
 #endif
272
 
272
 
273
 /**
273
 /**
274
+ * I2C Position Encoders
275
+ */
276
+#if ENABLED(I2C_POSITION_ENCODERS)
277
+  #if DISABLED(BABYSTEPPING)
278
+    #error "I2C_POSITION_ENCODERS requires BABYSTEPPING."
279
+  #endif
280
+
281
+  #if I2CPE_ENCODER_CNT > 5 || I2CPE_ENCODER_CNT < 1
282
+    #error "I2CPE_ENCODER_CNT must be between 1 and 5."
283
+  #endif
284
+#endif
285
+
286
+/**
274
  * Babystepping
287
  * Babystepping
275
  */
288
  */
276
 #if ENABLED(BABYSTEPPING)
289
 #if ENABLED(BABYSTEPPING)
277
-  #if DISABLED(ULTRA_LCD)
290
+  #if DISABLED(ULTRA_LCD) && DISABLED(I2C_POSITION_ENCODERS)
278
     #error "BABYSTEPPING requires an LCD controller."
291
     #error "BABYSTEPPING requires an LCD controller."
279
   #elif ENABLED(SCARA)
292
   #elif ENABLED(SCARA)
280
     #error "BABYSTEPPING is not implemented for SCARA yet."
293
     #error "BABYSTEPPING is not implemented for SCARA yet."

+ 21
- 15
Marlin/enum.h View File

34
  *    between X_AXIS and X Head movement, like CoreXY bots
34
  *    between X_AXIS and X Head movement, like CoreXY bots
35
  */
35
  */
36
 enum AxisEnum {
36
 enum AxisEnum {
37
-  NO_AXIS = -1,
38
-  X_AXIS  = 0,
39
-  A_AXIS  = 0,
40
-  Y_AXIS  = 1,
41
-  B_AXIS  = 1,
42
-  Z_AXIS  = 2,
43
-  C_AXIS  = 2,
44
-  E_AXIS  = 3,
45
-  X_HEAD  = 4,
46
-  Y_HEAD  = 5,
47
-  Z_HEAD  = 6,
48
-  ALL_AXES = 100
37
+  NO_AXIS   = -1,
38
+  X_AXIS    = 0,
39
+  A_AXIS    = 0,
40
+  Y_AXIS    = 1,
41
+  B_AXIS    = 1,
42
+  Z_AXIS    = 2,
43
+  C_AXIS    = 2,
44
+  E_AXIS    = 3,
45
+  X_HEAD    = 4,
46
+  Y_HEAD    = 5,
47
+  Z_HEAD    = 6,
48
+  ALL_AXES  = 100
49
 };
49
 };
50
 
50
 
51
-#define LOOP_XYZ(VAR)  for (uint8_t VAR=X_AXIS; VAR<=Z_AXIS; VAR++)
52
-#define LOOP_XYZE(VAR) for (uint8_t VAR=X_AXIS; VAR<=E_AXIS; VAR++)
53
-#define LOOP_XYZE_N(VAR) for (uint8_t VAR=X_AXIS; VAR<XYZE_N; VAR++)
51
+#define LOOP_S_LE_N(VAR, S, N) for (uint8_t VAR=S; VAR<=N; VAR++)
52
+#define LOOP_S_L_N(VAR, S, N) for (uint8_t VAR=S; VAR<N; VAR++)
53
+#define LOOP_LE_N(VAR, N) LOOP_S_LE_N(VAR, 0, N)
54
+#define LOOP_L_N(VAR, N) LOOP_S_L_N(VAR, 0, N)
55
+
56
+#define LOOP_NA(VAR) LOOP_L_N(VAR, NUM_AXIS)
57
+#define LOOP_XYZ(VAR) LOOP_S_LE_N(VAR, X_AXIS, Z_AXIS)
58
+#define LOOP_XYZE(VAR) LOOP_S_LE_N(VAR, X_AXIS, E_AXIS)
59
+#define LOOP_XYZE_N(VAR) LOOP_S_L_N(VAR, X_AXIS, XYZE_N)
54
 
60
 
55
 typedef enum {
61
 typedef enum {
56
   LINEARUNIT_MM,
62
   LINEARUNIT_MM,

+ 19
- 0
Marlin/gcode.h View File

128
       return b;
128
       return b;
129
     }
129
     }
130
 
130
 
131
+    static volatile bool seen_any() {
132
+      return codebits[3] || codebits[2] || codebits[1] || codebits[0];
133
+    }
134
+
135
+    #define SEEN_TEST(L) TEST(codebits[(L - 'A') >> 3], (L - 'A') & 0x7)
136
+
131
   #else
137
   #else
132
 
138
 
133
     // Code is found in the string. If not found, value_ptr is unchanged.
139
     // Code is found in the string. If not found, value_ptr is unchanged.
139
       return b;
145
       return b;
140
     }
146
     }
141
 
147
 
148
+    static volatile bool seen_any() {
149
+      return *command_args == '\0';
150
+    }
151
+
152
+    #define SEEN_TEST(L) !!strchr(command_args, L)
153
+
142
   #endif // FASTER_GCODE_PARSER
154
   #endif // FASTER_GCODE_PARSER
143
 
155
 
144
   // Populate all fields by parsing a single line of GCode
156
   // Populate all fields by parsing a single line of GCode
148
   // Code value pointer was set
160
   // Code value pointer was set
149
   FORCE_INLINE static bool has_value() { return value_ptr != NULL; }
161
   FORCE_INLINE static bool has_value() { return value_ptr != NULL; }
150
 
162
 
163
+  // Seen and has value
164
+  FORCE_INLINE static bool seenval(const char c) { return seen(c) && has_value(); }
165
+
166
+  static volatile bool seen_axis() {
167
+    return SEEN_TEST('X') || SEEN_TEST('Y') || SEEN_TEST('Z') || SEEN_TEST('E');
168
+  }
169
+
151
   // Float removes 'E' to prevent scientific notation interpretation
170
   // Float removes 'E' to prevent scientific notation interpretation
152
   inline static float value_float() {
171
   inline static float value_float() {
153
     if (value_ptr) {
172
     if (value_ptr) {

+ 2
- 0
Marlin/macros.h View File

108
 #define HYPOT2(x,y) (sq(x)+sq(y))
108
 #define HYPOT2(x,y) (sq(x)+sq(y))
109
 #define HYPOT(x,y) sqrt(HYPOT2(x,y))
109
 #define HYPOT(x,y) sqrt(HYPOT2(x,y))
110
 
110
 
111
+#define SIGN(a) ((a>0)-(a<0))
112
+
111
 // Macros to contrain values
113
 // Macros to contrain values
112
 #define NOLESS(v,n) do{ if (v < n) v = n; }while(0)
114
 #define NOLESS(v,n) do{ if (v < n) v = n; }while(0)
113
 #define NOMORE(v,n) do{ if (v > n) v = n; }while(0)
115
 #define NOMORE(v,n) do{ if (v > n) v = n; }while(0)

Loading…
Cancel
Save