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