Go Down

Topic: Complementary PWM channels Output (Read 2151 times) previous topic - next topic

MartinL

Quote
When I try using the code in #28, the IDE tells me "PWM_DT_DTHUPD" and "PWM_DT_DTLUPD" was not declared in this scope.
I've now corrected it above, it's

Code: [Select]
PWM->PWM_CH_NUM[0].PWM_DTUPD = PWM_DTUPD_DTHUPD(0) | PWM_DTUPD_DTLUPD(2);

MartinL

Are your looking at the low side channel outputs, PWMLx, on your scope?

jonnygainz

I'm looking at both low side and high side for each channel in turn to compare.

jonnygainz

I tried the code in #30 and still nothing. This is my code:

Code: [Select]
/* This code generates Quasi-Square Waves at 60Hz*/
static byte stateCount = 0;
static float CPRDV = 0;
static float inverterFrequency = 60;
int divA = 42;

void setup()
{
  //Serial.begin(57600);
  PMC->PMC_PCER1 |= PMC_PCER1_PID36;                  // Enable PWM (Power On)

  PIOC->PIO_PDR |= PIO_PDR_P3 | PIO_PDR_P5 | PIO_PDR_P7;           // Setting pins 3,5,7 (DUE Pins 35, 37, 39) to PWM Peripheral, not GPIO
  PIOC->PIO_ABSR |= PIO_ABSR_P3 | PIO_ABSR_P5 | PIO_ABSR_P7;       // Setting pins to Peripheral B

  PIOC->PIO_PDR |= PIO_PDR_P2 | PIO_PDR_P4 | PIO_PDR_P6;           // Setting pins 2,4,6 (DUE Pins 34, 36, 38) to PWM Peripheral, not GPIO
  PIOC->PIO_ABSR |= PIO_ABSR_P2 | PIO_ABSR_P4 | PIO_ABSR_P6;       // Setting pins to Peripheral B

  PWM->PWM_CLK = PWM_CLK_PREA(0) | PWM_CLK_DIVA(divA);  // Set PWM clocke rate to 2MHz (84MHz/42)
  PWM->PWM_SCM |= PWM_SCM_SYNC0 | PWM_SCM_SYNC1 | PWM_SCM_SYNC2;      // Synchronizing of Channels 0, 1 and 2
  PWM->PWM_SCM |= PWM_SCM_UPDM_MODE1;                 // Manual Write of duty-cycle automatic trigger of the update
 
  NVIC_SetPriority(PWM_IRQn, 0);                      // Set the Nested Vector Interrupt Controller (NVIC) priority for the PWM controller to 0 (highest)
  NVIC_EnableIRQ(PWM_IRQn);                           // Connect PWM Controller to Nested Vector Interrupt Controller (NVIC)

  PWM->PWM_IER1 = PWM_IER1_CHID0;                     // Enable interrupt on PWM channel 0 triggered at end of PWM period

  calcCPRDV();
  /*Serial.print("CPRDV = ");
  Serial.print(CPRDV);
  Serial.print("\n");*/
  PWM->PWM_CH_NUM[0].PWM_CPRD = CPRDV;                // Channel 0 Period f = 2MHz/(CPRD)= 100Hz = 0.01s |CPRD = 2MHz/f
  PWM->PWM_CH_NUM[0].PWM_CMR = PWM_CMR_CPRE_CLKA | PWM_CMR_DTE;     // Period is left aligned,clock source is CLKA on Channel 0
  PWM->PWM_CH_NUM[1].PWM_CMR = PWM_CMR_DTE;
  PWM->PWM_CH_NUM[2].PWM_CMR = PWM_CMR_DTE;

  //PWM->PWM_CH_NUM[0].PWM_DT = PWM_DT_DTH(0)| PWM_DT_DTL(200);
  //PWM->PWM_CH_NUM[1].PWM_DT = PWM_DT_DTH(0)| PWM_DT_DTL(200);
  //PWM->PWM_CH_NUM[2].PWM_DT = PWM_DT_DTH(0)| PWM_DT_DTL(200);
 
  PWM->PWM_ENA = PWM_ENA_CHID0;                       // Enable synchronous PWM on Channel 0
}

