Browse Source

Improve STM32 timer conflict messages (#20544)

Jason Smith 3 years ago
parent
commit
094e822070
No account linked to committer's email address
1 changed files with 112 additions and 73 deletions
  1. 112
    73
      Marlin/src/HAL/STM32/timers.cpp

+ 112
- 73
Marlin/src/HAL/STM32/timers.cpp View File

@@ -27,7 +27,6 @@
27 27
 // Local defines
28 28
 // ------------------------
29 29
 
30
-
31 30
 // Default timer priorities. Override by specifying alternate priorities in the board pins file.
32 31
 // The TONE timer is not present here, as it currently cannot be set programmatically. It is set
33 32
 // by defining TIM_IRQ_PRIO in the variant.h or platformio.ini file, which adjusts the default
@@ -96,11 +95,6 @@
96 95
 #define STEP_TIMER_DEV _TIMER_DEV(STEP_TIMER)
97 96
 #define TEMP_TIMER_DEV _TIMER_DEV(TEMP_TIMER)
98 97
 
99
-#define __TIMER_IRQ_NAME(X) TIM##X##_IRQn
100
-#define _TIMER_IRQ_NAME(X) __TIMER_IRQ_NAME(X)
101
-#define STEP_TIMER_IRQ_NAME _TIMER_IRQ_NAME(STEP_TIMER)
102
-#define TEMP_TIMER_IRQ_NAME _TIMER_IRQ_NAME(TEMP_TIMER)
103
-
104 98
 // ------------------------
105 99
 // Private Variables
106 100
 // ------------------------
@@ -197,87 +191,132 @@ void SetTimerInterruptPriorities() {
197 191
   TERN_(HAS_SERVOS, libServo::setInterruptPriority(SERVO_TIMER_IRQ_PRIO, 0));
198 192
 }
199 193
 
200
-// This is a terrible hack to replicate the behavior used in the framework's SoftwareSerial.cpp
201
-// to choose a serial timer. It will select TIM7 on most boards used by Marlin, but this is more
202
-// resiliant to new MCUs which may not have a TIM7. Best practice is to explicitly specify
203
-// TIMER_SERIAL to avoid relying on framework selections which may not be predictable.
204
-#if !defined(TIMER_SERIAL)
205
-  #if defined (TIM18_BASE)
206
-    #define TIMER_SERIAL TIM18
207
-  #elif defined (TIM7_BASE)
208
-    #define TIMER_SERIAL TIM7
209
-  #elif defined (TIM6_BASE)
210
-    #define TIMER_SERIAL TIM6
211
-  #elif defined (TIM22_BASE)
212
-    #define TIMER_SERIAL TIM22
213
-  #elif defined (TIM21_BASE)
214
-    #define TIMER_SERIAL TIM21
215
-  #elif defined (TIM17_BASE)
216
-    #define TIMER_SERIAL TIM17
217
-  #elif defined (TIM16_BASE)
218
-    #define TIMER_SERIAL TIM16
219
-  #elif defined (TIM15_BASE)
220
-    #define TIMER_SERIAL TIM15
221
-  #elif defined (TIM14_BASE)
222
-    #define TIMER_SERIAL TIM14
223
-  #elif defined (TIM13_BASE)
224
-    #define TIMER_SERIAL TIM13
225
-  #elif defined (TIM11_BASE)
226
-    #define TIMER_SERIAL TIM11
227
-  #elif defined (TIM10_BASE)
228
-    #define TIMER_SERIAL TIM10
229
-  #elif defined (TIM12_BASE)
230
-    #define TIMER_SERIAL TIM12
231
-  #elif defined (TIM19_BASE)
232
-    #define TIMER_SERIAL TIM19
233
-  #elif defined (TIM9_BASE)
234
-    #define TIMER_SERIAL TIM9
235
-  #elif defined (TIM5_BASE)
236
-    #define TIMER_SERIAL TIM5
237
-  #elif defined (TIM4_BASE)
238
-    #define TIMER_SERIAL TIM4
239
-  #elif defined (TIM3_BASE)
240
-    #define TIMER_SERIAL TIM3
241
-  #elif defined (TIM2_BASE)
242
-    #define TIMER_SERIAL TIM2
243
-  #elif defined (TIM20_BASE)
244
-    #define TIMER_SERIAL TIM20
245
-  #elif defined (TIM8_BASE)
246
-    #define TIMER_SERIAL TIM8
247
-  #elif defined (TIM1_BASE)
248
-    #define TIMER_SERIAL TIM1
249
-  #else
250
-    #error No suitable timer found for SoftwareSerial, define TIMER_SERIAL in variant.h
194
+// ------------------------
195
+// Detect timer conflicts
196
+// ------------------------
197
+
198
+// This list serves two purposes. Firstly, it facilitates build-time mapping between
199
+// variant-defined timer names (such as TIM1) and timer numbers. It also replicates
200
+// the order of timers used in the framework's SoftwareSerial.cpp. The first timer in
201
+// this list will be automatically used by SoftwareSerial if it is not already defined
202
+// in the board's variant or compiler options.
203
+static constexpr struct {uintptr_t base_address; int timer_number;} stm32_timer_map[] = {
204
+  #ifdef TIM18_BASE
205
+    { uintptr_t(TIM18), 18 },
206
+  #endif
207
+  #ifdef TIM7_BASE
208
+    { uintptr_t(TIM7),   7 },
209
+  #endif
210
+  #ifdef TIM6_BASE
211
+    { uintptr_t(TIM6),   6 },
212
+  #endif
213
+  #ifdef TIM22_BASE
214
+    { uintptr_t(TIM22), 22 },
215
+  #endif
216
+  #ifdef TIM21_BASE
217
+    { uintptr_t(TIM21), 21 },
218
+  #endif
219
+  #ifdef TIM17_BASE
220
+    { uintptr_t(TIM17), 17 },
221
+  #endif
222
+  #ifdef TIM16_BASE
223
+    { uintptr_t(TIM16), 16 },
224
+  #endif
225
+  #ifdef TIM15_BASE
226
+    { uintptr_t(TIM15), 15 },
227
+  #endif
228
+  #ifdef TIM14_BASE
229
+    { uintptr_t(TIM14), 14 },
230
+  #endif
231
+  #ifdef TIM13_BASE
232
+    { uintptr_t(TIM13), 13 },
233
+  #endif
234
+  #ifdef TIM11_BASE
235
+    { uintptr_t(TIM11), 11 },
236
+  #endif
237
+  #ifdef TIM10_BASE
238
+    { uintptr_t(TIM10), 10 },
251 239
   #endif
240
+  #ifdef TIM12_BASE
241
+    { uintptr_t(TIM12), 12 },
242
+  #endif
243
+  #ifdef TIM19_BASE
244
+    { uintptr_t(TIM19), 19 },
245
+  #endif
246
+  #ifdef TIM9_BASE
247
+    { uintptr_t(TIM9),   9 },
248
+  #endif
249
+  #ifdef TIM5_BASE
250
+    { uintptr_t(TIM5),   5 },
251
+  #endif
252
+  #ifdef TIM4_BASE
253
+    { uintptr_t(TIM4),   4 },
254
+  #endif
255
+  #ifdef TIM3_BASE
256
+    { uintptr_t(TIM3),   3 },
257
+  #endif
258
+  #ifdef TIM2_BASE
259
+    { uintptr_t(TIM2),   2 },
260
+  #endif
261
+  #ifdef TIM20_BASE
262
+    { uintptr_t(TIM20), 20 },
263
+  #endif
264
+  #ifdef TIM8_BASE
265
+    { uintptr_t(TIM8),   8 },
266
+  #endif
267
+  #ifdef TIM1_BASE
268
+    { uintptr_t(TIM1),   1 }
269
+  #endif
270
+};
271
+
272
+// Convert from a timer base address to its integer timer number.
273
+static constexpr int get_timer_num_from_base_address(uintptr_t base_address) {
274
+  for (const auto &timer : stm32_timer_map)
275
+    if (timer.base_address == base_address) return timer.timer_number;
276
+  return 0;
277
+}
278
+
279
+// The platform's SoftwareSerial.cpp will use the first timer from stm32_timer_map.
280
+#if HAS_TMC_SW_SERIAL && !defined(TIMER_SERIAL)
281
+  #define  TIMER_SERIAL (stm32_timer_map[0].base_address)
252 282
 #endif
253 283
 
254
-// Place all timers used into an array, then recursively check for duplicates during compilation.
255
-// This does not currently account for timers used for PWM, such as for fans.
256
-// Timers are actually pointers. Convert to integers to simplify constexpr logic.
257
-static constexpr uintptr_t timers_in_use[] = {
258
-  uintptr_t(TEMP_TIMER_DEV),  // Override in pins file
259
-  uintptr_t(STEP_TIMER_DEV),  // Override in pins file
284
+// constexpr doesn't like using the base address pointers that timers evaluate to.
285
+// We can get away with casting them to uintptr_t, if we do so inside an array.
286
+// GCC will not currently do it directly to a uintptr_t.
287
+IF_ENABLED(HAS_TMC_SW_SERIAL, static constexpr uintptr_t timer_serial[] = {uintptr_t(TIMER_SERIAL)});
288
+IF_ENABLED(SPEAKER,           static constexpr uintptr_t timer_tone[]   = {uintptr_t(TIMER_TONE)});
289
+IF_ENABLED(HAS_SERVOS,        static constexpr uintptr_t timer_servo[]  = {uintptr_t(TIMER_SERVO)});
290
+
291
+enum TimerPurpose { TP_SERIAL, TP_TONE, TP_SERVO, TP_STEP, TP_TEMP };
292
+
293
+// List of timers, to enable checking for conflicts.
294
+// Includes the purpose of each timer to ease debugging when evaluating at build-time.
295
+// This cannot yet account for timers used for PWM output, such as for fans.
296
+static constexpr struct { TimerPurpose p; int t; } timers_in_use[] = {
260 297
   #if HAS_TMC_SW_SERIAL
261
-    uintptr_t(TIMER_SERIAL),  // Set in variant.h, or as a define in platformio.h if not present in variant.h
298
+    {TP_SERIAL, get_timer_num_from_base_address(timer_serial[0])},  // Set in variant.h, or as a define in platformio.h if not present in variant.h
262 299
   #endif
263 300
   #if ENABLED(SPEAKER)
264
-    uintptr_t(TIMER_TONE),    // Set in variant.h, or as a define in platformio.h if not present in variant.h
301
+    {TP_TONE, get_timer_num_from_base_address(timer_tone[0])},    // Set in variant.h, or as a define in platformio.h if not present in variant.h
265 302
   #endif
266 303
   #if HAS_SERVOS
267
-    uintptr_t(TIMER_SERVO),   // Set in variant.h, or as a define in platformio.h if not present in variant.h
304
+    {TP_SERVO, get_timer_num_from_base_address(timer_servo[0])},   // Set in variant.h, or as a define in platformio.h if not present in variant.h
268 305
   #endif
269
-  };
306
+  {TP_STEP, STEP_TIMER},
307
+  {TP_TEMP, TEMP_TIMER},
308
+};
270 309
 
271
-static constexpr bool verify_no_duplicate_timers() {
310
+static constexpr bool verify_no_timer_conflicts() {
272 311
   LOOP_L_N(i, COUNT(timers_in_use))
273 312
     LOOP_S_L_N(j, i + 1, COUNT(timers_in_use))
274
-      if (timers_in_use[i] == timers_in_use[j]) return false;
313
+      if (timers_in_use[i].t == timers_in_use[j].t) return false;
275 314
   return true;
276 315
 }
277 316
 
278
-// If this assertion fails at compile time, review the timers_in_use array. If default_envs is
279
-// defined properly in platformio.ini, VS Code can evaluate the array when hovering over it,
280
-// making it easy to identify the conflicting timers.
281
-static_assert(verify_no_duplicate_timers(), "One or more timer conflict detected");
317
+// If this assertion fails at compile time, review the timers_in_use array.
318
+// If default_envs is defined properly in platformio.ini, VS Code can evaluate the array
319
+// when hovering over it, making it easy to identify the conflicting timers.
320
+static_assert(verify_no_timer_conflicts(), "One or more timer conflict detected. Examine \"timers_in_use\" to help identify conflict.");
282 321
 
283 322
 #endif // ARDUINO_ARCH_STM32 && !STM32GENERIC

Loading…
Cancel
Save