Skip to content

Commit 939e5ab

Browse files
authored
Merge pull request #79 from youngkin/master
Add support for PWM balanced mode
2 parents ab13b61 + c34d95b commit 939e5ab

File tree

2 files changed

+120
-41
lines changed

2 files changed

+120
-41
lines changed

examples/pwmbalanced/pwm.go

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
/*
2+
3+
A PWM example by @youngkin, using the go-rpio library
4+
5+
Fades a PWM hardware pin in and out using PWM mode balanced (vs. markspace)
6+
*/
7+
8+
package main
9+
10+
import (
11+
"os"
12+
"time"
13+
14+
"github.com/stianeikeland/go-rpio/v4"
15+
)
16+
17+
func main() {
18+
err := rpio.Open()
19+
if err != nil {
20+
os.Exit(1)
21+
}
22+
defer rpio.Close()
23+
24+
pin := rpio.Pin(19)
25+
pin.Mode(rpio.Pwm)
26+
pin.Freq(64000)
27+
pin.DutyCycleWithPwmMode(0, 32, rpio.Balanced)
28+
// the LED will be blinking at 2000Hz
29+
// (source frequency divided by cycle length => 64000/32 = 2000)
30+
31+
// five times smoothly fade in and out
32+
for i := 0; i < 5; i++ {
33+
for i := uint32(0); i < 32; i++ { // increasing brightness
34+
pin.DutyCycleWithPwmMode(i, 32, rpio.Balanced)
35+
time.Sleep(time.Second / 32)
36+
}
37+
for i := uint32(32); i > 0; i-- { // decreasing brightness
38+
pin.DutyCycleWithPwmMode(i, 32, rpio.Balanced)
39+
time.Sleep(time.Second / 32)
40+
}
41+
}
42+
43+
pin.DutyCycleWithPwmMode(0, 32, rpio.Balanced)
44+
}

rpio.go