void loop()
{
 
}

void PWM_Handler()                       // PWM Interrupt Service Routine (ISR)
{
  if (PWM->PWM_ISR1 & PWM_ISR1_CHID0)    // Check if an update condition has occured
  {       
    update_Duty_Cycle();                 // Update the duty cycles
  }
}

void calcCPRDV ()
{
  //inverterFrequency = 60;
  static float numOfVectors = 6;
  static float adMCKFreq = 84000000;
  static float timerFreq = (adMCKFreq/divA); //2MHz
  static float period = 1/inverterFrequency;
  static float Ts = period/numOfVectors;
  static float switchingFreq = 1/Ts;
  CPRDV = (timerFreq/(1*switchingFreq));
  /*Serial.print("Inverter Frequency = "); Serial.print(inverterFrequency);Serial.print("\n");
  Serial.print("Num of Vectors = "); Serial.print(numOfVectors); Serial.print("\n");
  Serial.print("Timer Freq = "); Serial.print(timerFreq); Serial.print("\n");
  Serial.print("Period = "); Serial.print(period, 8); Serial.print("\n");
  Serial.print("SSV Period = "); Serial.print(Ts, 8); Serial.print("\n");
  Serial.print("Switching Freq = "); Serial.print(switchingFreq); Serial.print("\n");*/

}

