Continue to Site

Welcome to our site!

Electro Tech is an online community (with over 170,000 members) who enjoy talking about and building electronic circuits, projects and gadgets. To participate you need to register. Registration is free. Click here to register now.

  • Welcome to our site! Electro Tech is an online community (with over 170,000 members) who enjoy talking about and building electronic circuits, projects and gadgets. To participate you need to register. Registration is free. Click here to register now.

12 simultaneous & unique frequencies (PWM)

Status
Not open for further replies.

wip

Member
Hi everyone,

What I am trying to do
Outputting 12 simultaneous & unique frequencies (ie: 220hz, 440hz + 10 others).

What I am using
Atmega640
2x 8-bit timers
4x 16-bit timers
12 PWM channels

Problem 1) 8-bit timers
In "Fast PWM" mode, using the 8-bit timers, I cannot set the TOP value (it's fixed at 255). So I can only change the "prescaler" (1024,256,128,64,32,8,1). Doesn't correspond to my frequencies. I was looking at the CTC mode, from what I understand, I could set the compare on match and toggle a pin, but will I be able to use 2 simultaneous & unique value for compare on match?

Problem 2) 16-bit timers
With the 16-bit timers, I can set the TOP value in a register, but this value is shared for both channels (A/B). It won't be possible to have 2 simultaneous & unique frequency for each channel?

Any suggestion or info would be much appreciated,
Thanks!
 
Do your frequencies have to be very specific ones, or is it sufficient that they are just different from each other (which might simplify the code)?
 
Very specific for example:
207.652, 220.000, 233.082, 246.942, 261.626, 277.183 hz

I do understand that I won't be able to have that precision, but for example 207hz will be fine.
 
To get 220hz, just output every other pulse from the 440hz. Are there any other multiples you can do that with?
 
Sadly it won't work, I am trying to have a basic polyphonic (12 voices) midi synth:
pitch 36 is 207.652
pitch 37 is 220.000
pitch 38 is 233.082
pitch 39 is 246.942
pitch 40 is 261.626
pitch 41 is 277.183
pitch 42 is 293.665
pitch 43 is 311.127
pitch 44 is 329.628
pitch 45 is 349.228
pitch 46 is 369.994
pitch 47 is 391.995

I think the way to go is to use the CTC mode to toggle the pin (compare match), but I am not sure, will I be able to output those frequencies at the same time on 12 pins?
 
I'm afraid you will have to do it with CPU. Have I high frequency timer interrupt, say every 10us, have 12 independent software counters (one for each frequency). Decrement all counters in each timer interrupt and when a counter goes to zero, flip the corresponding output and re-set the counter to the desired value. I'm not sure if your MCU is fast enough for that. You might need to upgrade.
 
Watch lectures from 13 to 16.. if you are impatient, then lecture 15 will get your motivation up to watch the others.
https://www.youtube.com/playlist?list=PLD7F7ED1F3505D8D5

You can skip the first 16 minutes. From that on it is all you want to know. Rest of the lectures add more theory and details.
Lecture 15: Noise generator, Direct Digital Synthesis, PWM

After that go to lecture 16.. it is all about implementation.. that is code.
 
Last edited:
Ok looks like it is impossible to output 12 unique & simultaneous frequencies using an Atmage640 (12 pwm channels - 6 timers: 2x 8-bit, 4x 16-bit). I tried the CTC mode & Fast PWM but in both case the channel B is always related to channel A is some ways.

So right now the only solution I have is the one recommended by NorthGuy. I tried it, I can only get down to 136us before my USB disconnect (I am using V-USB). Is this will be enough to have precision over the 12 frequencies? I didn't really tested out, but 136us is 7352.94hz which is enough for my needs but maybe I will not be able to set the desire frequency with the decremented variable (let's say 23 vs 24 is too much of a big frequency step - ie: 432hz - 459hz)?

Someone on irc #avr said:
"or using 6 pwm and 6 bitbang"?
 
Last edited:
Why you need to output the frequencies to 12 different output channels.. aren't you going to mix them together anyway? All you need is one pwm channel. Mix the 12 channels in software.
 
Last edited:
I wish I could :)
I have to control 12 independant speakers.

My conclusion:
Using an Atmage640 at 16mhz with V-USB and 12 ADC channels = impossible to output 12 simultaneous and unique frequencies (with enough precision ie: 430hz, 422hz).

My current idea:
Use many Attiny with ISP communication. I really want to avoid this ($, time).
 
I wish I could :)
I have to control 12 independant speakers.
Can I ask why?

My current idea:
Use many Attiny with ISP communication. I really want to avoid this ($, time).

Maybe one AVR that is dedicated to software pwm and one that gives it commands (duty cycles for each channel).
 
Will need to test if 12 software PWM is doable (with enough precision) on AtmegaX with 20mhz & ISP. Someone on the #AVR channel suggested using binary pwm?!

I feel ashamed by the above statement:
Apollo 11 had a 2.048 MHz CPU...

/me hiding
 
Last edited:
That is doable.. it depends how much processor time you need to do some other "intelligent" stuff. I would do all 12 channels in software to simplify the design. That is a really efficient loop..

