Audio exciters driven simultaneously

Considering that I need a frequency of 100Hz, supposing that I want a counter equal to 60, the desired period is equal to: (1/f)/60 = 166,67microseconds, correct?

Yes. By the way 60 is quite short for a look up table, consider 256 or 512, as if the number of entries is a power of two it is simple to make the count wrap round.

I looked around the internet but I didn't find anything that could me help in choosing the prescaler value

That is because that information is in the processor's data sheet. Do you understand what a prescaler value is? It is the division from the master clock to use before decrementing the count down register.
From the data sheet of a ATmega 328 chip.

The Timer/Counter can be clocked directly by the system clock (by setting the CSn2:0 = 1). This provides the fastest operation, with a maximum Timer/Counter clock frequency equal to system clock frequency (fCLK_I/O). Alternatively, one of four taps from the prescaler can be used as a clock source. The prescaled clock has a frequency of either fCLK_I/O/8, fCLK_I/O/64, fCLK_I/O/256, or
fCLK_I/O/1024.

The frequency of the fCLK_I/O is half that of the crystal of 16MHz.
So calculate how many clock pulses you will need for each interrupt ( that is the 166.67 uS you had before ). Now if that is greater than 256 then you can't get it in the 8 bit counter register. So see what the smallest value of prescaler you can get so that it is lower than 256.

Grumpy_Mike:
The frequency of the fCLK_I/O is half that of the crystal of 16MHz.

AFAIK frequency of I/O clock is equal to the main clock. Unless you use the System Clock Prescaler to reduce the frequency (not used in Arduino by default and there is little reason to do so) the fCLK_I/O is equal to frequency of the crystal, not half of it.

Thanks for the correction.

Well, tell me if this math is right. I followed the formulas used in the commented code that I posted:

  • 1 / 100Hz = 10ms
  • period = 10ms/256 = 39.06us with 256 as size of the lookup table
  • 48MHz is the clock frequency
  • Considering that the prescaler value of the arduino m0 are: 1,2,4,8,64,256,1024:
    48MHz/1 = 48MHz => 20.83nsec
    48MHz/2 = 24MHz => 41.66nsec
    48MHz/4 = 12MHz => 83.33nsec
    48MHz/8 = 6MHz => 166,66nsec
    48MHz/64 = 750KHz => 1.33usec
    48MHz/256 = 187.5KHz => 5.33usec
    48MHz/1024 = 46.87KHz => 21.34usec
  • Dividing the period by the clock divided by the prescaler, I obtain:
    39.06usec/20.83nsec = 1875.18
    39.06usec/41.66nsec = 937.6
    39.06usec/83.33nsec = 468.74
    39.06usec/166.66nsec = 234.37
    39.06usec/1.33usec = 29.37
    39.06usec/5.33usec = 7.33
    39.06usec/21.34usec = 1.83

Now according to the guy's calculation, due to the fact that the register is 8bit, I have to subtract 256 with one of the number of the final calculation. Due to the fact that I need a number close to 256, I should get 1.83 and so, have a reload of the timer with 254, correct?

Moreover the count of the interrupt has to arrive to 256 and then has to be reset, right?

39.06usec/166.66nsec = 234.37

Is the prescaler value you need, so a value of 8.

due to the fact that the register is 8bit, I have to subtract 256 with one of the number of the final calculation.

No.
You want a count of 234 which is less than 256. So reload with 234. Notice you will not get exactly 100Hz but that is computers and counting chains for you.

Moreover the count of the interrupt has to arrive to 256 and then has to be reset, right?

Yes but if you use a byte for the counter then it will reset automatically as it wraps round.

But looking at the code of the guide, I still don't get the meaning of the variable tcnt2 that reloads the timer at every interrupt, also because it is not used or modified anywhere.

   /* Reload the timer */
    TCNT2 = tcnt2;

So what you are suggesting is to have

if (count1 == 60)
    {
        digitalWrite(FREQ1, toggle1 == 0 ? HIGH : LOW);
        toggle1 = ~toggle1;
        count1 = 0;
    }

