3pwm signal for 3 phase BLDC control

#ifndef __PGMSPACE_H_
#define __PGMSPACE_H_ 1

#include <inttypes.h>

#define PROGMEM
#define PGM_P  const char *
#define PSTR(str) (str)

typedef void prog_void;
typedef char prog_char;
typedef unsigned char prog_uchar;
typedef int8_t prog_int8_t;
typedef uint8_t prog_uint8_t;
typedef int16_t prog_int16_t;
typedef uint16_t prog_uint16_t;
typedef int32_t prog_int32_t;
typedef uint32_t prog_uint32_t;

#define strcpy_P(dest, src) strcpy((dest), (src))
#define strcat_P(dest, src) strcat((dest), (src))
#define strcmp_P(a, b) strcmp((a), (b))

#define pgm_read_byte(addr) (*(const unsigned char *)(addr))
#define pgm_read_word(addr) (*(const unsigned short *)(addr))
#define pgm_read_dword(addr) (*(const unsigned long *)(addr))
#define pgm_read_float(addr) (*(const float *)(addr))

#define pgm_read_byte_near(addr) pgm_read_byte(addr)
#define pgm_read_word_near(addr) pgm_read_word(addr)
#define pgm_read_dword_near(addr) pgm_read_dword(addr)
#define pgm_read_float_near(addr) pgm_read_float(addr)
#define pgm_read_byte_far(addr) pgm_read_byte(addr)
#define pgm_read_word_far(addr) pgm_read_word(addr)
#define pgm_read_dword_far(addr) pgm_read_dword(addr)
#define pgm_read_float_far(addr) pgm_read_float(addr)

#endif
// Look Up table of a single sine period divied up into 256 values. Refer to PWM to sine.xls on how the values was calculated
PROGMEM  prog_uchar sine256[]  = {
  127,130,133,136,139,143,146,149,152,155,158,161,164,167,170,173,176,178,181,184,187,190,192,195,198,200,203,205,208,210,212,215,217,219,221,223,225,227,229,231,233,234,236,238,239,240,
  242,243,244,245,247,248,249,249,250,251,252,252,253,253,253,254,254,254,254,254,254,254,253,253,253,252,252,251,250,249,249,248,247,245,244,243,242,240,239,238,236,234,233,231,229,227,225,223,
  221,219,217,215,212,210,208,205,203,200,198,195,192,190,187,184,181,178,176,173,170,167,164,161,158,155,152,149,146,143,139,136,133,130,127,124,121,118,115,111,108,105,102,99,96,93,90,87,84,81,78,
  76,73,70,67,64,62,59,56,54,51,49,46,44,42,39,37,35,33,31,29,27,25,23,21,20,18,16,15,14,12,11,10,9,7,6,5,5,4,3,2,2,1,1,1,0,0,0,0,0,0,0,1,1,1,2,2,3,4,5,5,6,7,9,10,11,12,14,15,16,18,20,21,23,25,27,29,31,
  33,35,37,39,42,44,46,49,51,54,56,59,62,64,67,70,73,76,78,81,84,87,90,93,96,99,102,105,108,111,115,118,121,124

};
#define cbi(sfr, bit) (_SFR_BYTE(sfr) &= ~_BV(bit)) //define a bit to have the properties of a clear bit operator
#define sbi(sfr, bit) (_SFR_BYTE(sfr) |= _BV(bit))//define a bit to have the properties of a set bit operator

int PWM1= 11;// PWM1 output, phase 1
int PWM2 = 3; //![WM2 ouput, phase 2
int PWM3 = 10; //PWM3 output, phase 3
int offset_1 = 85; //offset 1 is 120 degrees out of phase with previous phase, Refer to PWM to sine.xls
int offset_2 = 170; //offset 2 is 120 degrees out of phase with offset 1. Refer to PWM to sine.xls
int program_exec_time = 6; //monitor how quickly the interrupt trigger
int ISR_exec_time = 7; //monitor how long the interrupt takes

