Multiple simultaneous PWM outputs

CircuitPython on hardware including Adafruit's boards, and CircuitPython libraries using Blinka on host computers.

Moderators: adafruit_support_bill, adafruit

Please be positive and constructive with your questions and comments.
Locked
User avatar
mikmik_78
 
Posts: 4
Joined: Thu Aug 19, 2021 11:53 pm

Multiple simultaneous PWM outputs

Post by mikmik_78 »

Hello everyone,

I am working on a CircuitPython-Grand Central M4 based project where I need to output 12x40 kHz PWM signals with varying duty cycle. Each PWM is assigned a specific output pin and a list of duty cycle values. The PWMs need to be generated quasi simultaneously, at least with minimum delay. In the attached drawing, I illustrated for a few of the output pins what I want to get and what I measured with a scope using the simple code below (the code is just for explanation purposes, the list of duty cycle in the real application contains 100s of values). Well, no need to say that the results are far off the expectations.

There is a large delay in-between the pulses on the different outputs. The "apparent" duty-cycle/frequency of the generated signals are completely messed up. Is the delay mainly due to the internal functioning of the PWMOut class when modifying the duty_cycle attribute? Something like an internal timer that prevents the execution of further instructions in the code before a certain state has been reached? From a more general perspective, would you have any recommendations on how to approach the problem?

Code: Select all

# libraries
import board
import pulseio

#Output PWM frequency
fPWM=40000

#Output PWM pins and signals
pwm_pins=[board.A1, board.A2, board.A12,board.A15,board.D32, board.D34, 
          board.D38,board.D39,board.D14, board.D15, board.D27,board.D28]
pwm_signals=[pulseio.PWMOut(i, frequency = fPWM) for i in pwm_pins]

#Dummy values of the duty cycles for the PWM signals. Each signal is assigned a duty cycle list with length  'len_dc_list' 
len_dc_list=2
pwm_dc_list=[[50+10*j+20*i for i in range(0,len_dc_list,1)] for j in range(0,len(pwm_pins),1)]

#Loop varying duty cycles of the output PWM pins
for j in range(0,len_dc_list,1):
    for i in range(0,len(pwm_signals),1):
        pwm_signals[i].duty_cycle=pwm_dc_list[i][j]*2**8
        pwm_signals[i].duty_cycle=0
Attachments
PWM.JPG
PWM.JPG (49.42 KiB) Viewed 124 times

User avatar
tannewt
 
Posts: 3304
Joined: Thu Oct 06, 2016 8:48 pm

Re: Multiple simultaneous PWM outputs

Post by tannewt »

Have you looked at PulseOut? That might be closer to what you want if you want a series of varying length pulses over time. PWMOut isn't really meant to be cycle modifiable.

User avatar
mikmik_78
 
Posts: 4
Joined: Thu Aug 19, 2021 11:53 pm

Re: Multiple simultaneous PWM outputs

Post by mikmik_78 »

tannewt wrote:Have you looked at PulseOut? That might be closer to what you want if you want a series of varying length pulses over time. PWMOut isn't really meant to be cycle modifiable.
Yes, I had a look at PulseOut and have some difficulties with it. First off all, there is a still an important delay between the different outputs. It is mentioned in the PulseOut.send() documentation that This method waits until the whole array of pulses has been sent and ensures the signal is off afterwards.. I guess it means that the next line of code can't be executed before this condition is met, is it correct? Anyway to circumvent this?

I met an additional issue in the code below. There is no pulse generated after the first loop on j, any idea why?

Code: Select all

# libraries
import board
import pulseio
import array

#Output PWM frequency
fPWM=40000

#Definition output PWM pins
pwm_pins=[board.A1, board.A2, board.A12,board.A15,board.D32, board.D34, 
          board.D38,board.D39,board.D14, board.D15, board.D27,board.D28]

