diff --git a/platforms/raspi/pwm_pin.go b/platforms/raspi/pwm_pin.go index 226aadaf..57f08073 100644 --- a/platforms/raspi/pwm_pin.go +++ b/platforms/raspi/pwm_pin.go @@ -9,13 +9,12 @@ import ( "gobot.io/x/gobot/sysfs" ) -const piBlasterPeriod = 10000000 - // PWMPin is the Raspberry Pi implementation of the PWMPinner interface. // It uses Pi Blaster. type PWMPin struct { - pin string - dc uint32 + pin string + dc uint32 + period uint32 } // NewPwmPin returns a new PWMPin @@ -51,11 +50,19 @@ func (p *PWMPin) InvertPolarity(invert bool) (err error) { // Period returns the current PWM period for pin func (p *PWMPin) Period() (period uint32, err error) { - return piBlasterPeriod, nil + if p.period == 0 { + return p.period, errors.New("Raspi PWM pin period not set") + } + + return p.period, nil } -// SetPeriod does not do anything when using PiBlaster +// SetPeriod uses PiBlaster setting and cannot be changed once set func (p *PWMPin) SetPeriod(period uint32) (err error) { + if p.period != 0 { + return errors.New("Cannot set the period of individual PWM pins on Raspi") + } + p.period = period return nil } @@ -66,12 +73,16 @@ func (p *PWMPin) DutyCycle() (duty uint32, err error) { // SetDutyCycle writes the duty cycle to the pin func (p *PWMPin) SetDutyCycle(duty uint32) (err error) { - if duty > piBlasterPeriod { + if p.period == 0 { + return errors.New("Raspi PWM pin period not set") + } + + if duty > p.period { return errors.New("Duty cycle exceeds period.") } p.dc = duty - val := gobot.FromScale(float64(p.dc), 0, piBlasterPeriod) + val := gobot.FromScale(float64(p.dc), 0, float64(p.period)) // never go below minimum allowed duty for pi blaster if val < 0.05 { diff --git a/platforms/raspi/pwm_pin_test.go b/platforms/raspi/pwm_pin_test.go index f9ecf428..b80e58ce 100644 --- a/platforms/raspi/pwm_pin_test.go +++ b/platforms/raspi/pwm_pin_test.go @@ -20,11 +20,14 @@ func TestPwmPin(t *testing.T) { val, _ = pin.Polarity() gobottest.Assert(t, val, "normal") - period, _ := pin.Period() - gobottest.Assert(t, period, uint32(10000000)) - gobottest.Assert(t, pin.SetPeriod(1000), nil) + period, err := pin.Period() + gobottest.Assert(t, err, errors.New("Raspi PWM pin period not set")) + gobottest.Assert(t, pin.SetDutyCycle(10000), errors.New("Raspi PWM pin period not set")) + + gobottest.Assert(t, pin.SetPeriod(20000000), nil) period, _ = pin.Period() - gobottest.Assert(t, period, uint32(10000000)) + gobottest.Assert(t, period, uint32(20000000)) + gobottest.Assert(t, pin.SetPeriod(10000000), errors.New("Cannot set the period of individual PWM pins on Raspi")) dc, _ := pin.DutyCycle() gobottest.Assert(t, dc, uint32(0)) diff --git a/platforms/raspi/raspi_adaptor.go b/platforms/raspi/raspi_adaptor.go index ba838f20..a1bf0551 100644 --- a/platforms/raspi/raspi_adaptor.go +++ b/platforms/raspi/raspi_adaptor.go @@ -34,15 +34,17 @@ type Adaptor struct { spiDevices [2]spi.Connection spiDefaultMode int spiDefaultMaxSpeed int64 + PiBlasterPeriod uint32 } // NewAdaptor creates a Raspi Adaptor func NewAdaptor() *Adaptor { r := &Adaptor{ - mutex: &sync.Mutex{}, - name: gobot.DefaultName("RaspberryPi"), - digitalPins: make(map[int]*sysfs.DigitalPin), - pwmPins: make(map[int]*PWMPin), + mutex: &sync.Mutex{}, + name: gobot.DefaultName("RaspberryPi"), + digitalPins: make(map[int]*sysfs.DigitalPin), + pwmPins: make(map[int]*PWMPin), + PiBlasterPeriod: 10000000, } content, _ := readFile() for _, v := range strings.Split(string(content), "\n") { @@ -261,6 +263,7 @@ func (r *Adaptor) PWMPin(pin string) (raspiPWMPin sysfs.PWMPinner, err error) { if r.pwmPins[i] == nil { r.pwmPins[i] = NewPWMPin(strconv.Itoa(i)) + r.pwmPins[i].SetPeriod(r.PiBlasterPeriod) } return r.pwmPins[i], nil @@ -273,7 +276,7 @@ func (r *Adaptor) PwmWrite(pin string, val byte) (err error) { return err } - duty := uint32(gobot.FromScale(float64(val), 0, 255) * piBlasterPeriod) + duty := uint32(gobot.FromScale(float64(val), 0, 255) * float64(r.PiBlasterPeriod)) return sysfsPin.SetDutyCycle(duty) } @@ -284,7 +287,7 @@ func (r *Adaptor) ServoWrite(pin string, angle byte) (err error) { return err } - duty := uint32(gobot.FromScale(float64(angle), 0, 180) * piBlasterPeriod) + duty := uint32(gobot.FromScale(float64(angle), 0, 180) * float64(r.PiBlasterPeriod)) return sysfsPin.SetDutyCycle(duty) } diff --git a/platforms/raspi/raspi_adaptor_test.go b/platforms/raspi/raspi_adaptor_test.go index 9032e773..44b3bf23 100644 --- a/platforms/raspi/raspi_adaptor_test.go +++ b/platforms/raspi/raspi_adaptor_test.go @@ -110,6 +110,7 @@ func TestAdaptorFinalize(t *testing.T) { func TestAdaptorDigitalPWM(t *testing.T) { a := initTestAdaptor() + a.PiBlasterPeriod = 20000000 gobottest.Assert(t, a.PwmWrite("7", 4), nil) @@ -118,6 +119,10 @@ func TestAdaptorDigitalPWM(t *testing.T) { }) sysfs.SetFilesystem(fs) + pin, _ := a.PWMPin("7") + period, _ := pin.Period() + gobottest.Assert(t, period, uint32(20000000)) + gobottest.Assert(t, a.PwmWrite("7", 255), nil) gobottest.Assert(t, strings.Split(fs.Files["/dev/pi-blaster"].Contents, "\n")[0], "4=1") @@ -128,6 +133,14 @@ func TestAdaptorDigitalPWM(t *testing.T) { gobottest.Assert(t, a.PwmWrite("notexist", 1), errors.New("Not a valid pin")) gobottest.Assert(t, a.ServoWrite("notexist", 1), errors.New("Not a valid pin")) + + pin, _ = a.PWMPin("12") + period, _ = pin.Period() + gobottest.Assert(t, period, uint32(20000000)) + + gobottest.Assert(t, pin.SetDutyCycle(1.5*1000*1000), nil) + + gobottest.Assert(t, strings.Split(fs.Files["/dev/pi-blaster"].Contents, "\n")[0], "18=0.075") } func TestAdaptorDigitalIO(t *testing.T) {