void update_Duty_Cycle()
{
 
 
    if(stateCount < 6)
    {
          PWM->PWM_CH_NUM[0].PWM_CDTYUPD = 0;
          PWM->PWM_CH_NUM[1].PWM_CDTYUPD = 0;
          PWM->PWM_CH_NUM[2].PWM_CDTYUPD = 0;
          stateCount++;
          /*Serial.print("Counter = ");
          Serial.print(stateCount); Serial.print("\n");*/
    }
    else
    {
 
      static byte count = 0;
      static float p = CPRDV;
      static float n = 0;
      //Serial.print("Sector = "); Serial.print(count); Serial.print("\n");
      switch(count)
        {
          case 0: //pnn = 100 => 012
            PWM->PWM_CH_NUM[0].PWM_DTUPD = PWM_DTUPD_DTHUPD(0)| PWM_DTUPD_DTLUPD(2);
            PWM->PWM_CH_NUM[1].PWM_DTUPD = PWM_DTUPD_DTHUPD(0)| PWM_DTUPD_DTLUPD(2);
            PWM->PWM_CH_NUM[2].PWM_DTUPD = PWM_DTUPD_DTHUPD(0)| PWM_DTUPD_DTLUPD(2);
           
            PWM->PWM_CH_NUM[0].PWM_CDTYUPD = p;
            PWM->PWM_CH_NUM[1].PWM_CDTYUPD = n;
            PWM->PWM_CH_NUM[2].PWM_CDTYUPD = n;
          break;
          case 1: //ppn = 110 => 012
            PWM->PWM_CH_NUM[0].PWM_DTUPD = PWM_DTUPD_DTHUPD(0)| PWM_DTUPD_DTLUPD(2);
            PWM->PWM_CH_NUM[1].PWM_DTUPD = PWM_DTUPD_DTHUPD(0)| PWM_DTUPD_DTLUPD(2);
            PWM->PWM_CH_NUM[2].PWM_DTUPD = PWM_DTUPD_DTHUPD(0)| PWM_DTUPD_DTLUPD(2);
           
            PWM->PWM_CH_NUM[0].PWM_CDTYUPD = p;
            PWM->PWM_CH_NUM[1].PWM_CDTYUPD = p;
            PWM->PWM_CH_NUM[2].PWM_CDTYUPD = n;
          break;
          case 2: //npn = 010 => 012
            PWM->PWM_CH_NUM[0].PWM_DTUPD = PWM_DTUPD_DTHUPD(0)| PWM_DTUPD_DTLUPD(2);
            PWM->PWM_CH_NUM[1].PWM_DTUPD = PWM_DTUPD_DTHUPD(0)| PWM_DTUPD_DTLUPD(2);
            PWM->PWM_CH_NUM[2].PWM_DTUPD = PWM_DTUPD_DTHUPD(0)| PWM_DTUPD_DTLUPD(2);
           
            PWM->PWM_CH_NUM[0].PWM_CDTYUPD = n;
            PWM->PWM_CH_NUM[1].PWM_CDTYUPD = p;
            PWM->PWM_CH_NUM[2].PWM_CDTYUPD = n;
          break;
          case 3: //npp = 011 => 012
            PWM->PWM_CH_NUM[0].PWM_DTUPD = PWM_DTUPD_DTHUPD(0)| PWM_DTUPD_DTLUPD(2);
            PWM->PWM_CH_NUM[1].PWM_DTUPD = PWM_DTUPD_DTHUPD(0)| PWM_DTUPD_DTLUPD(2);
            PWM->PWM_CH_NUM[2].PWM_DTUPD = PWM_DTUPD_DTHUPD(0)| PWM_DTUPD_DTLUPD(2);
           
            PWM->PWM_CH_NUM[0].PWM_CDTYUPD = n;
            PWM->PWM_CH_NUM[1].PWM_CDTYUPD = p;
            PWM->PWM_CH_NUM[2].PWM_CDTYUPD = p;
          break;
          case 4: //nnp = 001 => 012
            PWM->PWM_CH_NUM[0].PWM_DTUPD = PWM_DTUPD_DTHUPD(0)| PWM_DTUPD_DTLUPD(2);
            PWM->PWM_CH_NUM[1].PWM_DTUPD = PWM_DTUPD_DTHUPD(0)| PWM_DTUPD_DTLUPD(2);
            PWM->PWM_CH_NUM[2].PWM_DTUPD = PWM_DTUPD_DTHUPD(0)| PWM_DTUPD_DTLUPD(2);
           
            PWM->PWM_CH_NUM[0].PWM_CDTYUPD = n;
            PWM->PWM_CH_NUM[1].PWM_CDTYUPD = n;
            PWM->PWM_CH_NUM[2].PWM_CDTYUPD = p;
          break;
          case 5: //pnp = 101 => 012
            PWM->PWM_CH_NUM[0].PWM_DTUPD = PWM_DTUPD_DTHUPD(0)| PWM_DTUPD_DTLUPD(2);
            PWM->PWM_CH_NUM[1].PWM_DTUPD = PWM_DTUPD_DTHUPD(0)| PWM_DTUPD_DTLUPD(2);
            PWM->PWM_CH_NUM[2].PWM_DTUPD = PWM_DTUPD_DTHUPD(0)| PWM_DTUPD_DTLUPD(2);
           
            PWM->PWM_CH_NUM[0].PWM_CDTYUPD = p;
            PWM->PWM_CH_NUM[1].PWM_CDTYUPD = n;
            PWM->PWM_CH_NUM[2].PWM_CDTYUPD = p;

          break;
          default:
          break;
        }
        count = (count + 1) % 6;


    }
}

jonnygainz

Could it be that the UPDULOCK bit must be set in order to allow the dead time registers to be updated?

jonnygainz

Any luck? I've tried everything I can think of, and it's not working.

jonnygainz

MartinL, If I would like to set the period of the pwm from a function, but the function takes input from a keypad and then uses the value inputted by the keypad to choose a case in a switch, how would be the best way to go about doing that?

MartinL

#37
Feb 28, 2019, 09:51 am Last Edit: Feb 28, 2019, 09:52 am by MartinL
Hi,

To change the period of the signals just requires you to change the period PER registers for channel 0:

Code: [Select]
PWM->PWM_CH_NUM[0].PWM_CPRD = CPRDV;
As your PWM signals span across a number of PWM cycles, I imagine it would be easiest to place to insert this line is in your update_Duty_Cycle() function, specifically in the one of the 6 output states. This also ensures that any changes happen at the beginning of your state output cycle.

