Go Down

Topic: Automatic update via a PDC DMA (Read 9205 times) previous topic - next topic

jonnygainz

Jan 10, 2019, 05:27 pm Last Edit: Jan 10, 2019, 06:45 pm by jonnygainz Reason: Add my code
I am working on a Space Vector PWM three phase inverter, but before I do that I need to generate three phase quasi-square waves. To do this i need to vary the duty cycle of three complementary channels to feed the base of 6 transistors. I need the duty cycles of each channel to be varied as follows:

                        Leg A   Leg B   Leg C
Circuit Config 1     p          n          n
Circuit Config 2     p          p          n
Circuit Config 3     n          p          n
Circuit Config 4     n          p          p
Circuit Config 5     n          n          p
Circuit Config 6     p          n          p
(These 6 Configs need to be looped to produce a continuous waveform)

where 'p' represents when the upper transistor of the Voltage Source Inverter is on and 'n' represents when the lower transistor of the VSI is on.

What i would like to know is how to configure the Due using the Automatic update via a PDC DMA to achieve this.

I was thinking that when the WRDY gives the interrupt and is ready to receive the new duty cycles, an interrupt handler could be used to increment a counter which would increment a switch case, seeing that it's only 6 different circuit configurations required, 6 cases would be made, and each case would have the duty cycle update value based on which channel's duty cycle needs to be updated.

This is my code:

Code: [Select]
void setup()
{

  PMC->PMC_PCER1 |= PMC_PCER1_PID36; //Enable PWM (Power On)
 
  PWM->PWM_DIS = PWM_DIS_CHID0;       //Disable PWM on Channel 0

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

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

  PWM->PWM_CLK = PWM_CLK_PREA(0) | PWM_CLK_DIVA(42); //Set PWM clocke rate to 2MHz (84MHz/42)
  PWM->PWM_CH_NUM[0].PWM_CMR = PWM_CMR_CPRE_CLKA; // Period is left aligned,clock source is CLKA on Channel 0

  REG_PWM_SCM |= PWM_SCM_SYNC0 | PWM_SCM_SYNC1; // Synchronizing of Channels 0, 1 and 2

  REG_PWM_SCM |= PWM_SCM_UPDM_MODE2;  // Manual Write of duty-cycle automatic trigger of the update

  PWM->PWM_SCUP = PWM_SCUP_UPR(0); // Defining update period (UPR + 1)

  PWM_SCM_PTRM == 0; // The WRDY flag in PWM_ISR2 and the PDC transfer request are set to 1 as soon as the update period is elapsed.

  

  REG_PWM_CPRD0 = 1000000; //Channel 0 Period f = 2MHz/(2*CPRD)
  REG_PWM_CDTY0 = 0; //Channel 0 Duty Cycle x% = (CDTY/ CPRD)*100%
  REG_PWM_CPRD1 = 1000000;
  REG_PWM_CDTY1 = 1000000;

  PWM->PWM_ENA = PWM_ENA_CHID0; // Enable PWM on Channel 0

  NVIC_EnableIRQ(TC0_IRQn); // enable TC0 interrupts
}

void loop()
{
  // put your main code here, to run repeatedly:

}

void PWM_Interrupt_Handler()
{
  
}



jonnygainz

I looked at this forum already, and I'm having some difficulty understanding how you used the Automatic update via a PDC DMA and how to apply it to my application.

ard_newbie


Post your code (between code tags).

jonnygainz

I did, i edited the post and added in the code

jonnygainz

I'm not trying to generate a wave on each channel, I'm sending signals to the base of 6 IGBTs toggling them on and off to output the quasi square waves, what I/m using the Due for is to make an Induction Motor Drive.

ard_newbie


You didn't use the example sketch I gave you for synchro PWM with automatic aupdate of duty cycle with PDC DMA.

If you declare 3 buffers of 6 elements, BufferA is what you call legA, BufferB is BufferA 2/6 phase shifted from BufferA, and BufferC is 4/6 phase shifted from BufferA.