double dfreq;
const double refclk=31376.6;      // measured output frequency

// variables used inside interrupt service declared as voilatile
volatile byte current_count;              // Keep track of where the current count is in sine 256 array
volatile byte ms4_delay;             //variable used to generate a 4ms delay
volatile byte c4ms;              // after every 4ms this variable is incremented, its used to create a delay of 1 second
volatile unsigned long phase_accumulator;   // pahse accumulator
volatile unsigned long tword_m;  // dds tuning word m, refer to DDS_calculator (from Martin Nawrath) for explination.

void setup()
{
  pinMode(PWM1, OUTPUT);      //sets the digital pin as output
  pinMode(PWM2, OUTPUT);      //sets the digital pin as output
  pinMode(PWM3, OUTPUT);      //sets the digital pin as output
  pinMode(program_exec_time, OUTPUT);      //sets the digital pin as output
  pinMode(9, OUTPUT);         //sets the digital pin as output
  sbi(PORTD,program_exec_time); //Sets the pin
  Setup_timer1();
  Setup_timer2();
  
  //Disable Timer 1 interrupt to avoid any timing delays
  cbi (TIMSK0,TOIE0);              //disable Timer0 !!! delay() is now not available
  sbi (TIMSK2,TOIE2);              //enable Timer2 Interrupt

  dfreq=1000.0;                    //initial output frequency = 1000.o Hz
  tword_m=pow(2,32)*dfreq/refclk;  //calulate DDS new tuning word 

}
void loop()
{
  while(1) 
  {
      sbi(PORTD,program_exec_time); //Sets the pin 
      if (c4ms > 250) // c4ms = 4ms, thus 4ms *250 = 1 second delay
       {                 
        c4ms=0;                          //Reset c4ms
        dfreq=analogRead(0);             //Read voltage on analog 1 to see desired output frequency, 0V = 0Hz, 5V = 1.023kHz
        cbi (TIMSK2,TOIE2);              //Disable Timer2 Interrupt
        tword_m=pow(2,32)*dfreq/refclk;  //Calulate DDS new tuning word
        sbi (TIMSK2,TOIE2);              //Enable Timer2 Interrupt 
      }
  }
}

//Timer 1 setup
//Set prscaler to 1, PWM mode to phase correct PWM,  16000000/510 = 31372.55 Hz clock
void Setup_timer1(void)
{
  // Timer1 Clock Prescaler to : 1
  sbi (TCCR1B, CS10);
  cbi (TCCR1B, CS11);
  cbi (TCCR1B, CS12);
  
  // Timer1 PWM Mode set to Phase Correct PWM
  cbi (TCCR1A, COM1A0);
  sbi (TCCR1A, COM1A1);
  cbi (TCCR1A, COM1B0); 
  sbi (TCCR1A, COM1B1);

  // Mode 1 / Phase Correct PWM
  sbi (TCCR1A, WGM10); 
  cbi (TCCR1A, WGM11);
  cbi (TCCR1B, WGM12);
  cbi (TCCR1B, WGM13);
}

//Timer 1 setup
//Set prscaler to 1, PWM mode to phase correct PWM,  16000000/510 = 31372.55 Hz clock
void Setup_timer2() 
{
  // Timer2 Clock Prescaler to : 1
  sbi (TCCR2B, CS20);
  cbi (TCCR2B, CS21);
  cbi (TCCR2B, CS22);

  // Timer2 PWM Mode set to Phase Correct PWM
  cbi (TCCR2A, COM2A0);  // clear Compare Match
  sbi (TCCR2A, COM2A1);
  cbi (TCCR2A, COM2B0); 
  sbi (TCCR2A, COM2B1);
  
  // Mode 1  / Phase Correct PWM
  sbi (TCCR2A, WGM20);  
  cbi (TCCR2A, WGM21);
  cbi (TCCR2B, WGM22);
}


