| 158 | // ============================================================================= |
| 159 | |
| 160 | int attach_timer_handler(const isr_config_t& config, isr_handle_t* out_handle) FL_NOEXCEPT { |
| 161 | if (!config.handler) { |
| 162 | FL_WARN("AVR ISR: handler is null"); |
| 163 | return -1; // Invalid parameter |
| 164 | } |
| 165 | |
| 166 | if (config.frequency_hz == 0) { |
| 167 | FL_WARN("AVR ISR: frequency_hz is 0"); |
| 168 | return -2; // Invalid frequency |
| 169 | } |
| 170 | |
| 171 | // Check if timer is already in use |
| 172 | if (g_avr_timer_data != nullptr) { |
| 173 | FL_WARN("AVR ISR: Timer1 already in use (only one timer supported)"); |
| 174 | return -16; // Timer already in use |
| 175 | } |
| 176 | |
| 177 | // Allocate handle data |
| 178 | auto handle_owner = fl::make_unique<avr_isr_handle_data>(); |
| 179 | auto* handle_data = handle_owner.get(); |
| 180 | if (!handle_data) { |
| 181 | FL_WARN("AVR ISR: failed to allocate handle data"); |
| 182 | return -3; // Out of memory |
| 183 | } |
| 184 | |
| 185 | // Calculate timer configuration |
| 186 | u8 prescaler_idx; |
| 187 | u16 ocr_value; |
| 188 | if (!calculate_timer_config(config.frequency_hz, prescaler_idx, ocr_value)) { |
| 189 | FL_WARN("AVR ISR: frequency " << config.frequency_hz << " Hz out of range"); |
| 190 | return -2; // Invalid frequency (out of range) |
| 191 | } |
| 192 | |
| 193 | // Calculate actual achieved frequency |
| 194 | u32 actual_freq = calculate_actual_frequency(prescaler_idx, ocr_value); |
| 195 | |
| 196 | // Warn if frequency error is >5% |
| 197 | i32 freq_error = static_cast<i32>(actual_freq) - static_cast<i32>(config.frequency_hz); |
| 198 | i32 error_pct = (freq_error * 100) / static_cast<i32>(config.frequency_hz); |
| 199 | if (error_pct > 5 || error_pct < -5) { |
| 200 | FL_WARN("AVR ISR: frequency error " << error_pct << "% (requested " |
| 201 | << config.frequency_hz << " Hz, actual " << actual_freq << " Hz)"); |
| 202 | } |
| 203 | |
| 204 | FL_DBG("AVR ISR: Timer1 config: prescaler=" << PRESCALERS[prescaler_idx].value |
| 205 | << ", OCR1A=" << ocr_value << ", actual_freq=" << actual_freq << " Hz"); |
| 206 | |
| 207 | // Store configuration |
| 208 | handle_data->mIsTimer = true; |
| 209 | handle_data->mUserHandler = config.handler; |
| 210 | handle_data->mUserData = config.user_data; |
| 211 | handle_data->mFrequencyHz = actual_freq; |
| 212 | handle_data->mPrescalerIndex = prescaler_idx; |
| 213 | handle_data->mOcrValue = ocr_value; |
| 214 | |
| 215 | // Release ownership - pointer is now managed by the C API (g_avr_timer_data + out_handle) |
| 216 | g_avr_timer_data = handle_data; |
| 217 | handle_owner.release(); |
nothing calls this directly
no test coverage detected