1
votes

I'm working on a project on the Arduino Due to make a digital synthesizer. I will use PWM to generate an output wave at the desired notefrequency, and sample the wave at 192kHz.

I am able to get PWM out at the desired frequency and adjust the pulse width (tested with oscilloscope), but I need to configure the interrupt so that I can calculate the next needed PWM value for different types of waves at different audible frequencies (sin, sawtooth, etc.). Each time the interrupt triggers (at 192kHz), a function will calculate the next pulse width value based on the note value (audible frequency).

Here is my code so far. I am able to get the PWM to work, but my dummy code in the interrupt (which turns a pin on and off) does not toggle the pin. I have tested this with an oscilloscope. I know that the problem with the code is the interrupt not triggering/executing properly, because the output pin is set high in the setup code, and that also happens on the oscilloscope. However, the interrupt test pin does not toggle. What am I missing?

uint32_t pwmPin = 8; // PWM output pin
uint32_t channel = g_APinDescription[pwmPin].ulPWMChannel;
uint32_t sampFreq = 192000ul; // sample at 192kHz
uint32_t clkAFreq = 42000000ul;
uint32_t pwmFreq = (clkAFreq * 2)/sampFreq;
uint16_t dutyPercent = 50;
uint16_t dutyAct = pwmFreq * (100-dutyPercent) / 100;

void setup() {
  // put your setup code here, to run once:


 pinMode(5, OUTPUT); // interrupt test pin

  pmc_enable_periph_clk(PWM_INTERFACE_ID);
  PWMC_ConfigureClocks(clkAFreq, 0, VARIANT_MCK);

  PIO_Configure(
     g_APinDescription[pwmPin].pPort,
     g_APinDescription[pwmPin].ulPinType,
     g_APinDescription[pwmPin].ulPin,
     g_APinDescription[pwmPin].ulPinConfiguration);

  //uint32_t
  channel = g_APinDescription[pwmPin].ulPWMChannel;
  PWMC_ConfigureChannel(PWM_INTERFACE, channel, clkAFreq, 0, 0);
  PWMC_SetPeriod(PWM_INTERFACE, channel, pwmFreq);
  PWMC_EnableChannel(PWM_INTERFACE, channel);
  PWMC_SetDutyCycle(PWM_INTERFACE, channel, dutyAct);
  PWMC_EnableChannelIt(PWM_INTERFACE, channel);
  dutyPercent = 50; //square wave

}

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

}


void PWM_Handler()          // this is what I looked up in startup_sam4s.c
{
  int i;                    // 
  digitalWrite(5, HIGH);    // toggle pin
  i++;                      // delay a little
  digitalWrite(5, LOW);     // toggle pin
}
1
Can you clarify "sample the wave at 192kHz"? Is an external device is sampling the Arduino output? Or the Arduino is sampling an external input?mhopeng
I normally think of PWM signals as square waves with a modulated duty cycle. What do you expect the "PWM value for different types of waves (sin, sawtooth, etc.)" to look like?mhopeng
No value for i is defined in the PWM_Handler(), so the time that pin 5 is high is very short. Try using delay(500) instead.mhopeng
The PWM outputs at a constant 192kHz. So the frequency of the PWM output always rises at 192kHz. The program then calculates the next pulse width for the desired waveform and frequency.snazziosity
And where is the call to PWM_Handler() made?mhopeng

1 Answers

2
votes

For anyone who stumbles across this, here is how I got the interrupt to work:

uint32_t totalTime = 0; // total elapsed time

uint32_t pwmPin = 8; // PWM output pin
uint32_t irqPin = 5; // interrupt test pin
uint32_t channel = g_APinDescription[pwmPin].ulPWMChannel; // set channel for PWM out
uint32_t sampFreq = 192000ul; // sampling frequency (Hz)
uint16_t maxDutyCount = 255;
uint32_t clkAFreq = 42000000ul; // clock frequency (Hz)
uint32_t pwmFreq = (clkAFreq * 2)/sampFreq; // calculate PWM frequency
uint16_t dutyPercent = 128; // starting duty percent
uint16_t dutyAct = pwmFreq * (maxDutyCount-dutyPercent) / maxDutyCount; // 

void setup() {
  // put your setup code here, to run once:
  pinMode(irqPin, OUTPUT);

  pmc_enable_periph_clk(PWM_INTERFACE_ID);
  PWMC_ConfigureClocks(clkAFreq, 0, VARIANT_MCK);

  PIO_Configure(
   g_APinDescription[pwmPin].pPort,
   g_APinDescription[pwmPin].ulPinType,
   g_APinDescription[pwmPin].ulPin,
   g_APinDescription[pwmPin].ulPinConfiguration);

   channel = g_APinDescription[pwmPin].ulPWMChannel; // channel 5
   PWMC_ConfigureChannel(PWM_INTERFACE, channel, clkAFreq, 0, 0);
   PWMC_SetPeriod(PWM_INTERFACE, channel, pwmFreq);
   PWMC_EnableChannel(PWM_INTERFACE, channel);
   PWMC_SetDutyCycle(PWM_INTERFACE, channel, dutyAct);
   PWM_INTERFACE->PWM_IER1 = 0x20; //enable interrupt on channel 5
   PWM_INTERFACE->PWM_IDR1 = 0xFFFFFFDF; //enable interrupt on channel 5
   PWM_INTERFACE->PWM_IER2 = 0x00002001; //enable interrupt on channel 5
   PWM_INTERFACE->PWM_IDR2 = 0xFFFFDFFE; //enable interrupt on channel 5

   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, channel);
   //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)
}

void loop() {
}


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
//your code here!
}