Browse Source

Catch a TMC address conflict early (#19458)

Jason Smith 3 years ago
parent
commit
5d0429ee2a
No account linked to committer's email address

+ 105
- 0
Marlin/src/module/stepper/trinamic.cpp View File

209
   #if AXIS_HAS_UART(X)
209
   #if AXIS_HAS_UART(X)
210
     #ifdef X_HARDWARE_SERIAL
210
     #ifdef X_HARDWARE_SERIAL
211
       TMC_UART_DEFINE(HW, X, X);
211
       TMC_UART_DEFINE(HW, X, X);
212
+      #define X_HAS_HW_SERIAL 1
212
     #else
213
     #else
213
       TMC_UART_DEFINE(SW, X, X);
214
       TMC_UART_DEFINE(SW, X, X);
215
+      #define X_HAS_SW_SERIAL 1
214
     #endif
216
     #endif
215
   #endif
217
   #endif
216
   #if AXIS_HAS_UART(X2)
218
   #if AXIS_HAS_UART(X2)
217
     #ifdef X2_HARDWARE_SERIAL
219
     #ifdef X2_HARDWARE_SERIAL
218
       TMC_UART_DEFINE(HW, X2, X);
220
       TMC_UART_DEFINE(HW, X2, X);
221
+      #define X2_HAS_HW_SERIAL 1
219
     #else
222
     #else
220
       TMC_UART_DEFINE(SW, X2, X);
223
       TMC_UART_DEFINE(SW, X2, X);
224
+      #define X2_HAS_SW_SERIAL 1
221
     #endif
225
     #endif
222
   #endif
226
   #endif
223
   #if AXIS_HAS_UART(Y)
227
   #if AXIS_HAS_UART(Y)
224
     #ifdef Y_HARDWARE_SERIAL
228
     #ifdef Y_HARDWARE_SERIAL
225
       TMC_UART_DEFINE(HW, Y, Y);
229
       TMC_UART_DEFINE(HW, Y, Y);
230
+      #define Y_HAS_HW_SERIAL 1
226
     #else
231
     #else
227
       TMC_UART_DEFINE(SW, Y, Y);
232
       TMC_UART_DEFINE(SW, Y, Y);
233
+      #define Y_HAS_SW_SERIAL 1
228
     #endif
234
     #endif
229
   #endif
235
   #endif
230
   #if AXIS_HAS_UART(Y2)
236
   #if AXIS_HAS_UART(Y2)
231
     #ifdef Y2_HARDWARE_SERIAL
237
     #ifdef Y2_HARDWARE_SERIAL
232
       TMC_UART_DEFINE(HW, Y2, Y);
238
       TMC_UART_DEFINE(HW, Y2, Y);
239
+      #define Y2_HAS_HW_SERIAL 1
233
     #else
240
     #else
234
       TMC_UART_DEFINE(SW, Y2, Y);
241
       TMC_UART_DEFINE(SW, Y2, Y);
242
+      #define Y2_HAS_SW_SERIAL 1
235
     #endif
243
     #endif
236
   #endif
244
   #endif
237
   #if AXIS_HAS_UART(Z)
245
   #if AXIS_HAS_UART(Z)
238
     #ifdef Z_HARDWARE_SERIAL
246
     #ifdef Z_HARDWARE_SERIAL
239
       TMC_UART_DEFINE(HW, Z, Z);
247
       TMC_UART_DEFINE(HW, Z, Z);
248
+      #define Z_HAS_HW_SERIAL 1
240
     #else
249
     #else
241
       TMC_UART_DEFINE(SW, Z, Z);
250
       TMC_UART_DEFINE(SW, Z, Z);
251
+      #define Z_HAS_SW_SERIAL 1
242
     #endif
252
     #endif
243
   #endif
253
   #endif
244
   #if AXIS_HAS_UART(Z2)
254
   #if AXIS_HAS_UART(Z2)
245
     #ifdef Z2_HARDWARE_SERIAL
255
     #ifdef Z2_HARDWARE_SERIAL
246
       TMC_UART_DEFINE(HW, Z2, Z);
256
       TMC_UART_DEFINE(HW, Z2, Z);
257
+      #define Z2_HAS_HW_SERIAL 1
247
     #else
258
     #else
248
       TMC_UART_DEFINE(SW, Z2, Z);
259
       TMC_UART_DEFINE(SW, Z2, Z);
260
+      #define Z2_HAS_SW_SERIAL 1
249
     #endif
261
     #endif
250
   #endif
262
   #endif
251
   #if AXIS_HAS_UART(Z3)
263
   #if AXIS_HAS_UART(Z3)
252
     #ifdef Z3_HARDWARE_SERIAL
264
     #ifdef Z3_HARDWARE_SERIAL
253
       TMC_UART_DEFINE(HW, Z3, Z);
265
       TMC_UART_DEFINE(HW, Z3, Z);
266
+      #define Z3_HAS_HW_SERIAL 1
254
     #else
267
     #else
255
       TMC_UART_DEFINE(SW, Z3, Z);
268
       TMC_UART_DEFINE(SW, Z3, Z);
269
+      #define Z3_HAS_SW_SERIAL 1
256
     #endif
270
     #endif
257
   #endif
271
   #endif
258
   #if AXIS_HAS_UART(Z4)
272
   #if AXIS_HAS_UART(Z4)
259
     #ifdef Z4_HARDWARE_SERIAL
273
     #ifdef Z4_HARDWARE_SERIAL
260
       TMC_UART_DEFINE(HW, Z4, Z);
274
       TMC_UART_DEFINE(HW, Z4, Z);
275
+      #define Z4_HAS_HW_SERIAL 1
261
     #else
276
     #else
262
       TMC_UART_DEFINE(SW, Z4, Z);
277
       TMC_UART_DEFINE(SW, Z4, Z);
278
+      #define Z4_HAS_SW_SERIAL 1
263
     #endif
279
     #endif
264
   #endif
280
   #endif
265
   #if AXIS_HAS_UART(E0)
281
   #if AXIS_HAS_UART(E0)
266
     #ifdef E0_HARDWARE_SERIAL
282
     #ifdef E0_HARDWARE_SERIAL
267
       TMC_UART_DEFINE_E(HW, 0);
283
       TMC_UART_DEFINE_E(HW, 0);
284
+      #define E0_HAS_HW_SERIAL 1
268
     #else
285
     #else
269
       TMC_UART_DEFINE_E(SW, 0);
286
       TMC_UART_DEFINE_E(SW, 0);
287
+      #define E0_HAS_SW_SERIAL 1
270
     #endif
288
     #endif
271
   #endif
289
   #endif
272
   #if AXIS_HAS_UART(E1)
290
   #if AXIS_HAS_UART(E1)
273
     #ifdef E1_HARDWARE_SERIAL
291
     #ifdef E1_HARDWARE_SERIAL
274
       TMC_UART_DEFINE_E(HW, 1);
292
       TMC_UART_DEFINE_E(HW, 1);
293
+      #define E1_HAS_HW_SERIAL 1
275
     #else
294
     #else
276
       TMC_UART_DEFINE_E(SW, 1);
295
       TMC_UART_DEFINE_E(SW, 1);
296
+      #define E1_HAS_SW_SERIAL 1
277
     #endif
297
     #endif
278
   #endif
298
   #endif
279
   #if AXIS_HAS_UART(E2)
299
   #if AXIS_HAS_UART(E2)
280
     #ifdef E2_HARDWARE_SERIAL
300
     #ifdef E2_HARDWARE_SERIAL
281
       TMC_UART_DEFINE_E(HW, 2);
301
       TMC_UART_DEFINE_E(HW, 2);
302
+      #define E2_HAS_HW_SERIAL 1
282
     #else
303
     #else
283
       TMC_UART_DEFINE_E(SW, 2);
304
       TMC_UART_DEFINE_E(SW, 2);
305
+      #define E2_HAS_SW_SERIAL 1
284
     #endif
306
     #endif
285
   #endif
307
   #endif
286
   #if AXIS_HAS_UART(E3)
308
   #if AXIS_HAS_UART(E3)
287
     #ifdef E3_HARDWARE_SERIAL
309
     #ifdef E3_HARDWARE_SERIAL
288
       TMC_UART_DEFINE_E(HW, 3);
310
       TMC_UART_DEFINE_E(HW, 3);
311
+      #define E3_HAS_HW_SERIAL 1
289
     #else
312
     #else
290
       TMC_UART_DEFINE_E(SW, 3);
313
       TMC_UART_DEFINE_E(SW, 3);
314
+      #define E3_HAS_SW_SERIAL 1
291
     #endif
315
     #endif
292
   #endif
316
   #endif
293
   #if AXIS_HAS_UART(E4)
317
   #if AXIS_HAS_UART(E4)
294
     #ifdef E4_HARDWARE_SERIAL
318
     #ifdef E4_HARDWARE_SERIAL
295
       TMC_UART_DEFINE_E(HW, 4);
319
       TMC_UART_DEFINE_E(HW, 4);
320
+      #define E4_HAS_HW_SERIAL 1
296
     #else
321
     #else
297
       TMC_UART_DEFINE_E(SW, 4);
322
       TMC_UART_DEFINE_E(SW, 4);
323
+      #define E4_HAS_SW_SERIAL 1
298
     #endif
324
     #endif
299
   #endif
325
   #endif
300
   #if AXIS_HAS_UART(E5)
326
   #if AXIS_HAS_UART(E5)
301
     #ifdef E5_HARDWARE_SERIAL
327
     #ifdef E5_HARDWARE_SERIAL
302
       TMC_UART_DEFINE_E(HW, 5);
328
       TMC_UART_DEFINE_E(HW, 5);
329
+      #define E5_HAS_HW_SERIAL 1
303
     #else
330
     #else
304
       TMC_UART_DEFINE_E(SW, 5);
331
       TMC_UART_DEFINE_E(SW, 5);
332
+      #define E5_HAS_SW_SERIAL 1
305
     #endif
333
     #endif
306
   #endif
334
   #endif
307
   #if AXIS_HAS_UART(E6)
335
   #if AXIS_HAS_UART(E6)
308
     #ifdef E6_HARDWARE_SERIAL
336
     #ifdef E6_HARDWARE_SERIAL
309
       TMC_UART_DEFINE_E(HW, 6);
337
       TMC_UART_DEFINE_E(HW, 6);
338
+      #define E6_HAS_HW_SERIAL 1
310
     #else
339
     #else
311
       TMC_UART_DEFINE_E(SW, 6);
340
       TMC_UART_DEFINE_E(SW, 6);
341
+      #define E6_HAS_SW_SERIAL 1
312
     #endif
342
     #endif
313
   #endif
343
   #endif
314
   #if AXIS_HAS_UART(E7)
344
   #if AXIS_HAS_UART(E7)
315
     #ifdef E7_HARDWARE_SERIAL
345
     #ifdef E7_HARDWARE_SERIAL
316
       TMC_UART_DEFINE_E(HW, 7);
346
       TMC_UART_DEFINE_E(HW, 7);
347
+      #define E7_HAS_HW_SERIAL 1
317
     #else
348
     #else
318
       TMC_UART_DEFINE_E(SW, 7);
349
       TMC_UART_DEFINE_E(SW, 7);
350
+      #define E7_HAS_SW_SERIAL 1
319
     #endif
351
     #endif
320
   #endif
352
   #endif
321
 
353
 
769
   stepper.set_directions();
801
   stepper.set_directions();
770
 }
