issues on PWM sine wave generator

hello
I’m a beginner on Arduino. I apologize for my English.
I’m trying to generate sinusoidal half-wave PWMs on pins 5 and 6. Then I will send the sinusoids to two bjt or mosfet connected in push pull and connected on the primary of a transformer as shown in figure 1: “Generatore sinusoidi Schema elettrico.jpg”
Now the outputs are connected, via RC filter, to the inputs of an oscilloscope. The code works perfectly and I get the sinusoids, only that each cycle lasts 120 mS while in Italy we have the mains current frequency at 50 Hz, so 20 mS per cycle: see “Generatore Sinusoidi funzionante con filtro RC.jpg”

The code:

// SINUSOIDE_MIA 5
// Define Pins
#define pin5 5
#define pin6 6
#define maxIndex 78 // (actually the array is 79 units, but to avoid operations during For cycles)
unsigned long ulngDelayT=0; // delay time
unsigned long ulngStartCycleT=0; // start time of last cycle

//original array 312 values from Pier Aisa's video #367 : 
// https://www.youtube.com/watch?v=BWouNZAFWG0
//  code Pwmsin2canali.ino
// byte sinPWM[]={1,2,5,7,10,12,15,17,19,22,24,27 ...};
//
// array taken from Pier Aisa's arry reduced to 1/4 of the original length, 
//taking one element of the array every two, 
//and then divided by two in order to store only a 1/4 of the whole wavelength

byte sinPWM[]={
1  ,5  ,10 ,15 ,19 ,24 ,30 ,34 ,39 ,44 ,49 ,54 ,59 ,64 ,69 ,73 ,78 ,83 ,88 ,
92 ,97 ,101,106,110,115,119,124,128,132,136,140,144,148,152,156,160,164,168,
171,175,178,182,185,188,192,195,198,201,204,207,209,212,215,217,220,222,224,
226,228,230,232,234,236,237,239,240,242,243,244,245,246,247,247,248,248,249,
249,250,250};

void setup()
{
pinMode(pin5, OUTPUT);
pinMode(pin6, OUTPUT);
Serial.begin(115200);
}

// main loop
void loop()
{
   // stores new wave start time  
   ulngStartCycleT=millis();
   // 1/4 cycle
   for (int i=1; i < maxIndex; i++){
      analogWrite(pin5, sinPWM[i]);
      Serial.println(sinPWM[i]);// prints the values on plotter or serial monitor
   } 
   // 2/4 cycle
   for (int i=maxIndex; i > 0; i--){
      analogWrite(pin5, sinPWM[i]);
      Serial.println(sinPWM[i]);// prints the values on plotter or serial monitor
   } 
   // to prevent spurious appearance on unused pin
   analogWrite(pin5, 0);
   // delay to prevent absorption conflict between BJTs
   ulngDelayT = millis();
   while (millis() - ulngDelayT  < 2){};
   //
   // 3/4 cycle
   for (int i=1; i < maxIndex; i++){
      analogWrite(pin6, sinPWM[i]);
      Serial.println(sinPWM[i]);// prints the values on plotter or serial monitor
   }
   // 4/4 cycle 
   for (int i=maxIndex; i > 0; i--){
      analogWrite(pin6, sinPWM[i]);
      Serial.println(sinPWM[i]);// prints the values on plotter or serial monitor
   } 
   // to prevent spurious appearance on unused pin
     analogWrite(pin6, 0);
    // delay to prevent absorption conflict between BJTs
   ulngDelayT = millis();
   while (millis() - ulngDelayT  < 2){};
   //
   // calculates the elapsed time to get a whole wave
   ulngDelayT = millis() - ulngStartCycleT;
   Serial.print("Cycle time in mS:");
   Serial.println(ulngDelayT);
   //delay(1000);
 }

Let me describe the problem:
to get 20 mS I removed the printing instructions on the serial plotter Serial.println(sinPWM*) but at this point I get a*
mess visible in : “Generatore Sinusoidi NON funzionante con filtro RC.jpg”
I tried everything. I plugged in another RC filter to no avail.
Can anyone help me?


Generatore Sinusoidi funzionante con filtro RC.jpg
Generatore Sinusoidi NON funzionante con filtro RC.jpg

