avr: unify GPIO pin/port code

All the AVRs that I've looked at had the same pin/port structure, with
the possible states being input/floating, input/pullup, low, and high
(with the same PORT/DDR registers). The main difference is the number of
available ports and pins. To reduce the amount of code and avoid
duplication (and thus errors) I decided to centralize this, following
the design used by the atmega2560 but while using a trick to save
tracking a few registers.

In the process, I noticed that the Pin.Get() function was incorrect on
the atmega2560 implementation. It is now fixed in the unified code.
This commit is contained in:
Ayke van Laethem
2020-05-14 23:18:15 +02:00
committed by Ron Evans
parent 424d775bf4
commit da505a6b17
5 changed files with 111 additions and 99 deletions
+36
View File
@@ -5,6 +5,7 @@ package machine
import (
"device/avr"
"runtime/volatile"
"unsafe"
)
type PinMode uint8
@@ -14,6 +15,41 @@ const (
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)
} else {
// configure input: clear output bit
ddr.ClearBits(mask)
}
}
// Get returns the current value of a GPIO pin.
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