//Timer2 Interrupt Service at 31372,550 KHz = 32uSec
//This is the timebase REFCLOCK for the DDS generator
//FOUT = (M (REFCLK)) / (2 exp 32)
//Runtime : 8 microseconds
ISR(TIMER2_OVF_vect)
{
  cbi(PORTD,program_exec_time); //Clear the pin
  sbi(PORTD,ISR_exec_time);          // Sets the pin

  phase_accumulator=phase_accumulator+tword_m; //Adds tuning M word to previoud phase accumulator. refer to DDS_calculator (from Martin Nawrath) for explination.
  current_count=phase_accumulator >> 24;     // use upper 8 bits of phase_accumulator as frequency information                      
  
  OCR2A=pgm_read_byte_near(sine256 + current_count); // read value fron ROM sine table and send to PWM
  OCR2B=pgm_read_byte_near(sine256 + (uint8_t)(current_count + offset_1)); // read value fron ROM sine table and send to PWM, 120 Degree out of phase of PWM1
  
  OCR1A = pgm_read_byte_near(sine256 + (uint8_t)(current_count + offset_2));// read value fron ROM sine table and send to PWM, 120 Degree out of phase of PWM2
  OCR1B = pgm_read_byte_near(sine256 + (uint8_t)(current_count + offset_2));// read value fron ROM sine table and send to PWM, 120 Degree out of phase of PWM2
  
  //increment variable ms4_delay every 4mS/125 =  milliseconds 32uS
  if(ms4_delay++ == 125) 
  {  
    c4ms++;
    ms4_delay=0; //reset count
   }   

cbi(PORTD,ISR_exec_time);            //Clear the pin
}

hi people, i am doing on a project to control a BLDC from tigermotor MT2826. i am using arduino uno to produce out 3 pwm signal from output pwm pins 3,10,11. MOSFET driver irs2336d will be used to drive the mosfets since irs2336 require 3High and 3LOw signal input, i be using directly after the arduino will be a standard 12.5khz Chebyshev low pass filter to filter out the 32khz and to a comparator and finally to a NAND gates to compare the signal from the arduino.

problem encounter is that i could not varies the frequency from the analog pin a1 to control the speed of the motor. any advise i could work on? thanks you

regards

Your preset pot is reversed.

But then again that circuit doesn't make sense as nothing is driving the Chebyshev filter
anyway...

I don't understand this: "the frequency from the analog pin a1"

Hi mark,

noted about the preset pot. changed it to (1) instead of (0)

the low pass filter is to get rid of the 32khz sampling frequency in the output signal pin3,10,11 of the arduino board.

by meaning that, 0hz to 1023hz will be from 0v to 5v hence i suppose to be able to see the pwn signal changed with respect to the value the pot is reading for example 5v = 1023hz output from pin 3,10,11? or only when connected to a 3phase inverter i be able to see ? as the pin3,10,11 fixed at 32khz?

thanks.

You need to provide a full diagram, I still have no idea what A1 is sampling - your code
only mentions A0.

Also change:

     cbi (TIMSK2,TOIE2);              //Disable Timer2 Interrupt
      tword_m=pow(2,32)*dfreq/refclk;  //Calulate DDS new tuning word
      sbi (TIMSK2,TOIE2);              //Enable Timer2 Interrupt

to

      unsigned long tuning = pow(2,32)*dfreq/refclk;  //Calulate DDS new tuning word
      noInterrupts () ; 
      tword_m = tuning ;
      interrupts () ;

Don't disable individual interrupts or you'll lose interrupts, just inhibit
interrupt handling using noInterrupts(), and only for long enough to
write the tuning word to the volatile variable - don't hold up
interrupts for the time it takes to calculate the value

#ifndef __PGMSPACE_H_
#define __PGMSPACE_H_ 1

#include <inttypes.h>

#define PROGMEM
#define PGM_P  const char *
#define PSTR(str) (str)