Lines changed: 76 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -3,31 +3,31 @@ Package rpio provides GPIO access on the Raspberry PI without any need
33
for external c libraries (eg. WiringPi or BCM2835).
44
55
Supports simple operations such as:
6-
- Pin mode/direction (input/output/clock/pwm,alt0,alt1,alt2,alt3,alt4,alt5)
7-
- Pin write (high/low)
8-
- Pin read (high/low)
9-
- Pin edge detection (no/rise/fall/any)
10-
- Pull up/down/off
6+
- Pin mode/direction (input/output/clock/pwm,alt0,alt1,alt2,alt3,alt4,alt5)
7+
- Pin write (high/low)
8+
- Pin read (high/low)
9+
- Pin edge detection (no/rise/fall/any)
10+
- Pull up/down/off
1111
Also clock/pwm related oparations:
12-
- Set Clock frequency
13-
- Set Duty cycle
12+
- Set Clock frequency
13+
- Set Duty cycle
1414
And SPI oparations:
15-
- SPI transmit/recieve/exchange bytes
16-
- Chip select
17-
- Set speed
15+
- SPI transmit/recieve/exchange bytes
16+
- Set speed
17+
- Chip select
1818
1919
Example of use:
2020
21-
rpio.Open()
22-
defer rpio.Close()
21+
rpio.Open()
22+
defer rpio.Close()
2323
24-
pin := rpio.Pin(4)
25-
pin.Output()
24+
pin := rpio.Pin(4)
25+
pin.Output()
2626
27-
for {
28-
pin.Toggle()
29-
time.Sleep(time.Second)
30-
}
27+
for {
28+
pin.Toggle()
29+
time.Sleep(time.Second)
30+
}
3131
3232
The library use the raw BCM2835 pinouts, not the ports as they are mapped
3333
on the output pins for the raspberry pi, and not the wiringPi convention.
@@ -100,10 +100,10 @@ const (
100100

101101
// BCM 2711 has a different mechanism for pull-up/pull-down/enable
102102
const (
103-
GPPUPPDN0 = 57 // Pin pull-up/down for pins 15:0
104-
GPPUPPDN1 = 58 // Pin pull-up/down for pins 31:16
105-
GPPUPPDN2 = 59 // Pin pull-up/down for pins 47:32
106-
GPPUPPDN3 = 60 // Pin pull-up/down for pins 57:48
103+
GPPUPPDN0 = 57 // Pin pull-up/down for pins 15:0
104+
GPPUPPDN1 = 58 // Pin pull-up/down for pins 31:16
105+
GPPUPPDN2 = 59 // Pin pull-up/down for pins 47:32
106+
GPPUPPDN3 = 60 // Pin pull-up/down for pins 57:48
107107
)
108108

109109
var (
@@ -146,6 +146,12 @@ const (
146146
High
147147
)
148148

149+
// Which PWM algorithm to use, Balanced or Mark/Space
150+
const (
151+
Balanced = true
152+
MarkSpace = false
153+
)
154+
149155
// Pull Up / Down / Off
150156
const (
151157
PullOff Pull = iota
@@ -222,6 +228,12 @@ func (pin Pin) DutyCycle(dutyLen, cycleLen uint32) {
222228
SetDutyCycle(pin, dutyLen, cycleLen)
223229
}
224230

231+
// DutyCycleWithPwmMode: Set duty cycle for Pwm pin while also specifying which PWM
232+
// mode to use, Balanced or MarkSpace (see doc of SetDutyCycleWithPwmMode)
233+
func (pin Pin) DutyCycleWithPwmMode(dutyLen, cycleLen uint32, mode bool) {
234+
SetDutyCycleWithPwmMode(pin, dutyLen, cycleLen, mode)
235+
}
236+
225237
// Mode: Set pin Mode
226238
func (pin Pin) Mode(mode Mode) {
227239
PinMode(pin, mode)
@@ -259,7 +271,7 @@ func (pin Pin) PullOff() {
259271

260272
func (pin Pin) ReadPull() Pull {
261273
if !isBCM2711() {
262-
return PullNone // Can't read pull-up/pull-down state on other Pi boards
274+
return PullNone // Can't read pull-up/pull-down state on other Pi boards
263275
}
264276

265277
reg := GPPUPPDN0 + (uint8(pin) >> 4)
@@ -272,7 +284,7 @@ func (pin Pin) ReadPull() Pull {
272284
case 2:
273285
return PullDown
274286
default:
275-
return PullNone // Invalid
287+
return PullNone // Invalid
276288
}
277289
}
278290

@@ -481,51 +493,51 @@ func EdgeDetected(pin Pin) bool {
481493
}
482494

483495
func PullMode(pin Pin, pull Pull) {
484-
496+
485497
memlock.Lock()
486498
defer memlock.Unlock()
487499

488500
if isBCM2711() {
489501
pullreg := GPPUPPDN0 + (pin >> 4)
490502
pullshift := (pin & 0xf) << 1
491-
492-
var p uint32
493-
503+
504+
var p uint32
505+
494506
switch pull {
495507
case PullOff:
496508
p = 0
497509
case PullUp:
498510
p = 1
499511
case PullDown:
500-
p = 2;
512+
p = 2
501513
}
502-
514+
503515
// This is verbatim C code from raspi-gpio.c
504516
pullbits := gpioMem[pullreg]
505517
pullbits &= ^(3 << pullshift)
506518
pullbits |= (p << pullshift)
507-
gpioMem[pullreg]= pullbits
519+
gpioMem[pullreg] = pullbits
508520
} else {
509521
// Pull up/down/off register has offset 38 / 39, pull is 37
510522
pullClkReg := pin/32 + 38
511523
pullReg := 37
512524
shift := pin % 32
513-
525+
514526
switch pull {
515527
case PullDown, PullUp:
516528
gpioMem[pullReg] |= uint32(pull)
517529
case PullOff:
518530
gpioMem[pullReg] &^= 3
519531
}
520-
532+
521533
// Wait for value to clock in, this is ugly, sorry :(
522534
time.Sleep(time.Microsecond)
523-
535+
524536
gpioMem[pullClkReg] = 1 << shift
525-
537+
526538
// Wait for value to clock in
527539
time.Sleep(time.Microsecond)
528-
540+
529541
gpioMem[pullReg] &^= 3
530542
gpioMem[pullClkReg] = 0
531543
}
@@ -550,7 +562,7 @@ func SetFreq(pin Pin, freq int) {
550562
if isBCM2711() {
551563
sourceFreq = 52000000
552564
}
553-
const divMask = 4095 // divi and divf have 12 bits each
565+
const divMask = 4095 // divi and divf have 12 bits each
554566

555567
divi := uint32(sourceFreq / freq)
556568
divf := uint32(((sourceFreq % freq) << 12) / freq)
@@ -627,12 +639,26 @@ func SetFreq(pin Pin, freq int) {
627639
// The channels are:
628640
// channel 1 (pwm0) for pins 12, 18, 40
629641
// channel 2 (pwm1) for pins 13, 19, 41, 45.
642+
//
643+
// NOTE without root permission this function will simply do nothing successfully
630644
func SetDutyCycle(pin Pin, dutyLen, cycleLen uint32) {
645+
SetDutyCycleWithPwmMode(pin, dutyLen, cycleLen, MarkSpace)
646+
647+
}
648+
649+
// SetDutyCycleWithPwmMode extends SetDutyCycle to allow for the specification of the PWM
650+
// algorithm to be used, Balanced or Mark/Space. The constants Balanced or MarkSpace
651+
// as the value. See 'SetDutyCycle(pin, dutyLen, cycleLen)' above for more information
652+
// regarding how to use 'SetDutyCycleWithPwmMode()'.
653+
//
654+
// NOTE without root permission this function will simply do nothing successfully
655+
func SetDutyCycleWithPwmMode(pin Pin, dutyLen, cycleLen uint32, mode bool) {
631656
const pwmCtlReg = 0
632657
var (
633658
pwmDatReg uint
634659
pwmRngReg uint
635660
shift uint // offset inside ctlReg
661+
636662
)
637663

638664
switch pin {
@@ -646,20 +672,29 @@ func SetDutyCycle(pin Pin, dutyLen, cycleLen uint32) {
646672
shift = 8
647673
default:
648674
return
675+
649676
}
650677

651678
const ctlMask = 255 // ctl setting has 8 bits for each channel
652679
const pwen = 1 << 0 // enable pwm
653-
const msen = 1 << 7 // use M/S transition instead of pwm algorithm
680+
var msen uint32 = 0
681+
// The MSEN1 field in the CTL register is at offset 7. This block starts with the assumption
682+
// that 'msen' will be associated with channel 'pwm0'. If this is not the case, 'msen' will
683+
// be further shifted in the next code block below to the MSEN2 field at offset 15.
684+
if mode == MarkSpace {
685+
msen = 1 << 7
686+
}
654687

655-
// reset settings
688+
// Shifting 'pwen' and 'msen' puts the associated values at the correct offset within the CTL
689+
// register ('pwmCtlReg'). In addition, 'msen' is associated with a PWM channel depending on the
690+
// value of 'pin' (see above). 'msen' will either stay at offset 7, as set above for channel 'pwm0',
691+
// or be shifted 8 bits if the the associated 'pin' is on channel 'pwm1'.
656692
pwmMem[pwmCtlReg] = pwmMem[pwmCtlReg]&^(ctlMask<<shift) | msen<<shift | pwen<<shift
693+
657694
// set duty cycle
658695
pwmMem[pwmDatReg] = dutyLen
659696
pwmMem[pwmRngReg] = cycleLen
660697
time.Sleep(time.Microsecond * 10)
661-
662-
// NOTE without root permission this changes will simply do nothing successfully
663698
}
664699

665700
// StopPwm: Stop pwm for both channels

0 commit comments

Comments
 (0)