github.com/tinygo-org/tinygo@v0.31.3-0.20240404173401-90b0bf646c27/src/machine/machine_rp2040_rtc.go (about) 1 //go:build rp2040 2 3 // Implementation based on code located here: 4 // https://github.com/raspberrypi/pico-sdk/blob/master/src/rp2_common/hardware_rtc/rtc.c 5 6 package machine 7 8 import ( 9 "device/rp" 10 "errors" 11 "runtime/interrupt" 12 "unsafe" 13 ) 14 15 type rtcType rp.RTC_Type 16 17 type rtcTime struct { 18 Year int16 19 Month int8 20 Day int8 21 Dotw int8 22 Hour int8 23 Min int8 24 Sec int8 25 } 26 27 var RTC = (*rtcType)(unsafe.Pointer(rp.RTC)) 28 29 const ( 30 second = 1 31 minute = 60 * second 32 hour = 60 * minute 33 day = 24 * hour 34 ) 35 36 var ( 37 rtcAlarmRepeats bool 38 rtcCallback func() 39 rtcEpoch = rtcTime{ 40 Year: 1970, Month: 1, Day: 1, Dotw: 4, Hour: 0, Min: 0, Sec: 0, 41 } 42 ) 43 44 var ( 45 ErrRtcDelayTooSmall = errors.New("RTC interrupt deplay is too small, shall be at least 1 second") 46 ErrRtcDelayTooLarge = errors.New("RTC interrupt deplay is too large, shall be no more than 1 day") 47 ) 48 49 // SetInterrupt configures delayed and optionally recurring interrupt by real time clock. 50 // 51 // Delay is specified in whole seconds, allowed range depends on platform. 52 // Zero delay disables previously configured interrupt, if any. 53 // 54 // RP2040 implementation allows delay to be up to 1 day, otherwise a respective error is emitted. 55 func (rtc *rtcType) SetInterrupt(delay uint32, repeat bool, callback func()) error { 56 57 // Verify delay range 58 if delay > day { 59 return ErrRtcDelayTooLarge 60 } 61 62 // De-configure delayed interrupt if delay is zero 63 if delay == 0 { 64 rtc.disableInterruptMatch() 65 return nil 66 } 67 68 // Configure delayed interrupt 69 rtc.setDivider() 70 71 rtcAlarmRepeats = repeat 72 rtcCallback = callback 73 74 err := rtc.setTime(rtcEpoch) 75 if err != nil { 76 return err 77 } 78 rtc.setAlarm(toAlarmTime(delay), callback) 79 80 return nil 81 } 82 83 func toAlarmTime(delay uint32) rtcTime { 84 result := rtcEpoch 85 remainder := delay + 1 // needed "+1", otherwise alarm fires one second too early 86 if remainder >= hour { 87 result.Hour = int8(remainder / hour) 88 remainder %= hour 89 } 90 if remainder >= minute { 91 result.Min = int8(remainder / minute) 92 remainder %= minute 93 } 94 result.Sec = int8(remainder) 95 return result 96 } 97 98 func (rtc *rtcType) setDivider() { 99 // Get clk_rtc freq and make sure it is running 100 rtcFreq := configuredFreq[clkRTC] 101 if rtcFreq == 0 { 102 panic("can not set RTC divider, clock is not running") 103 } 104 105 // Take rtc out of reset now that we know clk_rtc is running 106 resetBlock(rp.RESETS_RESET_RTC) 107 unresetBlockWait(rp.RESETS_RESET_RTC) 108 109 // Set up the 1 second divider. 110 // If rtc_freq is 400 then clkdiv_m1 should be 399 111 rtcFreq -= 1 112 113 // Check the freq is not too big to divide 114 if rtcFreq > rp.RTC_CLKDIV_M1_CLKDIV_M1_Msk { 115 panic("can not set RTC divider, clock frequency is too big to divide") 116 } 117 118 // Write divide value 119 rtc.CLKDIV_M1.Set(rtcFreq) 120 } 121 122 // setTime configures RTC with supplied time, initialises and activates it. 123 func (rtc *rtcType) setTime(t rtcTime) error { 124 125 // Disable RTC and wait while it is still running 126 rtc.CTRL.Set(0) 127 for rtc.isActive() { 128 } 129 130 rtc.SETUP_0.Set((uint32(t.Year) << rp.RTC_SETUP_0_YEAR_Pos) | 131 (uint32(t.Month) << rp.RTC_SETUP_0_MONTH_Pos) | 132 (uint32(t.Day) << rp.RTC_SETUP_0_DAY_Pos)) 133 134 rtc.SETUP_1.Set((uint32(t.Dotw) << rp.RTC_SETUP_1_DOTW_Pos) | 135 (uint32(t.Hour) << rp.RTC_SETUP_1_HOUR_Pos) | 136 (uint32(t.Min) << rp.RTC_SETUP_1_MIN_Pos) | 137 (uint32(t.Sec) << rp.RTC_SETUP_1_SEC_Pos)) 138 139 // Load setup values into RTC clock domain 140 rtc.CTRL.SetBits(rp.RTC_CTRL_LOAD) 141 142 // Enable RTC and wait for it to be running 143 rtc.CTRL.SetBits(rp.RTC_CTRL_RTC_ENABLE) 144 for !rtc.isActive() { 145 } 146 147 return nil 148 } 149 150 func (rtc *rtcType) isActive() bool { 151 return rtc.CTRL.HasBits(rp.RTC_CTRL_RTC_ACTIVE) 152 } 153 154 // setAlarm configures alarm in RTC and arms it. 155 // The callback is executed in the context of an interrupt handler, 156 // so regular restructions for this sort of code apply: no blocking, no memory allocation, etc. 157 func (rtc *rtcType) setAlarm(t rtcTime, callback func()) { 158 159 rtc.disableInterruptMatch() 160 161 // Clear all match enable bits 162 rtc.IRQ_SETUP_0.ClearBits(rp.RTC_IRQ_SETUP_0_YEAR_ENA | rp.RTC_IRQ_SETUP_0_MONTH_ENA | rp.RTC_IRQ_SETUP_0_DAY_ENA) 163 rtc.IRQ_SETUP_1.ClearBits(rp.RTC_IRQ_SETUP_1_DOTW_ENA | rp.RTC_IRQ_SETUP_1_HOUR_ENA | rp.RTC_IRQ_SETUP_1_MIN_ENA | rp.RTC_IRQ_SETUP_1_SEC_ENA) 164 165 // Only add to setup if it isn't -1 and set the match enable bits for things we care about 166 if t.Year >= 0 { 167 rtc.IRQ_SETUP_0.SetBits(uint32(t.Year) << rp.RTC_SETUP_0_YEAR_Pos) 168 rtc.IRQ_SETUP_0.SetBits(rp.RTC_IRQ_SETUP_0_YEAR_ENA) 169 } 170 171 if t.Month >= 0 { 172 rtc.IRQ_SETUP_0.SetBits(uint32(t.Month) << rp.RTC_SETUP_0_MONTH_Pos) 173 rtc.IRQ_SETUP_0.SetBits(rp.RTC_IRQ_SETUP_0_MONTH_ENA) 174 } 175 176 if t.Day >= 0 { 177 rtc.IRQ_SETUP_0.SetBits(uint32(t.Day) << rp.RTC_SETUP_0_DAY_Pos) 178 rtc.IRQ_SETUP_0.SetBits(rp.RTC_IRQ_SETUP_0_DAY_ENA) 179 } 180 181 if t.Dotw >= 0 { 182 rtc.IRQ_SETUP_1.SetBits(uint32(t.Dotw) << rp.RTC_SETUP_1_DOTW_Pos) 183 rtc.IRQ_SETUP_1.SetBits(rp.RTC_IRQ_SETUP_1_DOTW_ENA) 184 } 185 186 if t.Hour >= 0 { 187 rtc.IRQ_SETUP_1.SetBits(uint32(t.Hour) << rp.RTC_SETUP_1_HOUR_Pos) 188 rtc.IRQ_SETUP_1.SetBits(rp.RTC_IRQ_SETUP_1_HOUR_ENA) 189 } 190 191 if t.Min >= 0 { 192 rtc.IRQ_SETUP_1.SetBits(uint32(t.Min) << rp.RTC_SETUP_1_MIN_Pos) 193 rtc.IRQ_SETUP_1.SetBits(rp.RTC_IRQ_SETUP_1_MIN_ENA) 194 } 195 196 if t.Sec >= 0 { 197 rtc.IRQ_SETUP_1.SetBits(uint32(t.Sec) << rp.RTC_SETUP_1_SEC_Pos) 198 rtc.IRQ_SETUP_1.SetBits(rp.RTC_IRQ_SETUP_1_SEC_ENA) 199 } 200 201 // Enable the IRQ at the proc 202 interrupt.New(rp.IRQ_RTC_IRQ, rtcHandleInterrupt).Enable() 203 204 // Enable the IRQ at the peri 205 rtc.INTE.Set(rp.RTC_INTE_RTC) 206 207 rtc.enableInterruptMatch() 208 } 209 210 func (rtc *rtcType) enableInterruptMatch() { 211 // Set matching and wait for it to be enabled 212 rtc.IRQ_SETUP_0.SetBits(rp.RTC_IRQ_SETUP_0_MATCH_ENA) 213 for !rtc.IRQ_SETUP_0.HasBits(rp.RTC_IRQ_SETUP_0_MATCH_ACTIVE) { 214 } 215 } 216 217 func (rtc *rtcType) disableInterruptMatch() { 218 // Disable matching and wait for it to stop being active 219 rtc.IRQ_SETUP_0.ClearBits(rp.RTC_IRQ_SETUP_0_MATCH_ENA) 220 for rtc.IRQ_SETUP_0.HasBits(rp.RTC_IRQ_SETUP_0_MATCH_ACTIVE) { 221 } 222 } 223 224 func rtcHandleInterrupt(itr interrupt.Interrupt) { 225 // Always disable the alarm to clear the current IRQ. 226 // Even if it is a repeatable alarm, we don't want it to keep firing. 227 // If it matches on a second it can keep firing for that second. 228 RTC.disableInterruptMatch() 229 230 // Call user callback function 231 if rtcCallback != nil { 232 rtcCallback() 233 } 234 235 if rtcAlarmRepeats { 236 // If it is a repeatable alarm, reset time and re-enable the alarm. 237 RTC.setTime(rtcEpoch) 238 RTC.enableInterruptMatch() 239 } 240 }