consider ( tested with two LEDs the are active LOW)

// SINUSOIDE_MIA 5

#define LEDS
#ifdef LEDS
# define PinP 10
# define PinN 11
#else
# define PinP 5
# define PinN 6
#endif

byte sinPWM[]={
   1  ,5  ,10 ,15 ,19 ,24 ,30 ,34 ,39 ,44 ,49 ,54 ,59 ,64 ,69 ,73 ,78 ,83 ,88 ,
   92 ,97 ,101,106,110,115,119,124,128,132,136,140,144,148,152,156,160,164,168,
   171,175,178,182,185,188,192,195,198,201,204,207,209,212,215,217,220,222,224,
   226,228,230,232,234,236,237,239,240,242,243,244,245,246,247,247,248,248,249,
249,250,250};

#define N_PWM   sizeof (sinPWM)

byte  pin  = PinP;
int   idx  = 0;
int   dIdx = 1;

unsigned long msecLst = 0;
unsigned long msec;
unsigned long period  = 5;

// -----------------------------------------------------------------------------
void loop ()
{
   msec    = millis ();
   if (msec - msecLst > period)  {
       msecLst = msec;

#ifdef LEDS
       analogWrite (pin, 255 - sinPWM [idx]);
#else
       analogWrite (pin, sinPWM [idx]);
#endif
       idx += dIdx;

       if ((N_PWM-1) <= idx)
           dIdx = -dIdx;
       else if (0 == idx)  {
           dIdx = -dIdx;
           pin  = PinP == pin ? PinN : PinP;
       }
   }
}

// -----------------------------------------------------------------------------
void setup ()
{
   Serial.begin (115200);
   Serial.println ("pwm");

#ifdef LEDS
   analogWrite (PinP, 255);    // for LEDs
   analogWrite (PinN, 255);
#else
   analogWrite (PinP, 0);
   analogWrite (PinN, 0);
#endif

   pinMode (PinP, OUTPUT);
   pinMode (PinN, OUTPUT);
}

Thank you for your quick response. Unfortunately, I have to go to work now, I hope I can try your code tomorrow morning. I forgot to say my Arduino is Mega2560, I don't know if that's important.

you might also consider generating your 1/4 wave sin table in your code. while keeping the sampling rate (i.e. period) constant, changing the size of the table also affects it’s frequency

#define MAX_PWM 256
#define N_PWM   100
byte sinPWM [N_PWM];

// -----------------------------------------------------------------------------
void setup ()
{
    ...
    
    for (unsigned n = 0; n < N_PWM; n++)  {
       sinPWM [n] = MAX_PWM * sin ((PI / 2) * n / N_PWM);
       Serial.println (sinPWM [n]);
    }
}

Now I had time to look at your code.

The code is very interesting, but you forgot I’m a beginner at using Arduino :slight_smile:

There are some things I didn’t know : “ifdef”, “sizeof” but don’t worry, I’ll slowly learn them.

By connecting the oscilloscope on pin 10 and pin 11 I got the picture shown in “gcjr_code_pict_1.jpg”, “gcjr_code_pict_2”. A movie would have been better but it would have been too big.The pulse from min to max has a period of about 1 second.
In your other code the mathematical generation of the array was the same method used by the author of the array Pier Aisa as mentioned in my code.
Unfortunately I’m busy again today, but I’ll be in touch in the next few days because I’d like to resolve this code problem.

gcjr_code_pict_1.jpg

gcjr_code_pict_2.jpg

gianfard: There are some things I didn't know : "ifdef", "sizeof" but don't worry, I'll slowly learn them.

i don't have your hardware, so i changed the pins being used and i prefer to post code that i tested. commenting out ("//") the #define LEDS, meaning it is no longer defined will mean the ifdef fails and the else conditions throughout the code and your code will be in effect

gianfard: The pulse from min to max has a period of about 1 second.

i set the period relatively high so that i can see the modulation on the LEDs. in your code, the sampling rate was limited by the print.

you should set "period" to the desired period of a complete oscillation divided by 4 times the length of your 1/4 wave sine table (e.g. 3 usec = 1 msec / (4 * 83))

