rp2040: rtc delayed interrupt
Этот коммит содержится в:
		
							родитель
							
								
									1065f06e57
								
							
						
					
					
						коммит
						4d0dfbd6fd
					
				
					 3 изменённых файлов: 277 добавлений и 0 удалений
				
			
		
							
								
								
									
										2
									
								
								Makefile
									
										
									
									
									
								
							
							
						
						
									
										2
									
								
								Makefile
									
										
									
									
									
								
							|  | @ -474,6 +474,8 @@ smoketest: | |||
| 	@$(MD5SUM) test.hex | ||||
| 	$(TINYGO) build -size short -o test.hex -target=pca10040            examples/pininterrupt | ||||
| 	@$(MD5SUM) test.hex | ||||
| 	$(TINYGO) build -size short -o test.hex -target=nano-rp2040         examples/rtcinterrupt | ||||
| 	@$(MD5SUM) test.hex | ||||
| 	$(TINYGO) build -size short -o test.hex -target=pca10040            examples/serial | ||||
| 	@$(MD5SUM) test.hex | ||||
| 	$(TINYGO) build -size short -o test.hex -target=pca10040            examples/systick | ||||
|  |  | |||
							
								
								
									
										35
									
								
								src/examples/rtcinterrupt/rtcinterrupt.go
									
										
									
									
									
										Обычный файл
									
								
							
							
						
						
									
										35
									
								
								src/examples/rtcinterrupt/rtcinterrupt.go
									
										
									
									
									
										Обычный файл
									
								
							|  | @ -0,0 +1,35 @@ | |||
| //go:build rp2040 | ||||
| 
 | ||||
| package main | ||||
| 
 | ||||
| // This example demonstrates scheduling a delayed interrupt by real time clock. | ||||
| // | ||||
| // An interrupt may execute user callback function or used for its side effects | ||||
| // like waking up from sleep or dormant states. | ||||
| // | ||||
| // The interrupt can be configured to repeat. | ||||
| // | ||||
| // There is no separate method to disable interrupt, use 0 delay for that. | ||||
| // | ||||
| // Unfortunately, it is not possible to use time.Duration to work with RTC directly, | ||||
| // that would introduce a circular dependency between "machine" and "time" packages. | ||||
| 
 | ||||
| import ( | ||||
| 	"fmt" | ||||
| 	"machine" | ||||
| 	"time" | ||||
| ) | ||||
| 
 | ||||
| func main() { | ||||
| 
 | ||||
| 	// Schedule and enable recurring interrupt. | ||||
| 	// The callback function is executed in the context of an interrupt handler, | ||||
| 	// so regular restructions for this sort of code apply: no blocking, no memory allocation, etc. | ||||
| 	delay := time.Minute + 12*time.Second | ||||
| 	machine.RTC.SetInterrupt(uint32(delay.Seconds()), true, func() { println("Peekaboo!") }) | ||||
| 
 | ||||
| 	for { | ||||
| 		fmt.Printf("%v\r\n", time.Now().Format(time.RFC3339)) | ||||
| 		time.Sleep(1 * time.Second) | ||||
| 	} | ||||
| } | ||||
							
								
								
									
										240
									
								
								src/machine/machine_rp2040_rtc.go
									
										
									
									
									
										Обычный файл
									
								
							
							
						
						
									
										240
									
								
								src/machine/machine_rp2040_rtc.go
									
										
									
									
									
										Обычный файл
									
								
							|  | @ -0,0 +1,240 @@ | |||
| //go:build rp2040 | ||||
| 
 | ||||
| // Implementation based on code located here: | ||||
| // https://github.com/raspberrypi/pico-sdk/blob/master/src/rp2_common/hardware_rtc/rtc.c | ||||
| 
 | ||||
| package machine | ||||
| 
 | ||||
| import ( | ||||
| 	"device/rp" | ||||
| 	"errors" | ||||
| 	"runtime/interrupt" | ||||
| 	"unsafe" | ||||
| ) | ||||
| 
 | ||||
| type rtcType rp.RTC_Type | ||||
| 
 | ||||
