Interrupt based time. Adjust tick cost when timer-0 is reconfigured (the time precision affected when timer-0 reconfigured). Keep all time in nanoseconds.
Interrupt based time. Adjust tick cost every 1 minute and when timer-0 is reconfigured (the time precision affected when timer-0 reconfigured). Keep all time in nanoseconds.
Этот коммит содержится в:
родитель
d0633f6228
коммит
92150bd1c5
5 изменённых файлов: 141 добавлений и 17 удалений
16
src/device/avr/avr_tiny85.go
Обычный файл
16
src/device/avr/avr_tiny85.go
Обычный файл
|
@ -0,0 +1,16 @@
|
||||||
|
//go:build avr && attiny85
|
||||||
|
// +build avr,attiny85
|
||||||
|
|
||||||
|
package avr
|
||||||
|
|
||||||
|
// The attiny85 have only TIMSK when other AVR boards have TIMSK0 and TIMSK1, etc.
|
||||||
|
// Create an alias of TIMSK, TIMSK_OCIE0A, TIMSK_OCIE0B and TIMSK_TOIE0 so we can use
|
||||||
|
// common code to mask interrupts.
|
||||||
|
|
||||||
|
var TIMSK0 = TIMSK
|
||||||
|
|
||||||
|
const (
|
||||||
|
TIMSK0_OCIE0A = TIMSK_OCIE0A
|
||||||
|
TIMSK0_OCIE0B = TIMSK_OCIE0B
|
||||||
|
TIMSK0_TOIE0 = TIMSK_TOIE0
|
||||||
|
)
|
|
@ -179,6 +179,9 @@ func (pwm PWM) Configure(config PWMConfig) error {
|
||||||
avr.TCCR0B.Set(prescaler)
|
avr.TCCR0B.Set(prescaler)
|
||||||
// Set the PWM mode to fast PWM (mode = 3).
|
// Set the PWM mode to fast PWM (mode = 3).
|
||||||
avr.TCCR0A.Set(avr.TCCR0A_WGM00 | avr.TCCR0A_WGM01)
|
avr.TCCR0A.Set(avr.TCCR0A_WGM00 | avr.TCCR0A_WGM01)
|
||||||
|
// monotonic timer is using the same time as PWM:0
|
||||||
|
// we must adust internal settings of monotonic timer when PWM:0 settings changed
|
||||||
|
AdjustMonotonicTimer()
|
||||||
} else {
|
} else {
|
||||||
avr.TCCR2B.Set(prescaler)
|
avr.TCCR2B.Set(prescaler)
|
||||||
// Set the PWM mode to fast PWM (mode = 3).
|
// Set the PWM mode to fast PWM (mode = 3).
|
||||||
|
@ -714,6 +717,9 @@ func (pwm PWM) Set(channel uint8, value uint32) {
|
||||||
avr.TCCR0A.SetBits(avr.TCCR0A_COM0B1)
|
avr.TCCR0A.SetBits(avr.TCCR0A_COM0B1)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// monotonic timer is using the same time as PWM:0
|
||||||
|
// we must adust internal settings of monotonic timer when PWM:0 settings changed
|
||||||
|
AdjustMonotonicTimer()
|
||||||
case 1:
|
case 1:
|
||||||
mask := interrupt.Disable()
|
mask := interrupt.Disable()
|
||||||
switch channel {
|
switch channel {
|
||||||
|
|
|
@ -69,6 +69,9 @@ func (pwm PWM) Configure(config PWMConfig) error {
|
||||||
avr.TCCR0B.Set(prescaler)
|
avr.TCCR0B.Set(prescaler)
|
||||||
// Set the PWM mode to fast PWM (mode = 3).
|
// Set the PWM mode to fast PWM (mode = 3).
|
||||||
avr.TCCR0A.Set(avr.TCCR0A_WGM00 | avr.TCCR0A_WGM01)
|
avr.TCCR0A.Set(avr.TCCR0A_WGM00 | avr.TCCR0A_WGM01)
|
||||||
|
// monotonic timer is using the same time as PWM:0
|
||||||
|
// we must adust internal settings of monotonic timer when PWM:0 settings changed
|
||||||
|
AdjustMonotonicTimer()
|
||||||
} else {
|
} else {
|
||||||
avr.TCCR2B.Set(prescaler)
|
avr.TCCR2B.Set(prescaler)
|
||||||
// Set the PWM mode to fast PWM (mode = 3).
|
// Set the PWM mode to fast PWM (mode = 3).
|
||||||
|
@ -395,6 +398,9 @@ func (pwm PWM) Set(channel uint8, value uint32) {
|
||||||
avr.TCCR0A.SetBits(avr.TCCR0A_COM0B1)
|
avr.TCCR0A.SetBits(avr.TCCR0A_COM0B1)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// monotonic timer is using the same time as PWM:0
|
||||||
|
// we must adust internal settings of monotonic timer when PWM:0 settings changed
|
||||||
|
AdjustMonotonicTimer()
|
||||||
case 1:
|
case 1:
|
||||||
mask := interrupt.Disable()
|
mask := interrupt.Disable()
|
||||||
switch channel {
|
switch channel {
|
||||||
|
|
|
@ -4,6 +4,7 @@ package machine
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"device/avr"
|
"device/avr"
|
||||||
|
"runtime/interrupt"
|
||||||
"runtime/volatile"
|
"runtime/volatile"
|
||||||
"unsafe"
|
"unsafe"
|
||||||
)
|
)
|
||||||
|
@ -142,3 +143,76 @@ func (a ADC) Get() uint16 {
|
||||||
|
|
||||||
return uint16(avr.ADCL.Get()) | uint16(avr.ADCH.Get())<<8
|
return uint16(avr.ADCL.Get()) | uint16(avr.ADCH.Get())<<8
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var Ticks int64 // nanoseconds since start
|
||||||
|
var tickNanos int64 // nanoseconds per each tick
|
||||||
|
|
||||||
|
func InitMonotonicTimer() {
|
||||||
|
tickNanos = 0
|
||||||
|
Ticks = 0
|
||||||
|
|
||||||
|
interrupt.New(avr.IRQ_TIMER0_OVF, func(i interrupt.Interrupt) {
|
||||||
|
Ticks += tickNanos
|
||||||
|
})
|
||||||
|
|
||||||
|
// initial initialization of the Timer0
|
||||||
|
// - Mask interrupt
|
||||||
|
avr.TIMSK0.ClearBits(avr.TIMSK0_TOIE0 | avr.TIMSK0_OCIE0A | avr.TIMSK0_OCIE0B)
|
||||||
|
|
||||||
|
// - Write new values to TCNT2, OCR2x, and TCCR2x.
|
||||||
|
avr.TCNT0.Set(0)
|
||||||
|
avr.OCR0A.Set(0xff)
|
||||||
|
// - Set mode 3
|
||||||
|
avr.TCCR0A.Set(avr.TCCR0A_WGM00 | avr.TCCR0A_WGM01)
|
||||||
|
// - Set prescaler 1
|
||||||
|
avr.TCCR0B.Set(avr.TCCR0B_CS00)
|
||||||
|
|
||||||
|
AdjustMonotonicTimer()
|
||||||
|
|
||||||
|
// - Unmask interrupt
|
||||||
|
avr.TIMSK0.SetBits(avr.TIMSK0_TOIE0)
|
||||||
|
}
|
||||||
|
|
||||||
|
func AdjustMonotonicTimer() {
|
||||||
|
// adjust the tickNanos
|
||||||
|
tickNanos = currentTickNanos()
|
||||||
|
}
|
||||||
|
|
||||||
|
func currentTickNanos() int64 {
|
||||||
|
// this time depends on clk_IO, prescale, mode and OCR0A
|
||||||
|
// assuming the clock source is CPU clock
|
||||||
|
prescaler := int64(avr.TCCR0B.Get() & 0x7)
|
||||||
|
clock := (int64(1e12) / prescaler) / int64(CPUFrequency())
|
||||||
|
mode := avr.TCCR0A.Get() & 0x7
|
||||||
|
|
||||||
|
/*
|
||||||
|
Mode WGM02 WGM01 WGM00 Timer/Counter TOP Update of TOV Flag
|
||||||
|
Mode of Operation OCRx at Set on
|
||||||
|
0 0 0 0 Normal 0xFF Immediate MAX
|
||||||
|
1 0 0 1 PWM, Phase Correct 0xFF TOP BOTTOM
|
||||||
|
2 0 1 0 CTC OCRA Immediate MAX
|
||||||
|
3 0 1 1 Fast PWM 0xFF BOTTOM MAX
|
||||||
|
5 1 0 1 PWM, Phase Correct OCRA TOP BOTTOM
|
||||||
|
7 1 1 1 Fast PWM OCRA BOTTOM TOP
|
||||||
|
*/
|
||||||
|
switch mode {
|
||||||
|
case 0, 3:
|
||||||
|
// normal & fast PWM
|
||||||
|
// TOV0 Interrupt when moving from MAX (0xff) to 0x00
|
||||||
|
return clock * 256 / 1000
|
||||||
|
case 1:
|
||||||
|
// Phase Correct PWM
|
||||||
|
// TOV0 Interrupt when moving from MAX (0xff) to 0x00
|
||||||
|
return clock * 256 * 2 / 1000
|
||||||
|
case 2, 7:
|
||||||
|
// CTC & fast PWM
|
||||||
|
// TOV0 Interrupt when moving from MAX (OCRA) to 0x00
|
||||||
|
return clock * int64(avr.OCR0A.Get()) / 1000
|
||||||
|
case 5:
|
||||||
|
// Phase Correct PWM
|
||||||
|
// TOV0 Interrupt when moving from MAX (OCRA) to 0x00
|
||||||
|
return clock * int64(avr.OCR0A.Get()) * 2 / 1000
|
||||||
|
}
|
||||||
|
|
||||||
|
return clock / 1000 // for unknown
|
||||||
|
}
|
||||||
|
|
|
@ -4,14 +4,15 @@ package runtime
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"device/avr"
|
"device/avr"
|
||||||
|
"machine"
|
||||||
|
"runtime/interrupt"
|
||||||
"unsafe"
|
"unsafe"
|
||||||
)
|
)
|
||||||
|
|
||||||
const BOARD = "arduino"
|
const BOARD = "arduino"
|
||||||
|
|
||||||
type timeUnit uint32
|
// timeUnit in nanoseconds
|
||||||
|
type timeUnit int64
|
||||||
var currentTime timeUnit
|
|
||||||
|
|
||||||
// Watchdog timer periods. These can be off by a large margin (hence the jump
|
// Watchdog timer periods. These can be off by a large margin (hence the jump
|
||||||
// between 64ms and 125ms which is not an exact double), so don't rely on this
|
// between 64ms and 125ms which is not an exact double), so don't rely on this
|
||||||
|
@ -27,6 +28,10 @@ const (
|
||||||
WDT_PERIOD_2S
|
WDT_PERIOD_2S
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const timerRecalibrateInterval = 6e7 // 1 minute
|
||||||
|
|
||||||
|
var nextTimerRecalibrate timeUnit
|
||||||
|
|
||||||
//go:extern _sbss
|
//go:extern _sbss
|
||||||
var _sbss [0]byte
|
var _sbss [0]byte
|
||||||
|
|
||||||
|
@ -56,32 +61,49 @@ func postinit() {
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
initUART()
|
initUART()
|
||||||
|
machine.InitMonotonicTimer()
|
||||||
|
nextTimerRecalibrate = ticks() + timerRecalibrateInterval
|
||||||
}
|
}
|
||||||
|
|
||||||
const tickNanos = 1024 * 16384 // roughly 16ms in nanoseconds
|
|
||||||
|
|
||||||
func ticksToNanoseconds(ticks timeUnit) int64 {
|
func ticksToNanoseconds(ticks timeUnit) int64 {
|
||||||
return int64(ticks) * tickNanos
|
return int64(ticks)
|
||||||
}
|
}
|
||||||
|
|
||||||
func nanosecondsToTicks(ns int64) timeUnit {
|
func nanosecondsToTicks(ns int64) timeUnit {
|
||||||
return timeUnit(ns / tickNanos)
|
return timeUnit(ns)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Sleep this number of ticks of 16ms.
|
// Sleep this number of ticks of nanoseconds.
|
||||||
//
|
|
||||||
// TODO: not very accurate. Improve accuracy by calibrating on startup and every
|
|
||||||
// once in a while.
|
|
||||||
func sleepTicks(d timeUnit) {
|
func sleepTicks(d timeUnit) {
|
||||||
currentTime += d
|
waitTill := ticks() + d
|
||||||
for d != 0 {
|
// recalibrate if we have some time (>100ms) and it was a while when we did it last time.
|
||||||
sleepWDT(WDT_PERIOD_16MS)
|
if d > 100000 {
|
||||||
d -= 1
|
now := waitTill - d
|
||||||
|
if nextTimerRecalibrate < now {
|
||||||
|
nextTimerRecalibrate = now + timerRecalibrateInterval
|
||||||
|
machine.AdjustMonotonicTimer()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for {
|
||||||
|
// wait for interrupt
|
||||||
|
avr.Asm("sleep")
|
||||||
|
if waitTill <= ticks() {
|
||||||
|
// done waiting
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if hasScheduler {
|
||||||
|
// The interrupt may have awoken a goroutine, so bail out early.
|
||||||
|
return
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func ticks() timeUnit {
|
// ticks return time since start in nanoseconds
|
||||||
return currentTime
|
func ticks() (ticks timeUnit) {
|
||||||
|
state := interrupt.Disable()
|
||||||
|
ticks = timeUnit(machine.Ticks)
|
||||||
|
interrupt.Restore(state)
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func exit(code int) {
|
func exit(code int) {
|
||||||
|
|
Загрузка…
Создание таблицы
Сослаться в новой задаче