To get a PWM signal with 5khz frequency using Arduino Uno

Hi,
I have been trying to generate a PWM 5khz frequency whose duty cycle varies.
I my project I'm trying to control my motor using Arduino. Where I sense the angle, 3-phase current using sensors and get them as inputs.
Now, with the help of angle I found the speed then using PI- controller in Arduino I compared ref speed to the actual speed and give the output error to the PI controller. The output of this controller is the reference current which I then compare with all three current inputs and the error would be fed to another PI controller.
Now, these 3 PI controllers gives 3 different PWM duty cycle values( from 0 to 1 ).
Now, I need these duty cycle to work as a PWM signal with 5khz frequency) or in other words I need to compare these values with a 5khz repeating sequence.
I have attached my code below for the reference.

#include <PID_v2.h>
#define COUNT_PER_REV 600
#define ENCODER_A 2
#define ENCODER_B 3

boolean Dir               = true;

volatile long pulse_count = 0;

int a                         = 90;
int b                    = 30;
int c                          = 60;
int val                   = 0;
int num                   = 0;
int count                 = 1;
int potPin                = 0;
int Position              = 0;
int interval              = 1000;
int refcurrent            = 0;
int sensitivity           = 100;
int offsetVoltage         = 1650;
int rotationalAngle       = 0;

const int Ia              = A0;
const int Ib              = A2;
const int Ic              = A3;
const int IS              = A4;

long currentMillis        = 0;
long previousMillis       = 0;

float rpm                 = 0;
float erroRS              = 0;
float erroR1              = 0;
float erroR2              = 0;
float erroR3              = 0;
float refrpm              = 0;
float avgcurrent1         = 0;
float avgcurrent2         = 0;
float avgcurrent3         = 0;
float avgcurrentS         = 0;
float prevcurrent1         = 0;
float prevcurrent2         = 0;
float prevcurrent3         = 0;
float prevcurrentS         = 0;

double adcValue1          = 0.0;
double adcValue2          = 0.0;
double adcValue3          = 0.0;
double adcValueS          = 0.0;
double duty_cycle1        = 0.0;
double duty_cycle2        = 0.0;
double duty_cycle3        = 0.0;
double adcvoltage1        = 0.0;
double adcvoltage2        = 0.0;
double adcvoltage3        = 0.0;
double adcvoltageS        = 0.0;
double currentValue1      = 0.0;
double currentValue2      = 0.0;
double currentValue3      = 0.0;
double currentValueS      = 0.0;
double Kp = 0.000541, Ki = 0.042301, Kd = 0;
double kp = 0.001221, ki = 0.034444, kd = 0;

PID_v2 mySDPID(Kp, Ki, Kd, PID::Direct);
PID_v2 myI1PID(kp, ki, kd, PID::Direct);
PID_v2 myI2PID(kp, ki, kd, PID::Direct);
PID_v2 myI3PID(kp, ki, kd, PID::Direct);

void setup()
{
  Serial.begin(9600); 
  pinMode(5, OUTPUT);
  pinMode(ENCODER_A , INPUT_PULLUP);
  pinMode(ENCODER_B , INPUT_PULLUP); 
  pinMode(Ia,INPUT);
  pinMode(Ib,INPUT);
  pinMode(Ic,INPUT);
  pinMode(IS,INPUT);
  pinMode(9, OUTPUT);
  pinMode(10,OUTPUT);
  pinMode(11,OUTPUT);
  attachInterrupt(digitalPinToInterrupt(ENCODER_A), pulse, RISING);
  attachInterrupt(digitalPinToInterrupt(ENCODER_A), encoderA, CHANGE);
  attachInterrupt(digitalPinToInterrupt(ENCODER_B), encoderB, CHANGE);
  
}