Thank you gcjr (your name is Greg ?) for your patience.
Now I got 20 mS semi-waves, but as you can see in the figure “gcjr_code_pict_3_20mS.jpg” the zero volt line is missing and the semi-waves are not in phase between pin 10 and 11 (the RC filter is now 3300 ohm+470nF).
I have programming experience (assembly on Commodore 64 for 3 years, Clipper on Dos and Windows for 15 years, Visual Basic for other 15 years until two years ago) but I always hated C language :slight_smile: too many semicolons and too many curly brackets :slight_smile:
Now wanting to learn something about Arduino I’m forced to learn the basics of C, but my knowledge doesn’t go beyond what’s described on Arduino Reference - Arduino Reference for beginners :slight_smile:
Now unfortunately I don’t understand your code: for example the line “pin = PinP == pin ? PinN : PinP;” is a mystery to me. It’s code for programmers with a lot of experience on C.
Now if I don’t understand a code I don’t like to use it. In Pier Aisa’s video from where I got the array there was a method to get sine waves with “hardware timers” but it’s too sophisticated for me.

Can you explain me why the code I reported in my first message doesn’t work if I remove the print on the serial monitor ?
Guido
Post scriptum: I modified the code by writing only:
unsigned long period = 40;

msec=micros();

gianfard: Now I got 20 mS semi-waves, but as you can see in the figure "gcr_code_pict_3_20mS.jpg" the zero volt line is missing and the semi-waves are not in phase between pin 10 and 11 (the RC filter is now 3300 ohm+470nF).

is this with your code or my code?

gianfard: I don't understand your code: for example the line "pin = PinP == pin ? PinN : PinP;" is a mystery to me. It's code for programmers with a lot of experience on C.

it shorthand for an if/else statement called a ternary operator that alternates which pin is used

seeing these things helps us learn

gianfard: Can you explain me why the code I reported in my first message doesn't work if I remove the print on the serial monitor ?

i ran your code, captured and plotted the output from the serial monitor and see 2 positive side sinusoids. so the values look correct, but i don't have timing. based on your scope trace there's a problem.

i'll explain the issues I see with your code. the major issue is the sampling rate is not fixed, affected by prints and pauses. i think the minor ones need to be fixed, after you fix the sampling rate

the minor issues are - typically there is a symbol (N) representing the # of table entries, you have a maxIndex - i believe the first entry in your table should be zero (so the cycle ends at zero)

  • your loops don't start with index 0
  • and don't end with maxIndex (one less as if maxIndex were the table size, N)
  • in the reverse direction, the loops don't end at zero (if you have a table size, initialize n to N-1)

  • i don't know why there is a pause between the two halves of the cycle

  • because the code is duplicated, the same problem exist in the duplicated code

  • there's an additional pause and more prints at the end of the cycle

i don't know if you have time for any prints, depends on your sampling rate

from working with digital signal processors, it's preferable to have a loop do one simple thing that can be modified to change performance. hence my code simply outputs a table entry to a pin. the index is either incremented or decremented using a variable and the pin is a variable which are modified by a simple conditional test. there's no duplicated code

At first I apologize for my silence, but I was testing the arrays. I discovered, as you said, that the elements are starting from zero. I was sure, because I read it somewhere, that in C language the arrays start from 1, which I thought was pretty unusual.

gcjr:
is this with your code or my code?

It’s your code, where I only changed the lines:

“unsigned long period = 40;”

“msec=micros();”

So I got 10 mS semi-waves, but distorted as you could see.

it shorthand for an if/else statement called a ternary operator that alternates which pin is used
seeing these things helps us learn

It’s very interesting, thanks

i’ll explain the issues I see with your code. the major issue is the sampling rate is not fixed, affected by prints and pauses. i think the minor ones need to be fixed, after you fix the sampling rate

In my first draft of the code I had applied a continuous control of the time taken by each wave, and if it was very different from 20 mS I would have added or removed micros() delay instructions. But as I said as soon as I deleted the print instructions instead of sinusoids I got the mess shown in my first message. After that I removed all the code that was not strictly necessary.

  • your loops don’t start with index 0
  • and don’t end with maxIndex (one less as if maxIndex were the table size, N)
  • in the reverse direction, the loops don’t end at zero
    (if you have a table size, initialize n to N-1)