this code with the count that is reset when it is equal to 234 instead of 60, right?

So what you are suggesting is to have .......

No, no one is suggesting that in any way.
Each time the timer interrupts happens then you have to load the new value in for next time.
Each time the timer interrupts happen you have to increment the count.
Each time the timer interrupt happens you have to load in the value pointed by count in the look up table and output it to the D/A.

Each time the timer interrupts happens then you have to load the new value in for next time.

What do you mean? What new value does it have to be loaded for next time?

I agree with you for the other two operations but I miss the first one.

What is the reload timer (tcnt2) code that I posted used for at each timer interrupt?

In case I would like to change and use the PWM method, I just have to use a code similar to the one posted in the previous post, correct?

What new value does it have to be loaded for next time?

The value in the count down register. This could happen automatically depending on the specific timer. But it dosn't matter if you do it again.

What is the reload timer (tcnt2) code that I posted used for at each timer interrupt?

The value in the count down register.

In case I would like to change and use the PWM method, I just have to use a code similar to the one posted in the previous post, correct?

No. You have to use a second timer and use the value loaded from the look up table into the PWM register. Note that their are not enough timers to make 5 waveforms using PWM.

there are not enough timers to make 5 waveforms using PWM

Looking at the samd21 data sheet (Arduino m0 pro micro controller), I noticed that the number of timers goes from 3 to 7. There are just 4 timers for the waveforms, right?

Just for clarifing, for count you mean the index of the lookup table? otherwise what is the count used for?

And still don't get why at every interrupt I have to set the tcnt2 if it is not modified in any instruction.

By the way, I don't understand why I should use a count down register. I want to use PWM (I'm sorry I have changed idea) and from what I understood, I need a lookup table with the values of the sine wave, an interrupt handler to increment the index of the lookup table, a timer that produces the interrupt and the function analogWrite() to output the sine wave values. Supposing the size of the lookup table equals to 256, the counter must go from 0 to 255 in order to output all the sine wave values and once it reaches 255, it must reset to 0 in order to repeat the sine wave. In order to have a frequency of about 100Hz, after all the math done in the previous post, we arrived at the conclusion that I need to use the value 234 but for what? Use it as the size of the lookup table? I don't get the point. Could you be more specific? Thank you for the patience you are having.

And still don't get why at every interrupt I have to set the tcnt2 if it is not modified in any instruction.

Because the hardware changes it.

Just for clarifing, for count you mean the index of the lookup table?

Yes.

