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 }