| type rtcTime struct { | ||||
| 	Year  int16 | ||||
| 	Month int8 | ||||
| 	Day   int8 | ||||
| 	Dotw  int8 | ||||
| 	Hour  int8 | ||||
| 	Min   int8 | ||||
| 	Sec   int8 | ||||
| } | ||||
| 
 | ||||
| var RTC = (*rtcType)(unsafe.Pointer(rp.RTC)) | ||||
| 
 | ||||
| const ( | ||||
| 	second = 1 | ||||
| 	minute = 60 * second | ||||
| 	hour   = 60 * minute | ||||
| 	day    = 24 * hour | ||||
| ) | ||||
| 
 | ||||
| var ( | ||||
| 	rtcAlarmRepeats bool | ||||
| 	rtcCallback     func() | ||||
| 	rtcEpoch        = rtcTime{ | ||||
| 		Year: 1970, Month: 1, Day: 1, Dotw: 4, Hour: 0, Min: 0, Sec: 0, | ||||
| 	} | ||||
| ) | ||||
| 
 | ||||
| var ( | ||||
| 	ErrRtcDelayTooSmall = errors.New("RTC interrupt deplay is too small, shall be at least 1 second") | ||||
| 	ErrRtcDelayTooLarge = errors.New("RTC interrupt deplay is too large, shall be no more than 1 day") | ||||
| ) | ||||
| 
 | ||||
| // SetInterrupt configures delayed and optionally recurring interrupt by real time clock. | ||||
| // | ||||
| // Delay is specified in whole seconds, allowed range depends on platform. | ||||
| // Zero delay disables previously configured interrupt, if any. | ||||
| // | ||||
| // RP2040 implementation allows delay to be up to 1 day, otherwise a respective error is emitted. | ||||
| func (rtc *rtcType) SetInterrupt(delay uint32, repeat bool, callback func()) error { | ||||
| 
 | ||||
| 	// Verify delay range | ||||
| 	if delay > day { | ||||
| 		return ErrRtcDelayTooLarge | ||||
| 	} | ||||
| 
 | ||||
| 	// De-configure delayed interrupt if delay is zero | ||||
| 	if delay == 0 { | ||||
| 		rtc.disableInterruptMatch() | ||||
| 		return nil | ||||
| 	} | ||||
| 
 | ||||
| 	// Configure delayed interrupt | ||||
| 	rtc.setDivider() | ||||
| 
 | ||||
| 	rtcAlarmRepeats = repeat | ||||
| 	rtcCallback = callback | ||||
| 
 | ||||
| 	err := rtc.setTime(rtcEpoch) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	rtc.setAlarm(toAlarmTime(delay), callback) | ||||
| 
 | ||||
| 	return nil | ||||
| } | ||||
| 
 | ||||
| func toAlarmTime(delay uint32) rtcTime { | ||||
| 	result := rtcEpoch | ||||
| 	remainder := delay + 1 // needed "+1", otherwise alarm fires one second too early | ||||
| 	if remainder >= hour { | ||||
| 		result.Hour = int8(remainder / hour) | ||||
| 		remainder %= hour | ||||
| 	} | ||||
| 	if remainder >= minute { | ||||
| 		result.Min = int8(remainder / minute) | ||||
| 		remainder %= minute | ||||
| 	} | ||||
| 	result.Sec = int8(remainder) | ||||
| 	return result | ||||
| } | ||||
| 
 | ||||
| func (rtc *rtcType) setDivider() { | ||||
| 	// Get clk_rtc freq and make sure it is running | ||||
| 	rtcFreq := configuredFreq[clkRTC] | ||||
| 	if rtcFreq == 0 { | ||||
| 		panic("can not set RTC divider, clock is not running") | ||||
| 	} | ||||
| 
 | ||||
| 	// Take rtc out of reset now that we know clk_rtc is running | ||||
| 	resetBlock(rp.RESETS_RESET_RTC) | ||||
| 	unresetBlockWait(rp.RESETS_RESET_RTC) | ||||
| 
 | ||||
| 	// Set up the 1 second divider. | ||||
| 	// If rtc_freq is 400 then clkdiv_m1 should be 399 | ||||
| 	rtcFreq -= 1 | ||||
| 
 | ||||
| 	// Check the freq is not too big to divide | ||||
| 	if rtcFreq > rp.RTC_CLKDIV_M1_CLKDIV_M1_Msk { | ||||
| 		panic("can not set RTC divider, clock frequency is too big to divide") | ||||
| 	} | ||||
| 
 | ||||
| 	// Write divide value | ||||
| 	rtc.CLKDIV_M1.Set(rtcFreq) | ||||
| } | ||||
| 
 | ||||