In my tests with arrays, I found that an element of the array that did not exist was read, resulting in a random value. Now I’m going to try to correct the code

  • i don’t know why there is a pause between the two halves of the cycle

As it is written as an annotation in the source code the delay of 2 mS:
"ulngDelayT = millis();
while (millis() - ulngDelayT < 2){}; "
is inserted to make sure that one bjt is turned off while the other one turns on, to avoid that they are both in conduction. In this type of circuit there would be no major damage, but in other types of circuits there could be major damage.

from working with digital signal processors, it’s preferable to have a loop do one simple thing that can be modified to change performance. hence my code simply outputs a table entry to a pin. the index is either incremented or decremented using a variable and the pin is a variable which are modified by a simple conditional test. there’s no duplicated code

That’s a good idea. On Arduino there is not the same amount of ram that I can find on PCs I’m used to :slight_smile:

I corrected the code of my program "SINUSOIDE_MIA 5.ino", and now the arrays are read correctly without random values. But nothing has changed, if I remove the "Serial.println(sinPWM*)" instructions I get a mess. :confused:*

post your code. post your code printing the values capture a scope trace

another thing to consider is the PWM frequency (976 Hz on Uno)

several complete PWM cycles needs to occur before changing it for those values to have an affect. 20 ms / 78 = ~25 usec which is too fast.

you could try a smaller table, maybe 10-15 values

gcjr:
post your code.
post your code printing the values
capture a scope trace

In addition to modifying the array reading code I have removed all the delay cycles from my main program, and I also attach the oscilloscope trace.

// SINUSOIDE_MIA_7 
// Define Pins
#define pin5 5
#define pin6 6
#define maxIndex 78 
unsigned long ulngDelayT=0; // delay time
unsigned long ulngStartCycleT=0; // start time of last cycle
unsigned long ulngMicroDelayT=0; // micro delay time beetween every analogWrite

//original array 312 values from Pier Aisa's video #367 : 
// https://www.youtube.com/watch?v=BWouNZAFWG0
//  code Pwmsin2canali.ino
// byte sinPWM[]={1,2,5,7,10,12,15,17,19,22,24,27 ...};
//
// array taken from Pier Aisa's arry reduced to 1/4 of the original length, 
//taking one element of the array every two, 
//and then divided by two in order to store only a 1/4 of the whole wavelength

byte sinPWM[]={
1  ,5  ,10 ,15 ,19 ,24 ,30 ,34 ,39 ,44 ,49 ,54 ,59 ,64 ,69 ,73 ,78 ,83 ,88 ,
92 ,97 ,101,106,110,115,119,124,128,132,136,140,144,148,152,156,160,164,168,
171,175,178,182,185,188,192,195,198,201,204,207,209,212,215,217,220,222,224,
226,228,230,232,234,236,237,239,240,242,243,244,245,246,247,247,248,248,249,
249,250,250};

void setup()
{
pinMode(pin5, OUTPUT);
pinMode(pin6, OUTPUT);
Serial.begin(115200);
}

// main loop
void loop()
{
   // stores new wave start time  
   ulngStartCycleT=millis();
   // 1/4 cycle
   // for (int i=1; i < maxIndex; i++){ OLD
   for (int i=0; i <= maxIndex; i++){ 
      analogWrite(pin5, sinPWM[i]);
      Serial.println(sinPWM[i]);// prints the values on plotter or serial monitor
   } 
   // 2/4 cycle
   // for (int i=maxIndex; i > 0; i--){   OLD
   for (int i=maxIndex; i > -1; i--){
      analogWrite(pin5, sinPWM[i]);
      Serial.println(sinPWM[i]);// prints the values on plotter or serial monitor
   } 
   // to prevent spurious appearance on unused pin
   analogWrite(pin5, 0);
   //
   // 3/4 cycle
   //for (int i=1; i < maxIndex; i++){ OLD
   for (int i=0; i <= maxIndex; i++){ 
      analogWrite(pin6, sinPWM[i]);
      Serial.println(sinPWM[i]);// prints the values on plotter or serial monitor
   }
   // 4/4 cycle 
   // for (int i=maxIndex; i > 0; i--){ OLD
   for (int i=maxIndex; i > -1; i--){ 
      analogWrite(pin6, sinPWM[i]);
      Serial.println(sinPWM[i]);// prints the values on plotter or serial monitor
   } 
   // to prevent spurious appearance on unused pin
     analogWrite(pin6, 0);
   //
   // calculates the elapsed time to get a whole wave
   ulngDelayT = millis() - ulngStartCycleT;
   Serial.print("Cycle time in mS:");
   Serial.println(ulngDelayT);
 
 }

