| Line | Branch | Exec | Source |
|---|---|---|---|
| 1 | // ====================================================================== | ||
| 2 | // \title RtcDriver.cpp | ||
| 3 | // \author tumbar | ||
| 4 | // \brief cpp file for RtcDriver component implementation class | ||
| 5 | // ====================================================================== | ||
| 6 | |||
| 7 | #include "fprime-samd/Drv/RtcDriver/RtcDriver.hpp" | ||
| 8 | #include "Fw/Types/Assert.hpp" | ||
| 9 | #include "Os/RawTime.hpp" | ||
| 10 | #include "config/FwAssertArgTypeAliasAc.h" | ||
| 11 | #include "fprime-samd/Drv/RtcDriver/RawTime.hpp" | ||
| 12 | #include "fprime-samd/Drv/RtcDriver/RtcDriverHardware.hpp" | ||
| 13 | #include "samd-config/RtcDriverConfig.hpp" | ||
| 14 | |||
| 15 | namespace Os { | ||
| 16 | namespace Samd21 { | ||
| 17 | |||
| 18 | ✗ | Os::Samd21::Samd21RawTimeHandle timeNow() { | |
| 19 | ✗ | Os::Samd21::Samd21RawTimeHandle time; | |
| 20 | |||
| 21 | ✗ | ::Samd21::RtcHardware::RtcState& state = ::Samd21::RtcHardware::getRtcState(); | |
| 22 | |||
| 23 | ✗ | if (state.configured) { | |
| 24 | ✗ | U32 seconds_offset = state.n_ticks / static_cast<U32>(state.rate_hz); | |
| 25 | ✗ | U32 subsecond_ticks = state.n_ticks % static_cast<U32>(state.rate_hz); | |
| 26 | |||
| 27 | ✗ | constexpr U32 USEC_PER_SEC = 1000000; | |
| 28 | ✗ | U32 subtick_counter = ::Samd21::RtcHardware::RtcHal::readRtcCounter(); | |
| 29 | |||
| 30 | // Use 64-bit arithmetic to avoid overflow, then cast result back to U32 | ||
| 31 | // Maximum intermediate value: 32768 * 1000000 = 32,768,000,000 (fits in U64) | ||
| 32 | U32 usecond_offset = | ||
| 33 | ✗ | static_cast<U32>((static_cast<U64>(subsecond_ticks) * USEC_PER_SEC) / static_cast<U32>(state.rate_hz)); | |
| 34 | U32 useconds_counter = | ||
| 35 | ✗ | static_cast<U32>((static_cast<U64>(subtick_counter) * USEC_PER_SEC) / state.clock_rate_hz); | |
| 36 | |||
| 37 | // Compute the final time | ||
| 38 | ✗ | U32 useconds = usecond_offset + useconds_counter; | |
| 39 | ✗ | U32 seconds = seconds_offset + (useconds / USEC_PER_SEC); | |
| 40 | ✗ | useconds %= USEC_PER_SEC; | |
| 41 | |||
| 42 | ✗ | time.m_seconds = seconds; | |
| 43 | ✗ | time.m_micros = useconds; | |
| 44 | } else { | ||
| 45 | // RTC is not configured, we have no way of determining the processor time | ||
| 46 | ✗ | time.m_seconds = 0; | |
| 47 | ✗ | time.m_micros = 0; | |
| 48 | } | ||
| 49 | |||
| 50 | ✗ | return time; | |
| 51 | } | ||
| 52 | |||
| 53 | } // namespace Samd21 | ||
| 54 | } // namespace Os | ||
| 55 | |||
| 56 | namespace Samd21 { | ||
| 57 | |||
| 58 | // ---------------------------------------------------------------------- | ||
| 59 | // Component construction and destruction | ||
| 60 | // ---------------------------------------------------------------------- | ||
| 61 | |||
| 62 | ✗ | RtcDriver ::RtcDriver(const char* const compName) : RtcDriverComponentBase(compName) {} | |
| 63 | |||
| 64 | ✗ | void RtcDriver ::configure(ClockSource clk_source, TickRate rate) { | |
| 65 | ✗ | RtcHardware::RtcState& state = RtcHardware::getRtcState(); | |
| 66 | ✗ | FW_ASSERT(!state.configured); | |
| 67 | |||
| 68 | // Validate the rate is one of our predefined rates | ||
| 69 | ✗ | switch (rate) { | |
| 70 | ✗ | case TickRate::TICK_1_HZ: | |
| 71 | case TickRate::TICK_2_HZ: | ||
| 72 | case TickRate::TICK_4_HZ: | ||
| 73 | case TickRate::TICK_8_HZ: | ||
| 74 | case TickRate::TICK_16_HZ: | ||
| 75 | case TickRate::TICK_32_HZ: | ||
| 76 | case TickRate::TICK_64_HZ: | ||
| 77 | case TickRate::TICK_128_HZ: | ||
| 78 | ✗ | break; | |
| 79 | ✗ | default: | |
| 80 | ✗ | FW_ASSERT(0, static_cast<FwAssertArgType>(rate)); | |
| 81 | break; | ||
| 82 | } | ||
| 83 | |||
| 84 | // Select the clock source | ||
| 85 | ✗ | switch (clk_source) { | |
| 86 | ✗ | case RtcDriver::ClockSource::InternalOscillator: | |
| 87 | ✗ | state.clock_rate_hz = RtcDriver::INTERNAL_CLOCK_FREQ_HZ; | |
| 88 | ✗ | break; | |
| 89 | ✗ | case RtcDriver::ClockSource::ExternalOscillator: | |
| 90 | ✗ | state.clock_rate_hz = ::Samd21::RtcDriverConfig::EXTERNAL_CLOCK_FREQ_HZ; | |
| 91 | ✗ | break; | |
| 92 | ✗ | case RtcDriver::ClockSource::UltraLowPowerOscillator: | |
| 93 | ✗ | state.clock_rate_hz = RtcDriver::INTERNAL_CLOCK_FREQ_HZ; | |
| 94 | ✗ | break; | |
| 95 | ✗ | default: | |
| 96 | ✗ | FW_ASSERT(0, static_cast<FwAssertArgType>(clk_source)); | |
| 97 | break; | ||
| 98 | } | ||
| 99 | |||
| 100 | // Configure GCLK and get the clock rate | ||
| 101 | ✗ | RtcHardware::RtcHal::configureGclk(clk_source); | |
| 102 | |||
| 103 | // Configure RTC peripheral | ||
| 104 | ✗ | RtcHardware::RtcHal::configureRtc(state.clock_rate_hz, rate); | |
| 105 | |||
| 106 | // Initialize state | ||
| 107 | ✗ | state.n_ticks = 0; | |
| 108 | ✗ | state.rate_hz = rate; | |
| 109 | ✗ | state.configured = true; | |
| 110 | ✗ | } | |
| 111 | |||
| 112 | ✗ | void RtcDriver ::enable() { | |
| 113 | ✗ | RtcHardware::RtcState& state = RtcHardware::getRtcState(); | |
| 114 | ✗ | FW_ASSERT(state.configured); | |
| 115 | |||
| 116 | // Enable the RTC peripheral | ||
| 117 | ✗ | RtcHardware::RtcHal::enableRtc(); | |
| 118 | ✗ | } | |
| 119 | |||
| 120 | ✗ | bool RtcDriver ::activeIn_handler(FwIndexType portNum, U32 context) { | |
| 121 | // Acknowledge the ISR on every cycle | ||
| 122 | ✗ | RtcHardware::RtcState& state = RtcHardware::getRtcState(); | |
| 123 | ✗ | state.deadline_reached = true; | |
| 124 | |||
| 125 | ✗ | if (state.wakeup_interrupt) { | |
| 126 | // Acknowledge the interrupt | ||
| 127 | ✗ | state.wakeup_interrupt = false; | |
| 128 | |||
| 129 | // The RTC woke us up, emit the signal | ||
| 130 | ✗ | Os::RawTime cycle_start; | |
| 131 | ✗ | auto status = cycle_start.now(); | |
| 132 | ✗ | FW_ASSERT(status == Os::RawTimeInterface::Status::OP_OK, status); | |
| 133 | |||
| 134 | ✗ | this->tlmWrite_CycleOverrun(state.deadline_exceeded); | |
| 135 | ✗ | this->CycleOut_out(0, cycle_start); | |
| 136 | |||
| 137 | // A cycle was emitted this tick, so work was done | ||
| 138 | ✗ | return true; | |
| 139 | ✗ | } | |
| 140 | |||
| 141 | // No pending wakeup interrupt: nothing to do this tick | ||
| 142 | ✗ | return false; | |
| 143 | } | ||
| 144 | |||
| 145 | ✗ | RtcDriver ::~RtcDriver() {} | |
| 146 | |||
| 147 | } // namespace Samd21 | ||
| 148 |