typedef void prog_void;
typedef char prog_char;
typedef unsigned char prog_uchar;
typedef int8_t prog_int8_t;
typedef uint8_t prog_uint8_t;
typedef int16_t prog_int16_t;
typedef uint16_t prog_uint16_t;
typedef int32_t prog_int32_t;
typedef uint32_t prog_uint32_t;

#define strcpy_P(dest, src) strcpy((dest), (src))
#define strcat_P(dest, src) strcat((dest), (src))
#define strcmp_P(a, b) strcmp((a), (b))

#define pgm_read_byte(addr) (*(const unsigned char *)(addr))
#define pgm_read_word(addr) (*(const unsigned short *)(addr))
#define pgm_read_dword(addr) (*(const unsigned long *)(addr))
#define pgm_read_float(addr) (*(const float *)(addr))

#define pgm_read_byte_near(addr) pgm_read_byte(addr)
#define pgm_read_word_near(addr) pgm_read_word(addr)
#define pgm_read_dword_near(addr) pgm_read_dword(addr)
#define pgm_read_float_near(addr) pgm_read_float(addr)
#define pgm_read_byte_far(addr) pgm_read_byte(addr)
#define pgm_read_word_far(addr) pgm_read_word(addr)
#define pgm_read_dword_far(addr) pgm_read_dword(addr)
#define pgm_read_float_far(addr) pgm_read_float(addr)

#endif
// Look Up table of a single sine period divied up into 256 values. Refer to PWM to sine.xls on how the values was calculated
PROGMEM  prog_uchar sine256[]  = {
  127,130,133,136,139,143,146,149,152,155,158,161,164,167,170,173,176,178,181,184,187,190,192,195,198,200,203,205,208,210,212,215,217,219,221,223,225,227,229,231,233,234,236,238,239,240,
  242,243,244,245,247,248,249,249,250,251,252,252,253,253,253,254,254,254,254,254,254,254,253,253,253,252,252,251,250,249,249,248,247,245,244,243,242,240,239,238,236,234,233,231,229,227,225,223,
  221,219,217,215,212,210,208,205,203,200,198,195,192,190,187,184,181,178,176,173,170,167,164,161,158,155,152,149,146,143,139,136,133,130,127,124,121,118,115,111,108,105,102,99,96,93,90,87,84,81,78,
  76,73,70,67,64,62,59,56,54,51,49,46,44,42,39,37,35,33,31,29,27,25,23,21,20,18,16,15,14,12,11,10,9,7,6,5,5,4,3,2,2,1,1,1,0,0,0,0,0,0,0,1,1,1,2,2,3,4,5,5,6,7,9,10,11,12,14,15,16,18,20,21,23,25,27,29,31,
  33,35,37,39,42,44,46,49,51,54,56,59,62,64,67,70,73,76,78,81,84,87,90,93,96,99,102,105,108,111,115,118,121,124

};
#define cbi(sfr, bit) (_SFR_BYTE(sfr) &= ~_BV(bit)) //define a bit to have the properties of a clear bit operator
#define sbi(sfr, bit) (_SFR_BYTE(sfr) |= _BV(bit))//define a bit to have the properties of a set bit operator

int PWM1= 11;// PWM1 output, phase 1
int PWM2 = 3; //[WM2 ouput, phase 2
int PWM3 = 10; //PWM3 output, phase 3
int offset_1 = 85; //offset 1 is 120 degrees out of phase with previous phase, Refer to PWM to sine.xls
int offset_2 = 170; //offset 2 is 120 degrees out of phase with offset 1. Refer to PWM to sine.xls
int program_exec_time = 6; //monitor how quickly the interrupt trigger
int ISR_exec_time = 7; //monitor how long the interrupt takes

double dfreq;
const double refclk=31376.6;      // measured output frequency

