PWM Support for atmega1280
Add arduino mega 1280 PWM test
Этот коммит содержится в:
родитель
5707022951
коммит
cb886a35c9
3 изменённых файлов: 809 добавлений и 1 удалений
2
Makefile
2
Makefile
|
@ -380,6 +380,8 @@ ifneq ($(AVR), 0)
|
|||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=arduino-mega1280 examples/blinky1
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=arduino-mega1280 examples/pwm
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=arduino-nano examples/blinky1
|
||||
@$(MD5SUM) test.hex
|
||||
$(TINYGO) build -size short -o test.hex -target=digispark examples/blinky1
|
||||
|
|
12
src/examples/pwm/arduino-mega1280.go
Обычный файл
12
src/examples/pwm/arduino-mega1280.go
Обычный файл
|
@ -0,0 +1,12 @@
|
|||
// +build arduino_mega1280
|
||||
|
||||
package main
|
||||
|
||||
import "machine"
|
||||
|
||||
var (
|
||||
// Configuration on an Arduino Uno.
|
||||
pwm = machine.Timer3
|
||||
pinA = machine.PH3 // pin 6 on the Mega
|
||||
pinB = machine.PH4 // pin 7 on the Mega
|
||||
)
|
|
@ -5,6 +5,7 @@ package machine
|
|||
import (
|
||||
"device/avr"
|
||||
"runtime/volatile"
|
||||
"runtime/interrupt"
|
||||
)
|
||||
|
||||
const irq_USART0_RX = avr.IRQ_USART0_RX
|
||||
|
@ -127,6 +128,799 @@ func (p Pin) getPortMask() (*volatile.Register8, uint8) {
|
|||
}
|
||||
}
|
||||
|
||||
// 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 PB7 and PG5
|
||||
Timer1 = PWM{1} // 16 bit timer for PB5 and PB6
|
||||
Timer2 = PWM{2} // 8 bit timer for PB4 and PH6
|
||||
Timer3 = PWM{3} // 16 bit timer for PE3, PE4 and PE5
|
||||
Timer4 = PWM{4} // 16 bit timer for PH3, PH4 and PH5
|
||||
Timer5 = PWM{5} // 16 bit timer for PL3, PL4 and PL5
|
||||
)
|
||||
|
||||
// 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)
|
||||
} 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, 3, 4, 5:
|
||||
// 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
|
||||
}
|
||||
|
||||
|
||||
// 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
|
||||
var prescalingTop uint8
|
||||
switch {
|
||||
case top <= maxTop:
|
||||
prescalingTop = 3<<3 | 1 // no prescaling
|
||||
case top/8 <= maxTop:
|
||||
prescalingTop = 3<<3 | 2 // divide by 8
|
||||
top /= 8
|
||||
case top/64 <= maxTop:
|
||||
prescalingTop = 3<<3 | 3 // divide by 64
|
||||
top /= 64
|
||||
case top/256 <= maxTop:
|
||||
prescalingTop = 3<<3 | 4 // divide by 256
|
||||
top /= 256
|
||||
case top/1024 <= maxTop:
|
||||
prescalingTop = 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
|
||||
|
||||
switch pwm.num {
|
||||
case 1:
|
||||
avr.TCCR1A.Set(avr.TCCR1A_WGM11)
|
||||
avr.TCCR1B.Set(prescalingTop)
|
||||
avr.ICR1H.Set(uint8(top >> 8))
|
||||
avr.ICR1L.Set(uint8(top))
|
||||
case 3:
|
||||
avr.TCCR3A.Set(avr.TCCR3A_WGM31)
|
||||
avr.TCCR3B.Set(prescalingTop)
|
||||
avr.ICR3H.Set(uint8(top >> 8))
|
||||
avr.ICR3L.Set(uint8(top))
|
||||
case 4:
|
||||
avr.TCCR4A.Set(avr.TCCR4A_WGM41)
|
||||
avr.TCCR4B.Set(prescalingTop)
|
||||
avr.ICR4H.Set(uint8(top >> 8))
|
||||
avr.ICR4L.Set(uint8(top))
|
||||
case 5:
|
||||
avr.TCCR5A.Set(avr.TCCR5A_WGM51)
|
||||
avr.TCCR5B.Set(prescalingTop)
|
||||
avr.ICR5H.Set(uint8(top >> 8))
|
||||
avr.ICR5L.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 == 0 || pwm.num == 2 {
|
||||
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
|
||||
}
|
||||
|
||||
var prescaler uint8
|
||||
|
||||
switch pwm.num {
|
||||
case 1:
|
||||
prescaler = avr.TCCR1B.Get() & 0x7
|
||||
case 3:
|
||||
prescaler = avr.TCCR3B.Get() & 0x7
|
||||
case 4:
|
||||
prescaler = avr.TCCR4B.Get() & 0x7
|
||||
case 5:
|
||||
prescaler = avr.TCCR5B.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
|
||||
}
|
||||
|
||||
switch pwm.num {
|
||||
case 1:
|
||||
// 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)
|
||||
case 3:
|
||||
// Warning: this change is not atomic!
|
||||
avr.ICR3H.Set(uint8(top >> 8))
|
||||
avr.ICR3L.Set(uint8(top))
|
||||
|
||||
// ... and because of that, set the counter back to zero to avoid most of
|
||||
// the effects of this non-atomicity.
|
||||
avr.TCNT3H.Set(0)
|
||||
avr.TCNT3L.Set(0)
|
||||
case 4:
|
||||
// Warning: this change is not atomic!
|
||||
avr.ICR4H.Set(uint8(top >> 8))
|
||||
avr.ICR4L.Set(uint8(top))
|
||||
|
||||
// ... and because of that, set the counter back to zero to avoid most of
|
||||
// the effects of this non-atomicity.
|
||||
avr.TCNT4H.Set(0)
|
||||
avr.TCNT4L.Set(0)
|
||||
case 5:
|
||||
// Warning: this change is not atomic!
|
||||
avr.ICR5H.Set(uint8(top >> 8))
|
||||
avr.ICR5L.Set(uint8(top))
|
||||
|
||||
// ... and because of that, set the counter back to zero to avoid most of
|
||||
// the effects of this non-atomicity.
|
||||
avr.TCNT5H.Set(0)
|
||||
avr.TCNT5L.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 {
|
||||
switch pwm.num {
|
||||
case 1:
|
||||
// Timer 1 has a configurable top value.
|
||||
low := avr.ICR1L.Get()
|
||||
high := avr.ICR1H.Get()
|
||||
return uint32(high)<<8 | uint32(low) + 1
|
||||
case 3:
|
||||
// Timer 3 has a configurable top value.
|
||||
low := avr.ICR3L.Get()
|
||||
high := avr.ICR3H.Get()
|
||||
return uint32(high)<<8 | uint32(low) + 1
|
||||
case 4:
|
||||
// Timer 4 has a configurable top value.
|
||||
low := avr.ICR4L.Get()
|
||||
high := avr.ICR4H.Get()
|
||||
return uint32(high)<<8 | uint32(low) + 1
|
||||
case 5:
|
||||
// Timer 5 has a configurable top value.
|
||||
low := avr.ICR5L.Get()
|
||||
high := avr.ICR5H.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())
|
||||
case 3:
|
||||
mask := interrupt.Disable()
|
||||
low := avr.TCNT3L.Get()
|
||||
high := avr.TCNT3H.Get()
|
||||
interrupt.Restore(mask)
|
||||
return uint32(high)<<8 | uint32(low)
|
||||
case 4:
|
||||
mask := interrupt.Disable()
|
||||
low := avr.TCNT4L.Get()
|
||||
high := avr.TCNT4H.Get()
|
||||
interrupt.Restore(mask)
|
||||
return uint32(high)<<8 | uint32(low)
|
||||
case 5:
|
||||
mask := interrupt.Disable()
|
||||
low := avr.TCNT5L.Get()
|
||||
high := avr.TCNT5H.Get()
|
||||
interrupt.Restore(mask)
|
||||
return uint32(high)<<8 | uint32(low)
|
||||
}
|
||||
|
||||
// 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
|
||||
case 3:
|
||||
prescaler = avr.TCCR3B.Get() & 0x7
|
||||
case 4:
|
||||
prescaler = avr.TCCR4B.Get() & 0x7
|
||||
case 5:
|
||||
prescaler = avr.TCCR5B.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 PB7: // channel A
|
||||
avr.TCCR0A.SetBits(avr.TCCR0A_COM0A1)
|
||||
return 0, nil
|
||||
case PG5: // channel B
|
||||
avr.TCCR0A.SetBits(avr.TCCR0A_COM0B1)
|
||||
return 1, nil
|
||||
}
|
||||
case 1:
|
||||
switch pin {
|
||||
case PB5: // channel A
|
||||
avr.TCCR1A.SetBits(avr.TCCR1A_COM1A1)
|
||||
return 0, nil
|
||||
case PB6: // channel B
|
||||
avr.TCCR1A.SetBits(avr.TCCR1A_COM1B1)
|
||||
return 1, nil
|
||||
}
|
||||
case 2:
|
||||
switch pin {
|
||||
case PB4: // channel A
|
||||
avr.TCCR2A.SetBits(avr.TCCR2A_COM2A1)
|
||||
return 0, nil
|
||||
case PH6: // channel B
|
||||
avr.TCCR2A.SetBits(avr.TCCR2A_COM2B1)
|
||||
return 1, nil
|
||||
}
|
||||
case 3:
|
||||
switch pin {
|
||||
case PE3: // channel A
|
||||
avr.TCCR3A.SetBits(avr.TCCR3A_COM3A1)
|
||||
return 0, nil
|
||||
case PE4: //channel B
|
||||
avr.TCCR3A.SetBits(avr.TCCR3A_COM3B1)
|
||||
return 1, nil
|
||||
case PE5: //channel C
|
||||
avr.TCCR3A.SetBits(avr.TCCR3A_COM3C1)
|
||||
return 2, nil
|
||||
}
|
||||
case 4:
|
||||
switch pin {
|
||||
case PH3: // channel A
|
||||
avr.TCCR4A.SetBits(avr.TCCR4A_COM4A1)
|
||||
return 0, nil
|
||||
case PH4: //channel B
|
||||
avr.TCCR4A.SetBits(avr.TCCR4A_COM4B1)
|
||||
return 1, nil
|
||||
case PH5: //channel C
|
||||
avr.TCCR4A.SetBits(avr.TCCR4A_COM4C1)
|
||||
return 2, nil
|
||||
}
|
||||
case 5:
|
||||
switch pin {
|
||||
case PL3: // channel A
|
||||
avr.TCCR5A.SetBits(avr.TCCR5A_COM5A1)
|
||||
return 0, nil
|
||||
case PL4: //channel B
|
||||
avr.TCCR5A.SetBits(avr.TCCR5A_COM5B1)
|
||||
return 1, nil
|
||||
case PL5: //channel C
|
||||
avr.TCCR5A.SetBits(avr.TCCR5A_COM5C1)
|
||||
return 2, 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, PB7
|
||||
if inverting {
|
||||
avr.PORTB.SetBits(1 << 7) // PB7 high
|
||||
avr.TCCR0A.SetBits(avr.TCCR0A_COM0A0)
|
||||
} else {
|
||||
avr.PORTB.ClearBits(1 << 7) // PB7 low
|
||||
avr.TCCR0A.ClearBits(avr.TCCR0A_COM0A0)
|
||||
}
|
||||
case 1: // channel B, PG5
|
||||
if inverting {
|
||||
avr.PORTG.SetBits(1 << 5) // PG5 high
|
||||
avr.TCCR0A.SetBits(avr.TCCR0A_COM0B0)
|
||||
} else {
|
||||
avr.PORTG.ClearBits(1 << 5) // PG5 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, PB5
|
||||
if inverting {
|
||||
avr.PORTB.SetBits(1 << 5) // PB5 high
|
||||
} else {
|
||||
avr.PORTB.ClearBits(1 << 5) // PB5 low
|
||||
}
|
||||
case 1: // channel B, PB6
|
||||
if inverting {
|
||||
avr.PORTB.SetBits(1 << 6) // PB6 high
|
||||
} else {
|
||||
avr.PORTB.ClearBits(1 << 6) // PB6 low
|
||||
}
|
||||
}
|
||||
case 2:
|
||||
switch channel {
|
||||
case 0: // channel A, PB4
|
||||
if inverting {
|
||||
avr.PORTB.SetBits(1 << 4) // PB4 high
|
||||
avr.TCCR2A.SetBits(avr.TCCR2A_COM2A0)
|
||||
} else {
|
||||
avr.PORTB.ClearBits(1 << 4) // PB4 low
|
||||
avr.TCCR2A.ClearBits(avr.TCCR2A_COM2A0)
|
||||
}
|
||||
case 1: // channel B, PH6
|
||||
if inverting {
|
||||
avr.PORTH.SetBits(1 << 6) // PH6 high
|
||||
avr.TCCR2A.SetBits(avr.TCCR2A_COM2B0)
|
||||
} else {
|
||||
avr.PORTH.ClearBits(1 << 6) // PH6 low
|
||||
avr.TCCR2A.ClearBits(avr.TCCR2A_COM2B0)
|
||||
}
|
||||
}
|
||||
case 3:
|
||||
// Note: the COM3A0/COM3B0 bit is not set with the configuration below.
|
||||
// It will be set the following call to Set(), however.
|
||||
switch channel {
|
||||
case 0: // channel A, PE3
|
||||
if inverting {
|
||||
avr.PORTE.SetBits(1 << 3) // PE3 high
|
||||
} else {
|
||||
avr.PORTE.ClearBits(1 << 3) // PE3 low
|
||||
}
|
||||
case 1: // channel B, PE4
|
||||
if inverting {
|
||||
avr.PORTE.SetBits(1 << 4) // PE4 high
|
||||
} else {
|
||||
avr.PORTE.ClearBits(1 << 4) // PE4 low
|
||||
}
|
||||
case 2: // channel C, PE5
|
||||
if inverting {
|
||||
avr.PORTE.SetBits(1 << 5) // PE4 high
|
||||
} else {
|
||||
avr.PORTE.ClearBits(1 << 5) // PE4 low
|
||||
}
|
||||
}
|
||||
case 4:
|
||||
// Note: the COM3A0/COM3B0 bit is not set with the configuration below.
|
||||
// It will be set the following call to Set(), however.
|
||||
switch channel {
|
||||
case 0: // channel A, PH3
|
||||
if inverting {
|
||||
avr.PORTH.SetBits(1 << 3) // PH3 high
|
||||
} else {
|
||||
avr.PORTH.ClearBits(1 << 3) // PH3 low
|
||||
}
|
||||
case 1: // channel B, PH4
|
||||
if inverting {
|
||||
avr.PORTH.SetBits(1 << 4) // PH4 high
|
||||
} else {
|
||||
avr.PORTH.ClearBits(1 << 4) // PH4 low
|
||||
}
|
||||
case 2: // channel C, PH5
|
||||
if inverting {
|
||||
avr.PORTH.SetBits(1 << 5) // PH4 high
|
||||
} else {
|
||||
avr.PORTH.ClearBits(1 << 5) // PH4 low
|
||||
}
|
||||
}
|
||||
case 5:
|
||||
// Note: the COM3A0/COM3B0 bit is not set with the configuration below.
|
||||
// It will be set the following call to Set(), however.
|
||||
switch channel {
|
||||
case 0: // channel A, PL3
|
||||
if inverting {
|
||||
avr.PORTL.SetBits(1 << 3) // PL3 high
|
||||
} else {
|
||||
avr.PORTL.ClearBits(1 << 3) // PL3 low
|
||||
}
|
||||
case 1: // channel B, PL4
|
||||
if inverting {
|
||||
avr.PORTL.SetBits(1 << 4) // PL4 high
|
||||
} else {
|
||||
avr.PORTL.ClearBits(1 << 4) // PL4 low
|
||||
}
|
||||
case 2: // channel C, PH5
|
||||
if inverting {
|
||||
avr.PORTL.SetBits(1 << 5) // PL4 high
|
||||
} else {
|
||||
avr.PORTL.ClearBits(1 << 5) // PL4 low
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 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)
|
||||
}
|
||||
}
|
||||
case 1:
|
||||
mask := interrupt.Disable()
|
||||
switch channel {
|
||||
case 0: // channel A, PB5
|
||||
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 << 5) { // 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, PB6
|
||||
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 << 6) { // is PB6 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)
|
||||
}
|
||||
}
|
||||
case 3:
|
||||
mask := interrupt.Disable()
|
||||
switch channel {
|
||||
case 0: // channel A, PE3
|
||||
if value == 0 {
|
||||
avr.TCCR3A.ClearBits(avr.TCCR3A_COM3A1 | avr.TCCR3A_COM3A0)
|
||||
} else {
|
||||
value := uint16(value) - 1 // yes, this is safe (it relies on underflow)
|
||||
avr.OCR3AH.Set(uint8(value >> 8))
|
||||
avr.OCR3AL.Set(uint8(value))
|
||||
if avr.PORTE.HasBits(1 << 3) { // is PE3 high?
|
||||
// Yes, set the inverting bit.
|
||||
avr.TCCR3A.SetBits(avr.TCCR3A_COM3A1 | avr.TCCR3A_COM3A0)
|
||||
} else {
|
||||
// No, output is non-inverting.
|
||||
avr.TCCR3A.SetBits(avr.TCCR3A_COM3A1)
|
||||
}
|
||||
}
|
||||
case 1: // channel B, PE4
|
||||
if value == 0 {
|
||||
avr.TCCR3A.ClearBits(avr.TCCR3A_COM3B1 | avr.TCCR3A_COM3B0)
|
||||
} else {
|
||||
value := uint16(value) - 1 // yes, this is safe (it relies on underflow)
|
||||
avr.OCR3BH.Set(uint8(value >> 8))
|
||||
avr.OCR3BL.Set(uint8(value))
|
||||
if avr.PORTE.HasBits(1 << 4) { // is PE4 high?
|
||||
// Yes, set the inverting bit.
|
||||
avr.TCCR3A.SetBits(avr.TCCR3A_COM3B1 | avr.TCCR3A_COM3B0)
|
||||
} else {
|
||||
// No, output is non-inverting.
|
||||
avr.TCCR3A.SetBits(avr.TCCR3A_COM3B1)
|
||||
}
|
||||
}
|
||||
case 2: // channel C, PE5
|
||||
if value == 0 {
|
||||
avr.TCCR3A.ClearBits(avr.TCCR3A_COM3C1 | avr.TCCR3A_COM3C0)
|
||||
} else {
|
||||
value := uint16(value) - 1 // yes, this is safe (it relies on underflow)
|
||||
avr.OCR3CH.Set(uint8(value >> 8))
|
||||
avr.OCR3CL.Set(uint8(value))
|
||||
if avr.PORTE.HasBits(1 << 5) { // is PE5 high?
|
||||
// Yes, set the inverting bit.
|
||||
avr.TCCR3A.SetBits(avr.TCCR3A_COM3C1 | avr.TCCR3A_COM3C0)
|
||||
} else {
|
||||
// No, output is non-inverting.
|
||||
avr.TCCR3A.SetBits(avr.TCCR3A_COM3C1)
|
||||
}
|
||||
}
|
||||
}
|
||||
interrupt.Restore(mask)
|
||||
case 4:
|
||||
mask := interrupt.Disable()
|
||||
switch channel {
|
||||
case 0: // channel A, PH3
|
||||
if value == 0 {
|
||||
avr.TCCR4A.ClearBits(avr.TCCR4A_COM4A1 | avr.TCCR4A_COM4A0)
|
||||
} else {
|
||||
value := uint16(value) - 1 // yes, this is safe (it relies on underflow)
|
||||
avr.OCR4AH.Set(uint8(value >> 8))
|
||||
avr.OCR4AL.Set(uint8(value))
|
||||
if avr.PORTH.HasBits(1 << 3) { // is PH3 high?
|
||||
// Yes, set the inverting bit.
|
||||
avr.TCCR4A.SetBits(avr.TCCR4A_COM4A1 | avr.TCCR4A_COM4A0)
|
||||
} else {
|
||||
// No, output is non-inverting.
|
||||
avr.TCCR4A.SetBits(avr.TCCR4A_COM4A1)
|
||||
}
|
||||
}
|
||||
case 1: // channel B, PH4
|
||||
if value == 0 {
|
||||
avr.TCCR4A.ClearBits(avr.TCCR4A_COM4B1 | avr.TCCR4A_COM4B0)
|
||||
} else {
|
||||
value := uint16(value) - 1 // yes, this is safe (it relies on underflow)
|
||||
avr.OCR4BH.Set(uint8(value >> 8))
|
||||
avr.OCR4BL.Set(uint8(value))
|
||||
if avr.PORTH.HasBits(1 << 4) { // is PH4 high?
|
||||
// Yes, set the inverting bit.
|
||||
avr.TCCR4A.SetBits(avr.TCCR4A_COM4B1 | avr.TCCR4A_COM4B0)
|
||||
} else {
|
||||
// No, output is non-inverting.
|
||||
avr.TCCR4A.SetBits(avr.TCCR4A_COM4B1)
|
||||
}
|
||||
}
|
||||
case 2: // channel C, PH5
|
||||
if value == 0 {
|
||||
avr.TCCR4A.ClearBits(avr.TCCR4A_COM4C1 | avr.TCCR4A_COM4C0)
|
||||
} else {
|
||||
value := uint16(value) - 1 // yes, this is safe (it relies on underflow)
|
||||
avr.OCR4CH.Set(uint8(value >> 8))
|
||||
avr.OCR4CL.Set(uint8(value))
|
||||
if avr.PORTH.HasBits(1 << 5) { // is PH5 high?
|
||||
// Yes, set the inverting bit.
|
||||
avr.TCCR4A.SetBits(avr.TCCR4A_COM4C1 | avr.TCCR4A_COM4C0)
|
||||
} else {
|
||||
// No, output is non-inverting.
|
||||
avr.TCCR4A.SetBits(avr.TCCR4A_COM4C1)
|
||||
}
|
||||
}
|
||||
}
|
||||
interrupt.Restore(mask)
|
||||
case 5:
|
||||
mask := interrupt.Disable()
|
||||
switch channel {
|
||||
case 0: // channel A, PL3
|
||||
if value == 0 {
|
||||
avr.TCCR5A.ClearBits(avr.TCCR5A_COM5A1 | avr.TCCR5A_COM5A0)
|
||||
} else {
|
||||
value := uint16(value) - 1 // yes, this is safe (it relies on underflow)
|
||||
avr.OCR5AH.Set(uint8(value >> 8))
|
||||
avr.OCR5AL.Set(uint8(value))
|
||||
if avr.PORTL.HasBits(1 << 3) { // is PL3 high?
|
||||
// Yes, set the inverting bit.
|
||||
avr.TCCR5A.SetBits(avr.TCCR5A_COM5A1 | avr.TCCR5A_COM5A0)
|
||||
} else {
|
||||
// No, output is non-inverting.
|
||||
avr.TCCR5A.SetBits(avr.TCCR5A_COM5A1)
|
||||
}
|
||||
}
|
||||
case 1: // channel B, PL4
|
||||
if value == 0 {
|
||||
avr.TCCR5A.ClearBits(avr.TCCR5A_COM5B1 | avr.TCCR5A_COM5B0)
|
||||
} else {
|
||||
value := uint16(value) - 1 // yes, this is safe (it relies on underflow)
|
||||
avr.OCR5BH.Set(uint8(value >> 8))
|
||||
avr.OCR5BL.Set(uint8(value))
|
||||
if avr.PORTL.HasBits(1 << 4) { // is PL4 high?
|
||||
// Yes, set the inverting bit.
|
||||
avr.TCCR5A.SetBits(avr.TCCR5A_COM5B1 | avr.TCCR5A_COM5B0)
|
||||
} else {
|
||||
// No, output is non-inverting.
|
||||
avr.TCCR5A.SetBits(avr.TCCR5A_COM5B1)
|
||||
}
|
||||
}
|
||||
case 2: // channel C, PL5
|
||||
if value == 0 {
|
||||
avr.TCCR5A.ClearBits(avr.TCCR5A_COM5C1 | avr.TCCR5A_COM5C0)
|
||||
} else {
|
||||
value := uint16(value) - 1 // yes, this is safe (it relies on underflow)
|
||||
avr.OCR5CH.Set(uint8(value >> 8))
|
||||
avr.OCR5CL.Set(uint8(value))
|
||||
if avr.PORTL.HasBits(1 << 5) { // is PL5 high?
|
||||
// Yes, set the inverting bit.
|
||||
avr.TCCR5A.SetBits(avr.TCCR5A_COM5C1 | avr.TCCR5A_COM5C0)
|
||||
} else {
|
||||
// No, output is non-inverting.
|
||||
avr.TCCR5A.SetBits(avr.TCCR5A_COM5C1)
|
||||
}
|
||||
}
|
||||
}
|
||||
interrupt.Restore(mask)
|
||||
}
|
||||
}
|
||||
|
||||
// SPI configuration
|
||||
var SPI0 = SPI{
|
||||
spcr: avr.SPCR,
|
||||
|
|
Загрузка…
Создание таблицы
Сослаться в новой задаче