| 126 | // ============================================================================= |
| 127 | |
| 128 | inline int attach_timer_handler(const isr_config_t& config, isr_handle_t* out_handle) FL_NOEXCEPT { |
| 129 | if (!config.handler) { |
| 130 | ESP_LOGW(ESP32_ISR_TAG, "attachTimerHandler: handler is null"); |
| 131 | return -1; // Invalid parameter |
| 132 | } |
| 133 | |
| 134 | if (config.frequency_hz == 0) { |
| 135 | ESP_LOGW(ESP32_ISR_TAG, "attachTimerHandler: frequency_hz is 0"); |
| 136 | return -2; // Invalid frequency |
| 137 | } |
| 138 | |
| 139 | // Allocate handle data |
| 140 | auto handle_owner = fl::make_unique<esp32_isr_handle_data>(); |
| 141 | auto* handle_data = handle_owner.get(); |
| 142 | if (!handle_data) { |
| 143 | ESP_LOGW(ESP32_ISR_TAG, "attachTimerHandler: failed to allocate handle data"); |
| 144 | return -3; // Out of memory |
| 145 | } |
| 146 | |
| 147 | handle_data->is_timer = true; |
| 148 | handle_data->user_handler = config.handler; |
| 149 | handle_data->user_data = config.user_data; |
| 150 | |
| 151 | // Create general purpose timer |
| 152 | // For high frequencies (>1MHz), we need higher resolution to avoid rounding to 0 |
| 153 | // Choose resolution dynamically based on requested frequency |
| 154 | // |
| 155 | // IMPORTANT: ESP32 timer hardware requires clock divider >= 2 |
| 156 | // With 80MHz source clock, max resolution is 40MHz (80MHz / 2 = 40MHz) |
| 157 | u32 timer_resolution_hz; |
| 158 | u64 alarm_count; |
| 159 | |
| 160 | if (config.frequency_hz > 1000000) { |
| 161 | // For frequencies > 1MHz, use higher resolution |
| 162 | // Cap at 40MHz to ensure divider >= 2 (80MHz / 40MHz = 2) |
| 163 | timer_resolution_hz = 40000000; |
| 164 | alarm_count = timer_resolution_hz / config.frequency_hz; |
| 165 | } else { |
| 166 | // For lower frequencies, 1MHz resolution is sufficient |
| 167 | timer_resolution_hz = 1000000; |
| 168 | alarm_count = timer_resolution_hz / config.frequency_hz; |
| 169 | } |
| 170 | |
| 171 | // Ensure alarm_count is at least 1 to avoid ESP_ERR_INVALID_ARG |
| 172 | if (alarm_count == 0) { |
| 173 | ESP_LOGW(ESP32_ISR_TAG, "attachTimerHandler: frequency too high (%lu Hz), maximum is %lu Hz", |
| 174 | (unsigned long)config.frequency_hz, (unsigned long)timer_resolution_hz); |
| 175 | return -2; // Invalid frequency |
| 176 | } |
| 177 | |
| 178 | gptimer_config_t timer_config = {}; |
| 179 | timer_config.clk_src = GPTIMER_CLK_SRC_DEFAULT; |
| 180 | timer_config.direction = GPTIMER_COUNT_UP; |
| 181 | timer_config.resolution_hz = timer_resolution_hz; |
| 182 | |
| 183 | esp_err_t ret = gptimer_new_timer(&timer_config, &handle_data->timer_handle); |
| 184 | if (ret != ESP_OK) { |
| 185 | ESP_LOGW(ESP32_ISR_TAG, "attachTimerHandler: gptimer_new_timer failed: %s", esp_err_to_name(ret)); |
no test coverage detected