| // setTime configures RTC with supplied time, initialises and activates it. | ||||
| func (rtc *rtcType) setTime(t rtcTime) error { | ||||
| 
 | ||||
| 	// Disable RTC and wait while it is still running | ||||
| 	rtc.CTRL.Set(0) | ||||
| 	for rtc.isActive() { | ||||
| 	} | ||||
| 
 | ||||
| 	rtc.SETUP_0.Set((uint32(t.Year) << rp.RTC_SETUP_0_YEAR_Pos) | | ||||
| 		(uint32(t.Month) << rp.RTC_SETUP_0_MONTH_Pos) | | ||||
| 		(uint32(t.Day) << rp.RTC_SETUP_0_DAY_Pos)) | ||||
| 
 | ||||
| 	rtc.SETUP_1.Set((uint32(t.Dotw) << rp.RTC_SETUP_1_DOTW_Pos) | | ||||
| 		(uint32(t.Hour) << rp.RTC_SETUP_1_HOUR_Pos) | | ||||
| 		(uint32(t.Min) << rp.RTC_SETUP_1_MIN_Pos) | | ||||
| 		(uint32(t.Sec) << rp.RTC_SETUP_1_SEC_Pos)) | ||||
| 
 | ||||
| 	// Load setup values into RTC clock domain | ||||
| 	rtc.CTRL.SetBits(rp.RTC_CTRL_LOAD) | ||||
| 
 | ||||
| 	// Enable RTC and wait for it to be running | ||||
| 	rtc.CTRL.SetBits(rp.RTC_CTRL_RTC_ENABLE) | ||||
| 	for !rtc.isActive() { | ||||
| 	} | ||||
| 
 | ||||
| 	return nil | ||||
| } | ||||
| 
 | ||||
| func (rtc *rtcType) isActive() bool { | ||||
| 	return rtc.CTRL.HasBits(rp.RTC_CTRL_RTC_ACTIVE) | ||||
| } | ||||
| 
 | ||||
| // setAlarm configures alarm in RTC and arms it. | ||||
| // The callback is executed in the context of an interrupt handler, | ||||
| // so regular restructions for this sort of code apply: no blocking, no memory allocation, etc. | ||||
| func (rtc *rtcType) setAlarm(t rtcTime, callback func()) { | ||||
| 
 | ||||
| 	rtc.disableInterruptMatch() | ||||
| 
 | ||||
| 	// Clear all match enable bits | ||||
| 	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) | ||||
| 	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) | ||||
| 
 | ||||
| 	// Only add to setup if it isn't -1 and set the match enable bits for things we care about | ||||
| 	if t.Year >= 0 { | ||||
| 		rtc.IRQ_SETUP_0.SetBits(uint32(t.Year) << rp.RTC_SETUP_0_YEAR_Pos) | ||||
| 		rtc.IRQ_SETUP_0.SetBits(rp.RTC_IRQ_SETUP_0_YEAR_ENA) | ||||
| 	} | ||||
| 
 | ||||
| 	if t.Month >= 0 { | ||||
| 		rtc.IRQ_SETUP_0.SetBits(uint32(t.Month) << rp.RTC_SETUP_0_MONTH_Pos) | ||||
| 		rtc.IRQ_SETUP_0.SetBits(rp.RTC_IRQ_SETUP_0_MONTH_ENA) | ||||
| 	} | ||||
| 
 | ||||
