Skip to content

Commit da505a6

Browse files
aykevldeadprogram
authored andcommitted
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.
1 parent 424d775 commit da505a6

File tree

5 files changed

+111
-99
lines changed

5 files changed

+111
-99
lines changed

src/machine/machine_atmega1284p.go

Lines changed: 52 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -14,12 +14,58 @@ func CPUFrequency() uint32 {
1414
return 20000000
1515
}
1616

17+
const (
18+
portA Pin = iota * 8
19+
portB
20+
portC
21+
portD
22+
)
23+
24+
const (
25+
PA0 = portA + 0
26+
PA1 = portA + 1
27+
PA2 = portA + 2
28+
PA3 = portA + 3
29+
PA4 = portA + 4
30+
PA5 = portA + 5
31+
PA6 = portA + 6
32+
PA7 = portA + 7
33+
PB0 = portB + 0
34+
PB1 = portB + 1
35+
PB2 = portB + 2
36+
PB3 = portB + 3
37+
PB4 = portB + 4
38+
PB5 = portB + 5
39+
PB6 = portB + 6
40+
PB7 = portB + 7
41+
PC0 = portC + 0
42+
PC1 = portC + 1
43+
PC2 = portC + 2
44+
PC3 = portC + 3
45+
PC4 = portC + 4
46+
PC5 = portC + 5
47+
PC6 = portC + 6
48+
PC7 = portC + 7
49+
PD0 = portD + 0
50+
PD1 = portD + 1
51+
PD2 = portD + 2
52+
PD3 = portD + 3
53+
PD4 = portD + 4
54+
PD5 = portD + 5
55+
PD6 = portD + 6
56+
PD7 = portD + 7
57+
)
58+
59+
// getPortMask returns the PORTx register and mask for the pin.
1760
func (p Pin) getPortMask() (*volatile.Register8, uint8) {
18-
if p < 8 {
19-
return avr.PORTD, 1 << uint8(p)
20-
} else if p < 14 {
21-
return avr.PORTB, 1 << uint8(p-8)
22-
} else {
23-
return avr.PORTC, 1 << uint8(p-14)
61+
switch {
62+
case p >= PA0 && p <= PA7:
63+
return avr.PORTA, 1 << uint8(p-portA)
64+
case p >= PB0 && p <= PB7:
65+
return avr.PORTB, 1 << uint8(p-portB)
66+
case p >= PC0 && p <= PC7:
67+
return avr.PORTC, 1 << uint8(p-portC)
68+
default:
69+
return avr.PORTD, 1 << uint8(p-portD)
2470
}
2571
}

src/machine/machine_atmega2560.go

Lines changed: 13 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -96,53 +96,32 @@ const (
9696
PL7 = portE + 7
9797
)
9898

99-
// Configure sets the pin to input or output.
100-
func (p Pin) Configure(config PinConfig) {
101-
register, _, mask := p.getRegisterPortMask()
102-
if config.Mode == PinOutput { // set output bit
103-
register.SetBits(mask)
104-
} else { // configure input: clear output bit
105-
register.ClearBits(mask)
106-
}
107-
}
108-
109-
// Get returns the current value of a GPIO pin.
110-
func (p Pin) Get() bool {
111-
_, port, mask := p.getRegisterPortMask()
112-
return (port.Get() & mask) > 0
113-
}
114-
99+
// getPortMask returns the PORTx register and mask for the pin.
115100
func (p Pin) getPortMask() (*volatile.Register8, uint8) {
116-
_, port, mask := p.getRegisterPortMask()
117-
return port, mask
118-
}
119-
120-
// getRegisterPortMask returns the register, port, and mask for the pin
121-
func (p Pin) getRegisterPortMask() (*volatile.Register8, *volatile.Register8, uint8) {
122101
switch {
123102
case p >= PA0 && p <= PA7:
124-
return avr.DDRA, avr.PORTA, 1 << uint8(p-portA)
103+
return avr.PORTA, 1 << uint8(p-portA)
125104
case p >= PB0 && p <= PB7:
126-
return avr.DDRB, avr.PORTB, 1 << uint8(p-portB)
105+
return avr.PORTB, 1 << uint8(p-portB)
127106
case p >= PC0 && p <= PC7:
128-
return avr.DDRC, avr.PORTC, 1 << uint8(p-portC)
107+
return avr.PORTC, 1 << uint8(p-portC)
129108
case p >= PD0 && p <= PD7:
130-
return avr.DDRD, avr.PORTD, 1 << uint8(p-portD)
109+
return avr.PORTD, 1 << uint8(p-portD)
131110
case p >= PE0 && p <= PE6:
132-
return avr.DDRE, avr.PORTE, 1 << uint8(p-portE)
111+
return avr.PORTE, 1 << uint8(p-portE)
133112
case p >= PF0 && p <= PF7:
134-
return avr.DDRF, avr.PORTF, 1 << uint8(p-portF)
113+
return avr.PORTF, 1 << uint8(p-portF)
135114
case p >= PG0 && p <= PG5:
136-
return avr.DDRG, avr.PORTG, 1 << uint8(p-portG)
115+
return avr.PORTG, 1 << uint8(p-portG)
137116
case p >= PH0 && p <= PH6:
138-
return avr.DDRH, avr.PORTH, 1 << uint8(p-portH)
117+
return avr.PORTH, 1 << uint8(p-portH)
139118
case p >= PJ0 && p <= PJ1:
140-
return avr.DDRJ, avr.PORTJ, 1 << uint8(p-portJ)
119+
return avr.PORTJ, 1 << uint8(p-portJ)
141120
case p >= PK0 && p <= PK7:
142-
return avr.DDRK, avr.PORTK, 1 << uint8(p-portK)
121+
return avr.PORTK, 1 << uint8(p-portK)
143122
case p >= PL0 && p <= PL7:
144-
return avr.DDRL, avr.PORTL, 1 << uint8(p-portL)
123+
return avr.PORTL, 1 << uint8(p-portL)
145124
default:
146-
return avr.DDRB, avr.PORTA, 255
125+
return avr.PORTA, 255
147126
}
148127
}

src/machine/machine_atmega328p.go

Lines changed: 8 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -9,51 +9,15 @@ import (
99

1010
const irq_USART0_RX = avr.IRQ_USART_RX
1111

12-
// Configure sets the pin to input or output.
13-
func (p Pin) Configure(config PinConfig) {
14-
if config.Mode == PinOutput { // set output bit
15-
switch p / 8 {
16-
case 0: // port B
17-
avr.DDRB.SetBits(1 << uint8(p))
18-
case 1: // port C
19-
avr.DDRC.SetBits(1 << uint8(p-8))
20-
case 2: // port D
21-
avr.DDRD.SetBits(1 << uint8(p-16))
22-
}
23-
} else { // configure input: clear output bit
24-
switch p / 8 {
25-
case 0: // port B
26-
avr.DDRB.ClearBits(1 << uint8(p))
27-
case 1: // port C
28-
avr.DDRC.ClearBits(1 << uint8(p-8))
29-
case 2: // port D
30-
avr.DDRD.ClearBits(1 << uint8(p-16))
31-
}
32-
}
33-
}
34-
35-
// Get returns the current value of a GPIO pin.
36-
func (p Pin) Get() bool {
37-
var val uint8
38-
switch p / 8 {
39-
case 0: // port B
40-
val = avr.PINB.Get() & (1 << uint8(p))
41-
case 1: // port C
42-
val = avr.PINC.Get() & (1 << uint8(p-8))
43-
case 2: // port D
44-
val = avr.PIND.Get() & (1 << uint8(p-16))
45-
}
46-
return val != 0
47-
}
48-
12+
// getPortMask returns the PORTx register and mask for the pin.
4913
func (p Pin) getPortMask() (*volatile.Register8, uint8) {
50-
switch p / 8 {
51-
case 0: // port B
52-
return avr.PORTB, 1 << uint8(p)
53-
case 1:
54-
return avr.PORTC, 1 << uint8(p-8)
55-
default:
56-
return avr.PORTD, 1 << uint8(p-16)
14+
switch {
15+
case p >= PB0 && p <= PB7: // port B
16+
return avr.PORTB, 1 << uint8(p-portB)
17+
case p >= PC0 && p <= PC7: // port C
18+
return avr.PORTC, 1 << uint8(p-portC)
19+
default: // port D
20+
return avr.PORTD, 1 << uint8(p-portD)
5721
}
5822
}
5923

src/machine/machine_attiny85.go

Lines changed: 2 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -16,21 +16,8 @@ const (
1616
PB5
1717
)
1818

19-
// Configure sets the pin to input or output.
20-
func (p Pin) Configure(config PinConfig) {
21-
if config.Mode == PinOutput { // set output bit
22-
avr.DDRB.SetBits(1 << uint8(p))
23-
} else { // configure input: clear output bit
24-
avr.DDRB.ClearBits(1 << uint8(p))
25-
}
26-
}
27-
19+
// getPortMask returns the PORTx register and mask for the pin.
2820
func (p Pin) getPortMask() (*volatile.Register8, uint8) {
21+
// Very simple for the attiny85, which only has a single port.
2922
return avr.PORTB, 1 << uint8(p)
3023
}
31-
32-
// Get returns the current value of a GPIO pin.
33-
func (p Pin) Get() bool {
34-
val := avr.PINB.Get() & (1 << uint8(p))
35-
return (val > 0)
36-
}

src/machine/machine_avr.go

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ package machine
55
import (
66
"device/avr"
77
"runtime/volatile"
8+
"unsafe"
89
)
910

1011
type PinMode uint8
@@ -14,6 +15,41 @@ const (
1415
PinOutput
1516
)
1617

18+
// In all the AVRs I've looked at, the PIN/DDR/PORT registers followed a regular
19+
// pattern: PINx, DDRx, PORTx in this order without registers in between.
20+
// Therefore, if you know any of them, you can calculate the other two.
21+
//
22+
// For now, I've chosen to let the PORTx register be the one that is returned
23+
// for each specific chip and to calculate the others from that one. Setting an
24+
// output port (done using PORTx) is likely the most common operation and the
25+
// one that is the most time critical. For others, the PINx and DDRx register
26+
// can trivially be calculated using a subtraction.
27+
28+
// Configure sets the pin to input or output.
29+
func (p Pin) Configure(config PinConfig) {
30+
port, mask := p.getPortMask()
31+
// The DDRx register can be found by subtracting one from the PORTx
32+
// register, as this appears to be the case for many (most? all?) AVR chips.
33+
ddr := (*volatile.Register8)(unsafe.Pointer(uintptr(unsafe.Pointer(port)) - 1))
34+
if config.Mode == PinOutput {
35+
// set output bit
36+
ddr.SetBits(mask)
37+
} else {
38+
// configure input: clear output bit
39+
ddr.ClearBits(mask)
40+
}
41+
}
42+
43+
// Get returns the current value of a GPIO pin.
44+
func (p Pin) Get() bool {
45+
port, mask := p.getPortMask()
46+
// As noted above, the PINx register is always two registers below the PORTx
47+
// register, so we can find it simply by subtracting two from the PORTx
48+
// register address.
49+
pin := (*volatile.Register8)(unsafe.Pointer(uintptr(unsafe.Pointer(port)) - 2)) // PINA, PINB, etc
50+
return (pin.Get() & mask) > 0
51+
}
52+
1753
// Set changes the value of the GPIO pin. The pin must be configured as output.
1854
func (p Pin) Set(value bool) {
1955
if value { // set bits

0 commit comments

Comments
 (0)