diff --git a/src/machine/machine_atmega1284p.go b/src/machine/machine_atmega1284p.go index bc25f674..c80b0e3a 100644 --- a/src/machine/machine_atmega1284p.go +++ b/src/machine/machine_atmega1284p.go @@ -14,12 +14,58 @@ func CPUFrequency() uint32 { return 20000000 } +const ( + portA Pin = iota * 8 + portB + portC + portD +) + +const ( + PA0 = portA + 0 + PA1 = portA + 1 + PA2 = portA + 2 + PA3 = portA + 3 + PA4 = portA + 4 + PA5 = portA + 5 + PA6 = portA + 6 + PA7 = portA + 7 + PB0 = portB + 0 + PB1 = portB + 1 + PB2 = portB + 2 + PB3 = portB + 3 + PB4 = portB + 4 + PB5 = portB + 5 + PB6 = portB + 6 + PB7 = portB + 7 + PC0 = portC + 0 + PC1 = portC + 1 + PC2 = portC + 2 + PC3 = portC + 3 + PC4 = portC + 4 + PC5 = portC + 5 + PC6 = portC + 6 + PC7 = portC + 7 + PD0 = portD + 0 + PD1 = portD + 1 + PD2 = portD + 2 + PD3 = portD + 3 + PD4 = portD + 4 + PD5 = portD + 5 + PD6 = portD + 6 + PD7 = portD + 7 +) + +// getPortMask returns the PORTx register and mask for the pin. func (p Pin) getPortMask() (*volatile.Register8, uint8) { - if p < 8 { - return avr.PORTD, 1 << uint8(p) - } else if p < 14 { - return avr.PORTB, 1 << uint8(p-8) - } else { - return avr.PORTC, 1 << uint8(p-14) + switch { + case p >= PA0 && p <= PA7: + return avr.PORTA, 1 << uint8(p-portA) + case p >= PB0 && p <= PB7: + return avr.PORTB, 1 << uint8(p-portB) + case p >= PC0 && p <= PC7: + return avr.PORTC, 1 << uint8(p-portC) + default: + return avr.PORTD, 1 << uint8(p-portD) } } diff --git a/src/machine/machine_atmega2560.go b/src/machine/machine_atmega2560.go index e4c3f010..74a83f02 100644 --- a/src/machine/machine_atmega2560.go +++ b/src/machine/machine_atmega2560.go @@ -96,53 +96,32 @@ const ( PL7 = portE + 7 ) -// Configure sets the pin to input or output. -func (p Pin) Configure(config PinConfig) { - register, _, mask := p.getRegisterPortMask() - if config.Mode == PinOutput { // set output bit - register.SetBits(mask) - } else { // configure input: clear output bit - register.ClearBits(mask) - } -} - -// Get returns the current value of a GPIO pin. -func (p Pin) Get() bool { - _, port, mask := p.getRegisterPortMask() - return (port.Get() & mask) > 0 -} - +// getPortMask returns the PORTx register and mask for the pin. func (p Pin) getPortMask() (*volatile.Register8, uint8) { - _, port, mask := p.getRegisterPortMask() - return port, mask -} - -// getRegisterPortMask returns the register, port, and mask for the pin -func (p Pin) getRegisterPortMask() (*volatile.Register8, *volatile.Register8, uint8) { switch { case p >= PA0 && p <= PA7: - return avr.DDRA, avr.PORTA, 1 << uint8(p-portA) + return avr.PORTA, 1 << uint8(p-portA) case p >= PB0 && p <= PB7: - return avr.DDRB, avr.PORTB, 1 << uint8(p-portB) + return avr.PORTB, 1 << uint8(p-portB) case p >= PC0 && p <= PC7: - return avr.DDRC, avr.PORTC, 1 << uint8(p-portC) + return avr.PORTC, 1 << uint8(p-portC) case p >= PD0 && p <= PD7: - return avr.DDRD, avr.PORTD, 1 << uint8(p-portD) + return avr.PORTD, 1 << uint8(p-portD) case p >= PE0 && p <= PE6: - return avr.DDRE, avr.PORTE, 1 << uint8(p-portE) + return avr.PORTE, 1 << uint8(p-portE) case p >= PF0 && p <= PF7: - return avr.DDRF, avr.PORTF, 1 << uint8(p-portF) + return avr.PORTF, 1 << uint8(p-portF) case p >= PG0 && p <= PG5: - return avr.DDRG, avr.PORTG, 1 << uint8(p-portG) + return avr.PORTG, 1 << uint8(p-portG) case p >= PH0 && p <= PH6: - return avr.DDRH, avr.PORTH, 1 << uint8(p-portH) + return avr.PORTH, 1 << uint8(p-portH) case p >= PJ0 && p <= PJ1: - return avr.DDRJ, avr.PORTJ, 1 << uint8(p-portJ) + return avr.PORTJ, 1 << uint8(p-portJ) case p >= PK0 && p <= PK7: - return avr.DDRK, avr.PORTK, 1 << uint8(p-portK) + return avr.PORTK, 1 << uint8(p-portK) case p >= PL0 && p <= PL7: - return avr.DDRL, avr.PORTL, 1 << uint8(p-portL) + return avr.PORTL, 1 << uint8(p-portL) default: - return avr.DDRB, avr.PORTA, 255 + return avr.PORTA, 255 } } diff --git a/src/machine/machine_atmega328p.go b/src/machine/machine_atmega328p.go index 58265ed1..ae1356e8 100644 --- a/src/machine/machine_atmega328p.go +++ b/src/machine/machine_atmega328p.go @@ -9,51 +9,15 @@ import ( const irq_USART0_RX = avr.IRQ_USART_RX -// Configure sets the pin to input or output. -func (p Pin) Configure(config PinConfig) { - if config.Mode == PinOutput { // set output bit - switch p / 8 { - case 0: // port B - avr.DDRB.SetBits(1 << uint8(p)) - case 1: // port C - avr.DDRC.SetBits(1 << uint8(p-8)) - case 2: // port D - avr.DDRD.SetBits(1 << uint8(p-16)) - } - } else { // configure input: clear output bit - switch p / 8 { - case 0: // port B - avr.DDRB.ClearBits(1 << uint8(p)) - case 1: // port C - avr.DDRC.ClearBits(1 << uint8(p-8)) - case 2: // port D - avr.DDRD.ClearBits(1 << uint8(p-16)) - } - } -} - -// Get returns the current value of a GPIO pin. -func (p Pin) Get() bool { - var val uint8 - switch p / 8 { - case 0: // port B - val = avr.PINB.Get() & (1 << uint8(p)) - case 1: // port C - val = avr.PINC.Get() & (1 << uint8(p-8)) - case 2: // port D - val = avr.PIND.Get() & (1 << uint8(p-16)) - } - return val != 0 -} - +// getPortMask returns the PORTx register and mask for the pin. func (p Pin) getPortMask() (*volatile.Register8, uint8) { - switch p / 8 { - case 0: // port B - return avr.PORTB, 1 << uint8(p) - case 1: - return avr.PORTC, 1 << uint8(p-8) - default: - return avr.PORTD, 1 << uint8(p-16) + switch { + case p >= PB0 && p <= PB7: // port B + return avr.PORTB, 1 << uint8(p-portB) + case p >= PC0 && p <= PC7: // port C + return avr.PORTC, 1 << uint8(p-portC) + default: // port D + return avr.PORTD, 1 << uint8(p-portD) } } diff --git a/src/machine/machine_attiny85.go b/src/machine/machine_attiny85.go index b3de12fc..5a6c37c7 100644 --- a/src/machine/machine_attiny85.go +++ b/src/machine/machine_attiny85.go @@ -16,21 +16,8 @@ const ( PB5 ) -// Configure sets the pin to input or output. -func (p Pin) Configure(config PinConfig) { - if config.Mode == PinOutput { // set output bit - avr.DDRB.SetBits(1 << uint8(p)) - } else { // configure input: clear output bit - avr.DDRB.ClearBits(1 << uint8(p)) - } -} - +// getPortMask returns the PORTx register and mask for the pin. func (p Pin) getPortMask() (*volatile.Register8, uint8) { + // Very simple for the attiny85, which only has a single port. return avr.PORTB, 1 << uint8(p) } - -// Get returns the current value of a GPIO pin. -func (p Pin) Get() bool { - val := avr.PINB.Get() & (1 << uint8(p)) - return (val > 0) -} diff --git a/src/machine/machine_avr.go b/src/machine/machine_avr.go index 7d63b040..a1c6b069 100644 --- a/src/machine/machine_avr.go +++ b/src/machine/machine_avr.go @@ -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