Go Down

Topic: PWM triggered ADC with Event line (Read 5015 times) previous topic - next topic

ard_newbie

Attachinterrupt() is designed to detect a rising or falling edge of a digital signal. This may work with an anolog signal (of course between 0V and 3.3V) but it depends on the thresholds. I was thinking of an interrupt triggered by PWM_IER2.

What is pwmc0 and pwmc1 ??

You should begin to read carefully the PWM section of Sam3x datasheet.

joker691

#16
Mar 31, 2018, 07:02 pm Last Edit: Mar 31, 2018, 07:02 pm by joker691
Hi, I found something usefull that i modified for channel 0 and 1. When interrupt occurs, it toggle pin 7(to see if interrupts are working).
Channel0: https://imgur.com/a/XrvGX
Channel1: https://imgur.com/a/IWDXr

That look fine. But when both duties are set to zero value i still have interrupts. Is it ok?
In code I uncomented "PWMC_EnableChannel" and it is still working. Is it ok?


Code: [Select]


short duty0 = 2000;
short duty1 = 0;
long pwm_int_cntr;
short print_cntr = 1000;  // 1000 ms
unsigned long last_time;
bool toggle;

void setup() {

  Serial.begin (115200);

  pwmc_setup();

  pinMode (7, OUTPUT);
}


void loop() {

  if (millis() - last_time >= print_cntr) {

    last_time = millis();
    Serial.print ("pwm_int_cntr: "), Serial.println (pwm_int_cntr);
    pwm_int_cntr = 0;
  }
}


void pwmc_setup() { // Configure PWM channels 0 and 1 (PWML0,PWMH0,PWML1,PWMH1)

  REG_PIOC_PDR = 0x3FC;  // B1111111100, PIO Disable Register
  REG_PIOC_ABSR = REG_PIOC_ABSR | 0x3FCu; // B1111111100, Peripheral AB Select Register

  REG_PMC_PCER1 = REG_PMC_PCER1 | 16; //Peripheral Clock Enable Register 1 (activate clock for PWM, id36, bit5 of PMC_PCSR1)
  REG_PWM_ENA = REG_PWM_SR | B0011; //PWM Enable Register | PWM Status Register (activate channels 1 and 0)

  REG_PWM_CMR0 = 0x10000; // Channel 0 Mode Register: Dead Time Enable DTE=1
  REG_PWM_CMR1 = 0x10000; // Channel 1 Mode Register: Dead Time Enable DTE=1
  REG_PWM_DT0 = 0xA800A8; // Channel 0 Dead Time Register (168=2us for outputs PWML0,PWMH0)
  REG_PWM_DT1 = 0xA800A8; // Channel 1 Dead Time Register (168=2us for outputs PWML1,PWMH1)
  REG_PWM_CPRD0 = 4200;    // Channel 0 Period Register (84mhz/4200 = 20kHz)
  REG_PWM_CPRD1 = 4200;    // Channel 1 Period Register (84mhz/4200 = 20kHz)

  PWM_INTERFACE->PWM_IER1 = 0x6;             // enable interrupt on channel 0 and 1
  PWM_INTERFACE->PWM_IDR1 = 0xFFFFFFFC; // enable interrupt on channel 0 and 1
  PWM_INTERFACE->PWM_IER2 = 0x00000301; // enable interrupt on channel 0 and 1
  PWM_INTERFACE->PWM_IDR2 = 0xFFFFFCFE; // enable interrupt on channel 0 and 1

  NVIC_DisableIRQ(PWM_IRQn);  // set up interrupt
  NVIC_ClearPendingIRQ(PWM_IRQn);
  NVIC_SetPriority(PWM_IRQn, 0);
  NVIC_EnableIRQ((IRQn_Type)36); // NVIC_EnableIRQ(PWM_IRQn);
  //PWMC_EnableChannel(PWM_INTERFACE, 0);
  //PWMC_EnableChannel(PWM_INTERFACE, 1);
  //Enable of the Interrupts (writing CHIDx and FCHIDx
  //in PWM_IER1 register, and writing WRDYE, ENDTXE,
  //TXBUFE, UNRE, CMPMx and CMPUx in PWM_IER2 register)

  REG_PWM_CDTY0 = duty0;  // duty of channel 0
  REG_PWM_CDTY1 = duty1;  // duty of channel 1

}