For beginners I have also included the program to test the array, which can be extensively perfected

// SINUSOIDE_MIA TEST ARRAY
//original array 312 values from Pier Aisa's video #367 : 
// https://www.youtube.com/watch?v=BWouNZAFWG0
//  code Pwmsin2canali.ino
// byte sinPWM[]={1,2,5,7,10,12,15,17,19,22,24,27 ...};
//
// array taken from Pier Aisa's arry reduced to 1/4 of the original length, 
//taking one element of the array every two, 
//and then divided by two in order to store only a 1/4 of the whole wavelength
//
// 19 values x 4 + 3 = 79 values - 1 = 78 because starts from zero
// print the values to serial monitor. Every 1/4 of cycle start with "----"
#define maxIndex 78 

byte sinPWM[]={
1  ,5  ,10 ,15 ,19 ,24 ,30 ,34 ,39 ,44 ,49 ,54 ,59 ,64 ,69 ,73 ,78 ,83 ,88 ,
92 ,97 ,101,106,110,115,119,124,128,132,136,140,144,148,152,156,160,164,168,
171,175,178,182,185,188,192,195,198,201,204,207,209,212,215,217,220,222,224,
226,228,230,232,234,236,237,239,240,242,243,244,245,246,247,247,248,248,249,
249,250,250};

void setup()
{
Serial.begin(115200);
}

// main loop
void loop()
{
   // 1/4 cycle
   for (int i=0; i <= maxIndex; i++){
       if (i == 0) {
        Serial.println("----");
        } 
       Serial.println(sinPWM[i]);// prints the values on plotter or serial monitor
       //
      if (i < 5 || i > 73) {
        delay(1000); // to slow at the start and end of the array to see the numbers on serial monitor
      }
      else{
        delay(5);
      }
    } 
   
   // 2/4 cycle
   for (int i=maxIndex; i > -1; i--){
     if (i == maxIndex){
        Serial.println("----");
        } 
        Serial.println(sinPWM[i]);// prints the values on plotter or serial monitor
        //
       if (i < 5 || i > 73) {
        delay(1000); // to slow at the start and end of the array to see the numbers on serial monitor
      }
      else{
        delay(5);
      }

   } 
      //
   // 3/4 cycle
   for (int i=0; i <= maxIndex; i++){
       if (i == 0) {
        Serial.println("----");
        } 
       Serial.println(sinPWM[i]);// prints the values on plotter or serial monitor
       //
       if (i < 5 || i > 73) {
        delay(1000); // to slow at the start and end of the array to see the numbers on serial monitor
      }
      else{
        delay(5);
      }

   }
   // 4/4 cycle 
   for (int i=maxIndex; i > -1; i--){
      if (i == maxIndex){
        Serial.println("----");
        } 
       Serial.println(sinPWM[i]);// prints the values on plotter or serial monitor
      //
       if (i < 5 || i > 73) {
        delay(1000); // to slow at the start and end of the array to see the numbers on serial monitor
      }
      else{
        delay(5);
      }
   } 
 }

Sine waves from code 7 Double RC filter.jpg

gcjr: another thing to consider is the PWM frequency (976 Hz on Uno)

several complete PWM cycles needs to occur before changing it for those values to have an affect. 20 ms / 78 = ~25 usec which is too fast.

you could try a smaller table, maybe 10-15 values

I think that's a good explanation, too. By tomorrow I'll try with fewer elements in the array.

All this complicated code when you can just do:

void loop()
{
  unsigned long us = micros() % 20000 ;  // microseconds that wraps around at 50Hz rate
  float wave = sin (2 * M_PI * us / 20000)) ;  // scale that to 0..2pi and take sine.
  if (wave < 0.0)
  {
    analogWrite (5, -255 * wave) ;
    analogWrite (6, 0) ;
  }
  else
  {
    analogWrite (6, 255 * wave) ;
    analogWrite (5, 0) ;
  }
}
}

