Go Down

Topic: Help with mode 14 fast pwm required. (Read 1 time) previous topic - next topic

cyberheater

I'm trying different methods for sound synthesis.

The following code is meant to sweep a squarewave from 65Hz to 2Khz using mode 14 fast pwm mode but it there are gaps in the audio output as it sweeps up.  Any ideas what's wrong.

Code: [Select]

void setup() {  
 // set to mode 14 fast pwm.
 
 // TCCR1B
 // Bit                 7      6      5       4     3      2      1      0
 // Bit Name          COM1A1 COM1A0 COM1B1 COM1B0 -----  ----- WGM11  WGM10
 // Initial Value       0      0      0       0     0      0      0      0
 // changed to          1      1      1       1     0      0      1      0

 TCCR1A = B11110010;      
   
 // TCCR1B
 // Bit             7     6      5       4       3      2        1       0
 // Bit Name         ICNC1  ICES1       -----  WGM13      WGM12      CS12      CS11      CS10
 // Initial Value    0     0     0        0       0      0        0       0
 // changed to       0     0     0       1       1      0        1       1
 
 TCCR1B = B00011011;

 ICR1 = 5256;      // initial "top" value.
 OCR1A = 11;   // initial pulse duty value
 OCR1B = 14;      // initial pulse duty value
 pinMode(10,OUTPUT);            //  set pin 10 for output  
}

void loop()
{

for (float note_val=65; note_val < 2000 ; note_val++)  // generate a note value from 65Hz to 2KHz
 {
   float timer_val;  
   unsigned int final_val;
 
   timer_val = (1 / note_val) * 1000000 ;  // convert Hz to time period in micro seconds
 
   final_val = timer_val;  // convert float to unsigned int

   ICR1 = final_val;  // Load up ICR1 with desired frequency value
   OCR1B = final_val / 2;  // set duty cycle to 50%.  A pure squarewave

   delay (10);

 }
}




CasNet

Greetings,

I don't know if it's affecting your results, but it's generally good programming practice to use floating point constants in floating point constructs, so 1.0 instead of 1 and 1000000.0 instead of 1000000, etc., etc.

BTW, Why don't you update OCR1A after the initialization?

Regards,
David

cyberheater

#2
Jan 04, 2009, 09:01 pm Last Edit: Jan 04, 2009, 09:01 pm by cyberheater Reason: 1
Well I've checked the floating point code and It does work.  Your right that I should use floating point constants in floating point constructs.

In this case, mode 14.  Both OCR1A  and ICR1 are 16 bit numbers.  OCR1A controls the duty of the pulse width, ie, the compare point when the output flips and ICR1 determines the counter top value or frequency.
So for a 50% on/off squarewave OCR1A should equal /2 of ICR1.  I think this is right anyway.  I only bought a Arduino a couple of weeks ago and the atmel pdf is heavy going.

But as I decrease ICR1 (go higher in frequency), there are audio gaps .  As i'm updating OCR1A at the same time, the two should be in sync so I don't know why I get no audio output.
And I need to resolve this as I'll need to be able to cleanly sweep the audio spectrum for the Arduino synth I'm developing.

Any help would be greatly appreciated.

acleone

First, the idiom for setting up control registers is to use the constants that avr-libc defines, eg
Code: [Select]

TCCR1A =  _BV(COM1B0)      // toggle OC1B on compare match
     | _BV(WGM10)
     | _BV(WGM11);
TCCR1B =  _BV(WGM12)
     | _BV(WGM13);      // Fast PWM mode, OCR1A as TOP

([font=Courier New]_BV(x)[/font] is defined as [font=Courier New](1 << (x))[/font])

Second, since we are just generating a square wave, I would toggle on compare match instead of setting OCR1A/B to 50% of the top value.  Since we are changing top, lets use OCR1A as top because it is double buffered.

I would avoid floats entirely, so here's how to convert for toggle on compare match:
65Hz = F_CPU / ((top + 1) * 2 * prescale value)

solving for top we have
top = (F_CPU / (65Hz * 2 * prescale)) - 1
top = (16,000,000 / (65Hz * 2 * prescale = 1)) - 1 = 123075

ok, so a prescale value of 1 is too fine a resolution (the max for top is 0xFFFF = 65535).  Let's try a div 8 prescale:
top = (16,000,000 / (65Hz * 2 * prescale = 8)) - 1 = 15383

ok, now for 2k:
top = (16,000,000 / (2000Hz * 2 * prescale = 8)) - 1 = 499

ok, so top is going from 15383 to 499, and the prescale is div 8.
Code: [Select]

void setup() {
 pinMode(10, OUTPUT);

 TCCR1A = _BV(COM1B0)      // toggle OC1B on compare match
        | _BV(WGM10)
        | _BV(WGM11);
 TCCR1B = _BV(WGM12)
        | _BV(WGM13);      // Fast PWM mode, OCR1A as TOP
 OCR1B = 0;            // toggle when the counter is zero
 OCR1A = 15383;      // set top to the initial 65Hz
 TCCR1B |= _BV(CS11);      // set prescale to div 8 and start the timer
}

void loop() {
 for (uint16_t top = 15383; top >= 499; top--) {
   OCR1A = top;
   delay(10);
 }
}

cyberheater

Thanks acleone.

I got your code with some small modifications.
Code: [Select]
void setup() {
 pinMode(9, OUTPUT);

 TCCR1A = _BV(COM1A0)
        | _BV(COM1B0)      // toggle OC1B on compare match
        | _BV(WGM10)
        | _BV(WGM11);
 TCCR1B = _BV(WGM12)
        | _BV(WGM13);      // Fast PWM mode, OCR1A as TOP
 OCR1B = 0;            // toggle when the counter is zero
 OCR1A = 15383;      // set top to the initial 65Hz
 TCCR1B |= _BV(CS11);      // set prescale to div 8 and start the timer
}

void loop() {
 for (uint16_t top = 15383; top >= 499; top--) {
   OCR1A = top;
   delay(10);    
 }
}


Which outputs a smooth squarewave with no audio gaps.
I guess i'll have to implement PWM on the squarewave in hardware which I can do.


cyberheater

And here's the version which converts hz to microsecond values to set the correct 'top'.
Code: [Select]
void setup() {
 pinMode(9, OUTPUT);

 TCCR1A = _BV(COM1A0)
        | _BV(COM1B0)      // toggle OC1B on compare match
        | _BV(WGM10)
        | _BV(WGM11);
 TCCR1B = _BV(WGM12)
        | _BV(WGM13);      // Fast PWM mode, OCR1A as TOP
 OCR1B = 0;            // toggle when the counter is zero
 OCR1A = 15383;      // set top to the initial 65Hz
 TCCR1B |= _BV(CS11);      // set prescale to div 8 and start the timer
}

void loop() {
unsigned int top;
for (unsigned int note_val = 65; note_val < 2000; note_val++)  // g from 65hz to 2KHz
{
  top = (16000000 / (note_val * 2 * 8)) -1;
  OCR1A = top;
  delay(100);
}
}



Go Up