aboutsummaryrefslogtreecommitdiffhomepage
path: root/src/machine/machine_atsamd51.go
diff options
context:
space:
mode:
authorAyke van Laethem <[email protected]>2020-05-19 22:30:46 +0200
committerRon Evans <[email protected]>2021-04-06 20:36:10 +0200
commit72acda22b0a8d137405e41e9ed54cbfbcce7b26f (patch)
treebaed2cc5aa5bd3f4582a6ef4aa439928acdf38f9 /src/machine/machine_atsamd51.go
parentf880950c3efbd005d2e171a5fd1c879a3221367c (diff)
downloadtinygo-72acda22b0a8d137405e41e9ed54cbfbcce7b26f.tar.gz
tinygo-72acda22b0a8d137405e41e9ed54cbfbcce7b26f.zip
machine: refactor PWM support
This commit refactors PWM support in the machine package to be more flexible. The new API can be used to produce tones at a specific frequency and control servos in a portable way, by abstracting over counter widths and prescalers.
Diffstat (limited to 'src/machine/machine_atsamd51.go')
-rw-r--r--src/machine/machine_atsamd51.go528
1 files changed, 311 insertions, 217 deletions
diff --git a/src/machine/machine_atsamd51.go b/src/machine/machine_atsamd51.go
index 971658745..240b41272 100644
--- a/src/machine/machine_atsamd51.go
+++ b/src/machine/machine_atsamd51.go
@@ -40,9 +40,9 @@ const (
PinInput PinMode = 15
PinInputPullup PinMode = 16
PinOutput PinMode = 17
- PinPWME PinMode = PinTimer
- PinPWMF PinMode = PinTimerAlt
- PinPWMG PinMode = PinTCCPDEC
+ PinTCCE PinMode = PinTimer
+ PinTCCF PinMode = PinTimerAlt
+ PinTCCG PinMode = PinTCCPDEC
PinInputPulldown PinMode = 18
)
@@ -1570,256 +1570,350 @@ const (
QSPI_DATA3 = PA11
)
-// PWM
-const period = 0xFFFF
+// TCC is one timer peripheral, which consists of a counter and multiple output
+// channels (that can be connected to actual pins). You can set the frequency
+// using SetPeriod, but only for all the channels in this timer peripheral at
+// once.
+type TCC sam.TCC_Type
-// Configure configures a PWM pin for output.
-func (pwm PWM) Configure() error {
- // Set pin as output
- sam.PORT.GROUP[0].DIRSET.Set(1 << uint8(pwm.Pin))
- // Set pin to low
- sam.PORT.GROUP[0].OUTCLR.Set(1 << uint8(pwm.Pin))
+//go:inline
+func (tcc *TCC) timer() *sam.TCC_Type {
+ return (*sam.TCC_Type)(tcc)
+}
- // Enable the port multiplexer for pin
- pwm.setPinCfg(sam.PORT_GROUP_PINCFG_PMUXEN)
+// Configure enables and configures this TCC.
+func (tcc *TCC) Configure(config PWMConfig) error {
+ // Enable the TCC clock to be able to use the TCC.
+ tcc.configureClock()
- // Connect timer/mux to pin.
- pwmConfig := pwm.getMux()
+ // Disable timer (if it was enabled). This is necessary because
+ // tcc.setPeriod may want to change the prescaler bits in CTRLA, which is
+ // only allowed when the TCC is disabled.
+ tcc.timer().CTRLA.ClearBits(sam.TCC_CTRLA_ENABLE)
- if pwm.Pin&1 > 0 {
- // odd pin, so save the even pins
- val := pwm.getPMux() & sam.PORT_GROUP_PMUX_PMUXE_Msk
- pwm.setPMux(val | uint8(pwmConfig<<sam.PORT_GROUP_PMUX_PMUXO_Pos))
- } else {
- // even pin, so save the odd pins
- val := pwm.getPMux() & sam.PORT_GROUP_PMUX_PMUXO_Msk
- pwm.setPMux(val | uint8(pwmConfig<<sam.PORT_GROUP_PMUX_PMUXE_Pos))
- }
+ // Use "Normal PWM" (single-slope PWM)
+ tcc.timer().WAVE.Set(sam.TCC_WAVE_WAVEGEN_NPWM)
- // figure out which TCCX timer for this pin
- timer := pwm.getTimer()
- if timer == nil {
- return ErrInvalidOutputPin
+ // Wait for synchronization of all changed registers.
+ for tcc.timer().SYNCBUSY.Get() != 0 {
}
- // disable timer
- timer.CTRLA.ClearBits(sam.TCC_CTRLA_ENABLE)
- // Wait for synchronization
- for timer.SYNCBUSY.HasBits(sam.TCC_SYNCBUSY_ENABLE) {
- }
+ // Set the period and prescaler.
+ err := tcc.setPeriod(config.Period, true)
- // Set prescaler to 1/256
- // TCCx->CTRLA.reg = TCC_CTRLA_PRESCALER_DIV256 | TCC_CTRLA_PRESCSYNC_GCLK;
- timer.CTRLA.SetBits(sam.TCC_CTRLA_PRESCALER_DIV256 | sam.TCC_CTRLA_PRESCSYNC_GCLK)
+ // Enable the timer.
+ tcc.timer().CTRLA.SetBits(sam.TCC_CTRLA_ENABLE)
- // Use "Normal PWM" (single-slope PWM)
- timer.WAVE.SetBits(sam.TCC_WAVE_WAVEGEN_NPWM)
- // Wait for synchronization
- for timer.SYNCBUSY.HasBits(sam.TCC_SYNCBUSY_WAVE) {
+ // Wait for synchronization of all changed registers.
+ for tcc.timer().SYNCBUSY.Get() != 0 {
}
- // while (TCCx->SYNCBUSY.bit.CC0 || TCCx->SYNCBUSY.bit.CC1);
- for timer.SYNCBUSY.HasBits(sam.TCC_SYNCBUSY_CC0) ||
- timer.SYNCBUSY.HasBits(sam.TCC_SYNCBUSY_CC1) {
+ // Return any error that might have occured in the tcc.setPeriod call.
+ return err
+}
+
+// SetPeriod updates the period of this TCC 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 TCC 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 (tcc *TCC) SetPeriod(period uint64) error {
+ return tcc.setPeriod(period, false)
+}
+
+// setPeriod sets the period of this TCC, possibly updating the prescaler as
+// well. The prescaler can only modified when the TCC is disabled, that is, in
+// the Configure function.
+func (tcc *TCC) setPeriod(period uint64, updatePrescaler bool) error {
+ var top uint64
+ if period == 0 {
+ // Make sure the TOP value is at 0xffff (enough for a 16-bit timer).
+ top = 0xffff
+ } else {
+ // The formula below calculates the following formula, optimized:
+ // period * (120e6 / 1e9)
+ // This assumes that the chip is running from generic clock generator 0
+ // at 120MHz.
+ top = period * 3 / 25
+ }
+
+ maxTop := uint64(0xffff)
+ if tcc.timer() == sam.TCC0 || tcc.timer() == sam.TCC1 {
+ // Only TCC0 and TCC1 are 24-bit timers, the rest are 16-bit.
+ maxTop = 0xffffff
+ }
+
+ if updatePrescaler {
+ // This function was called during Configure(), with the timer disabled.
+ // Note that updating the prescaler can only happen while the peripheral
+ // is disabled.
+ var prescaler uint32
+ switch {
+ case top <= maxTop:
+ prescaler = sam.TCC_CTRLA_PRESCALER_DIV1
+ case top/2 <= maxTop:
+ prescaler = sam.TCC_CTRLA_PRESCALER_DIV2
+ top = top / 2
+ case top/4 <= maxTop:
+ prescaler = sam.TCC_CTRLA_PRESCALER_DIV4
+ top = top / 4
+ case top/8 <= maxTop:
+ prescaler = sam.TCC_CTRLA_PRESCALER_DIV8
+ top = top / 8
+ case top/16 <= maxTop:
+ prescaler = sam.TCC_CTRLA_PRESCALER_DIV16
+ top = top / 16
+ case top/64 <= maxTop:
+ prescaler = sam.TCC_CTRLA_PRESCALER_DIV64
+ top = top / 64
+ case top/256 <= maxTop:
+ prescaler = sam.TCC_CTRLA_PRESCALER_DIV256
+ top = top / 256
+ case top/1024 <= maxTop:
+ prescaler = sam.TCC_CTRLA_PRESCALER_DIV1024
+ top = top / 1024
+ default:
+ return ErrPWMPeriodTooLong
+ }
+ tcc.timer().CTRLA.Set((tcc.timer().CTRLA.Get() &^ sam.TCC_CTRLA_PRESCALER_Msk) | (prescaler << sam.TCC_CTRLA_PRESCALER_Pos))
+ } else {
+ // Do not update the prescaler, but use the already-configured
+ // prescaler. This is the normal SetPeriod case, where the prescaler
+ // must not be changed.
+ prescaler := (tcc.timer().CTRLA.Get() & sam.TCC_CTRLA_PRESCALER_Msk) >> sam.TCC_CTRLA_PRESCALER_Pos
+ switch prescaler {
+ case sam.TCC_CTRLA_PRESCALER_DIV1:
+ top /= 1 // no-op
+ case sam.TCC_CTRLA_PRESCALER_DIV2:
+ top /= 2
+ case sam.TCC_CTRLA_PRESCALER_DIV4:
+ top /= 4
+ case sam.TCC_CTRLA_PRESCALER_DIV8:
+ top /= 8
+ case sam.TCC_CTRLA_PRESCALER_DIV16:
+ top /= 16
+ case sam.TCC_CTRLA_PRESCALER_DIV64:
+ top /= 64
+ case sam.TCC_CTRLA_PRESCALER_DIV256:
+ top /= 256
+ case sam.TCC_CTRLA_PRESCALER_DIV1024:
+ top /= 1024
+ default:
+ // unreachable
+ }
+ if top > maxTop {
+ return ErrPWMPeriodTooLong
+ }
}
- // Set the initial value
- // TCCx->CC[tcChannel].reg = (uint32_t) value;
- pwm.setChannel(timer, 0)
+ // Set the period (the counter top).
+ tcc.timer().PER.Set(uint32(top) - 1)
- for timer.SYNCBUSY.HasBits(sam.TCC_SYNCBUSY_CC0) ||
- timer.SYNCBUSY.HasBits(sam.TCC_SYNCBUSY_CC1) {
+ // Wait for synchronization of CTRLA.PRESCALER and PER registers.
+ for tcc.timer().SYNCBUSY.Get() != 0 {
}
- // Set the period (the number to count to (TOP) before resetting timer)
- //TCC0->PER.reg = period;
- timer.PER.Set(period)
- // Wait for synchronization
- for timer.SYNCBUSY.HasBits(sam.TCC_SYNCBUSY_PER) {
- }
+ 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
+// tcc.Set (see tcc.Set for more information).
+func (tcc *TCC) Top() uint32 {
+ return tcc.timer().PER.Get() + 1
+}
- // enable timer
- timer.CTRLA.SetBits(sam.TCC_CTRLA_ENABLE)
- // Wait for synchronization
- for timer.SYNCBUSY.HasBits(sam.TCC_SYNCBUSY_ENABLE) {
+// Counter returns the current counter value of the timer in this TCC
+// peripheral. It may be useful for debugging.
+func (tcc *TCC) Counter() uint32 {
+ tcc.timer().CTRLBSET.Set(sam.TCC_CTRLBSET_CMD_READSYNC << sam.TCC_CTRLBSET_CMD_Pos)
+ for tcc.timer().SYNCBUSY.Get() != 0 {
}
+ return tcc.timer().COUNT.Get()
+}
- return nil
+// Constants that encode a TCC number and WO number together in a single byte.
+const (
+ pinTCC0 = 1 << 4 // keep the value 0 usable as "no value"
+ pinTCC1 = 2 << 4
+ pinTCC2 = 3 << 4
+ pinTCC3 = 4 << 4
+ pinTCC4 = 5 << 4
+ pinTCC0_0 = pinTCC0 | 0
+ pinTCC0_1 = pinTCC0 | 1
+ pinTCC0_2 = pinTCC0 | 2
+ pinTCC0_3 = pinTCC0 | 3
+ pinTCC0_4 = pinTCC0 | 4
+ pinTCC0_5 = pinTCC0 | 5
+ pinTCC0_6 = pinTCC0 | 6
+ pinTCC1_0 = pinTCC1 | 0
+ pinTCC1_2 = pinTCC1 | 2
+ pinTCC1_4 = pinTCC1 | 4
+ pinTCC1_6 = pinTCC1 | 6
+ pinTCC2_0 = pinTCC2 | 0
+ pinTCC2_2 = pinTCC2 | 2
+ pinTCC3_0 = pinTCC3 | 0
+ pinTCC4_0 = pinTCC4 | 0
+)
+
+// This is a copy of columns F and G (the TCC columns) of table 6-1 in the
+// datasheet:
+// http://ww1.microchip.com/downloads/en/DeviceDoc/60001507E.pdf
+// For example, "TCC0/WO[2]" is converted to pinTCC0_2.
+// Only the even pin numbers are stored here. The odd pin numbers are left out,
+// because their PWM output can be determined from the even number: just add one
+// to the wave output (WO) number.
+var pinTimerMapping = [...]struct{ F, G uint8 }{
+ // page 33
+ PC04 / 2: {pinTCC0_0, 0},
+ PA08 / 2: {pinTCC0_0, pinTCC1_4},
+ PA10 / 2: {pinTCC0_2, pinTCC1_6},
+ PB10 / 2: {pinTCC0_4, pinTCC1_0},
+ PB12 / 2: {pinTCC3_0, pinTCC0_0},
+ PB14 / 2: {pinTCC4_0, pinTCC0_2},
+ PD08 / 2: {pinTCC0_1, 0},
+ PD10 / 2: {pinTCC0_3, 0},
+ PD12 / 2: {pinTCC0_5, 0},
+ PC10 / 2: {pinTCC0_0, pinTCC1_4},
+ // page 34
+ PC12 / 2: {pinTCC0_2, pinTCC1_6},
+ PC14 / 2: {pinTCC0_4, pinTCC1_0},
+ PA12 / 2: {pinTCC0_6, pinTCC1_2},
+ PA14 / 2: {pinTCC2_0, pinTCC1_2},
+ PA16 / 2: {pinTCC1_0, pinTCC0_4},
+ PA18 / 2: {pinTCC1_2, pinTCC0_6},
+ PC16 / 2: {pinTCC0_0, 0},
+ PC18 / 2: {pinTCC0_2, 0},
+ PC20 / 2: {pinTCC0_4, 0},
+ PC22 / 2: {pinTCC0_6, 0},
+ PD20 / 2: {pinTCC1_0, 0},
+ PB16 / 2: {pinTCC3_0, pinTCC0_4},
+ PB18 / 2: {pinTCC1_0, 0},
+ // page 35
+ PB20 / 2: {pinTCC1_2, 0},
+ PA20 / 2: {pinTCC1_4, pinTCC0_0},
+ PA22 / 2: {pinTCC1_6, pinTCC0_2},
+ PA24 / 2: {pinTCC2_2, 0},
+ PB26 / 2: {pinTCC1_2, 0},
+ PB28 / 2: {pinTCC1_4, 0},
+ PA30 / 2: {pinTCC2_0, 0},
+ // page 36
+ PB30 / 2: {pinTCC4_0, pinTCC0_6},
+ PB02 / 2: {pinTCC2_2, 0},
}
-// Set turns on the duty cycle for a PWM pin using the provided value.
-func (pwm PWM) Set(value uint16) {
- // figure out which TCCX timer for this pin
- timer := pwm.getTimer()
- if timer == nil {
- // The Configure call above cannot have succeeded, so simply ignore this
- // error.
- return
+// findPinPadMapping returns the pin mode (PinTCCF or PinTCCG) and the channel
+// number for a given timer and pin. A zero PinMode is returned if no mapping
+// could be found.
+func findPinTimerMapping(timer uint8, pin Pin) (PinMode, uint8) {
+ if int(pin/2) >= len(pinTimerMapping) {
+ return 0, 0 // invalid pin number
}
- // Wait for synchronization
- for timer.SYNCBUSY.HasBits(sam.TCC_SYNCBUSY_CTRLB) {
+ mapping := pinTimerMapping[pin/2]
+
+ // Check for column F in the datasheet.
+ if mapping.F>>4-1 == timer {
+ return PinTCCF, mapping.F&0x0f + uint8(pin)&1
}
- for timer.SYNCBUSY.HasBits(sam.TCC_SYNCBUSY_CC0) ||
- timer.SYNCBUSY.HasBits(sam.TCC_SYNCBUSY_CC1) {
+
+ // Check for column G in the datasheet.
+ if mapping.G>>4-1 == timer {
+ return PinTCCG, mapping.G&0x0f + uint8(pin)&1
}
- // TCCx->CCBUF[tcChannel].reg = (uint32_t) value;
- pwm.setChannelBuffer(timer, uint32(value))
+ // Nothing found.
+ return 0, 0
+}
+
+// Channel returns a PWM channel for the given pin. Note that one channel may be
+// shared between multiple pins, and so will have the same duty cycle. If this
+// is not desirable, look for a different TCC or consider using a different pin.
+func (tcc *TCC) Channel(pin Pin) (uint8, error) {
+ pinMode, woOutput := findPinTimerMapping(tcc.timerNum(), pin)
- for timer.SYNCBUSY.HasBits(sam.TCC_SYNCBUSY_CC0) ||
- timer.SYNCBUSY.HasBits(sam.TCC_SYNCBUSY_CC1) {
+ if pinMode == 0 {
+ // No pin could be found.
+ return 0, ErrInvalidOutputPin
}
- // TCCx->CTRLBCLR.bit.LUPD = 1;
- timer.CTRLBCLR.SetBits(sam.TCC_CTRLBCLR_LUPD)
- for timer.SYNCBUSY.HasBits(sam.TCC_SYNCBUSY_CTRLB) {
+ // Convert from waveform output to channel, assuming WEXCTRL.OTMX equals 0.
+ // See table 49-4 "Output Matrix Channel Pin Routing Configuration" on page
+ // 1829 of the datasheet.
+ // The number of channels varies by TCC instance, hence the need to switch
+ // over them. For TCC2-4 the number of channels is equal to the number of
+ // waveform outputs, so the WO number maps directly to the channel number.
+ // For TCC0 and TCC1 this is not the case so they will need some special
+ // handling.
+ channel := woOutput
+ switch tcc.timer() {
+ case sam.TCC0:
+ channel = woOutput % 6
+ case sam.TCC1:
+ channel = woOutput % 4
}
-}
-// getPMux returns the value for the correct PMUX register for this pin.
-func (pwm PWM) getPMux() uint8 {
- return pwm.Pin.getPMux()
-}
+ // Enable the port multiplexer for pin
+ pin.setPinCfg(sam.PORT_GROUP_PINCFG_PMUXEN)
-// setPMux sets the value for the correct PMUX register for this pin.
-func (pwm PWM) setPMux(val uint8) {
- pwm.Pin.setPMux(val)
+ // Connect timer/mux to pin.
+ if pin&1 > 0 {
+ // odd pin, so save the even pins
+ val := pin.getPMux() & sam.PORT_GROUP_PMUX_PMUXE_Msk
+ pin.setPMux(val | uint8(pinMode<<sam.PORT_GROUP_PMUX_PMUXO_Pos))
+ } else {
+ // even pin, so save the odd pins
+ val := pin.getPMux() & sam.PORT_GROUP_PMUX_PMUXO_Msk
+ pin.setPMux(val | uint8(pinMode<<sam.PORT_GROUP_PMUX_PMUXE_Pos))
+ }
+
+ return channel, nil
}
-// getPinCfg returns the value for the correct PINCFG register for this pin.
-func (pwm PWM) getPinCfg() uint8 {
- return pwm.Pin.getPinCfg()
+// 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%.
+func (tcc *TCC) SetInverting(channel uint8, inverting bool) {
+ if inverting {
+ tcc.timer().WAVE.SetBits(1 << (sam.TCC_WAVE_POL0_Pos + channel))
+ } else {
+ tcc.timer().WAVE.ClearBits(1 << (sam.TCC_WAVE_POL0_Pos + channel))
+ }
+
+ // Wait for synchronization of the WAVE register.
+ for tcc.timer().SYNCBUSY.Get() != 0 {
+ }
}
-// setPinCfg sets the value for the correct PINCFG register for this pin.
-func (pwm PWM) setPinCfg(val uint8) {
- pwm.Pin.setPinCfg(val)
-}
-
-// setChannel sets the value for the correct channel for PWM on this pin.
-func (pwm PWM) setChannel(timer *sam.TCC_Type, val uint32) {
- switch pwm.Pin {
- case PA14:
- timer.CC[0].Set(val)
- case PA15:
- timer.CC[1].Set(val)
- case PA16:
- timer.CC[0].Set(val)
- case PA17:
- timer.CC[1].Set(val)
- case PA18:
- timer.CC[2].Set(val)
- case PA19:
- timer.CC[3].Set(val)
- case PA20:
- timer.CC[0].Set(val)
- case PA21:
- timer.CC[1].Set(val)
- case PA22:
- timer.CC[2].Set(val)
- case PA23:
- timer.CC[3].Set(val)
- case PB12:
- timer.CC[0].Set(val)
- case PB13:
- timer.CC[1].Set(val)
- case PB14:
- timer.CC[0].Set(val)
- case PB15:
- timer.CC[1].Set(val)
- case PB16:
- timer.CC[4].Set(val)
- case PB17:
- timer.CC[5].Set(val)
- case PB31:
- timer.CC[1].Set(val)
- default:
- return // not supported on this pin
- }
-}
-
-// setChannelBuffer sets the value for the correct channel buffer for PWM on this pin
-func (pwm PWM) setChannelBuffer(timer *sam.TCC_Type, val uint32) {
- switch pwm.Pin {
- case PA14:
- timer.CCBUF[0].Set(val)
- case PA15:
- timer.CCBUF[1].Set(val)
- case PA16:
- timer.CCBUF[0].Set(val)
- case PA17:
- timer.CCBUF[1].Set(val)
- case PA18:
- timer.CCBUF[2].Set(val)
- case PA19:
- timer.CCBUF[3].Set(val)
- case PA20:
- timer.CCBUF[0].Set(val)
- case PA21:
- timer.CCBUF[1].Set(val)
- case PA22:
- timer.CCBUF[2].Set(val)
- case PA23:
- timer.CCBUF[3].Set(val)
- case PB12:
- timer.CCBUF[0].Set(val)
- case PB13:
- timer.CCBUF[1].Set(val)
- case PB14:
- timer.CCBUF[0].Set(val)
- case PB15:
- timer.CCBUF[1].Set(val)
- case PB16:
- timer.CCBUF[4].Set(val)
- case PB17:
- timer.CCBUF[5].Set(val)
- case PB31:
- timer.CCBUF[1].Set(val)
- default:
- return // not supported on this pin
- }
-}
-
-// getMux returns the pin mode mux to be used for PWM on this pin.
-func (pwm PWM) getMux() PinMode {
- switch pwm.Pin {
- case PA14:
- return PinPWMF
- case PA15:
- return PinPWMF
- case PA16:
- return PinPWMF
- case PA17:
- return PinPWMF
- case PA18:
- return PinPWMF
- case PA19:
- return PinPWMF
- case PA20:
- return PinPWMG
- case PA21:
- return PinPWMG
- case PA22:
- return PinPWMG
- case PA23:
- return PinPWMG
- case PB12:
- return PinPWMF
- case PB13:
- return PinPWMF
- case PB14:
- return PinPWMF
- case PB15:
- return PinPWMF
- case PB16:
- return PinPWMG
- case PB17:
- return PinPWMG
- case PB31:
- return PinPWMF
- default:
- return 0 // not supported on this pin
+// 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:
+//
+// tcc.Set(channel, tcc.Top() / 4)
+//
+// tcc.Set(channel, 0) will set the output to low and tcc.Set(channel,
+// tcc.Top()) will set the output to high, assuming the output isn't inverted.
+func (tcc *TCC) Set(channel uint8, value uint32) {
+ // Update CCBUF, which provides double buffering. The update is applied on
+ // the next cycle.
+ tcc.timer().CCBUF[channel].Set(value)
+ for tcc.timer().SYNCBUSY.Get() != 0 {
}
}