machine/atmega328pb: refactor to enable extra uart
Этот коммит содержится в:
родитель
2d289addb7
коммит
6420e90124
10 изменённых файлов: 807 добавлений и 678 удалений
|
@ -718,6 +718,8 @@ ifneq ($(STM32), 0)
|
||||||
$(TINYGO) build -size short -o test.hex -target=swan examples/blinky1
|
$(TINYGO) build -size short -o test.hex -target=swan examples/blinky1
|
||||||
@$(MD5SUM) test.hex
|
@$(MD5SUM) test.hex
|
||||||
endif
|
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
|
$(TINYGO) build -size short -o test.hex -target=atmega1284p examples/serial
|
||||||
@$(MD5SUM) test.hex
|
@$(MD5SUM) test.hex
|
||||||
$(TINYGO) build -size short -o test.hex -target=arduino examples/blinky1
|
$(TINYGO) build -size short -o test.hex -target=arduino examples/blinky1
|
||||||
|
|
|
@ -2,11 +2,6 @@
|
||||||
|
|
||||||
package machine
|
package machine
|
||||||
|
|
||||||
// Return the current CPU frequency in hertz.
|
|
||||||
func CPUFrequency() uint32 {
|
|
||||||
return 16000000
|
|
||||||
}
|
|
||||||
|
|
||||||
// Digital pins, marked as plain numbers on the board.
|
// Digital pins, marked as plain numbers on the board.
|
||||||
const (
|
const (
|
||||||
D0 = PD0 // RX
|
D0 = PD0 // RX
|
||||||
|
|
|
@ -2,11 +2,6 @@
|
||||||
|
|
||||||
package machine
|
package machine
|
||||||
|
|
||||||
// Return the current CPU frequency in hertz.
|
|
||||||
func CPUFrequency() uint32 {
|
|
||||||
return 16000000
|
|
||||||
}
|
|
||||||
|
|
||||||
// Digital pins.
|
// Digital pins.
|
||||||
const (
|
const (
|
||||||
D0 = PD0 // RX0
|
D0 = PD0 // RX0
|
||||||
|
|
|
@ -2,6 +2,11 @@
|
||||||
|
|
||||||
package machine
|
package machine
|
||||||
|
|
||||||
|
// Return the current CPU frequency in hertz.
|
||||||
|
func CPUFrequency() uint32 {
|
||||||
|
return 16000000
|
||||||
|
}
|
||||||
|
|
||||||
const (
|
const (
|
||||||
// Note: start at port B because there is no port A.
|
// Note: start at port B because there is no port A.
|
||||||
portB Pin = iota * 8
|
portB Pin = iota * 8
|
||||||
|
|
51
src/machine/board_atmega328pb.go
Обычный файл
51
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
|
||||||
|
)
|
|
@ -11,10 +11,19 @@ import (
|
||||||
|
|
||||||
// I2C on AVR.
|
// I2C on AVR.
|
||||||
type I2C struct {
|
type I2C struct {
|
||||||
}
|
srReg *volatile.Register8
|
||||||
|
brReg *volatile.Register8
|
||||||
|
crReg *volatile.Register8
|
||||||
|
drReg *volatile.Register8
|
||||||
|
|
||||||
// I2C0 is the only I2C interface on most AVRs.
|
srPS0 byte
|
||||||
var I2C0 *I2C = nil
|
srPS1 byte
|
||||||
|
crEN byte
|
||||||
|
crINT byte
|
||||||
|
crSTO byte
|
||||||
|
crEA byte
|
||||||
|
crSTA byte
|
||||||
|
}
|
||||||
|
|
||||||
// I2CConfig is used to store config info for I2C.
|
// I2CConfig is used to store config info for I2C.
|
||||||
type I2CConfig struct {
|
type I2CConfig struct {
|
||||||
|
@ -37,16 +46,16 @@ func (i2c *I2C) Configure(config I2CConfig) error {
|
||||||
// SetBaudRate sets the communication speed for I2C.
|
// SetBaudRate sets the communication speed for I2C.
|
||||||
func (i2c *I2C) SetBaudRate(br uint32) error {
|
func (i2c *I2C) SetBaudRate(br uint32) error {
|
||||||
// Initialize twi prescaler and bit rate.
|
// 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:
|
// twi bit rate formula from atmega128 manual pg. 204:
|
||||||
// SCL Frequency = CPU Clock Frequency / (16 + (2 * TWBR))
|
// SCL Frequency = CPU Clock Frequency / (16 + (2 * TWBR))
|
||||||
// NOTE: TWBR should be 10 or higher for controller mode.
|
// NOTE: TWBR should be 10 or higher for controller mode.
|
||||||
// It is 72 for a 16mhz board with 100kHz TWI
|
// 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.
|
// Enable twi module.
|
||||||
avr.TWCR.Set(avr.TWCR_TWEN)
|
i2c.crReg.Set(i2c.crEN)
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -77,10 +86,10 @@ func (i2c *I2C) Tx(addr uint16, w, r []byte) error {
|
||||||
// start starts an I2C communication session.
|
// start starts an I2C communication session.
|
||||||
func (i2c *I2C) start(address uint8, write bool) {
|
func (i2c *I2C) start(address uint8, write bool) {
|
||||||
// Clear TWI interrupt flag, put start condition on SDA, and enable TWI.
|
// 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.
|
// 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.
|
// Write 7-bit shifted peripheral address.
|
||||||
|
@ -94,23 +103,23 @@ func (i2c *I2C) start(address uint8, write bool) {
|
||||||
// stop ends an I2C communication session.
|
// stop ends an I2C communication session.
|
||||||
func (i2c *I2C) stop() {
|
func (i2c *I2C) stop() {
|
||||||
// Send stop condition.
|
// 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.
|
// 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.
|
// writeByte writes a single byte to the I2C bus.
|
||||||
func (i2c *I2C) writeByte(data byte) error {
|
func (i2c *I2C) writeByte(data byte) error {
|
||||||
// Write data to register.
|
// Write data to register.
|
||||||
avr.TWDR.Set(data)
|
i2c.drReg.Set(data)
|
||||||
|
|
||||||
// Clear TWI interrupt flag and enable TWI.
|
// 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.
|
// Wait till data is transmitted.
|
||||||
for !avr.TWCR.HasBits(avr.TWCR_TWINT) {
|
for !i2c.crReg.HasBits(i2c.crINT) {
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -118,13 +127,13 @@ func (i2c *I2C) writeByte(data byte) error {
|
||||||
// readByte reads a single byte from the I2C bus.
|
// readByte reads a single byte from the I2C bus.
|
||||||
func (i2c *I2C) readByte() byte {
|
func (i2c *I2C) readByte() byte {
|
||||||
// Clear TWI interrupt flag and enable TWI.
|
// 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.
|
// 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.
|
// Always use UART0 as the serial output.
|
||||||
|
@ -221,6 +230,17 @@ type SPI struct {
|
||||||
spdr *volatile.Register8
|
spdr *volatile.Register8
|
||||||
spsr *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
|
// The io pins for the SPIx port set by the chip
|
||||||
sck Pin
|
sck Pin
|
||||||
sdi Pin
|
sdi Pin
|
||||||
|
@ -264,39 +284,39 @@ func (s SPI) Configure(config SPIConfig) error {
|
||||||
|
|
||||||
switch {
|
switch {
|
||||||
case frequencyDivider >= 128:
|
case frequencyDivider >= 128:
|
||||||
s.spcr.SetBits(avr.SPCR_SPR0 | avr.SPCR_SPR1)
|
s.spcr.SetBits(s.spcrR0 | s.spcrR1)
|
||||||
case frequencyDivider >= 64:
|
case frequencyDivider >= 64:
|
||||||
s.spcr.SetBits(avr.SPCR_SPR1)
|
s.spcr.SetBits(s.spcrR1)
|
||||||
case frequencyDivider >= 32:
|
case frequencyDivider >= 32:
|
||||||
s.spcr.SetBits(avr.SPCR_SPR1)
|
s.spcr.SetBits(s.spcrR1)
|
||||||
s.spsr.SetBits(avr.SPSR_SPI2X)
|
s.spsr.SetBits(s.spsrI2X)
|
||||||
case frequencyDivider >= 16:
|
case frequencyDivider >= 16:
|
||||||
s.spcr.SetBits(avr.SPCR_SPR0)
|
s.spcr.SetBits(s.spcrR0)
|
||||||
case frequencyDivider >= 8:
|
case frequencyDivider >= 8:
|
||||||
s.spcr.SetBits(avr.SPCR_SPR0)
|
s.spcr.SetBits(s.spcrR0)
|
||||||
s.spsr.SetBits(avr.SPSR_SPI2X)
|
s.spsr.SetBits(s.spsrI2X)
|
||||||
case frequencyDivider >= 4:
|
case frequencyDivider >= 4:
|
||||||
// The clock is already set to all 0's.
|
// The clock is already set to all 0's.
|
||||||
default: // defaults to fastest which is /2
|
default: // defaults to fastest which is /2
|
||||||
s.spsr.SetBits(avr.SPSR_SPI2X)
|
s.spsr.SetBits(s.spsrI2X)
|
||||||
}
|
}
|
||||||
|
|
||||||
switch config.Mode {
|
switch config.Mode {
|
||||||
case Mode1:
|
case Mode1:
|
||||||
s.spcr.SetBits(avr.SPCR_CPHA)
|
s.spcr.SetBits(s.spcrCPHA)
|
||||||
case Mode2:
|
case Mode2:
|
||||||
s.spcr.SetBits(avr.SPCR_CPOL)
|
s.spcr.SetBits(s.spcrCPHA)
|
||||||
case Mode3:
|
case Mode3:
|
||||||
s.spcr.SetBits(avr.SPCR_CPHA | avr.SPCR_CPOL)
|
s.spcr.SetBits(s.spcrCPHA | s.spcrCPOL)
|
||||||
default: // default is mode 0
|
default: // default is mode 0
|
||||||
}
|
}
|
||||||
|
|
||||||
if config.LSBFirst {
|
if config.LSBFirst {
|
||||||
s.spcr.SetBits(avr.SPCR_DORD)
|
s.spcr.SetBits(s.spcrDORD)
|
||||||
}
|
}
|
||||||
|
|
||||||
// enable SPI, set controller, set clock rate
|
// 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
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -305,7 +325,7 @@ func (s SPI) Configure(config SPIConfig) error {
|
||||||
func (s SPI) Transfer(b byte) (byte, error) {
|
func (s SPI) Transfer(b byte) (byte, error) {
|
||||||
s.spdr.Set(uint8(b))
|
s.spdr.Set(uint8(b))
|
||||||
|
|
||||||
for !s.spsr.HasBits(avr.SPSR_SPIF) {
|
for !s.spsr.HasBits(s.spsrSPIF) {
|
||||||
}
|
}
|
||||||
|
|
||||||
return byte(s.spdr.Get()), nil
|
return byte(s.spdr.Get()), nil
|
||||||
|
|
548
src/machine/machine_atmega328.go
Обычный файл
548
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)
|
||||||
|
}
|
|
@ -4,12 +4,49 @@ package machine
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"device/avr"
|
"device/avr"
|
||||||
"runtime/interrupt"
|
|
||||||
"runtime/volatile"
|
"runtime/volatile"
|
||||||
)
|
)
|
||||||
|
|
||||||
const irq_USART0_RX = avr.IRQ_USART_RX
|
const irq_USART0_RX = avr.IRQ_USART_RX
|
||||||
|
|
||||||
|
// 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
|
||||||
|
var SPI0 = SPI{
|
||||||
|
spcr: avr.SPCR,
|
||||||
|
spdr: avr.SPDR,
|
||||||
|
spsr: avr.SPSR,
|
||||||
|
|
||||||
|
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,
|
||||||
|
|
||||||
|
spsrI2X: avr.SPSR_SPI2X,
|
||||||
|
spsrSPIF: avr.SPSR_SPIF,
|
||||||
|
|
||||||
|
sck: PB5,
|
||||||
|
sdo: PB3,
|
||||||
|
sdi: PB4,
|
||||||
|
cs: PB2,
|
||||||
|
}
|
||||||
|
|
||||||
// getPortMask returns the PORTx register and mask for the pin.
|
// getPortMask returns the PORTx register and mask for the pin.
|
||||||
func (p Pin) getPortMask() (*volatile.Register8, uint8) {
|
func (p Pin) getPortMask() (*volatile.Register8, uint8) {
|
||||||
switch {
|
switch {
|
||||||
|
@ -21,552 +58,3 @@ func (p Pin) getPortMask() (*volatile.Register8, uint8) {
|
||||||
return avr.PORTD, 1 << uint8(p-portD)
|
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)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// SPI configuration
|
|
||||||
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
|
|
||||||
)
|
|
||||||
|
|
||||||
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)
|
|
||||||
}
|
|
||||||
|
|
|
@ -4,10 +4,105 @@ package machine
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"device/avr"
|
"device/avr"
|
||||||
|
"runtime/interrupt"
|
||||||
"runtime/volatile"
|
"runtime/volatile"
|
||||||
)
|
)
|
||||||
|
|
||||||
const irq_USART0_RX = avr.IRQ_USART0_RX
|
const irq_USART0_RX = avr.IRQ_USART0_RX
|
||||||
|
const irq_USART1_RX = avr.IRQ_USART1_RX
|
||||||
|
|
||||||
|
var (
|
||||||
|
UART1 = &_UART1
|
||||||
|
_UART1 = UART{
|
||||||
|
Buffer: NewRingBuffer(),
|
||||||
|
|
||||||
|
dataReg: avr.UDR1,
|
||||||
|
baudRegH: avr.UBRR1H,
|
||||||
|
baudRegL: avr.UBRR1L,
|
||||||
|
statusRegA: avr.UCSR1A,
|
||||||
|
statusRegB: avr.UCSR1B,
|
||||||
|
statusRegC: avr.UCSR1C,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
// Register the UART interrupt.
|
||||||
|
interrupt.New(irq_USART1_RX, _UART1.handleInterrupt)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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,
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
var SPI0 = SPI{
|
||||||
|
spcr: avr.SPCR0,
|
||||||
|
spdr: avr.SPDR0,
|
||||||
|
spsr: avr.SPSR0,
|
||||||
|
|
||||||
|
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,
|
||||||
|
|
||||||
|
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.
|
// getPortMask returns the PORTx register and mask for the pin.
|
||||||
func (p Pin) getPortMask() (*volatile.Register8, uint8) {
|
func (p Pin) getPortMask() (*volatile.Register8, uint8) {
|
||||||
|
@ -16,94 +111,9 @@ func (p Pin) getPortMask() (*volatile.Register8, uint8) {
|
||||||
return avr.PORTB, 1 << uint8(p-portB)
|
return avr.PORTB, 1 << uint8(p-portB)
|
||||||
case p >= PC0 && p <= PC7: // port C
|
case p >= PC0 && p <= PC7: // port C
|
||||||
return avr.PORTC, 1 << uint8(p-portC)
|
return avr.PORTC, 1 << uint8(p-portC)
|
||||||
default: // port D
|
case p >= PD0 && p <= PD7: // port D
|
||||||
return avr.PORTD, 1 << uint8(p-portD)
|
return avr.PORTD, 1 << uint8(p-portD)
|
||||||
|
default: // port E
|
||||||
|
return avr.PORTE, 1 << uint8(p-portE)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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)
|
|
||||||
|
|
||||||
// configure timer 2 for phase correct pwm (8-bit)
|
|
||||||
avr.TCCR2A.SetBits(avr.TCCR2A_WGM20)
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// SPI configuration
|
|
||||||
var SPI0 = SPI{
|
|
||||||
spcr: avr.SPCR0,
|
|
||||||
spdr: avr.SPDR0,
|
|
||||||
spsr: avr.SPSR0,
|
|
||||||
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}
|
|
||||||
|
|
15
targets/atmega328pb.json
Обычный файл
15
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"
|
||||||
|
]
|
||||||
|
}
|
Загрузка…
Создание таблицы
Сослаться в новой задаче