github.com/tinygo-org/tinygo@v0.31.3-0.20240404173401-90b0bf646c27/src/runtime/runtime_nrf52840.go (about) 1 //go:build nrf && nrf52840 2 3 package runtime 4 5 import ( 6 "device/arm" 7 "device/nrf" 8 "machine" 9 "machine/usb/cdc" 10 "runtime/interrupt" 11 "runtime/volatile" 12 ) 13 14 type timeUnit int64 15 16 //go:linkname systemInit SystemInit 17 func systemInit() 18 19 //export Reset_Handler 20 func main() { 21 if nrf.FPUPresent { 22 arm.SCB.CPACR.Set(0) // disable FPU if it is enabled 23 } 24 systemInit() 25 preinit() 26 run() 27 exit(0) 28 } 29 30 func init() { 31 cdc.EnableUSBCDC() 32 machine.USBDev.Configure(machine.UARTConfig{}) 33 machine.InitSerial() 34 initLFCLK() 35 initRTC() 36 } 37 38 func initLFCLK() { 39 if machine.HasLowFrequencyCrystal { 40 nrf.CLOCK.LFCLKSRC.Set(nrf.CLOCK_LFCLKSTAT_SRC_Xtal) 41 } 42 nrf.CLOCK.TASKS_LFCLKSTART.Set(1) 43 for nrf.CLOCK.EVENTS_LFCLKSTARTED.Get() == 0 { 44 } 45 nrf.CLOCK.EVENTS_LFCLKSTARTED.Set(0) 46 } 47 48 func initRTC() { 49 nrf.RTC1.TASKS_START.Set(1) 50 intr := interrupt.New(nrf.IRQ_RTC1, func(intr interrupt.Interrupt) { 51 if nrf.RTC1.EVENTS_COMPARE[0].Get() != 0 { 52 nrf.RTC1.EVENTS_COMPARE[0].Set(0) 53 nrf.RTC1.INTENCLR.Set(nrf.RTC_INTENSET_COMPARE0) 54 nrf.RTC1.EVENTS_COMPARE[0].Set(0) 55 rtc_wakeup.Set(1) 56 } 57 if nrf.RTC1.EVENTS_OVRFLW.Get() != 0 { 58 nrf.RTC1.EVENTS_OVRFLW.Set(0) 59 rtcOverflows.Set(rtcOverflows.Get() + 1) 60 } 61 }) 62 nrf.RTC1.INTENSET.Set(nrf.RTC_INTENSET_OVRFLW) 63 intr.SetPriority(0xc0) // low priority 64 intr.Enable() 65 } 66 67 func putchar(c byte) { 68 machine.Serial.WriteByte(c) 69 } 70 71 func getchar() byte { 72 for machine.Serial.Buffered() == 0 { 73 Gosched() 74 } 75 v, _ := machine.Serial.ReadByte() 76 return v 77 } 78 79 func buffered() int { 80 return machine.Serial.Buffered() 81 } 82 83 func sleepTicks(d timeUnit) { 84 for d != 0 { 85 ticks := uint32(d) & 0x7fffff // 23 bits (to be on the safe side) 86 rtc_sleep(ticks) 87 d -= timeUnit(ticks) 88 } 89 } 90 91 var rtcOverflows volatile.Register32 // number of times the RTC wrapped around 92 93 // ticksToNanoseconds converts RTC ticks (at 32768Hz) to nanoseconds. 94 func ticksToNanoseconds(ticks timeUnit) int64 { 95 // The following calculation is actually the following, but with both sides 96 // reduced to reduce the risk of overflow: 97 // ticks * 1e9 / 32768 98 return int64(ticks) * 1953125 / 64 99 } 100 101 // nanosecondsToTicks converts nanoseconds to RTC ticks (running at 32768Hz). 102 func nanosecondsToTicks(ns int64) timeUnit { 103 // The following calculation is actually the following, but with both sides 104 // reduced to reduce the risk of overflow: 105 // ns * 32768 / 1e9 106 return timeUnit(ns * 64 / 1953125) 107 } 108 109 // Monotonically increasing numer of ticks since start. 110 func ticks() timeUnit { 111 // For some ways of capturing the time atomically, see this thread: 112 // https://www.eevblog.com/forum/microcontrollers/correct-timing-by-timer-overflow-count/msg749617/#msg749617 113 // Here, instead of re-reading the counter register if an overflow has been 114 // detected, we simply try again because that results in (slightly) smaller 115 // code and is perhaps easier to prove correct. 116 for { 117 mask := interrupt.Disable() 118 counter := uint32(nrf.RTC1.COUNTER.Get()) 119 overflows := rtcOverflows.Get() 120 hasOverflow := nrf.RTC1.EVENTS_OVRFLW.Get() != 0 121 interrupt.Restore(mask) 122 123 if hasOverflow { 124 // There was an overflow. Try again. 125 continue 126 } 127 128 // The counter is 24 bits in size, so the number of overflows form the 129 // upper 32 bits (together 56 bits, which covers 71493 years at 130 // 32768kHz: I'd argue good enough for most purposes). 131 return timeUnit(overflows)<<24 + timeUnit(counter) 132 } 133 } 134 135 var rtc_wakeup volatile.Register8 136 137 func rtc_sleep(ticks uint32) { 138 nrf.RTC1.INTENSET.Set(nrf.RTC_INTENSET_COMPARE0) 139 rtc_wakeup.Set(0) 140 if ticks == 1 { 141 // Race condition (even in hardware) at ticks == 1. 142 // TODO: fix this in a better way by detecting it, like the manual 143 // describes. 144 ticks = 2 145 } 146 nrf.RTC1.CC[0].Set((nrf.RTC1.COUNTER.Get() + ticks) & 0x00ffffff) 147 for rtc_wakeup.Get() == 0 { 148 waitForEvents() 149 } 150 }