802
 }
771
 
803
 
804
+// TMC Slave Address Conflict Detection
805
+// 
806
+// Conflict detection is performed in the following way. Similar methods are used for
807
+// hardware and software serial, but the implementations are indepenent.
808
+// 
809
+// 1. Populate a data structure with UART parameters and addresses for all possible axis.
810
+//      If an axis is not in use, populate it with recognizable placeholder data.
811
+// 2. For each axis in use, static_assert using a constexpr function, which counts the
812
+//      number of matching/conflicting axis. If the value is not exactly 1, fail.
813
+
814
+#if ANY_AXIS_HAS(HW_SERIAL)
815
+  // Hardware serial names are compared as strings, since actually resolving them cannot occur in a constexpr.
816
+  // Using a fixed-length character array for the port name allows this to be constexpr compatible.
817
+  struct SanityHwSerialDetails { const char port[20]; uint32_t address; };
818
+  #define TMC_HW_DETAIL_ARGS(A) TERN(A##_HAS_HW_SERIAL, STRINGIFY(A##_HARDWARE_SERIAL), ""), TERN0(A##_HAS_HW_SERIAL, A##_SLAVE_ADDRESS)  
819
+  #define TMC_HW_DETAIL(A) {TMC_HW_DETAIL_ARGS(A)}
820
+  constexpr SanityHwSerialDetails sanity_tmc_hw_details[] = {
821
+    TMC_HW_DETAIL(X), TMC_HW_DETAIL(X2),
822
+    TMC_HW_DETAIL(Y), TMC_HW_DETAIL(Y2),
823
+    TMC_HW_DETAIL(Z), TMC_HW_DETAIL(Z2), TMC_HW_DETAIL(Z3), TMC_HW_DETAIL(Z4),
824
+    TMC_HW_DETAIL(E0), TMC_HW_DETAIL(E1), TMC_HW_DETAIL(E2), TMC_HW_DETAIL(E3), TMC_HW_DETAIL(E4), TMC_HW_DETAIL(E5), TMC_HW_DETAIL(E6), TMC_HW_DETAIL(E7)
825
+  };
826
+
827
+  // constexpr compatible string comparison
828
+  constexpr bool str_eq_ce(const char * a, const char * b) {
829
+    return *a == *b && (*a == '\0' || str_eq_ce(a+1,b+1));
830
+  }
831
+
832
+  constexpr bool sc_hw_done(size_t start, size_t end) { return start == end; }
833
+  constexpr bool sc_hw_skip(const char* port_name) { return !(*port_name); }
834
+  constexpr bool sc_hw_match(const char* port_name, uint32_t address, size_t start, size_t end) {
835
+    return !sc_hw_done(start, end) && !sc_hw_skip(port_name) && (address == sanity_tmc_hw_details[start].address && str_eq_ce(port_name, sanity_tmc_hw_details[start].port));
836
+  }
837
+  constexpr int count_tmc_hw_serial_matches(const char* port_name, uint32_t address, size_t start, size_t end) {
838
+    return sc_hw_done(start, end) ? 0 : ((sc_hw_skip(port_name) ? 0 : (sc_hw_match(port_name, address, start, end) ? 1 : 0)) + count_tmc_hw_serial_matches(port_name, address, start + 1, end));
839
+  }
840
+
841
+  #define TMC_HWSERIAL_CONFLICT_MSG(A) STRINGIFY(A) "_SLAVE_ADDRESS conflicts with another driver using the same " STRINGIFY(A) "_HARDWARE_SERIAL"
842
+  #define SA_NO_TMC_HW_C(A) static_assert(1 >= count_tmc_hw_serial_matches(TMC_HW_DETAIL_ARGS(A), 0, COUNT(sanity_tmc_hw_details)), TMC_HWSERIAL_CONFLICT_MSG(A));
843
+  SA_NO_TMC_HW_C(X);SA_NO_TMC_HW_C(X2);
844
+  SA_NO_TMC_HW_C(Y);SA_NO_TMC_HW_C(Y2);
845
+  SA_NO_TMC_HW_C(Z);SA_NO_TMC_HW_C(Z2);SA_NO_TMC_HW_C(Z3);SA_NO_TMC_HW_C(Z4);
846
+  SA_NO_TMC_HW_C(E0);SA_NO_TMC_HW_C(E1);SA_NO_TMC_HW_C(E2);SA_NO_TMC_HW_C(E3);SA_NO_TMC_HW_C(E4);SA_NO_TMC_HW_C(E5);SA_NO_TMC_HW_C(E6);SA_NO_TMC_HW_C(E7);
847
+#endif
848
+
849
+#if ANY_AXIS_HAS(SW_SERIAL)
850
+  struct SanitySwSerialDetails { int32_t txpin; int32_t rxpin; uint32_t address; };
851
+  #define TMC_SW_DETAIL_ARGS(A) TERN(A##_HAS_SW_SERIAL, A##_SERIAL_TX_PIN, -1), TERN(A##_HAS_SW_SERIAL, A##_SERIAL_RX_PIN, -1), TERN0(A##_HAS_SW_SERIAL, A##_SLAVE_ADDRESS)
852
+  #define TMC_SW_DETAIL(A) TMC_SW_DETAIL_ARGS(A)
853
+  constexpr SanitySwSerialDetails sanity_tmc_sw_details[] = {
854
+    TMC_SW_DETAIL(X), TMC_SW_DETAIL(X2),
855
+    TMC_SW_DETAIL(Y), TMC_SW_DETAIL(Y2),
856
+    TMC_SW_DETAIL(Z), TMC_SW_DETAIL(Z2), TMC_SW_DETAIL(Z3), TMC_SW_DETAIL(Z4),
857
+    TMC_SW_DETAIL(E0), TMC_SW_DETAIL(E1), TMC_SW_DETAIL(E2), TMC_SW_DETAIL(E3), TMC_SW_DETAIL(E4), TMC_SW_DETAIL(E5), TMC_SW_DETAIL(E6), TMC_SW_DETAIL(E7)
858
+  };
859
+
860
+  constexpr bool sc_sw_done(size_t start, size_t end) { return start == end; }
861
+  constexpr bool sc_sw_skip(int32_t txpin) { return txpin < 0; }
862
+  constexpr bool sc_sw_match(int32_t txpin, int32_t rxpin, uint32_t address, size_t start, size_t end) {
863
+    return !sc_sw_done(start, end) && !sc_sw_skip(txpin) && (txpin == sanity_tmc_sw_details[start].txpin || rxpin == sanity_tmc_sw_details[start].rxpin) && (address == sanity_tmc_sw_details[start].address);
864
+  }
865
+  constexpr int count_tmc_sw_serial_matches(int32_t txpin, int32_t rxpin, uint32_t address, size_t start, size_t end) {
866
+    return sc_sw_done(start, end) ? 0 : ((sc_sw_skip(txpin) ? 0 : (sc_sw_match(txpin, rxpin, address, start, end) ? 1 : 0)) + count_tmc_sw_serial_matches(txpin, rxpin, address, start + 1, end));
867
+  }
868
+
869
+  #define TMC_SWSERIAL_CONFLICT_MSG(A) STRINGIFY(A) "_SLAVE_ADDRESS conflicts with another driver using the same " STRINGIFY(A) "_SERIAL_RX_PIN or " STRINGIFY(A) "_SERIAL_TX_PIN"
870
+  #define SA_NO_TMC_SW_C(A) static_assert(1 >= count_tmc_sw_serial_matches(TMC_SW_DETAIL_ARGS(A), 0, COUNT(sanity_tmc_sw_details)), TMC_SWSERIAL_CONFLICT_MSG(A));
871
+  SA_NO_TMC_SW_C(X);SA_NO_TMC_SW_C(X2);
872
+  SA_NO_TMC_SW_C(Y);SA_NO_TMC_SW_C(Y2);
873
+  SA_NO_TMC_SW_C(Z);SA_NO_TMC_SW_C(Z2);SA_NO_TMC_SW_C(Z3);SA_NO_TMC_SW_C(Z4);
874
+  SA_NO_TMC_SW_C(E0);SA_NO_TMC_SW_C(E1);SA_NO_TMC_SW_C(E2);SA_NO_TMC_SW_C(E3);SA_NO_TMC_SW_C(E4);SA_NO_TMC_SW_C(E5);SA_NO_TMC_SW_C(E6);SA_NO_TMC_SW_C(E7);
875
+#endif
876
+
772
 #endif // HAS_TRINAMIC_CONFIG
