github.com/tinygo-org/tinygo@v0.31.3-0.20240404173401-90b0bf646c27/src/machine/machine_rp2040_pwm.go (about)

     1  //go:build rp2040
     2  
     3  package machine
     4  
     5  import (
     6  	"device/rp"
     7  	"errors"
     8  	"math"
     9  	"runtime/volatile"
    10  	"unsafe"
    11  )
    12  
    13  var (
    14  	ErrBadPeriod = errors.New("period outside valid range 8ns..268ms")
    15  )
    16  
    17  const (
    18  	maxPWMPins = 29
    19  )
    20  
    21  // pwmGroup is one PWM peripheral, which consists of a counter and two output
    22  // channels. You can set the frequency using SetPeriod,
    23  // but only for all the channels in this PWM peripheral at once.
    24  //
    25  // div: integer value to reduce counting rate by. Must be greater than or equal to 1.
    26  //
    27  // cc: counter compare level. Contains 2 channel levels. The 16 LSBs are Channel A's level (Duty Cycle)
    28  // and the 16 MSBs are Channel B's level.
    29  //
    30  // top: Wrap. Highest number counter will reach before wrapping over. usually 0xffff.
    31  //
    32  // csr: Clock mode. PWM_CH0_CSR_DIVMODE_xxx registers have 4 possible modes, of which Free-running is used.
    33  // csr contains output polarity bit at PWM_CH0_CSR_x_INV where x is the channel.
    34  // csr contains phase correction bit at PWM_CH0_CSR_PH_CORRECT_Msk.
    35  // csr contains PWM enable bit at PWM_CH0_CSR_EN. If not enabled PWM will not be active.
    36  //
    37  // ctr: PWM counter value.
    38  type pwmGroup struct {
    39  	CSR volatile.Register32
    40  	DIV volatile.Register32
    41  	CTR volatile.Register32
    42  	CC  volatile.Register32
    43  	TOP volatile.Register32
    44  }
    45  
    46  // Equivalent of
    47  //
    48  //	var pwmSlice []pwmGroup = (*[8]pwmGroup)(unsafe.Pointer(rp.PWM))[:]
    49  //	return &pwmSlice[index]
    50  //
    51  // 0x14 is the size of a pwmGroup.
    52  func getPWMGroup(index uintptr) *pwmGroup {
    53  	return (*pwmGroup)(unsafe.Add(unsafe.Pointer(rp.PWM), 0x14*index))
    54  }
    55  
    56  // Hardware Pulse Width Modulation (PWM) API
    57  // PWM peripherals available on RP2040. Each peripheral has 2 pins available for
    58  // a total of 16 available PWM outputs. Some pins may not be available on some boards.
    59  //
    60  // The RP2040 PWM block has 8 identical slices. Each slice can drive two PWM output signals, or
    61  // measure the frequency or duty cycle of an input signal. This gives a total of up to 16 controllable
    62  // PWM outputs. All 30 GPIOs can be driven by the PWM block
    63  //
    64  // The PWM hardware functions by continuously comparing the input value to a free-running counter. This produces a
    65  // toggling output where the amount of time spent at the high output level is proportional to the input value. The fraction of
    66  // time spent at the high signal level is known as the duty cycle of the signal.
    67  //
    68  // The default behaviour of a PWM slice is to count upward until the wrap value (\ref pwm_config_set_wrap) is reached, and then
    69  // immediately wrap to 0. PWM slices also offer a phase-correct mode, where the counter starts to count downward after
    70  // reaching TOP, until it reaches 0 again.
    71  var (
    72  	PWM0 = getPWMGroup(0)
    73  	PWM1 = getPWMGroup(1)
    74  	PWM2 = getPWMGroup(2)
    75  	PWM3 = getPWMGroup(3)
    76  	PWM4 = getPWMGroup(4)
    77  	PWM5 = getPWMGroup(5)
    78  	PWM6 = getPWMGroup(6)
    79  	PWM7 = getPWMGroup(7)
    80  )
    81  
    82  // Configure enables and configures this PWM.
    83  func (pwm *pwmGroup) Configure(config PWMConfig) error {
    84  	return pwm.init(config, true)
    85  }
    86  
    87  // Channel returns a PWM channel for the given pin. If pin does
    88  // not belong to PWM peripheral ErrInvalidOutputPin error is returned.
    89  // It also configures pin as PWM output.
    90  func (pwm *pwmGroup) Channel(pin Pin) (channel uint8, err error) {
    91  	if pin > maxPWMPins || pwmGPIOToSlice(pin) != pwm.peripheral() {
    92  		return 3, ErrInvalidOutputPin
    93  	}
    94  	pin.Configure(PinConfig{PinPWM})
    95  	return pwmGPIOToChannel(pin), nil
    96  }
    97  
    98  // Peripheral returns the RP2040 PWM peripheral which ranges from 0 to 7. Each
    99  // PWM peripheral has 2 channels, A and B which correspond to 0 and 1 in the program.
   100  // This number corresponds to the package's PWM0 throughout PWM7 handles
   101  func PWMPeripheral(pin Pin) (sliceNum uint8, err error) {
   102  	if pin > maxPWMPins {
   103  		return 0, ErrInvalidOutputPin
   104  	}
   105  	return pwmGPIOToSlice(pin), nil
   106  }
   107  
   108  // returns the number of the pwm peripheral (0-7)
   109  func (pwm *pwmGroup) peripheral() uint8 {
   110  	return uint8((uintptr(unsafe.Pointer(pwm)) - uintptr(unsafe.Pointer(rp.PWM))) / 0x14)
   111  }
   112  
   113  // SetPeriod updates the period of this PWM peripheral in nanoseconds.
   114  // To set a particular frequency, use the following formula:
   115  //
   116  //	period = 1e9 / frequency
   117  //
   118  // Where frequency is in hertz. If you use a period of 0, a period
   119  // that works well for LEDs will be picked.
   120  //
   121  // SetPeriod will try not to modify TOP if possible to reach the target period.
   122  // If the period is unattainable with current TOP SetPeriod will modify TOP
   123  // by the bare minimum to reach the target period. It will also enable phase
   124  // correct to reach periods above 130ms.
   125  func (p *pwmGroup) SetPeriod(period uint64) error {
   126  	if period == 0 {
   127  		period = 1e5
   128  	}
   129  	return p.setPeriod(period)
   130  }
   131  
   132  // Top returns the current counter top, for use in duty cycle calculation.
   133  //
   134  // The value returned here is hardware dependent. In general, it's best to treat
   135  // it as an opaque value that can be divided by some number and passed to Set
   136  // (see Set documentation for more information).
   137  func (p *pwmGroup) Top() uint32 {
   138  	return p.getWrap()
   139  }
   140  
   141  // Counter returns the current counter value of the timer in this PWM
   142  // peripheral. It may be useful for debugging.
   143  func (p *pwmGroup) Counter() uint32 {
   144  	return (p.CTR.Get() & rp.PWM_CH0_CTR_CH0_CTR_Msk) >> rp.PWM_CH0_CTR_CH0_CTR_Pos
   145  }
   146  
   147  // Period returns the used PWM period in nanoseconds.
   148  func (p *pwmGroup) Period() uint64 {
   149  	periodPerCycle := cpuPeriod()
   150  	top := p.getWrap()
   151  	phc := p.getPhaseCorrect()
   152  	Int, frac := p.getClockDiv()
   153  	// Line below can overflow if operations done without care.
   154  	return (16*uint64(Int) + uint64(frac)) * uint64((top+1)*(phc+1)*periodPerCycle) / 16 // cycles = (TOP+1) * (CSRPHCorrect + 1) * (DIV_INT + DIV_FRAC/16)
   155  }
   156  
   157  // SetInverting sets whether to invert the output of this channel.
   158  // Without inverting, a 25% duty cycle would mean the output is high for 25% of
   159  // the time and low for the rest. Inverting flips the output as if a NOT gate
   160  // was placed at the output, meaning that the output would be 25% low and 75%
   161  // high with a duty cycle of 25%.
   162  func (p *pwmGroup) SetInverting(channel uint8, inverting bool) {
   163  	channel &= 1
   164  	p.setInverting(channel, inverting)
   165  }
   166  
   167  // Set updates the channel value. This is used to control the channel duty
   168  // cycle, in other words the fraction of time the channel output is high (or low
   169  // when inverted). For example, to set it to a 25% duty cycle, use:
   170  //
   171  //	pwm.Set(channel, pwm.Top() / 4)
   172  //
   173  // pwm.Set(channel, 0) will set the output to low and pwm.Set(channel,
   174  // pwm.Top()) will set the output to high, assuming the output isn't inverted.
   175  func (p *pwmGroup) Set(channel uint8, value uint32) {
   176  	val := uint16(value)
   177  	channel &= 1
   178  	p.setChanLevel(channel, val)
   179  }
   180  
   181  // Get current level (last set by Set). Default value on initialization is 0.
   182  func (p *pwmGroup) Get(channel uint8) (value uint32) {
   183  	channel &= 1
   184  	return uint32(p.getChanLevel(channel))
   185  }
   186  
   187  // SetTop sets TOP control register. Max value is 16bit (0xffff).
   188  func (p *pwmGroup) SetTop(top uint32) {
   189  	p.setWrap(uint16(top))
   190  }
   191  
   192  // SetCounter sets counter control register. Max value is 16bit (0xffff).
   193  // Useful for synchronising two different PWM peripherals.
   194  func (p *pwmGroup) SetCounter(ctr uint32) {
   195  	p.CTR.Set(ctr)
   196  }
   197  
   198  // Enable enables or disables PWM peripheral channels.
   199  func (p *pwmGroup) Enable(enable bool) {
   200  	p.enable(enable)
   201  }
   202  
   203  // IsEnabled returns true if peripheral is enabled.
   204  func (p *pwmGroup) IsEnabled() (enabled bool) {
   205  	return (p.CSR.Get()&rp.PWM_CH0_CSR_EN_Msk)>>rp.PWM_CH0_CSR_EN_Pos != 0
   206  }
   207  
   208  // Initialise a PWM with settings from a configuration object.
   209  // If start is true then PWM starts on initialization.
   210  func (pwm *pwmGroup) init(config PWMConfig, start bool) error {
   211  	// Not enable Phase correction
   212  	pwm.setPhaseCorrect(false)
   213  
   214  	// Clock mode set by default to Free running
   215  	pwm.setDivMode(rp.PWM_CH0_CSR_DIVMODE_DIV)
   216  
   217  	// Set Output polarity (false/false)
   218  	pwm.setInverting(0, false)
   219  	pwm.setInverting(1, false)
   220  
   221  	// Set wrap. The highest value the counter will reach before returning to zero, also known as TOP.
   222  	pwm.setWrap(0xffff)
   223  	// period is set after TOP (Wrap).
   224  	err := pwm.SetPeriod(config.Period)
   225  	if err != nil {
   226  		return err
   227  	}
   228  	// period already set beforea
   229  	// Reset counter and compare (pwm level set to zero)
   230  	pwm.CTR.ReplaceBits(0, rp.PWM_CH0_CTR_CH0_CTR_Msk, 0) // PWM_CH0_CTR_RESET
   231  	pwm.CC.Set(0)                                         // PWM_CH0_CC_RESET
   232  
   233  	pwm.enable(start)
   234  	return nil
   235  }
   236  
   237  func (pwm *pwmGroup) setPhaseCorrect(correct bool) {
   238  	pwm.CSR.ReplaceBits(boolToBit(correct)<<rp.PWM_CH0_CSR_PH_CORRECT_Pos, rp.PWM_CH0_CSR_PH_CORRECT_Msk, 0)
   239  }
   240  
   241  // Takes any of the following:
   242  //
   243  //	rp.PWM_CH0_CSR_DIVMODE_DIV, rp.PWM_CH0_CSR_DIVMODE_FALL,
   244  //	rp.PWM_CH0_CSR_DIVMODE_LEVEL, rp.PWM_CH0_CSR_DIVMODE_RISE
   245  func (pwm *pwmGroup) setDivMode(mode uint32) {
   246  	pwm.CSR.ReplaceBits(mode<<rp.PWM_CH0_CSR_DIVMODE_Pos, rp.PWM_CH0_CSR_DIVMODE_Msk, 0)
   247  }
   248  
   249  // setPeriod sets the pwm peripheral period (frequency). Calculates DIV_INT,DIV_FRAC and sets it from following equation:
   250  //
   251  //	cycles = (TOP+1) * (CSRPHCorrect + 1) * (DIV_INT + DIV_FRAC/16)
   252  //
   253  // where cycles is amount of clock cycles per PWM period.
   254  func (pwm *pwmGroup) setPeriod(period uint64) error {
   255  	// This period calculation algorithm consists of
   256  	// 1. Calculating best-fit prescale at a slightly lower-than-max TOP value
   257  	// 2. Calculate TOP value to reach target period given the calculated prescale
   258  	// 3. Apply calculated Prescale from step 1 and calculated Top from step 2
   259  	const (
   260  		maxTop = math.MaxUint16
   261  		// start algorithm at 95% Top. This allows us to undershoot period with prescale.
   262  		topStart     = 95 * maxTop / 100
   263  		nanosecond   = 1                  // 1e-9 [s]
   264  		microsecond  = 1000 * nanosecond  // 1e-6 [s]
   265  		milliseconds = 1000 * microsecond // 1e-3 [s]
   266  		// Maximum Period is 268369920ns on rp2040, given by (16*255+15)*8*(1+0xffff)*(1+1)/16
   267  		// With no phase shift max period is half of this value.
   268  		maxPeriod = 268 * milliseconds
   269  	)
   270  
   271  	if period > maxPeriod || period < 8 {
   272  		return ErrBadPeriod
   273  	}
   274  	if period > maxPeriod/2 {
   275  		pwm.setPhaseCorrect(true) // Must enable Phase correct to reach large periods.
   276  	}
   277  
   278  	// clearing above expression:
   279  	//  DIV_INT + DIV_FRAC/16 = cycles / ( (TOP+1) * (CSRPHCorrect+1) )  // DIV_FRAC/16 is always 0 in this equation
   280  	// where cycles must be converted to time:
   281  	//  target_period = cycles * period_per_cycle ==> cycles = target_period/period_per_cycle
   282  	periodPerCycle := uint64(cpuPeriod())
   283  	phc := uint64(pwm.getPhaseCorrect())
   284  	rhs := 16 * period / ((1 + phc) * periodPerCycle * (1 + topStart)) // right-hand-side of equation, scaled so frac is not divided
   285  	whole := rhs / 16
   286  	frac := rhs % 16
   287  	switch {
   288  	case whole > 0xff:
   289  		whole = 0xff
   290  	case whole == 0:
   291  		// whole calculation underflowed so setting to minimum
   292  		// permissible value in DIV_INT register.
   293  		whole = 1
   294  		frac = 0
   295  	}
   296  
   297  	// Step 2 is acquiring a better top value. Clearing the equation:
   298  	// TOP =  cycles / ( (DIVINT+DIVFRAC/16) * (CSRPHCorrect+1) ) - 1
   299  	top := 16*period/((16*whole+frac)*periodPerCycle*(1+phc)) - 1
   300  	if top > maxTop {
   301  		top = maxTop
   302  	}
   303  	pwm.SetTop(uint32(top))
   304  	pwm.setClockDiv(uint8(whole), uint8(frac))
   305  	return nil
   306  }
   307  
   308  // Int is integer value to reduce counting rate by. Must be greater than or equal to 1. DIV_INT is bits 4:11 (8 bits).
   309  // frac's (DIV_FRAC) default value on reset is 0. Max value for frac is 15 (4 bits). This is known as a fixed-point
   310  // fractional number.
   311  //
   312  //	cycles = (TOP+1) * (CSRPHCorrect + 1) * (DIV_INT + DIV_FRAC/16)
   313  func (pwm *pwmGroup) setClockDiv(Int, frac uint8) {
   314  	pwm.DIV.ReplaceBits((uint32(frac)<<rp.PWM_CH0_DIV_FRAC_Pos)|
   315  		u32max(uint32(Int), 1)<<rp.PWM_CH0_DIV_INT_Pos, rp.PWM_CH0_DIV_FRAC_Msk|rp.PWM_CH0_DIV_INT_Msk, 0)
   316  }
   317  
   318  // Set the highest value the counter will reach before returning to 0. Also
   319  // known as TOP.
   320  //
   321  // The counter wrap value is double-buffered in hardware. This means that,
   322  // when the PWM is running, a write to the counter wrap value does not take
   323  // effect until after the next time the PWM slice wraps (or, in phase-correct
   324  // mode, the next time the slice reaches 0). If the PWM is not running, the
   325  // write is latched in immediately.
   326  func (pwm *pwmGroup) setWrap(wrap uint16) {
   327  	pwm.TOP.ReplaceBits(uint32(wrap)<<rp.PWM_CH0_TOP_CH0_TOP_Pos, rp.PWM_CH0_TOP_CH0_TOP_Msk, 0)
   328  }
   329  
   330  // enables/disables the PWM peripheral with rp.PWM_CH0_CSR_EN bit.
   331  func (pwm *pwmGroup) enable(enable bool) {
   332  	pwm.CSR.ReplaceBits(boolToBit(enable)<<rp.PWM_CH0_CSR_EN_Pos, rp.PWM_CH0_CSR_EN_Msk, 0)
   333  }
   334  
   335  func (pwm *pwmGroup) setInverting(channel uint8, invert bool) {
   336  	var pos uint8
   337  	var msk uint32
   338  	switch channel {
   339  	case 0:
   340  		pos = rp.PWM_CH0_CSR_A_INV_Pos
   341  		msk = rp.PWM_CH0_CSR_A_INV_Msk
   342  	case 1:
   343  		pos = rp.PWM_CH0_CSR_B_INV_Pos
   344  		msk = rp.PWM_CH0_CSR_B_INV_Msk
   345  	}
   346  	pwm.CSR.ReplaceBits(boolToBit(invert)<<pos, msk, 0)
   347  }
   348  
   349  // Set the current PWM counter compare value for one channel
   350  //
   351  // The counter compare register is double-buffered in hardware. This means
   352  // that, when the PWM is running, a write to the counter compare values does
   353  // not take effect until the next time the PWM slice wraps (or, in
   354  // phase-correct mode, the next time the slice reaches 0). If the PWM is not
   355  // running, the write is latched in immediately.
   356  // Channel is 0 for A, 1 for B.
   357  func (pwm *pwmGroup) setChanLevel(channel uint8, level uint16) {
   358  	var pos uint8
   359  	var mask uint32
   360  	switch channel {
   361  	case 0:
   362  		pos = rp.PWM_CH0_CC_A_Pos
   363  		mask = rp.PWM_CH0_CC_A_Msk
   364  	case 1:
   365  		pos = rp.PWM_CH0_CC_B_Pos
   366  		mask = rp.PWM_CH0_CC_B_Msk
   367  	}
   368  	pwm.CC.ReplaceBits(uint32(level)<<pos, mask, 0)
   369  }
   370  
   371  func (pwm *pwmGroup) getChanLevel(channel uint8) (level uint16) {
   372  	var pos uint8
   373  	var mask uint32
   374  	switch channel {
   375  	case 0:
   376  		pos = rp.PWM_CH0_CC_A_Pos
   377  		mask = rp.PWM_CH0_CC_A_Msk
   378  	case 1:
   379  		pos = rp.PWM_CH0_CC_B_Pos
   380  		mask = rp.PWM_CH0_CC_B_Msk
   381  	}
   382  
   383  	level = uint16((pwm.CC.Get() & mask) >> pos)
   384  	return level
   385  }
   386  
   387  func (pwm *pwmGroup) getWrap() (top uint32) {
   388  	return (pwm.TOP.Get() & rp.PWM_CH0_TOP_CH0_TOP_Msk) >> rp.PWM_CH0_TOP_CH0_TOP_Pos
   389  }
   390  
   391  func (pwm *pwmGroup) getPhaseCorrect() (phCorrect uint32) {
   392  	return (pwm.CSR.Get() & rp.PWM_CH0_CSR_PH_CORRECT_Msk) >> rp.PWM_CH0_CSR_PH_CORRECT_Pos
   393  }
   394  
   395  func (pwm *pwmGroup) getClockDiv() (Int, frac uint8) {
   396  	div := pwm.DIV.Get()
   397  	return uint8((div & rp.PWM_CH0_DIV_INT_Msk) >> rp.PWM_CH0_DIV_INT_Pos), uint8((div & rp.PWM_CH0_DIV_FRAC_Msk) >> rp.PWM_CH0_DIV_FRAC_Pos)
   398  }
   399  
   400  // pwmGPIOToSlice Determine the PWM channel that is attached to the specified GPIO.
   401  // gpio must be less than 30. Returns the PWM slice number that controls the specified GPIO.
   402  func pwmGPIOToSlice(gpio Pin) (slicenum uint8) {
   403  	return (uint8(gpio) >> 1) & 7
   404  }
   405  
   406  // Determine the PWM channel that is attached to the specified GPIO.
   407  // Each slice 0 to 7 has two channels, A and B.
   408  func pwmGPIOToChannel(gpio Pin) (channel uint8) {
   409  	return uint8(gpio) & 1
   410  }