tinygo/src/machine/machine_avr.go
Dmitriy Zakharkin 50401c05e8 move AVR interrupt related code to runtime
move AVR interrupt related code to runtime
address formatting
add volatile to access counters
2022-01-20 15:07:16 +01:00

151 строка
5,2 КиБ
Go

//go:build avr
// +build avr
package machine
import (
"device/avr"
"runtime/volatile"
"unsafe"
)
const deviceName = avr.DEVICE
const (
PinInput PinMode = iota
PinInputPullup
PinOutput
)
// In all the AVRs I've looked at, the PIN/DDR/PORT registers followed a regular
// pattern: PINx, DDRx, PORTx in this order without registers in between.
// Therefore, if you know any of them, you can calculate the other two.
//
// For now, I've chosen to let the PORTx register be the one that is returned
// for each specific chip and to calculate the others from that one. Setting an
// output port (done using PORTx) is likely the most common operation and the
// one that is the most time critical. For others, the PINx and DDRx register
// can trivially be calculated using a subtraction.
// Configure sets the pin to input or output.
func (p Pin) Configure(config PinConfig) {
port, mask := p.getPortMask()
// The DDRx register can be found by subtracting one from the PORTx
// register, as this appears to be the case for many (most? all?) AVR chips.
ddr := (*volatile.Register8)(unsafe.Pointer(uintptr(unsafe.Pointer(port)) - 1))
if config.Mode == PinOutput {
// set output bit
ddr.SetBits(mask)
// Note: if the pin was PinInputPullup before, it'll now be high.
// Otherwise it will be low.
} else {
// configure input: clear output bit
ddr.ClearBits(mask)
if config.Mode == PinInput {
// No pullup (floating).
// The transition may be one of the following:
// output high -> input pullup -> input (safe: output high and input pullup are similar)
// output low -> input -> input (safe: no extra transition)
port.ClearBits(mask)
} else {
// Pullup.
// The transition may be one of the following:
// output high -> input pullup -> input pullup (safe: no extra transition)
// output low -> input -> input pullup (possibly problematic)
// For the last transition (output low -> input -> input pullup),
// the transition may be problematic in some cases because there is
// an intermediate floating state (which may cause irratic
// interrupts, for example). If this is a problem, the application
// should set the pin high before configuring it as PinInputPullup.
// We can't do that here because setting it to high as an
// intermediate state may have other problems.
port.SetBits(mask)
}
}
}
// Get returns the current value of a GPIO pin when the pin is configured as an
// input or as an output.
func (p Pin) Get() bool {
port, mask := p.getPortMask()
// As noted above, the PINx register is always two registers below the PORTx
// register, so we can find it simply by subtracting two from the PORTx
// register address.
pin := (*volatile.Register8)(unsafe.Pointer(uintptr(unsafe.Pointer(port)) - 2)) // PINA, PINB, etc
return (pin.Get() & mask) > 0
}
// Set changes the value of the GPIO pin. The pin must be configured as output.
func (p Pin) Set(value bool) {
if value { // set bits
port, mask := p.PortMaskSet()
port.Set(mask)
} else { // clear bits
port, mask := p.PortMaskClear()
port.Set(mask)
}
}
// Return the register and mask to enable a given GPIO pin. This can be used to
// implement bit-banged drivers.
//
// Warning: there are no separate pin set/clear registers on the AVR. The
// returned mask is only valid as long as no other pin in the same port has been
// changed.
func (p Pin) PortMaskSet() (*volatile.Register8, uint8) {
port, mask := p.getPortMask()
return port, port.Get() | mask
}
// Return the register and mask to disable a given port. This can be used to
// implement bit-banged drivers.
//
// Warning: there are no separate pin set/clear registers on the AVR. The
// returned mask is only valid as long as no other pin in the same port has been
// changed.
func (p Pin) PortMaskClear() (*volatile.Register8, uint8) {
port, mask := p.getPortMask()
return port, port.Get() &^ mask
}
// InitADC initializes the registers needed for ADC.
func InitADC() {
// set a2d prescaler so we are inside the desired 50-200 KHz range at 16MHz.
avr.ADCSRA.SetBits(avr.ADCSRA_ADPS2 | avr.ADCSRA_ADPS1 | avr.ADCSRA_ADPS0)
// enable a2d conversions
avr.ADCSRA.SetBits(avr.ADCSRA_ADEN)
}
// Configure configures a ADCPin to be able to be used to read data.
func (a ADC) Configure(ADCConfig) {
return // no pin specific setup on AVR machine.
}
// Get returns the current value of a ADC pin, in the range 0..0xffff. The AVR
// has an ADC of 10 bits precision so the lower 6 bits will be zero.
func (a ADC) Get() uint16 {
// set the analog reference (high two bits of ADMUX) and select the
// channel (low 4 bits), masked to only turn on one ADC at a time.
// set the ADLAR bit (left-adjusted result) to get a value scaled to 16
// bits. This has the same effect as shifting the return value left by 6
// bits.
avr.ADMUX.Set(avr.ADMUX_REFS0 | avr.ADMUX_ADLAR | (uint8(a.Pin) & 0x07))
// start the conversion
avr.ADCSRA.SetBits(avr.ADCSRA_ADSC)
// ADSC is cleared when the conversion finishes
for ok := true; ok; ok = avr.ADCSRA.HasBits(avr.ADCSRA_ADSC) {
}
return uint16(avr.ADCL.Get()) | uint16(avr.ADCH.Get())<<8
}
// linked from runtime.adjustMonotonicTimer
func adjustMonotonicTimer()
// linked from runtime.initMonotonicTimer
func initMonotonicTimer()