I want to use PWM (I'm sorry I have changed idea)

Sigh, why are you making life more difficult for yourself? You hardly seem to have understanding to spare and you have added another level of complication.

Thank you for the patience you are having.

Warning, it is running out.

and the function analogWrite() to output the sine wave values.

No you don't use analogWrite, you change the appropriate register in the timer.

n order to have a frequency of about 100Hz, after all the math done in the previous post, we arrived at the conclusion that I need to use the value 234 but for what?

To control the time between each sample taken from the look up table and passed to the output. That count is decremented by the hardware at a rate given by the clock which is pre divided by the prescaler.

Could you be more specific?

Don't tempt me.

Ok now I got it, maybe :slight_smile:

The value 234 is the interval from one interrupt to another, so every time that the interrupt occurs it means that the value in the register is equal to 0 and I have to reload it to 234 in order to have another interrupt with a frequency of 100Hz.

So now, in the interrupt handler, I have to reload the value in the register, output the value of the lookup table in the PWM register and increment the index of the lookup table, right? I hope that this is the procedure. Do you agree with what I've just written? Moreover with 5 timers can I generate 2 different sine waves (2 because one sine wave is the audio output and the other one is the same for the other 4 exciters, with exception for the amplitude)?

Theoretically I just have to output one value at the time on 5 PWM registers which 4 of them are the same except for the amplitude and that's it no?

Theoretically I just have to output one value at the time on 5 PWM registers which 4 of them are the same except for the amplitude and that's it no?

No.

You want the phase to be different for each waveform so while your master signal just does this then each one of your extra oscillators need its own offset to add to the count to get the sample to use.

Do you agree with me in what I told you in the post #33?

Moreover the count of the master signal has to start from 0 while the one of the extra oscillators has to start from the half of the sine wave lookup table in order to cancel the master wave. In this way when the master signal is positive, the other waves are negative while when the master signal is negative, the other waves are positive. Do you think that it can work?

If I didn’t put you right that means I agree.

Do you think that it can work?

That is altogether an other matter. What people class as working is a very vague concept. It will not be perfect, nothing ever is, but their will be a degree of cancellation. I am not too sure what you aim is and what you will count as success. Or over what area you want the cancellation and at what distance the cancellation oscillators are from the vibration source.

Hi Grumpy_Mike, I tried to develop the system but I have a bottleneck due to the low frequency of the i2c communication. So I would like to implement the pwm sine wave generator. I developed this code till now, can you help me to modify it in order to use the PWM instead of the DAC?

#include <Wire.h>
#include <Adafruit_Sensor.h>
#include <Adafruit_MCP4725.h>
#include <Adafruit_MMA8451.h>
// Output 25kHz single slope PWM with timer TCC0 on D7

volatile int flag = 0;
int ind = -1;

Adafruit_MCP4725 dac;

#define DAC_RESOLUTION (10)

volatile int sintable[256] PROGMEM = {
    512,524,537,549,562,574,587,599,611,624,636,648,660,672,684,696,707,719,730,741,753,764,774,785,796,806,816,826,836,846,855,864,873,882,890,899,907,915,922,930,
937,944,950,957,963,968,974,979,984,989,993,997,1001,1004,1008,1011,1013,1015,1017,1019,1021,1022,1022,1023,1023,1023,1022,1022,1021,1019,1017,1015,1013,1011,1008,1004,1001,997,993,989,
984,979,974,968,963,957,950,944,937,930,922,915,907,899,890,882,873,864,855,846,836,826,816,806,796,785,774,764,753,741,730,719,707,696,684,672,660,648,636,624,
611,599,587,574,562,549,537,524,512,499,486,474,461,449,436,424,412,399,387,375,363,351,339,327,316,304,293,282,270,259,249,238,227,217,207,197,187,177,168,159,
150,141,133,124,116,108,101,93,86,79,73,66,60,55,49,44,39,34,30,26,22,19,15,12,10,8,6,4,2,1,1,0,0,0,1,1,2,4,6,8,
10,12,15,19,22,26,30,34,39,44,49,55,60,66,73,79,86,93,101,108,116,124,133,141,150,159,168,177,187,197,207,217,227,238,249,259,270,282,293,304,
316,327,339,351,363,375,387,399,412,424,436,449,461,474,486,499
};


void setup()
{ 
  ind = i+128;
  Wire.begin();
  Wire.setClock(400000);
  Serial.begin(9600);
  dac.begin(0x62);
  
  Serial.println("MCP4725A found!");

  REG_GCLK_GENDIV = GCLK_GENDIV_DIV(1) |          // Divide the 48MHz clock source by divisor 1: 48MHz/1=48MHz
                    GCLK_GENDIV_ID(4);            // Select Generic Clock (GCLK) 4
  while (GCLK->STATUS.bit.SYNCBUSY);              // Wait for synchronization

  REG_GCLK_GENCTRL = GCLK_GENCTRL_IDC |           // Set the duty cycle to 50/50 HIGH/LOW
                     GCLK_GENCTRL_GENEN |         // Enable GCLK4
                     GCLK_GENCTRL_SRC_DFLL48M |   // Set the 48MHz clock source
                     GCLK_GENCTRL_ID(4);          // Select GCLK4
  while (GCLK->STATUS.bit.SYNCBUSY);              // Wait for synchronization

  // Feed GCLK4 to TCC0 and TCC1
  REG_GCLK_CLKCTRL = GCLK_CLKCTRL_CLKEN |         // Enable GCLK4 to TCC0 and TCC1
                     GCLK_CLKCTRL_GEN_GCLK4 |     // Select GCLK4
                     GCLK_CLKCTRL_ID_TCC0_TCC1;   // Feed GCLK4 to TCC0 and TCC1
  while (GCLK->STATUS.bit.SYNCBUSY);              // Wait for synchronization

  // Normal (single slope) PWM operation: timers countinuously count up to PER register value and then is reset to 0
  REG_TCC0_WAVE |= TCC_WAVE_WAVEGEN_NPWM;         // Setup single slope PWM on TCC0
  while (TCC0->SYNCBUSY.bit.WAVE);                // Wait for synchronization

  // Each timer counts up to a maximum or TOP value set by the PER register,
  // this determines the frequency of the PWM operation:
  REG_TCC0_PER = 1919;                            // Set the frequency of the PWM on TCC0 to 25kHz
  while(TCC0->SYNCBUSY.bit.PER);                  // Wait for synchronization
 
  // Set the PWM signal to output 50% duty cycle on D7
  REG_TCC0_CCB3 = 959;                            // TCC0 CCB3 - on output on D7 50% duty cycle
  while(TCC0->SYNCBUSY.bit.CCB3);                 // Wait for synchronization

  NVIC_SetPriority(TCC0_IRQn, 0);    // Set the Nested Vector Interrupt Controller (NVIC) priority for TCC0 to 0 (highest)
  NVIC_EnableIRQ(TCC0_IRQn);         // Connect TCC0 to Nested Vector Interrupt Controller (NVIC)

  REG_TCC0_INTENSET = TC_INTENSET_OVF;            // Enable TCC0 overflow interrupt

  // Divide the 48MHz signal by 1 giving 48MHz (20.83ns) TCC0 timer tick and enable the outputs
  REG_TCC0_CTRLA |= TCC_CTRLA_PRESCALER_DIV1 |    // Divide GCLK4 by 1
                    TCC_CTRLA_ENABLE;             // Enable the TCC0 output
  while (TCC0->SYNCBUSY.bit.ENABLE);              // Wait for synchronization  
}

void loop() {
  if(flag){
    dac.setVoltage(pgm_read_word(&(sintable[ind])), false);
    flag = 0;
  }
}

// Ineterrupt handler called every time the TCC0 timer overflows
void TCC0_Handler()                              // Interrupt Service Routine (ISR) for timer TCC0
{     
  // Check for overflow (OVF) interrupt
  if (TCC0->INTFLAG.bit.OVF && TCC0->INTENSET.bit.OVF)             
  { 
    flag = 1;
    ind++;
    if(ind == 256)
      ind = 0;
    REG_TCC0_INTFLAG = TC_INTFLAG_OVF;         // Clear the OVF interrupt flag
  }
}

I don't know which board you are working on (M0 pro ?), however if you want to output 5 PWM signals (e.g. a sine wave for all PWM signals) with synchro frequencies and variable phase and amplitude, you might be interested by the DUE (I don't know whether the M0 pro has or not this feature).

The DUE has a PWM synchro feature, with the possibility of an automatic update of duty cycles via DMA ( buffer transfer without processor Intervention). Moreover, Inside the PWM interrupt Handler triggered after each period, you can update the amplitude and phase of each of the PWM signals for the next period.

Of course, it depends on the frequency of your synchro PWM signals (I have seen 100 Hz ?) because the update of 5 buffers should take place between two PWM interrupt triggers, plus the necessary time to do some math to calculate 4 new amplitudes and phases for the next period...

I don't know which board you are working on

I'm using arduino m0 pro and I want to output 4 sine waves that should have the same frequency of 100Hz and different amplitude. Due to the fact that the sine waves have a frequency of 100Hz each value of the lookupt table needs to be outputed with a frequency of 25kHz.

I want to use this board, is it possible to use it or do I have to change it?

As I previously said, I don't know the MO pro board. Read carefully the PWM section of the datasheet for the uc attached to this board, and you will have the answer.