#Dummy values of the duty cycles for the different PWM pins, for testing purposes
#A list with 'len_dc_list' values is assigned to each output 
len_dc_list=3
pwm_dc_list=[[50+10*j+20*i for i in range(0,len_dc_list,1)] for j in range(0,len(pwm_pins),1)]

#Duration of pulse
on_duration=array.array('H',[25])

#Set PWM duty cycle for each pins
for j in range(0,len_dc_list,1):
    for i in range(0,len(pwm_pins),1):
        pulseio.PulseOut(pulseio.PWMOut(pwm_pins[i], frequency = fPWM,duty_cycle=pwm_dc_list[i][j]*2**8)).send(on_duration)
Attachments
PWM2.JPG
PWM2.JPG (57.28 KiB) Viewed 106 times

User avatar
tannewt
 
Posts: 3304
Joined: Thu Oct 06, 2016 8:48 pm

Re: Multiple simultaneous PWM outputs

Post by tannewt »

Ah, ya. PulseOut does block on send.

Are you monitoring the serial output? I suspect you'll get an exception in that newer example because you are creating the PulseOut but not deinitializing it once you are done. This will leave the pins "in use" and prevent their subsequent use.

Unfortunately, I don't think there is an API to do what you want to do. What are you using these PWM signals for?

User avatar
mikmik_78
 
Posts: 4
Joined: Thu Aug 19, 2021 11:53 pm

Re: Multiple simultaneous PWM outputs

Post by mikmik_78 »

tannewt wrote:Ah, ya. PulseOut does block on send.

Are you monitoring the serial output? I suspect you'll get an exception in that newer example because you are creating the PulseOut but not deinitializing it once you are done. This will leave the pins "in use" and prevent their subsequent use.

Unfortunately, I don't think there is an API to do what you want to do. What are you using these PWM signals for?
Yes, I monitor the serial output. I do not get any error messages and according to it, the program run till completion. I tried to use the deinit() method after each pulse.send in the loop but still only get a single pulse fro each output.

I am using the PWM signals to drive arrays of actuators attached on a surface to generate specific vibration patterns. I am checking now if I could use audiopwmio. From my basic understanding, it seems that I could output PWM on at least two channels (quasi) simultaneously.

Now, I have a noob question. Knowing that I have absolutely no experience with low level programming, what would it take to write my own API to fit my needs? Years of computer science studies or a few months digging here and there for info and help on internet? Could you direct me to some good material on the topic?

User avatar
tannewt
 
Posts: 3304
Joined: Thu Oct 06, 2016 8:48 pm

Re: Multiple simultaneous PWM outputs

Post by tannewt »

This is a good reference for what adding a module takes: https://learn.adafruit.com/extending-circuitpython It's not fully updated but it's still similar enough.

User avatar
mikmik_78
 
Posts: 4
Joined: Thu Aug 19, 2021 11:53 pm

Re: Multiple simultaneous PWM outputs

Post by mikmik_78 »

tannewt wrote:This is a good reference for what adding a module takes: https://learn.adafruit.com/extending-circuitpython It's not fully updated but it's still similar enough.
Ok, thanks for the link. As I do not even understand the first sentence in it, I guess it will take quite some time before I reach my objective. I would like to clear some doubts before even starting to spend time on the topic.

I need to have 12*40 kHz PWM channels with different duty cycles on each channel and varying duty cycle for each PWM period. The PWM outputs should be quasi-simultaneous with a delay between channels less than 25 us. Do you have an idea if it is even possible to reach these specs with a custom CircuitPython module? Or should I shift right now to Arduino or something else?

User avatar
tannewt
 
Posts: 3304
Joined: Thu Oct 06, 2016 8:48 pm

Re: Multiple simultaneous PWM outputs

Post by tannewt »

You can do anything you can from Arduino in a CircuitPython module. Both are lower level and give you more fine grained control of the hardware.

Locked
Please be positive and constructive with your questions and comments.

Return to “Adafruit CircuitPython”