Skip to content

Software PWM Work #24

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 6 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
48 changes: 48 additions & 0 deletions examples/software pwm/pwm.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
/*

A Software Based PWM example by @Ronin11, using the go-rpio library

Toggles a LED on physical pin 10
Connect a LED with resistor from pin 10 to ground.

*/

package main

import (
"fmt"
"os"
"time"

"github.com/Ronin11/go-rpio"
)

const pin = rpio.Pin(10)

func main() {

if err := rpio.Open(); err != nil {
fmt.Println(err)
os.Exit(1)
}

// Unmap gpio memory when done
defer rpio.Close()

//Creates the PWM Signal running on the pin, at 2KHz, with a 0% PWM cycle.
pwm := rpio.CreateSofwarePWM(pin, 2000, 0, 32)

pwm.Start()
defer pwm.Stop()
// five times smoothly fade in and out
for i := 0; i < 5; i++ {
for i := uint32(0); i < 32; i++ { // increasing brightness
pwm.SetDutyCycle(i, 32)
time.Sleep(time.Second/32)
}
for i := uint32(32); i > 0; i-- { // decreasing brightness
pwm.SetDutyCycle(i, 32)
time.Sleep(time.Second/32)
}
}
}
73 changes: 73 additions & 0 deletions rpio.go
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,27 @@ type State uint8
type Pull uint8
type Edge uint8

type PwmStatus uint8
const (
STOPPED PwmStatus = 0
RUNNING PwmStatus = 1
)

type PwmState uint8
const (
OFF PwmState = 0
ON PwmState = 1
)

type SoftwarePWM struct {
pin Pin
freq uint32
dutyLen uint32
cycleLen uint32
status PwmStatus
state PwmState
}

// Memory offsets for gpio, see the spec for more details
const (
bcm2835Base = 0x20000000
Expand Down Expand Up @@ -547,6 +568,58 @@ func StartPwm() {
pwmMem[pwmCtlReg] = pwmMem[pwmCtlReg] | pwen<<8 | pwen
}

//Create a Software PWM struct
func CreateSofwarePWM(pin Pin, freq uint32, dutyLen uint32, cycleLen uint32) *SoftwarePWM{
//Set pin as Output
pin.Output()
return &SoftwarePWM{pin: pin, freq: freq, dutyLen: dutyLen, cycleLen: cycleLen}
}

//Sets the duty cycle for the software PWM
func (swPwm *SoftwarePWM) SetDutyCycle(dutyLen uint32, cycleLen uint32){
swPwm.dutyLen = dutyLen
swPwm.cycleLen = cycleLen
}

//Sets the frequency of the software PWM
func (swPwm *SoftwarePWM) SetFreq(freq uint32){
swPwm.freq = freq
}

//Starts the software PWM
func (swPwm *SoftwarePWM) Start(){
swPwm.status = RUNNING
swPwm.pwmLoop()
}

//Stops the software PWM
func (swPwm *SoftwarePWM) Stop(){
swPwm.status = STOPPED
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think go way to stop the loop might be to use "terminating" channel. Using status value like this is not thread safe.

swPwm.pin.Write(Low)
}


//Internal PWM execution loop
func (swPwm *SoftwarePWM) pwmLoop(){
go func(){
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No need to use anonymous function, you can use go swPwm.pwmLoop() in Start() method above.

var sleepInterval time.Duration
for swPwm.status == RUNNING {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is no need for ON/OFF state if you use something like this:

for {
  if status != RUNING { breake }
  pin.Write(High)
  time.Sleep(...)
  if status != RUNING { breake }
  pin.Write(Low)
  time.Sleep(...)
}

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Or better break using select and channel as I mentioned before.

select {
case <-stop:
   break
default:
}

I would also use time.Ticker instead of time.Sleep, for better accuracy.

//sleepInterval is the basic unit of time between pwm 'ticks'
sleepInterval = time.Second / time.Duration(swPwm.freq)
if swPwm.state == OFF {
swPwm.pin.Write(High)
swPwm.state = ON
sleepInterval = sleepInterval * time.Duration(swPwm.dutyLen)
} else {
swPwm.pin.Write(Low)
swPwm.state = OFF
sleepInterval = sleepInterval * time.Duration(swPwm.cycleLen - swPwm.dutyLen)
}
time.Sleep(sleepInterval)
}
}()
}

// Open and memory map GPIO memory range from /dev/mem .
// Some reflection magic is used to convert it to a unsafe []uint32 pointer
func Open() (err error) {
Expand Down