Improve STM32 timer conflict messages (#20544)
This commit is contained in:
		
							parent
							
								
									82540be931
								
							
						
					
					
						commit
						094e822070
					
				| @ -27,7 +27,6 @@ | |||||||
| // Local defines
 | // Local defines
 | ||||||
| // ------------------------
 | // ------------------------
 | ||||||
| 
 | 
 | ||||||
| 
 |  | ||||||
| // Default timer priorities. Override by specifying alternate priorities in the board pins file.
 | // Default timer priorities. Override by specifying alternate priorities in the board pins file.
 | ||||||
| // The TONE timer is not present here, as it currently cannot be set programmatically. It is set
 | // The TONE timer is not present here, as it currently cannot be set programmatically. It is set
 | ||||||
| // by defining TIM_IRQ_PRIO in the variant.h or platformio.ini file, which adjusts the default
 | // by defining TIM_IRQ_PRIO in the variant.h or platformio.ini file, which adjusts the default
 | ||||||
| @ -96,11 +95,6 @@ | |||||||
| #define STEP_TIMER_DEV _TIMER_DEV(STEP_TIMER) | #define STEP_TIMER_DEV _TIMER_DEV(STEP_TIMER) | ||||||
| #define TEMP_TIMER_DEV _TIMER_DEV(TEMP_TIMER) | #define TEMP_TIMER_DEV _TIMER_DEV(TEMP_TIMER) | ||||||
| 
 | 
 | ||||||
| #define __TIMER_IRQ_NAME(X) TIM##X##_IRQn |  | ||||||
| #define _TIMER_IRQ_NAME(X) __TIMER_IRQ_NAME(X) |  | ||||||
| #define STEP_TIMER_IRQ_NAME _TIMER_IRQ_NAME(STEP_TIMER) |  | ||||||
| #define TEMP_TIMER_IRQ_NAME _TIMER_IRQ_NAME(TEMP_TIMER) |  | ||||||
| 
 |  | ||||||
| // ------------------------
 | // ------------------------
 | ||||||
| // Private Variables
 | // Private Variables
 | ||||||
| // ------------------------
 | // ------------------------
 | ||||||
| @ -197,87 +191,132 @@ void SetTimerInterruptPriorities() { | |||||||
|   TERN_(HAS_SERVOS, libServo::setInterruptPriority(SERVO_TIMER_IRQ_PRIO, 0)); |   TERN_(HAS_SERVOS, libServo::setInterruptPriority(SERVO_TIMER_IRQ_PRIO, 0)); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // This is a terrible hack to replicate the behavior used in the framework's SoftwareSerial.cpp
 | // ------------------------
 | ||||||
| // to choose a serial timer. It will select TIM7 on most boards used by Marlin, but this is more
 | // Detect timer conflicts
 | ||||||
| // resiliant to new MCUs which may not have a TIM7. Best practice is to explicitly specify
 | // ------------------------
 | ||||||
| // TIMER_SERIAL to avoid relying on framework selections which may not be predictable.
 |  | ||||||
| #if !defined(TIMER_SERIAL) |  | ||||||
|   #if defined (TIM18_BASE) |  | ||||||
|     #define TIMER_SERIAL TIM18 |  | ||||||
|   #elif defined (TIM7_BASE) |  | ||||||
|     #define TIMER_SERIAL TIM7 |  | ||||||
|   #elif defined (TIM6_BASE) |  | ||||||
|     #define TIMER_SERIAL TIM6 |  | ||||||
|   #elif defined (TIM22_BASE) |  | ||||||
|     #define TIMER_SERIAL TIM22 |  | ||||||
|   #elif defined (TIM21_BASE) |  | ||||||
|     #define TIMER_SERIAL TIM21 |  | ||||||
|   #elif defined (TIM17_BASE) |  | ||||||
|     #define TIMER_SERIAL TIM17 |  | ||||||
|   #elif defined (TIM16_BASE) |  | ||||||
|     #define TIMER_SERIAL TIM16 |  | ||||||
|   #elif defined (TIM15_BASE) |  | ||||||
|     #define TIMER_SERIAL TIM15 |  | ||||||
|   #elif defined (TIM14_BASE) |  | ||||||
|     #define TIMER_SERIAL TIM14 |  | ||||||
|   #elif defined (TIM13_BASE) |  | ||||||
|     #define TIMER_SERIAL TIM13 |  | ||||||
|   #elif defined (TIM11_BASE) |  | ||||||
|     #define TIMER_SERIAL TIM11 |  | ||||||
|   #elif defined (TIM10_BASE) |  | ||||||
|     #define TIMER_SERIAL TIM10 |  | ||||||
|   #elif defined (TIM12_BASE) |  | ||||||
|     #define TIMER_SERIAL TIM12 |  | ||||||
|   #elif defined (TIM19_BASE) |  | ||||||
|     #define TIMER_SERIAL TIM19 |  | ||||||
|   #elif defined (TIM9_BASE) |  | ||||||
|     #define TIMER_SERIAL TIM9 |  | ||||||
|   #elif defined (TIM5_BASE) |  | ||||||
|     #define TIMER_SERIAL TIM5 |  | ||||||
|   #elif defined (TIM4_BASE) |  | ||||||
|     #define TIMER_SERIAL TIM4 |  | ||||||
|   #elif defined (TIM3_BASE) |  | ||||||
|     #define TIMER_SERIAL TIM3 |  | ||||||
|   #elif defined (TIM2_BASE) |  | ||||||
|     #define TIMER_SERIAL TIM2 |  | ||||||
|   #elif defined (TIM20_BASE) |  | ||||||
|     #define TIMER_SERIAL TIM20 |  | ||||||
|   #elif defined (TIM8_BASE) |  | ||||||
|     #define TIMER_SERIAL TIM8 |  | ||||||
|   #elif defined (TIM1_BASE) |  | ||||||
|     #define TIMER_SERIAL TIM1 |  | ||||||
|   #else |  | ||||||
|     #error No suitable timer found for SoftwareSerial, define TIMER_SERIAL in variant.h |  | ||||||
|   #endif |  | ||||||
| #endif |  | ||||||
| 
 | 
 | ||||||
| // Place all timers used into an array, then recursively check for duplicates during compilation.
 | // This list serves two purposes. Firstly, it facilitates build-time mapping between
 | ||||||
| // This does not currently account for timers used for PWM, such as for fans.
 | // variant-defined timer names (such as TIM1) and timer numbers. It also replicates
 | ||||||
| // Timers are actually pointers. Convert to integers to simplify constexpr logic.
 | // the order of timers used in the framework's SoftwareSerial.cpp. The first timer in
 | ||||||
| static constexpr uintptr_t timers_in_use[] = { | // this list will be automatically used by SoftwareSerial if it is not already defined
 | ||||||
|   uintptr_t(TEMP_TIMER_DEV),  // Override in pins file
 | // in the board's variant or compiler options.
 | ||||||
|   uintptr_t(STEP_TIMER_DEV),  // Override in pins file
 | static constexpr struct {uintptr_t base_address; int timer_number;} stm32_timer_map[] = { | ||||||
|   #if HAS_TMC_SW_SERIAL |   #ifdef TIM18_BASE | ||||||
|     uintptr_t(TIMER_SERIAL),  // Set in variant.h, or as a define in platformio.h if not present in variant.h
 |     { uintptr_t(TIM18), 18 }, | ||||||
|   #endif |   #endif | ||||||
|   #if ENABLED(SPEAKER) |   #ifdef TIM7_BASE | ||||||
|     uintptr_t(TIMER_TONE),    // Set in variant.h, or as a define in platformio.h if not present in variant.h
 |     { uintptr_t(TIM7),   7 }, | ||||||
|   #endif |   #endif | ||||||
|   #if HAS_SERVOS |   #ifdef TIM6_BASE | ||||||
|     uintptr_t(TIMER_SERVO),   // Set in variant.h, or as a define in platformio.h if not present in variant.h
 |     { uintptr_t(TIM6),   6 }, | ||||||
|  |   #endif | ||||||
|  |   #ifdef TIM22_BASE | ||||||
|  |     { uintptr_t(TIM22), 22 }, | ||||||
|  |   #endif | ||||||
|  |   #ifdef TIM21_BASE | ||||||
|  |     { uintptr_t(TIM21), 21 }, | ||||||
|  |   #endif | ||||||
|  |   #ifdef TIM17_BASE | ||||||
|  |     { uintptr_t(TIM17), 17 }, | ||||||
|  |   #endif | ||||||
|  |   #ifdef TIM16_BASE | ||||||
|  |     { uintptr_t(TIM16), 16 }, | ||||||
|  |   #endif | ||||||
|  |   #ifdef TIM15_BASE | ||||||
|  |     { uintptr_t(TIM15), 15 }, | ||||||
|  |   #endif | ||||||
|  |   #ifdef TIM14_BASE | ||||||
|  |     { uintptr_t(TIM14), 14 }, | ||||||
|  |   #endif | ||||||
|  |   #ifdef TIM13_BASE | ||||||
|  |     { uintptr_t(TIM13), 13 }, | ||||||
|  |   #endif | ||||||
|  |   #ifdef TIM11_BASE | ||||||
|  |     { uintptr_t(TIM11), 11 }, | ||||||
|  |   #endif | ||||||
|  |   #ifdef TIM10_BASE | ||||||
|  |     { uintptr_t(TIM10), 10 }, | ||||||
|  |   #endif | ||||||
|  |   #ifdef TIM12_BASE | ||||||
|  |     { uintptr_t(TIM12), 12 }, | ||||||
|  |   #endif | ||||||
|  |   #ifdef TIM19_BASE | ||||||
|  |     { uintptr_t(TIM19), 19 }, | ||||||
|  |   #endif | ||||||
|  |   #ifdef TIM9_BASE | ||||||
|  |     { uintptr_t(TIM9),   9 }, | ||||||
|  |   #endif | ||||||
|  |   #ifdef TIM5_BASE | ||||||
|  |     { uintptr_t(TIM5),   5 }, | ||||||
|  |   #endif | ||||||
|  |   #ifdef TIM4_BASE | ||||||
|  |     { uintptr_t(TIM4),   4 }, | ||||||
|  |   #endif | ||||||
|  |   #ifdef TIM3_BASE | ||||||
|  |     { uintptr_t(TIM3),   3 }, | ||||||
|  |   #endif | ||||||
|  |   #ifdef TIM2_BASE | ||||||
|  |     { uintptr_t(TIM2),   2 }, | ||||||
|  |   #endif | ||||||
|  |   #ifdef TIM20_BASE | ||||||
|  |     { uintptr_t(TIM20), 20 }, | ||||||
|  |   #endif | ||||||
|  |   #ifdef TIM8_BASE | ||||||
|  |     { uintptr_t(TIM8),   8 }, | ||||||
|  |   #endif | ||||||
|  |   #ifdef TIM1_BASE | ||||||
|  |     { uintptr_t(TIM1),   1 } | ||||||
|   #endif |   #endif | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| static constexpr bool verify_no_duplicate_timers() { | // Convert from a timer base address to its integer timer number.
 | ||||||
|  | static constexpr int get_timer_num_from_base_address(uintptr_t base_address) { | ||||||
|  |   for (const auto &timer : stm32_timer_map) | ||||||
|  |     if (timer.base_address == base_address) return timer.timer_number; | ||||||
|  |   return 0; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // The platform's SoftwareSerial.cpp will use the first timer from stm32_timer_map.
 | ||||||
|  | #if HAS_TMC_SW_SERIAL && !defined(TIMER_SERIAL) | ||||||
|  |   #define  TIMER_SERIAL (stm32_timer_map[0].base_address) | ||||||
|  | #endif | ||||||
|  | 
 | ||||||
|  | // constexpr doesn't like using the base address pointers that timers evaluate to.
 | ||||||
|  | // We can get away with casting them to uintptr_t, if we do so inside an array.
 | ||||||
|  | // GCC will not currently do it directly to a uintptr_t.
 | ||||||
|  | IF_ENABLED(HAS_TMC_SW_SERIAL, static constexpr uintptr_t timer_serial[] = {uintptr_t(TIMER_SERIAL)}); | ||||||
|  | IF_ENABLED(SPEAKER,           static constexpr uintptr_t timer_tone[]   = {uintptr_t(TIMER_TONE)}); | ||||||
|  | IF_ENABLED(HAS_SERVOS,        static constexpr uintptr_t timer_servo[]  = {uintptr_t(TIMER_SERVO)}); | ||||||
|  | 
 | ||||||
|  | enum TimerPurpose { TP_SERIAL, TP_TONE, TP_SERVO, TP_STEP, TP_TEMP }; | ||||||
|  | 
 | ||||||
|  | // List of timers, to enable checking for conflicts.
 | ||||||
|  | // Includes the purpose of each timer to ease debugging when evaluating at build-time.
 | ||||||
|  | // This cannot yet account for timers used for PWM output, such as for fans.
 | ||||||
|  | static constexpr struct { TimerPurpose p; int t; } timers_in_use[] = { | ||||||
|  |   #if HAS_TMC_SW_SERIAL | ||||||
|  |     {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
 | ||||||
|  |   #endif | ||||||
|  |   #if ENABLED(SPEAKER) | ||||||
|  |     {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
 | ||||||
|  |   #endif | ||||||
|  |   #if HAS_SERVOS | ||||||
|  |     {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
 | ||||||
|  |   #endif | ||||||
|  |   {TP_STEP, STEP_TIMER}, | ||||||
|  |   {TP_TEMP, TEMP_TIMER}, | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | static constexpr bool verify_no_timer_conflicts() { | ||||||
|   LOOP_L_N(i, COUNT(timers_in_use)) |   LOOP_L_N(i, COUNT(timers_in_use)) | ||||||
|     LOOP_S_L_N(j, i + 1, COUNT(timers_in_use)) |     LOOP_S_L_N(j, i + 1, COUNT(timers_in_use)) | ||||||
|       if (timers_in_use[i] == timers_in_use[j]) return false; |       if (timers_in_use[i].t == timers_in_use[j].t) return false; | ||||||
|   return true; |   return true; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // If this assertion fails at compile time, review the timers_in_use array. If default_envs is
 | // If this assertion fails at compile time, review the timers_in_use array.
 | ||||||
| // defined properly in platformio.ini, VS Code can evaluate the array when hovering over it,
 | // If default_envs is defined properly in platformio.ini, VS Code can evaluate the array
 | ||||||
| // making it easy to identify the conflicting timers.
 | // when hovering over it, making it easy to identify the conflicting timers.
 | ||||||
| static_assert(verify_no_duplicate_timers(), "One or more timer conflict detected"); | static_assert(verify_no_timer_conflicts(), "One or more timer conflict detected. Examine \"timers_in_use\" to help identify conflict."); | ||||||
| 
 | 
 | ||||||
| #endif // ARDUINO_ARCH_STM32 && !STM32GENERIC
 | #endif // ARDUINO_ARCH_STM32 && !STM32GENERIC
 | ||||||
|  | |||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user