|
| 1 | +// Copyright 2024 - 2025 Khalil Estell and the libhal contributors |
| 2 | +// |
| 3 | +// Licensed under the Apache License, Version 2.0 (the "License"); |
| 4 | +// you may not use this file except in compliance with the License. |
| 5 | +// You may obtain a copy of the License at |
| 6 | +// |
| 7 | +// http://www.apache.org/licenses/LICENSE-2.0 |
| 8 | +// |
| 9 | +// Unless required by applicable law or agreed to in writing, software |
| 10 | +// distributed under the License is distributed on an "AS IS" BASIS, |
| 11 | +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| 12 | +// See the License for the specific language governing permissions and |
| 13 | +// limitations under the License. |
| 14 | + |
| 15 | +#pragma once |
| 16 | + |
| 17 | +#include <libhal-arm-mcu/interrupt.hpp> |
| 18 | +#include <libhal/functional.hpp> |
| 19 | +#include <libhal/initializers.hpp> |
| 20 | +#include <libhal/units.hpp> |
| 21 | + |
| 22 | +namespace hal::stm32_generic { |
| 23 | + |
| 24 | +/** |
| 25 | + * @brief Implements shared timer setup and control logic common to all STM32 |
| 26 | + * series. |
| 27 | + * |
| 28 | + * This class provides the common functionality for timer configuration and |
| 29 | + * control, abstracting the parts of the timer interface that remain consistent |
| 30 | + * across different STM32 series. It is intended to be used by series-specific |
| 31 | + * implementations, which handle MCU-specific configuration details and pass |
| 32 | + * any required parameters to these generic functions. |
| 33 | + */ |
| 34 | +class timer final |
| 35 | +{ |
| 36 | +public: |
| 37 | + /** |
| 38 | + * @brief Construct timer uninitialized |
| 39 | + * |
| 40 | + * The purpose of this is to send the settings through the initialize function |
| 41 | + * instead, because all the settings are series-specific. Therefore in the |
| 42 | + * series-specific implementations of the timer, the address is deduced from |
| 43 | + * the timer, as well as all the interrupt configuration is done, then they |
| 44 | + * are passed to initialize. |
| 45 | + * |
| 46 | + * If this constructor is used, it is unsafe to call any API of this class |
| 47 | + * before calling the `initialize()` API with the correct inputs. Once that |
| 48 | + * API has been called without failure, then the other APIs will become |
| 49 | + * available. |
| 50 | + */ |
| 51 | + timer(hal::unsafe); |
| 52 | + |
| 53 | + /** |
| 54 | + * @brief Determine if the timer is currently running |
| 55 | + * |
| 56 | + * @return true - if a callback has been scheduled and has not been invoked |
| 57 | + * yet, false otherwise. |
| 58 | + */ |
| 59 | + [[nodiscard]] bool is_running(); |
| 60 | + |
| 61 | + /** |
| 62 | + * @brief Stops a scheduled event from happening. |
| 63 | + * |
| 64 | + * Does nothing if the timer is not currently running. |
| 65 | + * |
| 66 | + * Note that there must be sufficient time between the this call finishing and |
| 67 | + * the scheduled event's termination. If this call is too close to when the |
| 68 | + * schedule event expires, this function may not complete before the timer |
| 69 | + * interrupt is triggered. |
| 70 | + */ |
| 71 | + void cancel(); |
| 72 | + |
| 73 | + /** |
| 74 | + * @brief Schedule an interrupt to occur for this timer |
| 75 | + * |
| 76 | + * If this is called and the timer has already scheduled an event (in other |
| 77 | + * words, `is_running()` returns true), then the previous scheduled event will |
| 78 | + * be canceled and the new scheduled event will be started. |
| 79 | + * |
| 80 | + * If the delay time result in a tick period of 0, then the timer will execute |
| 81 | + * after 1 tick period. For example, if the tick period is 1ms and the |
| 82 | + * requested time delay is 500us, then the event will be scheduled for 1ms. |
| 83 | + * |
| 84 | + * If the tick period is 1ms and the requested time is 2.5ms then the event |
| 85 | + * will be scheduled after 2 tick periods or in 2ms. |
| 86 | + * |
| 87 | + * @param p_delay - the amount of time until the timer expires |
| 88 | + * @param p_timer_clock_frequency - the clock driving the timer |
| 89 | + * @throws hal::argument_out_of_domain - if p_delay cannot be achieved. |
| 90 | + */ |
| 91 | + void schedule(hal::time_duration p_delay, u32 p_timer_clock_frequency); |
| 92 | + |
| 93 | + /** |
| 94 | + * @brief Initialize the timer with the series-specific settings |
| 95 | + * |
| 96 | + * This is where the constructed uninitialized timer gets initialized. It gets |
| 97 | + * all the series-specific settings passed to it that it needs, and it |
| 98 | + * handles them appropriately. |
| 99 | + * |
| 100 | + * @param p_peripheral_address - the address of the chosen timer peripheral |
| 101 | + * @param initialize_interrupts_function - the function needed to initialize |
| 102 | + * interrupts for the specific series stm32. For example, for the stm32f1 |
| 103 | + * series, the function passed would be |
| 104 | + * `hal::stm32f1::initialize_interrupts()`. |
| 105 | + * @param p_irq - the irq number for the chosen timer |
| 106 | + * @param p_handler - the platform specific interrupt handler to be installed |
| 107 | + * @throws hal::device_or_resource_busy - if the timer interrupt vector is |
| 108 | + * already in use. |
| 109 | + */ |
| 110 | + void initialize(hal::unsafe, |
| 111 | + void* p_peripheral_address, |
| 112 | + void (*initialize_interrupts_function)(), |
| 113 | + cortex_m::irq_t p_irq, |
| 114 | + cortex_m::interrupt_pointer p_handler); |
| 115 | + |
| 116 | +private: |
| 117 | + /// Stores the base address of the timer |
| 118 | + void* m_reg = nullptr; |
| 119 | +}; |
| 120 | +} // namespace hal::stm32_generic |
0 commit comments