void loop() 
{
    mySDPID.Start(erroRS,0,0);
    myI1PID.Start(erroR1,0,0);
    myI2PID.Start(erroR2,0,0);
    myI3PID.Start(erroR3,0,0);
    currentMillis   = millis();
    refrpm          = (analogRead(potPin)*500)/1023;
    previousMillis  = currentMillis;
    rpm             = float(pulse_count * 60 / COUNT_PER_REV);
    rotationalAngle = (360 * Position) / (600 * 4);
    num             = rotationalAngle % 90;
    adcValue1       = analogRead(Ia);
    adcValue2       = analogRead(Ib);
    adcValue3       = analogRead(Ic);
    adcValueS       = analogRead(IS);
    adcvoltage1     = ((adcValue1/1023)*3300);
    adcvoltage2     = ((adcValue2/1023)*3300);
    adcvoltage3     = ((adcValue3/1023)*3300);
    adcvoltageS     = ((adcValueS/1023)*3300);
    currentValue1   = ((adcvoltage1 - offsetVoltage)/ sensitivity);  
    currentValue2   = ((adcvoltage2 - offsetVoltage)/ sensitivity);
    currentValue3   = ((adcvoltage3 - offsetVoltage)/ sensitivity);
    currentValueS   = ((adcvoltageS - offsetVoltage)/ sensitivity);
    erroR1          = (refcurrent - currentValue1);
    erroR2          = (refcurrent - currentValue2);
    erroR3          = (refcurrent - currentValue3);

    if (num == 90 || num == 0)
    {
      count = 1;
    }
    else
    {
      count++;
    }
      
    avgcurrent1 = (prevcurrent1 + currentValue1)/count;
    avgcurrent2 = (prevcurrent2 + currentValue2)/count;
    avgcurrent3 = (prevcurrent3 + currentValue3)/count;
    avgcurrentS = (prevcurrentS + currentValueS)/count;
    
    if (num == (a))
      {
        prevcurrent1 = 0;
        prevcurrent2 = 0;
        prevcurrent3 = 0;
        prevcurrentS = 0;
      }
    else
      {
        prevcurrent1 = avgcurrent1*count;
        prevcurrent2 = avgcurrent2*count;
        prevcurrent3 = avgcurrent3*count;
        prevcurrentS = avgcurrentS*count;
      }
      
    pulse_count = 0;
    
    if(refrpm > rpm)
      {
        if((refrpm - rpm)>10)
          {
            erroRS = 10;
          }
        else
          {
            erroRS = (refrpm - rpm);
          }
      }
    else
      {
        if((rpm - refrpm) > 10)
          {
            erroRS = -10;
          }
        else
          {
            erroRS = rpm - refrpm;
          }
      }
    if(erroR1>(0.02*refcurrent))
    {
      erroR1=0.02*refcurrent;
    }
    if(erroR2>(0.02*refcurrent))
    {
      erroR2=0.02*refcurrent;
    }
    if(erroR3>(0.02*refcurrent))
    {
      erroR3=0.02*refcurrent;
    }
    mySDPID.SetMode(AUTOMATIC);
    myI1PID.SetMode(AUTOMATIC);
    myI2PID.SetMode(AUTOMATIC);
    myI3PID.SetMode(AUTOMATIC);
    mySDPID.SetOutputLimits(0,1);
    myI1PID.SetOutputLimits(0,1);
    myI2PID.SetOutputLimits(0,1);
    myI3PID.SetOutputLimits(0,1);
    refcurrent  = mySDPID.Run(erroRS);
    duty_cycle1 = myI1PID.Run(erroR1);
    duty_cycle2 = myI2PID.Run(erroR2);
    duty_cycle3 = myI3PID.Run(erroR3);

}

void encoderA()
    {
      if(digitalRead(ENCODER_A) != digitalRead(ENCODER_B))
        {
          Position++;
        }
      else
        {
          Position--;
        }
    }
void encoderB()
  {
    if (digitalRead(ENCODER_A) == digitalRead(ENCODER_B))
        {
          Position++;
        }
    else
        {
          Position--;
        }
  }

void pulse() 
  {
    val = digitalRead(ENCODER_B);
    if(val == LOW) 
      {
        Dir = false;
      }
    else 
      {
        Dir = true;
      }
     
    if (Dir) 
      {
        pulse_count++;
      }
    else 
      {
        pulse_count--;
      }
  }

Please edit your post to add code tags. Select the code and use the "</>" editor button.

You generally should not do I/O of any kind in an interrupt routine, and especially not serial I/O, as it depends on interrupts.

Remove this line from the ISR:

Serial.println(num);
1 Like

Thank you, I have removed the serial and the ISR too right now I just need the dity_cycle_1,2,3 output to work at 5lhz

To get three channels of PWM on an UNO you need to use two timers. Pick a PWM mode and calculate the value of TOP that will get you the 5kHz frequency.

As I'm new to Arduino and I am not that good with the coding I don't know how to use timers to generate pwm with the 3 dutycycles that I have.

I recommend to study this excellent tutorial on timers: https://www.gammon.com.au/timers