877
 #endif // HAS_TRINAMIC_CONFIG

+ 2
- 2
buildroot/bin/opt_set View File

6
 SED=$(which gsed || which sed)
6
 SED=$(which gsed || which sed)
7
 
7
 
8
 # Logic for returning nonzero based on answer here: https://stackoverflow.com/a/15966279/104648
8
 # Logic for returning nonzero based on answer here: https://stackoverflow.com/a/15966279/104648
9
-eval "${SED} -i '/\(\/\/\)*\([[:blank:]]*\)\(#define \b${1}\b\).*$/{s//\2\3 ${2}/;h};\${x;/./{x;q0};x;q9}' Marlin/Configuration.h" ||
10
-eval "${SED} -i '/\(\/\/\)*\([[:blank:]]*\)\(#define \b${1}\b\).*$/{s//\2\3 ${2}/;h};\${x;/./{x;q0};x;q9}' Marlin/Configuration_adv.h" ||
9
+eval "${SED} -i '/\(\/\/\)*\([[:blank:]]*\)\(#define\s\+\b${1}\b\).*$/{s//\2\3 ${2}/;h};\${x;/./{x;q0};x;q9}' Marlin/Configuration.h" ||
10
+eval "${SED} -i '/\(\/\/\)*\([[:blank:]]*\)\(#define\s\+\b${1}\b\).*$/{s//\2\3 ${2}/;h};\${x;/./{x;q0};x;q9}' Marlin/Configuration_adv.h" ||
11
 eval "echo '#define ${@}' >>Marlin/Configuration_adv.h" ||