| 	if t.Day >= 0 { | ||||
| 		rtc.IRQ_SETUP_0.SetBits(uint32(t.Day) << rp.RTC_SETUP_0_DAY_Pos) | ||||
| 		rtc.IRQ_SETUP_0.SetBits(rp.RTC_IRQ_SETUP_0_DAY_ENA) | ||||
| 	} | ||||
| 
 | ||||
| 	if t.Dotw >= 0 { | ||||
| 		rtc.IRQ_SETUP_1.SetBits(uint32(t.Dotw) << rp.RTC_SETUP_1_DOTW_Pos) | ||||
| 		rtc.IRQ_SETUP_1.SetBits(rp.RTC_IRQ_SETUP_1_DOTW_ENA) | ||||
| 	} | ||||
| 
 | ||||
| 	if t.Hour >= 0 { | ||||
| 		rtc.IRQ_SETUP_1.SetBits(uint32(t.Hour) << rp.RTC_SETUP_1_HOUR_Pos) | ||||
| 		rtc.IRQ_SETUP_1.SetBits(rp.RTC_IRQ_SETUP_1_HOUR_ENA) | ||||
| 	} | ||||
| 
 | ||||
| 	if t.Min >= 0 { | ||||
| 		rtc.IRQ_SETUP_1.SetBits(uint32(t.Min) << rp.RTC_SETUP_1_MIN_Pos) | ||||
| 		rtc.IRQ_SETUP_1.SetBits(rp.RTC_IRQ_SETUP_1_MIN_ENA) | ||||
| 	} | ||||
| 
 | ||||
| 	if t.Sec >= 0 { | ||||
| 		rtc.IRQ_SETUP_1.SetBits(uint32(t.Sec) << rp.RTC_SETUP_1_SEC_Pos) | ||||
| 		rtc.IRQ_SETUP_1.SetBits(rp.RTC_IRQ_SETUP_1_SEC_ENA) | ||||
| 	} | ||||
| 
 | ||||
| 	// Enable the IRQ at the proc | ||||
| 	interrupt.New(rp.IRQ_RTC_IRQ, rtcHandleInterrupt).Enable() | ||||
| 
 | ||||
| 	// Enable the IRQ at the peri | ||||
| 	rtc.INTE.Set(rp.RTC_INTE_RTC) | ||||
| 
 | ||||
| 	rtc.enableInterruptMatch() | ||||
| } | ||||
| 
 | ||||
| func (rtc *rtcType) enableInterruptMatch() { | ||||
| 	// Set matching and wait for it to be enabled | ||||
| 	rtc.IRQ_SETUP_0.SetBits(rp.RTC_IRQ_SETUP_0_MATCH_ENA) | ||||
| 	for !rtc.IRQ_SETUP_0.HasBits(rp.RTC_IRQ_SETUP_0_MATCH_ACTIVE) { | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| func (rtc *rtcType) disableInterruptMatch() { | ||||
| 	// Disable matching and wait for it to stop being active | ||||
| 	rtc.IRQ_SETUP_0.ClearBits(rp.RTC_IRQ_SETUP_0_MATCH_ENA) | ||||
| 	for rtc.IRQ_SETUP_0.HasBits(rp.RTC_IRQ_SETUP_0_MATCH_ACTIVE) { | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| func rtcHandleInterrupt(itr interrupt.Interrupt) { | ||||
| 	// Always disable the alarm to clear the current IRQ. | ||||
| 	// Even if it is a repeatable alarm, we don't want it to keep firing. | ||||
| 	// If it matches on a second it can keep firing for that second. | ||||
| 	RTC.disableInterruptMatch() | ||||
| 
 | ||||
| 	// Call user callback function | ||||
| 	if rtcCallback != nil { | ||||
| 		rtcCallback() | ||||
| 	} | ||||
| 
 | ||||
| 	if rtcAlarmRepeats { | ||||
| 		// If it is a repeatable alarm, reset time and re-enable the alarm. | ||||
| 		RTC.setTime(rtcEpoch) | ||||
| 		RTC.enableInterruptMatch() | ||||
| 	} | ||||
| } | ||||
		Загрузка…
	
	Создание таблицы
		
		Сослаться в новой задаче
	
	 Yurii Soldak
						Yurii Soldak