// variables used inside interrupt service declared as voilatile
volatile byte current_count;              // Keep track of where the current count is in sine 256 array
volatile byte ms4_delay;             //variable used to generate a 4ms delay
volatile byte c4ms;              // after every 4ms this variable is incremented, its used to create a delay of 1 second
volatile unsigned long phase_accumulator;   // pahse accumulator
volatile unsigned long tword_m;  // dds tuning word m, refer to DDS_calculator (from Martin Nawrath) for explination.

void setup()
{
  pinMode(PWM1, OUTPUT);      //sets the digital pin as output
  pinMode(PWM2, OUTPUT);      //sets the digital pin as output
  pinMode(PWM3, OUTPUT);      //sets the digital pin as output
  pinMode(program_exec_time, OUTPUT);      //sets the digital pin as output
  pinMode(9, OUTPUT);         //sets the digital pin as output
  sbi(PORTD,program_exec_time); //Sets the pin
  Setup_timer1();
  Setup_timer2();
  
  //Disable Timer 1 interrupt to avoid any timing delays
  cbi (TIMSK0,TOIE0);              //disable Timer0 !!! delay() is now not available
  sbi (TIMSK2,TOIE2);              //enable Timer2 Interrupt

  dfreq=1000.0;                    //initial output frequency = 1000.o Hz
  tword_m=pow(2,32)*dfreq/refclk;  //calulate DDS new tuning word 

}
void loop()
{
  while(1) 
  {
      sbi(PORTD,program_exec_time); //Sets the pin 
      if (c4ms > 250) // c4ms = 4ms, thus 4ms *250 = 1 second delay
       {                 
        c4ms=0;                          //Reset c4ms
        dfreq=analogRead(0);             //Read voltage on analog 0 to see desired output frequency, 0V = 0Hz, 5V = 1.023kHz
      unsigned long tuning = pow(2,32)*dfreq/refclk;  //Calulate DDS new tuning word
      noInterrupts () ; 
      tword_m = tuning ;
      interrupts () ;

      }
  }
}

//Timer 1 setup
//Set prscaler to 1, PWM mode to phase correct PWM,  16000000/510 = 31372.55 Hz clock
void Setup_timer1(void)
{
  // Timer1 Clock Prescaler to : 1
  sbi (TCCR1B, CS10);
  cbi (TCCR1B, CS11);
  cbi (TCCR1B, CS12);
  
  // Timer1 PWM Mode set to Phase Correct PWM
  cbi (TCCR1A, COM1A0);
  sbi (TCCR1A, COM1A1);
  cbi (TCCR1A, COM1B0); 
  sbi (TCCR1A, COM1B1);

  // Mode 1 / Phase Correct PWM
  sbi (TCCR1A, WGM10); 
  cbi (TCCR1A, WGM11);
  cbi (TCCR1B, WGM12);
  cbi (TCCR1B, WGM13);
}

//Timer 1 setup
//Set prscaler to 1, PWM mode to phase correct PWM,  16000000/510 = 31372.55 Hz clock
void Setup_timer2() 
{
  // Timer2 Clock Prescaler to : 1
  sbi (TCCR2B, CS20);
  cbi (TCCR2B, CS21);
  cbi (TCCR2B, CS22);

  // Timer2 PWM Mode set to Phase Correct PWM
  cbi (TCCR2A, COM2A0);  // clear Compare Match
  sbi (TCCR2A, COM2A1);
  cbi (TCCR2A, COM2B0); 
  sbi (TCCR2A, COM2B1);
  
  // Mode 1  / Phase Correct PWM
  sbi (TCCR2A, WGM20);  
  cbi (TCCR2A, WGM21);
  cbi (TCCR2B, WGM22);
}


