diff options
-rw-r--r-- | GNUmakefile | 2 | ||||
-rw-r--r-- | src/machine/board_arduino.go | 5 | ||||
-rw-r--r-- | src/machine/board_arduino_nano.go | 5 | ||||
-rw-r--r-- | src/machine/board_atmega328p.go | 5 | ||||
-rw-r--r-- | src/machine/board_atmega328pb.go | 51 | ||||
-rw-r--r-- | src/machine/machine_atmega.go | 80 | ||||
-rw-r--r-- | src/machine/machine_atmega328.go | 548 | ||||
-rw-r--r-- | src/machine/machine_atmega328p.go | 582 | ||||
-rw-r--r-- | src/machine/machine_atmega328pb.go | 170 | ||||
-rw-r--r-- | targets/atmega328pb.json | 15 |
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" + ] +} |