diff options
-rw-r--r-- | Makefile | 4 | ||||
-rw-r--r-- | src/examples/pwm/arduino-mega1280.go | 12 | ||||
-rw-r--r-- | src/machine/machine_atmega1280.go | 794 |
3 files changed, 809 insertions, 1 deletions
@@ -378,7 +378,9 @@ ifneq ($(AVR), 0) @$(MD5SUM) test.hex $(TINYGO) build -size short -o test.hex -target=arduino -scheduler=tasks examples/blinky1 @$(MD5SUM) test.hex - $(TINYGO) build -size short -o test.hex -target=arduino-mega1280 examples/blinky1 + $(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 diff --git a/src/examples/pwm/arduino-mega1280.go b/src/examples/pwm/arduino-mega1280.go new file mode 100644 index 000000000..824d2bdad --- /dev/null +++ b/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 +) diff --git a/src/machine/machine_atmega1280.go b/src/machine/machine_atmega1280.go index 22217a32b..ae06c028f 100644 --- a/src/machine/machine_atmega1280.go +++ b/src/machine/machine_atmega1280.go @@ -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, |