void PWM_Handler(void) {  // PWM interrupt handler

  volatile long dummy = PWM_INTERFACE->PWM_ISR1; // clear interrupt flag
  dummy = PWM_INTERFACE->PWM_ISR2; // clear interrupt flag

  pwm_int_cntr++;
  toggle = !toggle;
  digitalWrite (7, toggle);

}



ard_newbie

Once more, don't use magic numbers in your code, it's a pain to debug. Use names that you find in Sam3x datasheet and PWM header files:

https://android.googlesource.com/platform/external/arduino-ide/+/f876b2abdebd02acfa4ba21e607327be4f9668d4/hardware/arduino/sam/system/CMSIS/Device/ATMEL/sam3xa/include/component

Here is an example sketch to output PWMH2 and PWML2 with an interrupt at the end of each PWM period and a dead time. Once a PWM channel is enabled (after setup() in loop()), you can modify frequency, duty cycle and dead times thru CPRDUPD, CDTYUPD and DTUPD registers. To disable a PWM channel, set PWM_DIS accordingly.

Code: [Select]

/***********************************************************************************************/
/*                 PWMH2 and PWML2 Enabled -   PWM frequency = 20 KHz                          */
/***********************************************************************************************/

void setup () {
  pinMode(LED_BUILTIN, OUTPUT);
  PWM_Init();
}

void loop() {
}

void PWM_Init() {
  // PWM Set-up on pins PC7 and PA20 (Arduino Pins 39(PWMH2) and 43(PWML2)): see Datasheet chap. 38.5.1 page 973
  PMC->PMC_PCER1 |= PMC_PCER1_PID36;                   // PWM power ON
  PWM->PWM_DIS = PWM_DIS_CHID2;                        // Disable PWM channel 2

  // Select Instance=PWM; Signal=PWMH2 (channel 2); I/O Line=PC7 (P7, Arduino pin 39, see pinout diagram) ; Peripheral type B
  PIOC->PIO_PDR |= PIO_PDR_P7;                          // Set the pin to the peripheral PWM, not the GPIO
  PIOC->PIO_ABSR |= PIO_PC7B_PWMH2;                     // Set PWM pin perhipheral type B

  // Select Instance=PWM; Signal=PWML2 (channel 2); I/O Line=PA20 (P20, Arduino pin 43, see pinout diagram) ; Peripheral type B
  PIOA->PIO_PDR |= PIO_PDR_P20;                          // Set the pin to the peripheral PWM, not the GPIO
  PIOA->PIO_ABSR |= PIO_PA20B_PWML2;                    // Set PWM pin perhipheral type B

  PWM->PWM_CLK = PWM_CLK_PREA(0) | PWM_CLK_DIVA(42);   // Set the PWM clock rate to 2MHz (84MHz/42). Waveform left aligned
  PWM->PWM_CH_NUM[2].PWM_CMR = PWM_CMR_CPRE_CLKA;      // The period is left aligned, clock source as CLKA on channel 2
  PWM->PWM_CH_NUM[2].PWM_CPRD = 100;                   // Channel 2 : Set the PWM frequency 2MHz/ CPRD = F ;
  PWM->PWM_CH_NUM[2].PWM_CDTY = 50;                    // Channel 2: Set the PWM duty cycle to x%= (CDTY/ CPRD)  * 100 % ;

  // Dead times
  PWM->PWM_CH_NUM[2].PWM_DT = PWM_DT_DTH(2);           // Dead Time for PWMH; 0 < DT < (CPRD - CDTY)  12 bit max
  PWM->PWM_CH_NUM[2].PWM_DT = PWM_DT_DTL(2);          // Dead Time for PWML; 0 < DT < (CPRD - CDTY)
 
  PWM->PWM_IER1 = PWM_IER1_CHID2;                      // Interrupt on end of counter period (CPRD)
  NVIC_EnableIRQ(PWM_IRQn);                            // Enable PWM interrupt

  PWM->PWM_ENA = PWM_ENA_CHID2;                        // Enable PWM Channel 2
}
void PWM_Handler() {
  static uint32_t Count;
  PWM->PWM_ISR1;      // Clear status register
  Count++;
  if (Count == 20000) {
    digitalWrite(LED_BUILTIN, !digitalRead(LED_BUILTIN)); // Toggle every 1 Hz
    Count = 0;
  }
}






joker691