11
 eval "echo '#define ${@}' >>Marlin/Configuration_adv.h" ||
12
 (echo "ERROR: opt_set Can't set or add ${1}" >&2 && exit 9)
12
 (echo "ERROR: opt_set Can't set or add ${1}" >&2 && exit 9)

+ 6
- 1
buildroot/tests/STM32F103RC_btt-tests View File

16
 opt_set X_DRIVER_TYPE TMC2209
16
 opt_set X_DRIVER_TYPE TMC2209
17
 opt_set Y_DRIVER_TYPE TMC2209
17
 opt_set Y_DRIVER_TYPE TMC2209
18
 opt_set Z_DRIVER_TYPE TMC2209
18
 opt_set Z_DRIVER_TYPE TMC2209
19
-opt_set E_DRIVER_TYPE TMC2209
19
+opt_set E0_DRIVER_TYPE TMC2209
20
+opt_set X_SLAVE_ADDRESS 0
21
+opt_set Y_SLAVE_ADDRESS 1
22
+opt_set Z_SLAVE_ADDRESS 2
23
+opt_set E0_SLAVE_ADDRESS 3
24
+
20
 exec_test $1 $2 "BigTreeTech SKR Mini E3 1.0 - Basic Config with TMC2209 HW Serial"
25
 exec_test $1 $2 "BigTreeTech SKR Mini E3 1.0 - Basic Config with TMC2209 HW Serial"
21
 
26
 
22
 # clean up
27
 # clean up

+ 4
- 0
buildroot/tests/esp32-tests View File

30
 opt_set Y_HARDWARE_SERIAL Serial1
30
 opt_set Y_HARDWARE_SERIAL Serial1
31
 opt_set Z_HARDWARE_SERIAL Serial1
31
 opt_set Z_HARDWARE_SERIAL Serial1
32
 opt_set E0_HARDWARE_SERIAL Serial1
32
 opt_set E0_HARDWARE_SERIAL Serial1
33
+opt_set X_SLAVE_ADDRESS 0
34
+opt_set Y_SLAVE_ADDRESS 1
35
+opt_set Z_SLAVE_ADDRESS 2
36
+opt_set E0_SLAVE_ADDRESS 3
33
 opt_enable HOTEND_IDLE_TIMEOUT
37
 opt_enable HOTEND_IDLE_TIMEOUT
34
 exec_test $1 $2 "ESP32, TMC HW Serial, Hotend Idle"
38
 exec_test $1 $2 "ESP32, TMC HW Serial, Hotend Idle"
35
 
39
 

Loading…
Cancel
Save