//Timer2 Interrupt Service at 31372,550 KHz = 32uSec
//This is the timebase REFCLOCK for the DDS generator
//FOUT = (M (REFCLK)) / (2 exp 32)
//Runtime : 8 microseconds
ISR(TIMER2_OVF_vect)
{
  cbi(PORTD,program_exec_time); //Clear the pin
  sbi(PORTD,ISR_exec_time);          // Sets the pin

  phase_accumulator=phase_accumulator+tword_m; //Adds tuning M word to previoud phase accumulator. refer to DDS_calculator (from Martin Nawrath) for explination.
  current_count=phase_accumulator >> 24;     // use upper 8 bits of phase_accumulator as frequency information                      
  
  OCR2A=pgm_read_byte_near(sine256 + current_count); // read value fron ROM sine table and send to PWM
  OCR2B=pgm_read_byte_near(sine256 + (uint8_t)(current_count + offset_1)); // read value fron ROM sine table and send to PWM, 120 Degree out of phase of PWM1
  
  OCR1A = pgm_read_byte_near(sine256 + (uint8_t)(current_count + offset_2));// read value fron ROM sine table and send to PWM, 120 Degree out of phase of PWM2
  OCR1B = pgm_read_byte_near(sine256 + (uint8_t)(current_count + offset_2));// read value fron ROM sine table and send to PWM, 120 Degree out of phase of PWM2
  
  //increment variable ms4_delay every 4mS/125 =  milliseconds 32uS
  if(ms4_delay++ == 125) 
  {  
    c4ms++;
    ms4_delay=0; //reset count
   }   

cbi(PORTD,ISR_exec_time);            //Clear the pin
}

in order to have the desired duty cycle of the output pwm signal, by adjusting the potio we can have the output frequency needed?

noted about the interupts.

Well it seems to be doing the right thing - have you printed out the tuning word (ie
frequency) value to check it is changing?

Are you sure the ISR is being called?

may i know printing the tuning word how can it be done?

Serial.println (tuning) ;

Hi mark,

serial println to a analog pin is my first time trying it. in order to see the change in value i need to adjust the potio to different voltage level that correspond to it's frequency? able to help getting the code guidance?

Now I'm completely confused...

Either you can control the value of tuning from the pot or you cannot,
either the value of tuning has an effect on the PWM or it does not.

So what is going on?

Hi,

Can we have a FULL circuit diagram please.
Pins and power supply.

WHY???
You have the project infront of you, we don't.
You know how it is wired, we don't.
You know how you are powering the motor and arduino, we don't.

Thanks... Tom...... :slight_smile:

Hi,

the circuit diagram attached is in the 1st message show the single phase circuit. 3 of them = 3 phase .
the output from the arduino uno ( pin 3,10,11) indicates 3 phase with 120deg apart from each other.
the outpin are fed into a low pass filter to filter out the 32khz frequency that is used for sampling.

the second circuit is to set a dc reference. 3.3v will be be the power supply from arduino uno pins. R1 = 10kohm, R2 = 20kohm, R3= 30kohm. for example. pin3 is taken to be fed into low pass filter. the output pin of the filter will be fed into a comparator to be split into 2 logic (high and low) after which into a NAND gates with the orginal PWM signal as the other input. the final output will be after another NAND.

hence, after filtering a sine wave will be obtain and fed into a comparator that produce a square wave and lastly the final output after another NAND will be the PWM signal (high and low) that will be fed into a mosfet driver which in my case i used irs2336d.

please advise.

regards

tested and run the schematic as attached. the output of the filter is a sine wave that change frequency with respect to tuning of the potio. but once the signal is fed into a LM339N comparator i could not get a sqaure wave that change frequency(dutycycle?) of the output. any advise?

circuit diagram

Hey Cxys,

Could you clarify me how many ohms have resistances R6, R5, R4 of your first circuit that you posted?

Thanks.

You are ANDing in the analog input to the output of the comparators? Why re-introduce all the
sampling noise after going out of your way to filter it out?

And the other thing - what voltage is the motor being driven at? You are feeding the winding
voltages direct into a 3.3V logic circuit which is a great way to fry a whole load of stuff… Or
is there some other part of the circuit you should 'fess up to rather than make us guess?