To select differing periods your keypad could set a variable either containing the period or that can be used to select a preset period in the update_Duty_Cycle() function. As the update_Duty_Cycle() function is called from within the PWM_Handler(), but set by the keypad from outside it, it will be necessary to declare the variable as "volatile":

Code: [Select]
volatile unsigned long periodValue;

jonnygainz

#38
Feb 28, 2019, 10:26 am Last Edit: Feb 28, 2019, 10:31 am by jonnygainz
So if I want to be able to set the period value before the program even starts with the first duty cycle value I should do something like at the top of my handler

Code: [Select]


if (x <1)
{
      setPeriod();
      x++
}

 

 And in this set period function I include the code that would allow the keypad to set the value of the period? Adding it to the top of the handler ensures that it is executed before any switching starts

jonnygainz

If it's done this way I'm guessing it will remain in the function as long as the person is setting the period value. But after that it'll never enter that loop again because x would be equal to 1. What you think?

jonnygainz

#40
Feb 28, 2019, 10:16 pm Last Edit: Feb 28, 2019, 10:19 pm by jonnygainz
Is there a way to set the period of the pwm before the program starts using they keypad? I don't want the pwm to start until I've set the period. See because it's an inverter I'm designing I need to input the frequency value before the PWM starts. It needs to be inputted from the keypad. I also need to display the supply frequency value during the operation of the inverter.

Is it possible to call a function in setup to set parameters for PWM like say period? I want to set parameters in the program using a keypad and LCD display where the LCD is prompting you to input a voltage, and based on this voltage value a frequency will be calculated for constant v/f control of an induction motor. this frequency value will then calculate a CPRD value and then load that into the CPRD register of the Due before the setup is exited. So basically I want to know if I set up the keypad and LCD in the setup before i enable the Channel counter and exit the step up, if i call a function that utilizes the keypad and LCD after they are initialized in the setup if I will be able to use the keypad and LCD.

jonnygainz

Is it possible to enable the pwm on a channel outside of the setup() function?

Code: [Select]
PWM->PWM_ENA = PWM_ENA_CHID0;

In essence call this piece of code after the period values have been set in the function.

MartinL

#42
Mar 01, 2019, 09:33 am Last Edit: Mar 01, 2019, 09:34 am by MartinL
Quote
Is it possible to enable the pwm on a channel outside of the setup() function?
Yes it is. However, if you disable the PWM Controller peripheral, (without switching the pins back to GPIO), they'll go high impedance and start to float. In might be better keep the PWM Controller enabled and to have a seventh "reset" state that sets the duty-cycle of all the outputs to 0.

Quote
Is it possible to call a function in setup to set parameters for PWM like say period?
Yes, it's possible to run any code in the setup() portion of the sketch, but it will only be run once. If your user only needs enter the code at the beginning, then the keypad input can be done at this stage. This means that you only need to set the PWM Controller's period (CPRD) register at the start. Also, in this instance there's no need to use a "volatile unsigned long" global variable or modify the update_Duty_Cycle() function.

Regarding the code pattern, I'd do something along these lines:

Code: [Select]
// ... Global variables

void setup() {
  // ...
  // ... Read keypad input
  switch(keypadInput)
  {
    case 0:
      PWM->PWM_CH_NUM[0].PWM_CPRD = 1000;      // Arbitary values
      break;
    case 1:
      PWM->PWM_CH_NUM[0].PWM_CPRD = 2000; 
      break;
    case 2:
      PWM->PWM_CH_NUM[0].PWM_CPRD = 3000;
      break;
    case 3:
      // ...
  }
  // ... continue with setup() ...
}

void loop() {
  // ... Main code loop 
}

void PWM_Handler() {
  // ...
  update_Duty_Cycle();
  // ...
}

void update_DutyCycle() {
  // ...
}

jonnygainz

How would the read keypad part of the code look if I'm using "Keypad.h" because I tried to set it in the setup but it didn't work.

jonnygainz

I need the code to be written such that it won't leave the function unless it receives an input from the keypad to set the values.

Go Up