It does need a little tweaking to handle the 32-bit wrap-around of the micros() value itself though,
so not quite that simple.

MarkT:
All this complicated code when you can just do:

each iteration of your code takes ~200+ usec and 90 samples per cycle. only 16 usec when using a table of pre-calculated values.

have you ever written DSP code? voice communication sampled at 8 kHz or 125 usec.

gia
what are the values of your R and C? i think you’re filtering too much

gcjr:
gia
what are the values of your R and C? i think you’re filtering too much

You guessed it, because my filter had a cut-off frequency of 54 Hz only: 820 ohms + 3.3 uF.
Now I’m using a filter with a frequency of about 100 Hz: 3300 ohm + 470 nF.
With the shorter array I was able to get a cycle much lower than 20 mS, so I can add delays in microseconds after each analogWrite to bring the cycle to 20 mS stable (see the picture and in the code delayMicroseconds(ulngMicroDelayT)) . Naturally these delays are calculated time by time based on the time taken by the last cycle.
I do not know if this procedure is correct.
But the waveforms at the oscilloscope are very distorted, and I don’t understand why.

Generatore Sinusoidi code 8-20 mS.jpg

MarkT: All this complicated code when you can just do:

Thanks for your help MarkT, but don't forget I'm a beginner. If it's possible I'll try your code the next few days to see the output on the oscilloscope

how did you get the “Generatore Sinusoidi funzionante con filtro RC.jpg” scope trace from your original post?

the last version of your code posted on July 12 still has the serial.prints() providing the timing. is there a version that uses micros()/millis() for timing?

presumably gcjr_code_pict_1.jpg shows both PWM outputs. why does the bottom (cyan) curve show an output that is predominantly HIGH with a very short period that is LOW. shouldn’t that bottom curve be at ground (LOW) 100% of the time?

the waveform samples should have a zero as the last value output on each half cycle

i think the RC cutoff frequency depends on the PWM frequency which i previously reported is 976Hz (although I thought it would be much higher - 200 kHz) and not dependent on the frequency of the waveform (50 Hz).

based on “Sine waves from code 7 Double RC filter.jpg”, where i suggested the RC may be too big, i felt that because when the wave form should have reached zero, it was still decaying.

it’s conceivable that the inductance of you coil will provide sufficient filtering and maybe all you need is something to protect against BEMF.

Generatore Sinusoidi code 8-20 mS.jpg

gcjr:
how did you get the “Generatore Sinusoidi funzionante con filtro RC.jpg” scope trace from your original post?

From the code shown in the code window in the same post, with a filter 33uF+47 ohm. As you can see the cycle is too long, about 150 mS, because the array was too long.

the last version of your code posted on July 12 still has the serial.prints() providing the timing. is there a version that uses micros()/millis() for timing?

I don’t understand your word “still”. the only version that uses micros()/millis() for timing is the last on July 13 because only with a short cycle < 20 mS i can apply the micro delays.

presumably gcjr_code_pict_1.jpg shows both PWM outputs. why does the bottom (cyan) curve show an output that is predominantly HIGH with a very short period that is LOW. shouldn’t that bottom curve be at ground (LOW) 100% of the time?

the waveform samples should have a zero as the last value output on each half cycle

I dont uderstand this phrase for my fault. As I said, both traces, the upper one and the lower one, pulse at frequency of about 1 second from 1% to 100%. As I said a movie would be better to show this movement . If you need it I can upload a movie on Youtube to show the oscilloscope screen.

i think the RC cutoff frequency depends on the PWM frequency which i previously reported is 976Hz (although I thought it would be much higher - 200 kHz) and not dependent on the frequency of the waveform (50 Hz).

As you can see in my first post with a 102 Hz filter (47 ohm+33 uF) I obtain a perfect sinusoidal wave, without any attenuation. So I think I must cut all the frequencies over 50 Hz.

it’s conceivable that the inductance of you coil will provide sufficient filtering and maybe all you need is something to protect against BEMF.

Yes, do you mean the coil of the trasformer ? I’ll connect diodes to protect the BJT.