#18
Mar 31, 2018, 11:46 pm Last Edit: Apr 01, 2018, 01:23 am by joker691
Many thanks, im trying to understand registers but somethimes only example to learn is from somebody elses code. I think it is clear to me but dead time delay is not working for any value. Also with DTUPD in main loop.
I added : PWM->PWM_CH_NUM[2].PWM_CMR |= PWM_CMR_DTE;  to enable dead time generator. Partly is working, but only on falling edge of PWMH2 (rising PWML2) . Do you know what is missing on other edge? Thanks again.

ard_newbie

#19
Apr 01, 2018, 05:32 am Last Edit: Oct 17, 2018, 05:19 pm by ard_newbie
Page 979:
Center aligned waveforms can be used to generate non overlapped waveforms

To force a dead time both at the beginning and at the end of each pulse, set CALG bit in PWM_CMR (center aligned):


Code: [Select]

/***********************************************************************************************/
/*                 PWMH2 and PWML2 Enabled -   PWM frequency = 20 KHz                          */
/***********************************************************************************************/

void setup () {
  pinMode(LED_BUILTIN, OUTPUT);
  PWM_Init();
}

void loop() {
}

void PWM_Init() {
  // PWM Set-up on pins PC7 and PA20 (Arduino Pins 39(PWMH2) and 43(PWML2)): see Datasheet chap. 38.5.1 page 973
  PMC->PMC_PCER1 |= PMC_PCER1_PID36;                   // PWM power ON
  PWM->PWM_DIS = PWM_DIS_CHID2;                        // Disable PWM channel 2

  // Select Instance=PWM; Signal=PWMH2 (channel 2); I/O Line=PC7 (P7, Arduino pin 39, see pinout diagram) ; Peripheral type B
  PIOC->PIO_PDR |= PIO_PDR_P7;                          // Set the pin to the peripheral PWM, not the GPIO
  PIOC->PIO_ABSR |= PIO_PC7B_PWMH2;                     // Set PWM pin perhipheral type B

  // Select Instance=PWM; Signal=PWML2 (channel 2); I/O Line=PA20 (P20, Arduino pin 43, see pinout diagram) ; Peripheral type B
  PIOA->PIO_PDR |= PIO_PDR_P20;                          // Set the pin to the peripheral PWM, not the GPIO
  PIOA->PIO_ABSR |= PIO_PA20B_PWML2;                    // Set PWM pin perhipheral type B

  PWM->PWM_CLK = PWM_CLK_PREA(0) | PWM_CLK_DIVA(42);   // Set the PWM clock rate to 2MHz (84MHz/42). Waveform center aligned
  PWM->PWM_CH_NUM[2].PWM_CMR = PWM_CMR_CPRE_CLKA       // clock source as CLKA on channel 2
                               | PWM_CMR_CALG          // The period is Center aligned
                               | PWM_CMR_DTE;          // Dead time enable
  PWM->PWM_CH_NUM[2].PWM_CPRD = 50;                   // Channel 2 : Set the PWM frequency 2MHz/(2* CPRD) = F ;
  PWM->PWM_CH_NUM[2].PWM_CDTY = 10;                    // Channel 2: Set the PWM duty cycle to x%= (CDTY/ CPRD)  * 100 % ;

  // Dead times
  PWM->PWM_CH_NUM[2].PWM_DT = PWM_DT_DTH(2) |           // Dead Time for PWMH; 0 < DT < (CPRD - CDTY)  12 bit max
  PWM_DT_DTL(2);          // Dead Time for PWML; 0 < DT < CDTY

  PWM->PWM_IER1 = PWM_IER1_CHID2;                      // Interrupt on end of counter period (2*CPRD)
  NVIC_EnableIRQ(PWM_IRQn);                            // Enable PWM interrupt

  PWM->PWM_ENA = PWM_ENA_CHID2;                        // Enable PWM Channel 2
}
void PWM_Handler() {
  static uint32_t Count;
  PWM->PWM_ISR1;      // Clear status register
  Count++;
  if (Count == 20000) {
    digitalWrite(LED_BUILTIN, !digitalRead(LED_BUILTIN)); // Toggle every 1 Hz
    Count = 0;
  }
}




joker691

I already tried to set center aligment but it is still left aligned. Strange is, that if i update dead time in main loop or in PWM isr then its centered.
Code: [Select]
PWM->PWM_CH_NUM[2].PWM_DTUPD = PWM_DT_DTH(2) | PWM_DT_DTL(2);

Go Up