aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--GNUmakefile2
-rw-r--r--src/machine/board_arduino.go5
-rw-r--r--src/machine/board_arduino_nano.go5
-rw-r--r--src/machine/board_atmega328p.go5
-rw-r--r--src/machine/board_atmega328pb.go51
-rw-r--r--src/machine/machine_atmega.go80
-rw-r--r--src/machine/machine_atmega328.go548
-rw-r--r--src/machine/machine_atmega328p.go582
-rw-r--r--src/machine/machine_atmega328pb.go170
-rw-r--r--targets/atmega328pb.json15
10 files changed, 796 insertions, 667 deletions
diff --git a/GNUmakefile b/GNUmakefile
index 32d5d4fec..56b0aaa73 100644
--- a/GNUmakefile
+++ b/GNUmakefile
@@ -718,6 +718,8 @@ ifneq ($(STM32), 0)
$(TINYGO) build -size short -o test.hex -target=swan examples/blinky1
@$(MD5SUM) test.hex
endif
+ $(TINYGO) build -size short -o test.hex -target=atmega328pb examples/blinkm
+ @$(MD5SUM) test.hex
$(TINYGO) build -size short -o test.hex -target=atmega1284p examples/serial
@$(MD5SUM) test.hex
$(TINYGO) build -size short -o test.hex -target=arduino examples/blinky1
diff --git a/src/machine/board_arduino.go b/src/machine/board_arduino.go
index a164406f2..a66889aee 100644
--- a/src/machine/board_arduino.go
+++ b/src/machine/board_arduino.go
@@ -2,11 +2,6 @@
package machine
-// Return the current CPU frequency in hertz.
-func CPUFrequency() uint32 {
- return 16000000
-}
-
// Digital pins, marked as plain numbers on the board.
const (
D0 = PD0 // RX
diff --git a/src/machine/board_arduino_nano.go b/src/machine/board_arduino_nano.go
index 8bc883364..c67721951 100644
--- a/src/machine/board_arduino_nano.go
+++ b/src/machine/board_arduino_nano.go
@@ -2,11 +2,6 @@
package machine
-// Return the current CPU frequency in hertz.
-func CPUFrequency() uint32 {
- return 16000000
-}
-
// Digital pins.
const (
D0 = PD0 // RX0
diff --git a/src/machine/board_atmega328p.go b/src/machine/board_atmega328p.go
index a00fec516..234bf3155 100644
--- a/src/machine/board_atmega328p.go
+++ b/src/machine/board_atmega328p.go
@@ -2,6 +2,11 @@
package machine
+// Return the current CPU frequency in hertz.
+func CPUFrequency() uint32 {
+ return 16000000
+}
+
const (
// Note: start at port B because there is no port A.
portB Pin = iota * 8
diff --git a/src/machine/board_atmega328pb.go b/src/machine/board_atmega328pb.go
new file mode 100644
index 000000000..e3e45f01f
--- /dev/null
+++ b/src/machine/board_atmega328pb.go
@@ -0,0 +1,51 @@
+//go:build avr && atmega328pb
+
+package machine
+
+// Return the current CPU frequency in hertz.
+func CPUFrequency() uint32 {
+ return 16000000
+}
+
+const (
+ // Note: start at port B because there is no port A.
+ portB Pin = iota * 8
+ portC
+ portD
+ portE
+)
+
+const (
+ PB0 = portB + 0
+ PB1 = portB + 1 // peripherals: Timer1 channel A
+ PB2 = portB + 2 // peripherals: Timer1 channel B
+ PB3 = portB + 3 // peripherals: Timer2 channel A
+ 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 // peripherals: Timer2 channel B
+ PD4 = portD + 4
+ PD5 = portD + 5 // peripherals: Timer0 channel B
+ PD6 = portD + 6 // peripherals: Timer0 channel A
+ PD7 = portD + 7
+ PE0 = portE + 0
+ PE1 = portE + 1
+ PE2 = portE + 2
+ PE3 = portE + 3
+ PE4 = portE + 4
+ PE5 = portE + 5
+ PE6 = portE + 6
+ PE7 = portE + 7
+)
diff --git a/src/machine/machine_atmega.go b/src/machine/machine_atmega.go
index c5d56b031..f1838a08a 100644
--- a/src/machine/machine_atmega.go
+++ b/src/machine/machine_atmega.go
@@ -11,11 +11,20 @@ import (
// I2C on AVR.
type I2C struct {
+ srReg *volatile.Register8
+ brReg *volatile.Register8
+ crReg *volatile.Register8
+ drReg *volatile.Register8
+
+ srPS0 byte
+ srPS1 byte
+ crEN byte
+ crINT byte
+ crSTO byte
+ crEA byte
+ crSTA byte
}
-// I2C0 is the only I2C interface on most AVRs.
-var I2C0 *I2C = nil
-
// I2CConfig is used to store config info for I2C.
type I2CConfig struct {
Frequency uint32
@@ -37,16 +46,16 @@ func (i2c *I2C) Configure(config I2CConfig) error {
// SetBaudRate sets the communication speed for I2C.
func (i2c *I2C) SetBaudRate(br uint32) error {
// Initialize twi prescaler and bit rate.
- avr.TWSR.SetBits((avr.TWSR_TWPS0 | avr.TWSR_TWPS1))
+ i2c.srReg.SetBits((i2c.srPS0 | i2c.srPS1))
// twi bit rate formula from atmega128 manual pg. 204:
// SCL Frequency = CPU Clock Frequency / (16 + (2 * TWBR))
// NOTE: TWBR should be 10 or higher for controller mode.
// It is 72 for a 16mhz board with 100kHz TWI
- avr.TWBR.Set(uint8(((CPUFrequency() / br) - 16) / 2))
+ i2c.brReg.Set(uint8(((CPUFrequency() / br) - 16) / 2))
// Enable twi module.
- avr.TWCR.Set(avr.TWCR_TWEN)
+ i2c.crReg.Set(i2c.crEN)
return nil
}
@@ -77,10 +86,10 @@ func (i2c *I2C) Tx(addr uint16, w, r []byte) error {
// start starts an I2C communication session.
func (i2c *I2C) start(address uint8, write bool) {
// Clear TWI interrupt flag, put start condition on SDA, and enable TWI.
- avr.TWCR.Set((avr.TWCR_TWINT | avr.TWCR_TWSTA | avr.TWCR_TWEN))
+ i2c.crReg.Set((i2c.crINT | i2c.crSTA | i2c.crEN))
// Wait till start condition is transmitted.
- for !avr.TWCR.HasBits(avr.TWCR_TWINT) {
+ for !i2c.crReg.HasBits(i2c.crINT) {
}
// Write 7-bit shifted peripheral address.
@@ -94,23 +103,23 @@ func (i2c *I2C) start(address uint8, write bool) {
// stop ends an I2C communication session.
func (i2c *I2C) stop() {
// Send stop condition.
- avr.TWCR.Set(avr.TWCR_TWEN | avr.TWCR_TWINT | avr.TWCR_TWSTO)
+ i2c.crReg.Set(i2c.crEN | i2c.crINT | i2c.crSTO)
// Wait for stop condition to be executed on bus.
- for !avr.TWCR.HasBits(avr.TWCR_TWSTO) {
+ for !i2c.crReg.HasBits(i2c.crSTO) {
}
}
// writeByte writes a single byte to the I2C bus.
func (i2c *I2C) writeByte(data byte) error {
// Write data to register.
- avr.TWDR.Set(data)
+ i2c.drReg.Set(data)
// Clear TWI interrupt flag and enable TWI.
- avr.TWCR.Set(avr.TWCR_TWEN | avr.TWCR_TWINT)
+ i2c.crReg.Set(i2c.crEN | i2c.crINT)
// Wait till data is transmitted.
- for !avr.TWCR.HasBits(avr.TWCR_TWINT) {
+ for !i2c.crReg.HasBits(i2c.crINT) {
}
return nil
}
@@ -118,13 +127,13 @@ func (i2c *I2C) writeByte(data byte) error {
// readByte reads a single byte from the I2C bus.
func (i2c *I2C) readByte() byte {
// Clear TWI interrupt flag and enable TWI.
- avr.TWCR.Set(avr.TWCR_TWEN | avr.TWCR_TWINT | avr.TWCR_TWEA)
+ i2c.crReg.Set(i2c.crEN | i2c.crINT | i2c.crEA)
// Wait till read request is transmitted.
- for !avr.TWCR.HasBits(avr.TWCR_TWINT) {
+ for !i2c.crReg.HasBits(i2c.crINT) {
}
- return byte(avr.TWDR.Get())
+ return byte(i2c.drReg.Get())
}
// Always use UART0 as the serial output.
@@ -221,6 +230,17 @@ type SPI struct {
spdr *volatile.Register8
spsr *volatile.Register8
+ spcrR0 byte
+ spcrR1 byte
+ spcrCPHA byte
+ spcrCPOL byte
+ spcrDORD byte
+ spcrSPE byte
+ spcrMSTR byte
+
+ spsrI2X byte
+ spsrSPIF byte
+
// The io pins for the SPIx port set by the chip
sck Pin
sdi Pin
@@ -264,39 +284,39 @@ func (s SPI) Configure(config SPIConfig) error {
switch {
case frequencyDivider >= 128:
- s.spcr.SetBits(avr.SPCR_SPR0 | avr.SPCR_SPR1)
+ s.spcr.SetBits(s.spcrR0 | s.spcrR1)
case frequencyDivider >= 64:
- s.spcr.SetBits(avr.SPCR_SPR1)
+ s.spcr.SetBits(s.spcrR1)
case frequencyDivider >= 32:
- s.spcr.SetBits(avr.SPCR_SPR1)
- s.spsr.SetBits(avr.SPSR_SPI2X)
+ s.spcr.SetBits(s.spcrR1)
+ s.spsr.SetBits(s.spsrI2X)
case frequencyDivider >= 16:
- s.spcr.SetBits(avr.SPCR_SPR0)
+ s.spcr.SetBits(s.spcrR0)
case frequencyDivider >= 8:
- s.spcr.SetBits(avr.SPCR_SPR0)
- s.spsr.SetBits(avr.SPSR_SPI2X)
+ s.spcr.SetBits(s.spcrR0)
+ s.spsr.SetBits(s.spsrI2X)
case frequencyDivider >= 4:
// The clock is already set to all 0's.
default: // defaults to fastest which is /2
- s.spsr.SetBits(avr.SPSR_SPI2X)
+ s.spsr.SetBits(s.spsrI2X)
}
switch config.Mode {
case Mode1:
- s.spcr.SetBits(avr.SPCR_CPHA)
+ s.spcr.SetBits(s.spcrCPHA)
case Mode2:
- s.spcr.SetBits(avr.SPCR_CPOL)
+ s.spcr.SetBits(s.spcrCPHA)
case Mode3:
- s.spcr.SetBits(avr.SPCR_CPHA | avr.SPCR_CPOL)
+ s.spcr.SetBits(s.spcrCPHA | s.spcrCPOL)
default: // default is mode 0
}
if config.LSBFirst {
- s.spcr.SetBits(avr.SPCR_DORD)
+ s.spcr.SetBits(s.spcrDORD)
}
// enable SPI, set controller, set clock rate
- s.spcr.SetBits(avr.SPCR_SPE | avr.SPCR_MSTR)
+ s.spcr.SetBits(s.spcrSPE | s.spcrMSTR)
return nil
}
@@ -305,7 +325,7 @@ func (s SPI) Configure(config SPIConfig) error {
func (s SPI) Transfer(b byte) (byte, error) {
s.spdr.Set(uint8(b))
- for !s.spsr.HasBits(avr.SPSR_SPIF) {
+ for !s.spsr.HasBits(s.spsrSPIF) {
}
return byte(s.spdr.Get()), nil
diff --git a/src/machine/machine_atmega328.go b/src/machine/machine_atmega328.go
new file mode 100644
index 000000000..e4b2bb06f
--- /dev/null
+++ b/src/machine/machine_atmega328.go
@@ -0,0 +1,548 @@
+//go:build avr && (atmega328p || atmega328pb)
+
+package machine
+
+import (
+ "device/avr"
+ "runtime/interrupt"
+ "runtime/volatile"
+)
+
+// PWM is one PWM peripheral, which consists of a counter and two output
+// channels (that can be connected to two fixed pins). You can set the frequency
+// using SetPeriod, but only for all the channels in this PWM peripheral at
+// once.
+type PWM struct {
+ num uint8
+}
+
+var (
+ Timer0 = PWM{0} // 8 bit timer for PD5 and PD6
+ Timer1 = PWM{1} // 16 bit timer for PB1 and PB2
+ Timer2 = PWM{2} // 8 bit timer for PB3 and PD3
+)
+
+// Configure enables and configures this PWM.
+//
+// For the two 8 bit timers, there is only a limited number of periods
+// available, namely the CPU frequency divided by 256 and again divided by 1, 8,
+// 64, 256, or 1024. For a MCU running at 16MHz, this would be a period of 16µs,
+// 128µs, 1024µs, 4096µs, or 16384µs.
+func (pwm PWM) Configure(config PWMConfig) error {
+ switch pwm.num {
+ case 0, 2: // 8-bit timers (Timer/counter 0 and Timer/counter 2)
+ // Calculate the timer prescaler.
+ // While we could configure a flexible top, that would sacrifice one of
+ // the PWM output compare registers and thus a PWM channel. I've chosen
+ // to instead limit this timer to a fixed number of frequencies.
+ var prescaler uint8
+ switch config.Period {
+ case 0, (uint64(1e9) * 256 * 1) / uint64(CPUFrequency()):
+ prescaler = 1
+ case (uint64(1e9) * 256 * 8) / uint64(CPUFrequency()):
+ prescaler = 2
+ case (uint64(1e9) * 256 * 64) / uint64(CPUFrequency()):
+ prescaler = 3
+ case (uint64(1e9) * 256 * 256) / uint64(CPUFrequency()):
+ prescaler = 4
+ case (uint64(1e9) * 256 * 1024) / uint64(CPUFrequency()):
+ prescaler = 5
+ default:
+ return ErrPWMPeriodTooLong
+ }
+
+ if pwm.num == 0 {
+ avr.TCCR0B.Set(prescaler)
+ // Set the PWM mode to fast PWM (mode = 3).
+ 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 {
+ avr.TCCR2B.Set(prescaler)
+ // Set the PWM mode to fast PWM (mode = 3).
+ avr.TCCR2A.Set(avr.TCCR2A_WGM20 | avr.TCCR2A_WGM21)
+ }
+ case 1: // Timer/counter 1
+ // The top value is the number of PWM ticks a PWM period takes. It is
+ // initially picked assuming an unlimited counter top and no PWM
+ // prescaler.
+ var top uint64
+ if config.Period == 0 {
+ // Use a top appropriate for LEDs. Picking a relatively low period
+ // here (0xff) for consistency with the other timers.
+ top = 0xff
+ } else {
+ // The formula below calculates the following formula, optimized:
+ // top = period * (CPUFrequency() / 1e9)
+ // By dividing the CPU frequency first (an operation that is easily
+ // optimized away) the period has less chance of overflowing.
+ top = config.Period * (uint64(CPUFrequency()) / 1000000) / 1000
+ }
+
+ avr.TCCR1A.Set(avr.TCCR1A_WGM11)
+
+ // The ideal PWM period may be larger than would fit in the PWM counter,
+ // which is 16 bits (see maxTop). Therefore, try to make the PWM clock
+ // speed lower with a prescaler to make the top value fit the maximum
+ // top value.
+ const maxTop = 0x10000
+ switch {
+ case top <= maxTop:
+ avr.TCCR1B.Set(3<<3 | 1) // no prescaling
+ case top/8 <= maxTop:
+ avr.TCCR1B.Set(3<<3 | 2) // divide by 8
+ top /= 8
+ case top/64 <= maxTop:
+ avr.TCCR1B.Set(3<<3 | 3) // divide by 64
+ top /= 64
+ case top/256 <= maxTop:
+ avr.TCCR1B.Set(3<<3 | 4) // divide by 256
+ top /= 256
+ case top/1024 <= maxTop:
+ avr.TCCR1B.Set(3<<3 | 5) // divide by 1024
+ top /= 1024
+ default:
+ return ErrPWMPeriodTooLong
+ }
+
+ // A top of 0x10000 is at 100% duty cycle. Subtract one because the
+ // counter counts from 0, not 1 (avoiding an off-by-one).
+ top -= 1
+
+ avr.ICR1H.Set(uint8(top >> 8))
+ avr.ICR1L.Set(uint8(top))
+ }
+ return nil
+}
+
+// SetPeriod updates the period of this PWM peripheral.
+// To set a particular frequency, use the following formula:
+//
+// period = 1e9 / frequency
+//
+// If you use a period of 0, a period that works well for LEDs will be picked.
+//
+// SetPeriod will not change the prescaler, but also won't change the current
+// value in any of the channels. This means that you may need to update the
+// value for the particular channel.
+//
+// Note that you cannot pick any arbitrary period after the PWM peripheral has
+// been configured. If you want to switch between frequencies, pick the lowest
+// frequency (longest period) once when calling Configure and adjust the
+// frequency here as needed.
+func (pwm PWM) SetPeriod(period uint64) error {
+ if pwm.num != 1 {
+ return ErrPWMPeriodTooLong // TODO better error message
+ }
+
+ // The top value is the number of PWM ticks a PWM period takes. It is
+ // initially picked assuming an unlimited counter top and no PWM
+ // prescaler.
+ var top uint64
+ if period == 0 {
+ // Use a top appropriate for LEDs. Picking a relatively low period
+ // here (0xff) for consistency with the other timers.
+ top = 0xff
+ } else {
+ // The formula below calculates the following formula, optimized:
+ // top = period * (CPUFrequency() / 1e9)
+ // By dividing the CPU frequency first (an operation that is easily
+ // optimized away) the period has less chance of overflowing.
+ top = period * (uint64(CPUFrequency()) / 1000000) / 1000
+ }
+
+ prescaler := avr.TCCR1B.Get() & 0x7
+ switch prescaler {
+ case 1:
+ top /= 1
+ case 2:
+ top /= 8
+ case 3:
+ top /= 64
+ case 4:
+ top /= 256
+ case 5:
+ top /= 1024
+ }
+
+ // A top of 0x10000 is at 100% duty cycle. Subtract one because the counter
+ // counts from 0, not 1 (avoiding an off-by-one).
+ top -= 1
+
+ if top > 0xffff {
+ return ErrPWMPeriodTooLong
+ }
+
+ // Warning: this change is not atomic!
+ avr.ICR1H.Set(uint8(top >> 8))
+ avr.ICR1L.Set(uint8(top))
+
+ // ... and because of that, set the counter back to zero to avoid most of
+ // the effects of this non-atomicity.
+ avr.TCNT1H.Set(0)
+ avr.TCNT1L.Set(0)
+
+ return nil
+}
+
+// Top returns the current counter top, for use in duty cycle calculation. It
+// will only change with a call to Configure or SetPeriod, otherwise it is
+// constant.
+//
+// The value returned here is hardware dependent. In general, it's best to treat
+// it as an opaque value that can be divided by some number and passed to Set
+// (see Set documentation for more information).
+func (pwm PWM) Top() uint32 {
+ if pwm.num == 1 {
+ // Timer 1 has a configurable top value.
+ low := avr.ICR1L.Get()
+ high := avr.ICR1H.Get()
+ return uint32(high)<<8 | uint32(low) + 1
+ }
+ // Other timers go from 0 to 0xff (0x100 or 256 in total).
+ return 256
+}
+
+// Counter returns the current counter value of the timer in this PWM
+// peripheral. It may be useful for debugging.
+func (pwm PWM) Counter() uint32 {
+ switch pwm.num {
+ case 0:
+ return uint32(avr.TCNT0.Get())
+ case 1:
+ mask := interrupt.Disable()
+ low := avr.TCNT1L.Get()
+ high := avr.TCNT1H.Get()
+ interrupt.Restore(mask)
+ return uint32(high)<<8 | uint32(low)
+ case 2:
+ return uint32(avr.TCNT2.Get())
+ }
+ // Unknown PWM.
+ return 0
+}
+
+// Period returns the used PWM period in nanoseconds. It might deviate slightly
+// from the configured period due to rounding.
+func (pwm PWM) Period() uint64 {
+ var prescaler uint8
+ switch pwm.num {
+ case 0:
+ prescaler = avr.TCCR0B.Get() & 0x7
+ case 1:
+ prescaler = avr.TCCR1B.Get() & 0x7
+ case 2:
+ prescaler = avr.TCCR2B.Get() & 0x7
+ }
+ top := uint64(pwm.Top())
+ switch prescaler {
+ case 1: // prescaler 1
+ return 1 * top * 1000 / uint64(CPUFrequency()/1e6)
+ case 2: // prescaler 8
+ return 8 * top * 1000 / uint64(CPUFrequency()/1e6)
+ case 3: // prescaler 64
+ return 64 * top * 1000 / uint64(CPUFrequency()/1e6)
+ case 4: // prescaler 256
+ return 256 * top * 1000 / uint64(CPUFrequency()/1e6)
+ case 5: // prescaler 1024
+ return 1024 * top * 1000 / uint64(CPUFrequency()/1e6)
+ default: // unknown clock source
+ return 0
+ }
+}
+
+// Channel returns a PWM channel for the given pin.
+func (pwm PWM) Channel(pin Pin) (uint8, error) {
+ pin.Configure(PinConfig{Mode: PinOutput})
+ pin.Low()
+ switch pwm.num {
+ case 0:
+ switch pin {
+ case PD6: // channel A
+ avr.TCCR0A.SetBits(avr.TCCR0A_COM0A1)
+ return 0, nil
+ case PD5: // channel B
+ avr.TCCR0A.SetBits(avr.TCCR0A_COM0B1)
+ return 1, nil
+ }
+ case 1:
+ switch pin {
+ case PB1: // channel A
+ avr.TCCR1A.SetBits(avr.TCCR1A_COM1A1)
+ return 0, nil
+ case PB2: // channel B
+ avr.TCCR1A.SetBits(avr.TCCR1A_COM1B1)
+ return 1, nil
+ }
+ case 2:
+ switch pin {
+ case PB3: // channel A
+ avr.TCCR2A.SetBits(avr.TCCR2A_COM2A1)
+ return 0, nil
+ case PD3: // channel B
+ avr.TCCR2A.SetBits(avr.TCCR2A_COM2B1)
+ return 1, nil
+ }
+ }
+ return 0, ErrInvalidOutputPin
+}
+
+// SetInverting sets whether to invert the output of this channel.
+// Without inverting, a 25% duty cycle would mean the output is high for 25% of
+// the time and low for the rest. Inverting flips the output as if a NOT gate
+// was placed at the output, meaning that the output would be 25% low and 75%
+// high with a duty cycle of 25%.
+//
+// Note: the invert state may not be applied on the AVR until the next call to
+// ch.Set().
+func (pwm PWM) SetInverting(channel uint8, inverting bool) {
+ switch pwm.num {
+ case 0:
+ switch channel {
+ case 0: // channel A
+ if inverting {
+ avr.PORTB.SetBits(1 << 6) // PB6 high
+ avr.TCCR0A.SetBits(avr.TCCR0A_COM0A0)
+ } else {
+ avr.PORTB.ClearBits(1 << 6) // PB6 low
+ avr.TCCR0A.ClearBits(avr.TCCR0A_COM0A0)
+ }
+ case 1: // channel B
+ if inverting {
+ avr.PORTB.SetBits(1 << 5) // PB5 high
+ avr.TCCR0A.SetBits(avr.TCCR0A_COM0B0)
+ } else {
+ avr.PORTB.ClearBits(1 << 5) // PB5 low
+ avr.TCCR0A.ClearBits(avr.TCCR0A_COM0B0)
+ }
+ }
+ case 1:
+ // Note: the COM1A0/COM1B0 bit is not set with the configuration below.
+ // It will be set the following call to Set(), however.
+ switch channel {
+ case 0: // channel A, PB1
+ if inverting {
+ avr.PORTB.SetBits(1 << 1) // PB1 high
+ } else {
+ avr.PORTB.ClearBits(1 << 1) // PB1 low
+ }
+ case 1: // channel B, PB2
+ if inverting {
+ avr.PORTB.SetBits(1 << 2) // PB2 high
+ } else {
+ avr.PORTB.ClearBits(1 << 2) // PB2 low
+ }
+ }
+ case 2:
+ switch channel {
+ case 0: // channel A
+ if inverting {
+ avr.PORTB.SetBits(1 << 3) // PB3 high
+ avr.TCCR2A.SetBits(avr.TCCR2A_COM2A0)
+ } else {
+ avr.PORTB.ClearBits(1 << 3) // PB3 low
+ avr.TCCR2A.ClearBits(avr.TCCR2A_COM2A0)
+ }
+ case 1: // channel B
+ if inverting {
+ avr.PORTD.SetBits(1 << 3) // PD3 high
+ avr.TCCR2A.SetBits(avr.TCCR2A_COM2B0)
+ } else {
+ avr.PORTD.ClearBits(1 << 3) // PD3 low
+ avr.TCCR2A.ClearBits(avr.TCCR2A_COM2B0)
+ }
+ }
+ }
+}
+
+// Set updates the channel value. This is used to control the channel duty
+// cycle, in other words the fraction of time the channel output is high (or low
+// when inverted). For example, to set it to a 25% duty cycle, use:
+//
+// pwm.Set(channel, pwm.Top() / 4)
+//
+// pwm.Set(channel, 0) will set the output to low and pwm.Set(channel,
+// pwm.Top()) will set the output to high, assuming the output isn't inverted.
+func (pwm PWM) Set(channel uint8, value uint32) {
+ switch pwm.num {
+ case 0:
+ value := uint16(value)
+ switch channel {
+ case 0: // channel A
+ if value == 0 {
+ avr.TCCR0A.ClearBits(avr.TCCR0A_COM0A1)
+ } else {
+ avr.OCR0A.Set(uint8(value - 1))
+ avr.TCCR0A.SetBits(avr.TCCR0A_COM0A1)
+ }
+ case 1: // channel B
+ if value == 0 {
+ avr.TCCR0A.ClearBits(avr.TCCR0A_COM0B1)
+ } else {
+ avr.OCR0B.Set(uint8(value) - 1)
+ 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:
+ mask := interrupt.Disable()
+ switch channel {
+ case 0: // channel A, PB1
+ if value == 0 {
+ avr.TCCR1A.ClearBits(avr.TCCR1A_COM1A1 | avr.TCCR1A_COM1A0)
+ } else {
+ value := uint16(value) - 1 // yes, this is safe (it relies on underflow)
+ avr.OCR1AH.Set(uint8(value >> 8))
+ avr.OCR1AL.Set(uint8(value))
+ if avr.PORTB.HasBits(1 << 1) { // is PB1 high?
+ // Yes, set the inverting bit.
+ avr.TCCR1A.SetBits(avr.TCCR1A_COM1A1 | avr.TCCR1A_COM1A0)
+ } else {
+ // No, output is non-inverting.
+ avr.TCCR1A.SetBits(avr.TCCR1A_COM1A1)
+ }
+ }
+ case 1: // channel B, PB2
+ if value == 0 {
+ avr.TCCR1A.ClearBits(avr.TCCR1A_COM1B1 | avr.TCCR1A_COM1B0)
+ } else {
+ value := uint16(value) - 1 // yes, this is safe (it relies on underflow)
+ avr.OCR1BH.Set(uint8(value >> 8))
+ avr.OCR1BL.Set(uint8(value))
+ if avr.PORTB.HasBits(1 << 2) { // is PB2 high?
+ // Yes, set the inverting bit.
+ avr.TCCR1A.SetBits(avr.TCCR1A_COM1B1 | avr.TCCR1A_COM1B0)
+ } else {
+ // No, output is non-inverting.
+ avr.TCCR1A.SetBits(avr.TCCR1A_COM1B1)
+ }
+ }
+ }
+ interrupt.Restore(mask)
+ case 2:
+ value := uint16(value)
+ switch channel {
+ case 0: // channel A
+ if value == 0 {
+ avr.TCCR2A.ClearBits(avr.TCCR2A_COM2A1)
+ } else {
+ avr.OCR2A.Set(uint8(value - 1))
+ avr.TCCR2A.SetBits(avr.TCCR2A_COM2A1)
+ }
+ case 1: // channel B
+ if value == 0 {
+ avr.TCCR2A.ClearBits(avr.TCCR2A_COM2B1)
+ } else {
+ avr.OCR2B.Set(uint8(value - 1))
+ avr.TCCR2A.SetBits(avr.TCCR2A_COM2B1)
+ }
+ }
+ }
+}
+
+// Pin Change Interrupts
+type PinChange uint8
+
+const (
+ PinRising PinChange = 1 << iota
+ PinFalling
+ PinToggle = PinRising | PinFalling
+)
+
+func (pin Pin) SetInterrupt(pinChange PinChange, callback func(Pin)) (err error) {
+
+ switch {
+ case pin >= PB0 && pin <= PB7:
+ // PCMSK0 - PCINT0-7
+ pinStates[0] = avr.PINB.Get()
+ pinIndex := pin - PB0
+ if pinChange&PinRising > 0 {
+ pinCallbacks[0][pinIndex][0] = callback
+ }
+ if pinChange&PinFalling > 0 {
+ pinCallbacks[0][pinIndex][1] = callback
+ }
+ if callback != nil {
+ avr.PCMSK0.SetBits(1 << pinIndex)
+ } else {
+ avr.PCMSK0.ClearBits(1 << pinIndex)
+ }
+ avr.PCICR.SetBits(avr.PCICR_PCIE0)
+ interrupt.New(avr.IRQ_PCINT0, handlePCINT0Interrupts)
+ case pin >= PC0 && pin <= PC7:
+ // PCMSK1 - PCINT8-14
+ pinStates[1] = avr.PINC.Get()
+ pinIndex := pin - PC0
+ if pinChange&PinRising > 0 {
+ pinCallbacks[1][pinIndex][0] = callback
+ }
+ if pinChange&PinFalling > 0 {
+ pinCallbacks[1][pinIndex][1] = callback
+ }
+ if callback != nil {
+ avr.PCMSK1.SetBits(1 << pinIndex)
+ } else {
+ avr.PCMSK1.ClearBits(1 << pinIndex)
+ }
+ avr.PCICR.SetBits(avr.PCICR_PCIE1)
+ interrupt.New(avr.IRQ_PCINT1, handlePCINT1Interrupts)
+ case pin >= PD0 && pin <= PD7:
+ // PCMSK2 - PCINT16-23
+ pinStates[2] = avr.PIND.Get()
+ pinIndex := pin - PD0
+ if pinChange&PinRising > 0 {
+ pinCallbacks[2][pinIndex][0] = callback
+ }
+ if pinChange&PinFalling > 0 {
+ pinCallbacks[2][pinIndex][1] = callback
+ }
+ if callback != nil {
+ avr.PCMSK2.SetBits(1 << pinIndex)
+ } else {
+ avr.PCMSK2.ClearBits(1 << pinIndex)
+ }
+ avr.PCICR.SetBits(avr.PCICR_PCIE2)
+ interrupt.New(avr.IRQ_PCINT2, handlePCINT2Interrupts)
+ default:
+ return ErrInvalidInputPin
+ }
+
+ return nil
+}
+
+var pinCallbacks [3][8][2]func(Pin)
+var pinStates [3]uint8
+
+func handlePCINTInterrupts(intr uint8, port *volatile.Register8) {
+ current := port.Get()
+ change := pinStates[intr] ^ current
+ pinStates[intr] = current
+ for i := uint8(0); i < 8; i++ {
+ if (change>>i)&0x01 != 0x01 {
+ continue
+ }
+ pin := Pin(intr*8 + i)
+ value := pin.Get()
+ if value && pinCallbacks[intr][i][0] != nil {
+ pinCallbacks[intr][i][0](pin)
+ }
+ if !value && pinCallbacks[intr][i][1] != nil {
+ pinCallbacks[intr][i][1](pin)
+ }
+ }
+}
+
+func handlePCINT0Interrupts(intr interrupt.Interrupt) {
+ handlePCINTInterrupts(0, avr.PINB)
+}
+
+func handlePCINT1Interrupts(intr interrupt.Interrupt) {
+ handlePCINTInterrupts(1, avr.PINC)
+}
+
+func handlePCINT2Interrupts(intr interrupt.Interrupt) {
+ handlePCINTInterrupts(2, avr.PIND)
+}
diff --git a/src/machine/machine_atmega328p.go b/src/machine/machine_atmega328p.go
index 0987a145c..1995403aa 100644
--- a/src/machine/machine_atmega328p.go
+++ b/src/machine/machine_atmega328p.go
@@ -4,457 +4,24 @@ package machine
import (
"device/avr"
- "runtime/interrupt"
"runtime/volatile"
)
const irq_USART0_RX = avr.IRQ_USART_RX
-// getPortMask returns the PORTx register and mask for the pin.
-func (p Pin) getPortMask() (*volatile.Register8, uint8) {
- 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)
- }
-}
-
-// PWM is one PWM peripheral, which consists of a counter and two output
-// channels (that can be connected to two fixed pins). You can set the frequency
-// using SetPeriod, but only for all the channels in this PWM peripheral at
-// once.
-type PWM struct {
- num uint8
-}
-
-var (
- Timer0 = PWM{0} // 8 bit timer for PD5 and PD6
- Timer1 = PWM{1} // 16 bit timer for PB1 and PB2
- Timer2 = PWM{2} // 8 bit timer for PB3 and PD3
-)
-
-// Configure enables and configures this PWM.
-//
-// For the two 8 bit timers, there is only a limited number of periods
-// available, namely the CPU frequency divided by 256 and again divided by 1, 8,
-// 64, 256, or 1024. For a MCU running at 16MHz, this would be a period of 16µs,
-// 128µs, 1024µs, 4096µs, or 16384µs.
-func (pwm PWM) Configure(config PWMConfig) error {
- switch pwm.num {
- case 0, 2: // 8-bit timers (Timer/counter 0 and Timer/counter 2)
- // Calculate the timer prescaler.
- // While we could configure a flexible top, that would sacrifice one of
- // the PWM output compare registers and thus a PWM channel. I've chosen
- // to instead limit this timer to a fixed number of frequencies.
- var prescaler uint8
- switch config.Period {
- case 0, (uint64(1e9) * 256 * 1) / uint64(CPUFrequency()):
- prescaler = 1
- case (uint64(1e9) * 256 * 8) / uint64(CPUFrequency()):
- prescaler = 2
- case (uint64(1e9) * 256 * 64) / uint64(CPUFrequency()):
- prescaler = 3
- case (uint64(1e9) * 256 * 256) / uint64(CPUFrequency()):
- prescaler = 4
- case (uint64(1e9) * 256 * 1024) / uint64(CPUFrequency()):
- prescaler = 5
- default:
- return ErrPWMPeriodTooLong
- }
-
- if pwm.num == 0 {
- avr.TCCR0B.Set(prescaler)
- // Set the PWM mode to fast PWM (mode = 3).
- 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 {
- avr.TCCR2B.Set(prescaler)
- // Set the PWM mode to fast PWM (mode = 3).
- avr.TCCR2A.Set(avr.TCCR2A_WGM20 | avr.TCCR2A_WGM21)
- }
- case 1: // Timer/counter 1
- // The top value is the number of PWM ticks a PWM period takes. It is
- // initially picked assuming an unlimited counter top and no PWM
- // prescaler.
- var top uint64
- if config.Period == 0 {
- // Use a top appropriate for LEDs. Picking a relatively low period
- // here (0xff) for consistency with the other timers.
- top = 0xff
- } else {
- // The formula below calculates the following formula, optimized:
- // top = period * (CPUFrequency() / 1e9)
- // By dividing the CPU frequency first (an operation that is easily
- // optimized away) the period has less chance of overflowing.
- top = config.Period * (uint64(CPUFrequency()) / 1000000) / 1000
- }
-
- avr.TCCR1A.Set(avr.TCCR1A_WGM11)
-
- // The ideal PWM period may be larger than would fit in the PWM counter,
- // which is 16 bits (see maxTop). Therefore, try to make the PWM clock
- // speed lower with a prescaler to make the top value fit the maximum
- // top value.
- const maxTop = 0x10000
- switch {
- case top <= maxTop:
- avr.TCCR1B.Set(3<<3 | 1) // no prescaling
- case top/8 <= maxTop:
- avr.TCCR1B.Set(3<<3 | 2) // divide by 8
- top /= 8
- case top/64 <= maxTop:
- avr.TCCR1B.Set(3<<3 | 3) // divide by 64
- top /= 64
- case top/256 <= maxTop:
- avr.TCCR1B.Set(3<<3 | 4) // divide by 256
- top /= 256
- case top/1024 <= maxTop:
- avr.TCCR1B.Set(3<<3 | 5) // divide by 1024
- top /= 1024
- default:
- return ErrPWMPeriodTooLong
- }
-
- // A top of 0x10000 is at 100% duty cycle. Subtract one because the
- // counter counts from 0, not 1 (avoiding an off-by-one).
- top -= 1
-
- avr.ICR1H.Set(uint8(top >> 8))
- avr.ICR1L.Set(uint8(top))
- }
- return nil
-}
-
-// SetPeriod updates the period of this PWM peripheral.
-// To set a particular frequency, use the following formula:
-//
-// period = 1e9 / frequency
-//
-// If you use a period of 0, a period that works well for LEDs will be picked.
-//
-// SetPeriod will not change the prescaler, but also won't change the current
-// value in any of the channels. This means that you may need to update the
-// value for the particular channel.
-//
-// Note that you cannot pick any arbitrary period after the PWM peripheral has
-// been configured. If you want to switch between frequencies, pick the lowest
-// frequency (longest period) once when calling Configure and adjust the
-// frequency here as needed.
-func (pwm PWM) SetPeriod(period uint64) error {
- if pwm.num != 1 {
- return ErrPWMPeriodTooLong // TODO better error message
- }
-
- // The top value is the number of PWM ticks a PWM period takes. It is
- // initially picked assuming an unlimited counter top and no PWM
- // prescaler.
- var top uint64
- if period == 0 {
- // Use a top appropriate for LEDs. Picking a relatively low period
- // here (0xff) for consistency with the other timers.
- top = 0xff
- } else {
- // The formula below calculates the following formula, optimized:
- // top = period * (CPUFrequency() / 1e9)
- // By dividing the CPU frequency first (an operation that is easily
- // optimized away) the period has less chance of overflowing.
- top = period * (uint64(CPUFrequency()) / 1000000) / 1000
- }
-
- prescaler := avr.TCCR1B.Get() & 0x7
- switch prescaler {
- case 1:
- top /= 1
- case 2:
- top /= 8
- case 3:
- top /= 64
- case 4:
- top /= 256
- case 5:
- top /= 1024
- }
-
- // A top of 0x10000 is at 100% duty cycle. Subtract one because the counter
- // counts from 0, not 1 (avoiding an off-by-one).
- top -= 1
-
- if top > 0xffff {
- return ErrPWMPeriodTooLong
- }
-
- // Warning: this change is not atomic!
- avr.ICR1H.Set(uint8(top >> 8))
- avr.ICR1L.Set(uint8(top))
-
- // ... and because of that, set the counter back to zero to avoid most of
- // the effects of this non-atomicity.
- avr.TCNT1H.Set(0)
- avr.TCNT1L.Set(0)
-
- return nil
-}
-
-// Top returns the current counter top, for use in duty cycle calculation. It
-// will only change with a call to Configure or SetPeriod, otherwise it is
-// constant.
-//
-// The value returned here is hardware dependent. In general, it's best to treat
-// it as an opaque value that can be divided by some number and passed to Set
-// (see Set documentation for more information).
-func (pwm PWM) Top() uint32 {
- if pwm.num == 1 {
- // Timer 1 has a configurable top value.
- low := avr.ICR1L.Get()
- high := avr.ICR1H.Get()
- return uint32(high)<<8 | uint32(low) + 1
- }
- // Other timers go from 0 to 0xff (0x100 or 256 in total).
- return 256
-}
-
-// Counter returns the current counter value of the timer in this PWM
-// peripheral. It may be useful for debugging.
-func (pwm PWM) Counter() uint32 {
- switch pwm.num {
- case 0:
- return uint32(avr.TCNT0.Get())
- case 1:
- mask := interrupt.Disable()
- low := avr.TCNT1L.Get()
- high := avr.TCNT1H.Get()
- interrupt.Restore(mask)
- return uint32(high)<<8 | uint32(low)
- case 2:
- return uint32(avr.TCNT2.Get())
- }
- // Unknown PWM.
- return 0
-}
-
-// Period returns the used PWM period in nanoseconds. It might deviate slightly
-// from the configured period due to rounding.
-func (pwm PWM) Period() uint64 {
- var prescaler uint8
- switch pwm.num {
- case 0:
- prescaler = avr.TCCR0B.Get() & 0x7
- case 1:
- prescaler = avr.TCCR1B.Get() & 0x7
- case 2:
- prescaler = avr.TCCR2B.Get() & 0x7
- }
- top := uint64(pwm.Top())
- switch prescaler {
- case 1: // prescaler 1
- return 1 * top * 1000 / uint64(CPUFrequency()/1e6)
- case 2: // prescaler 8
- return 8 * top * 1000 / uint64(CPUFrequency()/1e6)
- case 3: // prescaler 64
- return 64 * top * 1000 / uint64(CPUFrequency()/1e6)
- case 4: // prescaler 256
- return 256 * top * 1000 / uint64(CPUFrequency()/1e6)
- case 5: // prescaler 1024
- return 1024 * top * 1000 / uint64(CPUFrequency()/1e6)
- default: // unknown clock source
- return 0
- }
-}
-
-// Channel returns a PWM channel for the given pin.
-func (pwm PWM) Channel(pin Pin) (uint8, error) {
- pin.Configure(PinConfig{Mode: PinOutput})
- pin.Low()
- switch pwm.num {
- case 0:
- switch pin {
- case PD6: // channel A
- avr.TCCR0A.SetBits(avr.TCCR0A_COM0A1)
- return 0, nil
- case PD5: // channel B
- avr.TCCR0A.SetBits(avr.TCCR0A_COM0B1)
- return 1, nil
- }
- case 1:
- switch pin {
- case PB1: // channel A
- avr.TCCR1A.SetBits(avr.TCCR1A_COM1A1)
- return 0, nil
- case PB2: // channel B
- avr.TCCR1A.SetBits(avr.TCCR1A_COM1B1)
- return 1, nil
- }
- case 2:
- switch pin {
- case PB3: // channel A
- avr.TCCR2A.SetBits(avr.TCCR2A_COM2A1)
- return 0, nil
- case PD3: // channel B
- avr.TCCR2A.SetBits(avr.TCCR2A_COM2B1)
- return 1, nil
- }
- }
- return 0, ErrInvalidOutputPin
-}
-
-// SetInverting sets whether to invert the output of this channel.
-// Without inverting, a 25% duty cycle would mean the output is high for 25% of
-// the time and low for the rest. Inverting flips the output as if a NOT gate
-// was placed at the output, meaning that the output would be 25% low and 75%
-// high with a duty cycle of 25%.
-//
-// Note: the invert state may not be applied on the AVR until the next call to
-// ch.Set().
-func (pwm PWM) SetInverting(channel uint8, inverting bool) {
- switch pwm.num {
- case 0:
- switch channel {
- case 0: // channel A
- if inverting {
- avr.PORTB.SetBits(1 << 6) // PB6 high
- avr.TCCR0A.SetBits(avr.TCCR0A_COM0A0)
- } else {
- avr.PORTB.ClearBits(1 << 6) // PB6 low
- avr.TCCR0A.ClearBits(avr.TCCR0A_COM0A0)
- }
- case 1: // channel B
- if inverting {
- avr.PORTB.SetBits(1 << 5) // PB5 high
- avr.TCCR0A.SetBits(avr.TCCR0A_COM0B0)
- } else {
- avr.PORTB.ClearBits(1 << 5) // PB5 low
- avr.TCCR0A.ClearBits(avr.TCCR0A_COM0B0)
- }
- }
- case 1:
- // Note: the COM1A0/COM1B0 bit is not set with the configuration below.
- // It will be set the following call to Set(), however.
- switch channel {
- case 0: // channel A, PB1
- if inverting {
- avr.PORTB.SetBits(1 << 1) // PB1 high
- } else {
- avr.PORTB.ClearBits(1 << 1) // PB1 low
- }
- case 1: // channel B, PB2
- if inverting {
- avr.PORTB.SetBits(1 << 2) // PB2 high
- } else {
- avr.PORTB.ClearBits(1 << 2) // PB2 low
- }
- }
- case 2:
- switch channel {
- case 0: // channel A
- if inverting {
- avr.PORTB.SetBits(1 << 3) // PB3 high
- avr.TCCR2A.SetBits(avr.TCCR2A_COM2A0)
- } else {
- avr.PORTB.ClearBits(1 << 3) // PB3 low
- avr.TCCR2A.ClearBits(avr.TCCR2A_COM2A0)
- }
- case 1: // channel B
- if inverting {
- avr.PORTD.SetBits(1 << 3) // PD3 high
- avr.TCCR2A.SetBits(avr.TCCR2A_COM2B0)
- } else {
- avr.PORTD.ClearBits(1 << 3) // PD3 low
- avr.TCCR2A.ClearBits(avr.TCCR2A_COM2B0)
- }
- }
- }
-}
-
-// Set updates the channel value. This is used to control the channel duty
-// cycle, in other words the fraction of time the channel output is high (or low
-// when inverted). For example, to set it to a 25% duty cycle, use:
-//
-// pwm.Set(channel, pwm.Top() / 4)
-//
-// pwm.Set(channel, 0) will set the output to low and pwm.Set(channel,
-// pwm.Top()) will set the output to high, assuming the output isn't inverted.
-func (pwm PWM) Set(channel uint8, value uint32) {
- switch pwm.num {
- case 0:
- value := uint16(value)
- switch channel {
- case 0: // channel A
- if value == 0 {
- avr.TCCR0A.ClearBits(avr.TCCR0A_COM0A1)
- } else {
- avr.OCR0A.Set(uint8(value - 1))
- avr.TCCR0A.SetBits(avr.TCCR0A_COM0A1)
- }
- case 1: // channel B
- if value == 0 {
- avr.TCCR0A.ClearBits(avr.TCCR0A_COM0B1)
- } else {
- avr.OCR0B.Set(uint8(value) - 1)
- 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:
- mask := interrupt.Disable()
- switch channel {
- case 0: // channel A, PB1
- if value == 0 {
- avr.TCCR1A.ClearBits(avr.TCCR1A_COM1A1 | avr.TCCR1A_COM1A0)
- } else {
- value := uint16(value) - 1 // yes, this is safe (it relies on underflow)
- avr.OCR1AH.Set(uint8(value >> 8))
- avr.OCR1AL.Set(uint8(value))
- if avr.PORTB.HasBits(1 << 1) { // is PB1 high?
- // Yes, set the inverting bit.
- avr.TCCR1A.SetBits(avr.TCCR1A_COM1A1 | avr.TCCR1A_COM1A0)
- } else {
- // No, output is non-inverting.
- avr.TCCR1A.SetBits(avr.TCCR1A_COM1A1)
- }
- }
- case 1: // channel B, PB2
- if value == 0 {
- avr.TCCR1A.ClearBits(avr.TCCR1A_COM1B1 | avr.TCCR1A_COM1B0)
- } else {
- value := uint16(value) - 1 // yes, this is safe (it relies on underflow)
- avr.OCR1BH.Set(uint8(value >> 8))
- avr.OCR1BL.Set(uint8(value))
- if avr.PORTB.HasBits(1 << 2) { // is PB2 high?
- // Yes, set the inverting bit.
- avr.TCCR1A.SetBits(avr.TCCR1A_COM1B1 | avr.TCCR1A_COM1B0)
- } else {
- // No, output is non-inverting.
- avr.TCCR1A.SetBits(avr.TCCR1A_COM1B1)
- }
- }
- }
- interrupt.Restore(mask)
- case 2:
- value := uint16(value)
- switch channel {
- case 0: // channel A
- if value == 0 {
- avr.TCCR2A.ClearBits(avr.TCCR2A_COM2A1)
- } else {
- avr.OCR2A.Set(uint8(value - 1))
- avr.TCCR2A.SetBits(avr.TCCR2A_COM2A1)
- }
- case 1: // channel B
- if value == 0 {
- avr.TCCR2A.ClearBits(avr.TCCR2A_COM2B1)
- } else {
- avr.OCR2B.Set(uint8(value - 1))
- avr.TCCR2A.SetBits(avr.TCCR2A_COM2B1)
- }
- }
- }
+// I2C0 is the only I2C interface on most AVRs.
+var I2C0 = &I2C{
+ srReg: avr.TWSR,
+ brReg: avr.TWBR,
+ crReg: avr.TWCR,
+ drReg: avr.TWDR,
+ srPS0: avr.TWSR_TWPS0,
+ srPS1: avr.TWSR_TWPS1,
+ crEN: avr.TWCR_TWEN,
+ crINT: avr.TWCR_TWINT,
+ crSTO: avr.TWCR_TWSTO,
+ crEA: avr.TWCR_TWEA,
+ crSTA: avr.TWCR_TWSTA,
}
// SPI configuration
@@ -462,111 +29,32 @@ var SPI0 = SPI{
spcr: avr.SPCR,
spdr: avr.SPDR,
spsr: avr.SPSR,
- sck: PB5,
- sdo: PB3,
- sdi: PB4,
- cs: PB2}
-
-// Pin Change Interrupts
-type PinChange uint8
-const (
- PinRising PinChange = 1 << iota
- PinFalling
- PinToggle = PinRising | PinFalling
-)
+ spcrR0: avr.SPCR_SPR0,
+ spcrR1: avr.SPCR_SPR1,
+ spcrCPHA: avr.SPCR_CPHA,
+ spcrCPOL: avr.SPCR_CPOL,
+ spcrDORD: avr.SPCR_DORD,
+ spcrSPE: avr.SPCR_SPE,
+ spcrMSTR: avr.SPCR_MSTR,
-func (pin Pin) SetInterrupt(pinChange PinChange, callback func(Pin)) (err error) {
+ spsrI2X: avr.SPSR_SPI2X,
+ spsrSPIF: avr.SPSR_SPIF,
- switch {
- case pin >= PB0 && pin <= PB7:
- // PCMSK0 - PCINT0-7
- pinStates[0] = avr.PINB.Get()
- pinIndex := pin - PB0
- if pinChange&PinRising > 0 {
- pinCallbacks[0][pinIndex][0] = callback
- }
- if pinChange&PinFalling > 0 {
- pinCallbacks[0][pinIndex][1] = callback
- }
- if callback != nil {
- avr.PCMSK0.SetBits(1 << pinIndex)
- } else {
- avr.PCMSK0.ClearBits(1 << pinIndex)
- }
- avr.PCICR.SetBits(avr.PCICR_PCIE0)
- interrupt.New(avr.IRQ_PCINT0, handlePCINT0Interrupts)
- case pin >= PC0 && pin <= PC7:
- // PCMSK1 - PCINT8-14
- pinStates[1] = avr.PINC.Get()
- pinIndex := pin - PC0
- if pinChange&PinRising > 0 {
- pinCallbacks[1][pinIndex][0] = callback
- }
- if pinChange&PinFalling > 0 {
- pinCallbacks[1][pinIndex][1] = callback
- }
- if callback != nil {
- avr.PCMSK1.SetBits(1 << pinIndex)
- } else {
- avr.PCMSK1.ClearBits(1 << pinIndex)
- }
- avr.PCICR.SetBits(avr.PCICR_PCIE1)
- interrupt.New(avr.IRQ_PCINT1, handlePCINT1Interrupts)
- case pin >= PD0 && pin <= PD7:
- // PCMSK2 - PCINT16-23
- pinStates[2] = avr.PIND.Get()
- pinIndex := pin - PD0
- if pinChange&PinRising > 0 {
- pinCallbacks[2][pinIndex][0] = callback
- }
- if pinChange&PinFalling > 0 {
- pinCallbacks[2][pinIndex][1] = callback
- }
- if callback != nil {
- avr.PCMSK2.SetBits(1 << pinIndex)
- } else {
- avr.PCMSK2.ClearBits(1 << pinIndex)
- }
- avr.PCICR.SetBits(avr.PCICR_PCIE2)
- interrupt.New(avr.IRQ_PCINT2, handlePCINT2Interrupts)
- default:
- return ErrInvalidInputPin
- }
-
- return nil
+ sck: PB5,
+ sdo: PB3,
+ sdi: PB4,
+ cs: PB2,
}
-var pinCallbacks [3][8][2]func(Pin)
-var pinStates [3]uint8
-
-func handlePCINTInterrupts(intr uint8, port *volatile.Register8) {
- current := port.Get()
- change := pinStates[intr] ^ current
- pinStates[intr] = current
- for i := uint8(0); i < 8; i++ {
- if (change>>i)&0x01 != 0x01 {
- continue
- }
- pin := Pin(intr*8 + i)
- value := pin.Get()
- if value && pinCallbacks[intr][i][0] != nil {
- pinCallbacks[intr][i][0](pin)
- }
- if !value && pinCallbacks[intr][i][1] != nil {
- pinCallbacks[intr][i][1](pin)
- }
+// getPortMask returns the PORTx register and mask for the pin.
+func (p Pin) getPortMask() (*volatile.Register8, uint8) {
+ 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)
}
}
-
-func handlePCINT0Interrupts(intr interrupt.Interrupt) {
- handlePCINTInterrupts(0, avr.PINB)
-}
-
-func handlePCINT1Interrupts(intr interrupt.Interrupt) {
- handlePCINTInterrupts(1, avr.PINC)
-}
-
-func handlePCINT2Interrupts(intr interrupt.Interrupt) {
- handlePCINTInterrupts(2, avr.PIND)
-}
diff --git a/src/machine/machine_atmega328pb.go b/src/machine/machine_atmega328pb.go
index af36b63e1..091fb6337 100644
--- a/src/machine/machine_atmega328pb.go
+++ b/src/machine/machine_atmega328pb.go
@@ -4,89 +4,59 @@ package machine
import (
"device/avr"
+ "runtime/interrupt"
"runtime/volatile"
)
const irq_USART0_RX = avr.IRQ_USART0_RX
+const irq_USART1_RX = avr.IRQ_USART1_RX
-// getPortMask returns the PORTx register and mask for the pin.
-func (p Pin) getPortMask() (*volatile.Register8, uint8) {
- 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)
- }
-}
+var (
+ UART1 = &_UART1
+ _UART1 = UART{
+ Buffer: NewRingBuffer(),
-// InitPWM initializes the registers needed for PWM.
-func InitPWM() {
- // use waveform generation
- avr.TCCR0A.SetBits(avr.TCCR0A_WGM00)
-
- // set timer 0 prescale factor to 64
- avr.TCCR0B.SetBits(avr.TCCR0B_CS01 | avr.TCCR0B_CS00)
-
- // set timer 1 prescale factor to 64
- avr.TCCR1B.SetBits(avr.TCCR1B_CS11)
-
- // put timer 1 in 8-bit phase correct pwm mode
- avr.TCCR1A.SetBits(avr.TCCR1A_WGM10)
-
- // set timer 2 prescale factor to 64
- avr.TCCR2B.SetBits(avr.TCCR2B_CS22)
+ dataReg: avr.UDR1,
+ baudRegH: avr.UBRR1H,
+ baudRegL: avr.UBRR1L,
+ statusRegA: avr.UCSR1A,
+ statusRegB: avr.UCSR1B,
+ statusRegC: avr.UCSR1C,
+ }
+)
- // configure timer 2 for phase correct pwm (8-bit)
- avr.TCCR2A.SetBits(avr.TCCR2A_WGM20)
+func init() {
+ // Register the UART interrupt.
+ interrupt.New(irq_USART1_RX, _UART1.handleInterrupt)
}
-// Configure configures a PWM pin for output.
-func (pwm PWM) Configure() error {
- switch pwm.Pin / 8 {
- case 0: // port B
- avr.DDRB.SetBits(1 << uint8(pwm.Pin))
- case 2: // port D
- avr.DDRD.SetBits(1 << uint8(pwm.Pin-16))
- }
- return nil
+// I2C0 is the only I2C interface on most AVRs.
+var I2C0 = &I2C{
+ srReg: avr.TWSR0,
+ brReg: avr.TWBR0,
+ crReg: avr.TWCR0,
+ drReg: avr.TWDR0,
+ srPS0: avr.TWSR0_TWPS0,
+ srPS1: avr.TWSR0_TWPS1,
+ crEN: avr.TWCR0_TWEN,
+ crINT: avr.TWCR0_TWINT,
+ crSTO: avr.TWCR0_TWSTO,
+ crEA: avr.TWCR0_TWEA,
+ crSTA: avr.TWCR0_TWSTA,
}
-// Set turns on the duty cycle for a PWM pin using the provided value. On the AVR this is normally a
-// 8-bit value ranging from 0 to 255.
-func (pwm PWM) Set(value uint16) {
- value8 := uint8(value >> 8)
- switch pwm.Pin {
- case PD3:
- // connect pwm to pin on timer 2, channel B
- avr.TCCR2A.SetBits(avr.TCCR2A_COM2B1)
- avr.OCR2B.Set(value8) // set pwm duty
- case PD5:
- // connect pwm to pin on timer 0, channel B
- avr.TCCR0A.SetBits(avr.TCCR0A_COM0B1)
- avr.OCR0B.Set(value8) // set pwm duty
- case PD6:
- // connect pwm to pin on timer 0, channel A
- avr.TCCR0A.SetBits(avr.TCCR0A_COM0A1)
- avr.OCR0A.Set(value8) // set pwm duty
- case PB1:
- // connect pwm to pin on timer 1, channel A
- avr.TCCR1A.SetBits(avr.TCCR1A_COM1A1)
- // this is a 16-bit value, but we only currently allow the low order bits to be set
- avr.OCR1AL.Set(value8) // set pwm duty
- case PB2:
- // connect pwm to pin on timer 1, channel B
- avr.TCCR1A.SetBits(avr.TCCR1A_COM1B1)
- // this is a 16-bit value, but we only currently allow the low order bits to be set
- avr.OCR1BL.Set(value8) // set pwm duty
- case PB3:
- // connect pwm to pin on timer 2, channel A
- avr.TCCR2A.SetBits(avr.TCCR2A_COM2A1)
- avr.OCR2A.Set(value8) // set pwm duty
- default:
- panic("Invalid PWM pin")
- }
+var I2C1 = &I2C{
+ srReg: avr.TWSR1,
+ brReg: avr.TWBR1,
+ crReg: avr.TWCR1,
+ drReg: avr.TWDR1,
+ srPS0: avr.TWSR1_TWPS10,
+ srPS1: avr.TWSR1_TWPS11,
+ crEN: avr.TWCR1_TWEN1,
+ crINT: avr.TWCR1_TWINT1,
+ crSTO: avr.TWCR1_TWSTO1,
+ crEA: avr.TWCR1_TWEA1,
+ crSTA: avr.TWCR1_TWSTA1,
}
// SPI configuration
@@ -94,16 +64,56 @@ var SPI0 = SPI{
spcr: avr.SPCR0,
spdr: avr.SPDR0,
spsr: avr.SPSR0,
- sck: PB5,
- sdo: PB3,
- sdi: PB4,
- cs: PB2}
+
+ spcrR0: avr.SPCR0_SPR0,
+ spcrR1: avr.SPCR0_SPR1,
+ spcrCPHA: avr.SPCR0_CPHA,
+ spcrCPOL: avr.SPCR0_CPOL,
+ spcrDORD: avr.SPCR0_DORD,
+ spcrSPE: avr.SPCR0_SPE,
+ spcrMSTR: avr.SPCR0_MSTR,
+
+ spsrI2X: avr.SPSR0_SPI2X,
+ spsrSPIF: avr.SPSR0_SPIF,
+
+ sck: PB5,
+ sdo: PB3,
+ sdi: PB4,
+ cs: PB2,
+}
var SPI1 = SPI{
spcr: avr.SPCR1,
spdr: avr.SPDR1,
spsr: avr.SPSR1,
- sck: PC1,
- sdo: PE3,
- sdi: PC0,
- cs: PE2}
+
+ spcrR0: avr.SPCR1_SPR10,
+ spcrR1: avr.SPCR1_SPR11,
+ spcrCPHA: avr.SPCR1_CPHA1,
+ spcrCPOL: avr.SPCR1_CPOL1,
+ spcrDORD: avr.SPCR1_DORD1,
+ spcrSPE: avr.SPCR1_SPE1,
+ spcrMSTR: avr.SPCR1_MSTR1,
+
+ spsrI2X: avr.SPSR1_SPI2X1,
+ spsrSPIF: avr.SPSR1_SPIF1,
+
+ sck: PC1,
+ sdo: PE3,
+ sdi: PC0,
+ cs: PE2,
+}
+
+// getPortMask returns the PORTx register and mask for the pin.
+func (p Pin) getPortMask() (*volatile.Register8, uint8) {
+ 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)
+ case p >= PD0 && p <= PD7: // port D
+ return avr.PORTD, 1 << uint8(p-portD)
+ default: // port E
+ return avr.PORTE, 1 << uint8(p-portE)
+ }
+}
diff --git a/targets/atmega328pb.json b/targets/atmega328pb.json
new file mode 100644
index 000000000..c4e1b447f
--- /dev/null
+++ b/targets/atmega328pb.json
@@ -0,0 +1,15 @@
+{
+ "inherits": ["avr"],
+ "cpu": "atmega328pb",
+ "build-tags": ["atmega328pb", "atmega", "avr5"],
+ "ldflags": [
+ "--defsym=_bootloader_size=512",
+ "--defsym=_stack_size=512"
+ ],
+ "serial": "uart",
+ "linkerscript": "src/device/avr/atmega328pb.ld",
+ "extra-files": [
+ "targets/avr.S",
+ "src/device/avr/atmega328pb.s"
+ ]
+}