Need help: Fast 3 Phase PWM controlled Duty Cycle

So I created a three phase PWM using ARDUINO UNO with specific phase shift and duty cycle and stores the binary data in 31 x 30 arrays. The only faster way I found is using the port manipulation using Arduino alone. Now I want to control the duty cycle using a potentiometer, however, it seems that adding one line of code drastically drops the frequency from 31.25 kHz to 219.30 Hz.

Without Potentiometer: (value used is 0 - 30)

void loop(){
  while(true) {
    PORTB = pulses[15][i++];
    if (i >= sizeof(pulses[15])) i = 0;
  }

}

With potentiometer:

void loop(){
  while(true) {
    pot = analogRead(A5)/34.1;
    PORTB = pulses[pot][i++];
    if (i >= sizeof(pulses[pot])) i = 0;
  }

}

only by adding this line,

    pot = analogRead(A5)/34.1;

the frequency is drops so much, even without using the pot variable value in as the array index.

How do I make it faster or even return to the 31.25 kHz frequency?

1 Like

Check the pot less often.

2 Likes

are you looking for a three phase square wave?
e.g. using ESP32S3 RMT

1 Like

Hi,

I have already created a three phase square wave using the Arduino UNO. Though I want to change the hardware, but I am not allowed anymore. I made lots of experiments with it, and this is the the only faster way I know for now, using Arduino UNO alone.

However, I want to be controlling the duty cycle with a potentiometer with the analogRead, The value returned by the potentiometer will be from 0 to 30. I will be using this value to vary the array index in the loop() so that I can change the duty cycle. The PWM code is in binary code stored in an array outside of the loop like this 'const byte pulses[31][30]'. By using the value from the analogRead I can control this value 'pulses[pot][30] in the loop code to access the PWM binary code.

but it seems having an analogRead, slows things down so much. Is there anyway I can change this line to make it faster? or use the digital pins or other pins?

pot = analogRead(A5)/34.1;

Here is the loop code again:

void loop(){
  while(true) {
    pot = analogRead(A5)/34.1;
    PORTB = pulses[pot][i++];
    if (i >= sizeof(pulses[pot])) i = 0;
  }
  }

Thank you.

The answer is in the first reply to your thread. Reading an adc takes non-zero time. So read it less often.

Another approach to look at is setting your adc up to read via dma, although I don't know if the uno has that capability.

2 Likes

Using integer division will speed things up a bit. You also might want to adjust your arithmetic so you get the 30 result for more than just 1 of the 1024 possible different return values from analogRead. If the pot is a little sloppy, you may not be able to reach the "30" setting.

But the big speed boost will come from considering how fast you turn your pot or need to make adjustments. You likely do not need to adjust the pot at 31.25kHz. Throttling checking for pot changes to 10 or 100Hz would give a big speed boost.

2 Likes

analogRead is blocking so useless for time critical applications. Instead you need to configure the A2D converter, set it going making a conversion then come back when it's finished and read the results register directly. No, I don't know how to do that on a Uno so I can't give you sample code, you need to read the data sheet. As others have suggested you also need to be very careful about how often you take a reading, as in when you read the A2D results register, I suggest you try once every 100ms maximum and see if that does what you need.

Edit:
Also see if you can write your code so that you don't need to scale (multiply or divide) the result from reading the pot but instead use the value directly. If you do have to scale it then multiplication or division by powers of 2 is generally going to be faster as you can do that by right or left shifting.

2 Likes
1 Like

Thank you for the help, I think I get the idea. I will do the suggestions for awhile. then I shall come back for updates from my results, for future references.

1 Like

If you can live with /32, you do not need floating point conversion and your division will be done via bitshift. Way faster!!!

2 Likes

Alternatively, use the integer scale and divide approach. That is: find N/D that is closest to your number such that N * (maximum ADC reading) <= std::numeric_limits<uint32_t>::max() and D is a power of 2. Then, when you take your reading, scale via ADC_reading * N / D. N and D can be computed at runtime or compile time if you know the constant you're going to be using.

1 Like

Why not use a digital rotary pot, with interrupts.
Leo..

1 Like