While investigating problems with the GPIOZero unit-tests, I eventually managed to slim the problem down to this simple single test-script:
import RPi.GPIO as GPIO
import time
# GPIOs 22 and 27 need to be connected with a jumper
input_gpio = 27
output_gpio = 22
GPIO.setmode(GPIO.BCM)
GPIO.setup(input_gpio, GPIO.IN, pull_up_down=GPIO.PUD_DOWN)
GPIO.setup(output_gpio, GPIO.OUT)
p = GPIO.PWM(output_gpio, 100)
p.start(0)
do_start_stop = False
def test_duty_cycles(i):
print(" test_duty_cycles iteration %d" % i)
if do_start_stop:
p.stop()
p.start(0)
for dc in (40, 60):
p.ChangeDutyCycle(dc)
total = sum(GPIO.input(input_gpio) for i in range(20000))
avg = (total / 20000.0) * 100
print(" testing %.1f Hz, measured %.2f" % (dc, avg))
if abs(avg - dc) > 10:
print("* over 10 Hz difference!")
try:
for i in range(5):
test_duty_cycles(i+1)
finally:
GPIO.cleanup()
With do_start_stop set to False, then the script always runs fine (i.e. the measured frequency is always within 10Hz of the expected frequency. (Tested on up-to-date Raspbian Jessie on a RPi3 using both python and python3)
However when do_start_stop is set to True, then it never runs completely successfully e.g.
test_duty_cycles iteration 1
testing 40.0 Hz, measured 44.27
testing 60.0 Hz, measured 59.58
test_duty_cycles iteration 2
testing 40.0 Hz, measured 40.81
testing 60.0 Hz, measured 20.71
* over 10 Hz difference!
test_duty_cycles iteration 3
testing 40.0 Hz, measured 60.60
* over 10 Hz difference!
testing 60.0 Hz, measured 64.00
test_duty_cycles iteration 4
testing 40.0 Hz, measured 42.34
testing 60.0 Hz, measured 40.79
* over 10 Hz difference!
test_duty_cycles iteration 5
testing 40.0 Hz, measured 24.90
* over 10 Hz difference!
testing 60.0 Hz, measured 27.54
* over 10 Hz difference!
and it frequently errors with "Segmentation fault" at the end of the script (which seems to be more common with python3 than python2).
The GPIOZero unit-tests involve lots of starting and stopping of PWM, and these segmentation faults mean the PWM unit-tests all have to be disabled :-(
I can confirm this behavoir as well, the work around was to use ChangeDutyCycle(0),ChangeDutyCycle(X) instead of stop()/start(X). From looking in gdb, start/stop create and destroy threads and should only be called once on a PWM instance. Calling multiple triggers some thread-unsafe logic that in my case lead to segmentation faults as well.