C:
interrupt routine that is ececuted at constant intervall {
    counter++;
    (channel[0] > counter) ? (PIN0_HIGH) : (PIN0_LOW);
    (channel[1] > counter) ? (PIN1_HIGH) : (PIN1_LOW);
    (channel[2] > counter) ? (PIN2_HIGH) : (PIN2_LOW);
    (channel[3] > counter) ? (PIN3_HIGH) : (PIN3_LOW);
    (channel[4] > counter) ? (PIN4_HIGH) : (PIN4_LOW);
    (channel[5] > counter) ? (PIN5_HIGH) : (PIN5_LOW);
    (channel[6] > counter) ? (PIN6_HIGH) : (PIN6_LOW);
    (channel[7] > counter) ? (PIN7_HIGH) : (PIN7_LOW);
    (channel[8] > counter) ? (PIN8_HIGH) : (PIN8_LOW);
    (channel[9] > counter) ? (PIN9_HIGH) : (PIN9_LOW);
    (channel[10] > counter) ? (PIN10_HIGH) : (PIN10_LOW);
    (channel[11] > counter) ? (PIN11_HIGH) : (PIN11_LOW);
}

I think you can get good frequency out of that. Even higher pwm frequency if you put that in your main loop, but then it is difficult to keep freq constant.
 
Last edited:
Actually I think I got confused there.. are you trying to generate sine frequencies between 200 and 400 Hz?
 
Very nice of you, you even took the time to code for me, we need more of you and less of me :)

I am trying to output squarewave from 207.652hz to 440hz - 12 notes at the same time
 
Which one of these frequencies you do not need?
.. actually, could you list the actual frequencies you need..

207.65
220.00
233.08
246.94
261.63
277.18
293.66
311.13
329.63
349.23
369.99
392.00
415.30
440.00

I don't know anything about music.. My source for that list: https://www.phy.mtu.edu/~suits/notefreqs.html

EDIT: Apparently you do not need the two upper frequencies, because they are the same note as the two lower ones. right?
 
Last edited:
I think you could use a single 16-bit counter, and set the top value to a new one each time the counter triggers. I.e. on each trigger you calculate how long you need to count till the next output changes state, and which of the outputs it should be. Not really sure right now on how to calculate it, but if you start with just two signals and draw it on a piece of paper I am sure you will get there. Something along the way of adding the periods of all the sines and finding the next change, and some counting in modulo I guess.
 
Ok, I think this is the way to go if you want a software pwm (or square wave frequency generator).

3-channel example:
C:
/* set up counters */
uint16_t f1_counter = 0;
uint16_t f2_counter = 0;
uint16_t f3_counter = 0;

uint16_t f1_increment = calculate correct increment to get the frequency you want;
uint16_t f2_increment = calculate correct increment to get the frequency you want;
uint16_t f3_increment = calculate correct increment to get the frequency you want;

interrupt routine that is executed at constant interval {
    (f1_counter & 0x8000) ? (PIN1_HIGH) : (PIN1_LOW);
    (f2_counter & 0x8000) ? (PIN2_HIGH) : (PIN2_LOW);
    (f3_counter & 0x8000) ? (PIN3_HIGH) : (PIN3_LOW);

    f1_counter += f1_increment;
    f2_counter += f2_increment;
    f3_counter += f3_increment;
}

The fx_counter variable is tested if the last bit is set or not and the output is set high or low accordingly. The frequency is controlled by the amount of the fx_increment and the loop (interrupt) frequency. You can play with the values. 8bit counters gives you more speed, but less resolution (accuracy) etc. Loop frequency sets the resolution.

This scheme also lets you to go higher and higher.. I mean from 220 Hz to 440 Hz.
Instead of testing the last bit (bit 15).. test the bit before that (bit 14) and you double the frequency. Is that the same as going one octave up? sorry I'm really bad in music theory.
Anyway, if that is true, then you get that kind of control almost free.

Actually you have nice control of the accuracy of the frequency if you play with the increment value and what bit you are testing. I can't do the math now.. too drunk.

The math is:

Not sure if this is correct:
loopf / ((2^n)*increment)

loopf: the loop frequency that you execute the thing.
n: the bit you are testing if it is 1 or 0
increment: the counter increment

You want the loop frequency to be as fast as possible and then you have two variables to play with.
 
Last edited:
Will test this very soon! I think I will end up with your solution, that is: 1 uc for USB - ADC - ... / 1 uc for software PWM (communication with ISP). That way, I will be able to run the timer at +-10us or lower and have good precision over the frequencies. Will report back!

Cheers!
 
I did some calculations and it looks like with the method I came up with it is really hard to get all 12 frequencies with decent accuracy. I thin you need to dedicate one or more microcontrollers just to generate the signals. You need really fast loop to get enough resolution.

.. and there are problems with my code.. It does not work as I first thought. Sorry about that. I'll re-think that tomorrow.
 
Last edited:
Status
Not open for further replies.

New Articles From Microcontroller Tips

Back
Top