jonnygainz


jonnygainz

#8
Jan 10, 2019, 10:07 pm Last Edit: Jan 10, 2019, 10:08 pm by jonnygainz
I've updated my code here but it's not working, the idea is at when WRDY is set and new duty cycles can be received that it's updated in a specific order. I used 2 channels instead of three just to test to see if the LEDs on my bread board would switch every second (1Hz period @ 0 and 100% duty cycle alternating).

Code: [Select]
byte count = 0;

void setup()
{

  PMC->PMC_PCER1 |= PMC_PCER1_PID36; //Enable PWM (Power On)
 
  PWM->PWM_DIS = PWM_DIS_CHID0;       //Disable PWM on Channel 0

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

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

  PWM->PWM_CLK = PWM_CLK_PREA(0) | PWM_CLK_DIVA(42); //Set PWM clocke rate to 2MHz (84MHz/42)
  PWM->PWM_CH_NUM[0].PWM_CMR = PWM_CMR_CPRE_CLKA; // Period is left aligned,clock source is CLKA on Channel 0

  REG_PWM_SCM |= PWM_SCM_SYNC0 | PWM_SCM_SYNC1; // Synchronizing of Channels 0, 1 and 2

  REG_PWM_SCM |= PWM_SCM_UPDM_MODE1;  // Manual Write of duty-cycle automatic trigger of the update

  PWM->PWM_SCUP = PWM_SCUP_UPR(0); // Defining update period (UPR + 1)

  PWM->PWM_IER2 = PWM_IER2_WRDY; // Enable Interrupt when WRDY flag is set

  //PWM_SCM_PTRM == 0;; // The WRDY flag in PWM_ISR2 and the PDC transfer request are set to 1 as soon as the update period is elapsed.

  

  REG_PWM_CPRD0 = 1000000; //Channel 0 Period f = 2MHz/(2*CPRD)
  //REG_PWM_CDTY0 = 0; //Channel 0 Duty Cycle x% = (CDTY/ CPRD)*100%
  REG_PWM_CPRD1 = 1000000;
  //REG_PWM_CDTY1 = 1000000;

  PWM->PWM_ENA = PWM_ENA_CHID0; // Enable PWM on Channel 0

  NVIC_EnableIRQ(PWM_IRQn); // enable TC0 interrupts
}

void loop()
{
  // put your main code here, to run repeatedly:

}

void PWM_Interrupt_Handler()
{
  PWM->PWM_ISR2;      // Read ISR2 and clear status register
  update_Duty_Cycle();
  
}

void update_Duty_Cycle()
{
  if(count > 3){
    count = 0;}
    SquareWave(count);
    count++;
}

void SquareWave(byte Sector)
{
    switch(Sector)
    {
      case 0:
      REG_PWM_CDTYUPD0 = 1000000;
      REG_PWM_CDTYUPD1 = 0;
      break;
      case 1:
      REG_PWM_CDTYUPD1 = 1000000;
      break;
      case 2:
      REG_PWM_CDTYUPD0 = 0;
      break;
      case 3:
      REG_PWM_CDTYUPD1 = 0;
      break;
      default:
      break;
    }
}




 
 If you can help please do...

ard_newbie


UPDM_MODE1 is for manual write (of Duty cycles), not automatic update with PDC DMA.

In PWM_CPRD and PWM_CPRDUPD, how many bits are significant ? ---> to find out the answer, read page 1049, Sam3x datasheet.

jonnygainz

#10
Jan 11, 2019, 10:35 am Last Edit: Jan 11, 2019, 10:41 am by jonnygainz
I only switched to Mode 1 because I didn't understand how the PDC DMA works, but from my code can you see what I'm trying to achieve? Also in PWM_CPRDUPD, only the first 16 bits are significant, but what does that mean exactly? Is it that the maximum number the register can hold is 65536?

Go Up