PWM flicker between pwm duty changes
So after trying out quite a few different power ramp circuits from around the web to make an LED "breath", I decided I would give using pwm to change the brightness of the LED. Calling
fast-gpio pwm 1 50 50seemed to have the desired results, and other calls with varying duty percents had the desired effect. The LED would brighten or dim to about what you'd expect calling 10%, 50% or 100%. 50Hz also seemed to be the minimum frequency I could go to before the flicker became noticeable.
So then I wrote a python script to cycle from 1% duty cycle up to 100% and then back down to 1%. The script worked great! I could watch the LED brighten and dim over the course of a few seconds, but the flicker became very noticeable. I figured I'd crank the frequency up, so I set it to 5000 and ran the script again. Again, the flicker showed up. I tried 1000Hz, 2000Hz, numbers in between, but never got the flicker to go away.
So I figured, maybe it's because of the amount of time I'm waiting between duty changes. So I shortened the time between changes from 0.5 to 0.1, then to 0.01, then to 0.001. While the flickers became less noticeable at 0.1, that seemed to be as good as it got. Any subsequent fractions of a second didn't improve the situation.
Is this a limitation of the hardware? Is
fast-gpio pwmdoing something that causes the pin to go low between calls, creating the flicker? Is there a way around
fast-gpiothat I can call on to change the pwm duty percentage without re-calling the whole command?
Let me know if you've encountered this too and what you did to get around it! Right now those ramp circuits are looking pretty good...
Code below, in Python:
import os import sys import time import subprocess duty=1 pulse="50" sleeper=0.001 min=1 max=100 direction="up" read="fast-gpio read 2" pwm="fast-gpio pwm 1 " + pulse + " " high="> Read GPIO2: 1\n" os.system("gpioctl dirout 1") os.system("gpioctl dirin 2") status = subprocess.check_output(read, shell=True) while(status != high): print(duty) if(duty == max): direction="down" duty = duty - 1 elif(duty == min): direction="up" duty = duty + 1 elif(duty > min and direction == "up"): duty = duty + 1 elif(duty < max and direction == "down"): duty = duty - 1 status=subprocess.check_output(read, shell=True) os.system(pwm + str(duty)) time.sleep(sleeper) os.system("fast-gpio set 1 0")
WOW over a year and no replys. I think its because fast-gpio pwm is software based and the cpu is interrupting its attempts to provide a clean signal
Funny thing as I have done the exact same thing in nodejs.. Turns out I have the same issue.
I put it on my scope and I can see that there are periods in between the changes to the duty cycle that are not consistent lengths, and are either high or low.
This is because you and I are switching the duty cycles very inefficiently. A better way to do this would be to either compile the fast-gpio cpp library and import it as some sort of module directly, OR use some other hardware.
After giving it some thought I think I'm going with an arduino to do the actual PWM signal, and have the onion act as a form of controller. My PWM duty cycle was set at 50% and the Freq was at 2khz. On the scope I was getting +- 5% at ~ 1400 - 1600 khz.
The software solution here would be a bit of pain, and even after all that effort it'd still be software PWM.
Looking forward to hearing your solution !
For better PWM fine-tuning control, use device designed for such function, e.g., PCA9685 PWM controller IC.
Software emulated PWM is just part of MCU function. MCU still has other tasks and housekeeping functions to perform.
luz last edited by
Don't forget that the MT7688 SoC also has 4 very precise hardware PWM units built in! On the Omega2(+) though, you can only use 2 of them (PWM0 and 1) because it's lacking pins for PWM2 and 3 - on the SMT Omega2S, all 4 are available.
These PWMs would even work with standard Linux
/sys/class/pwminterface, if onion had enabled them in the device tree - which hasn't happened yet despite discussing it about it a year ago.