Pages: [1]   Go Down
Author Topic: Help with mode 14 fast pwm required.  (Read 1768 times)
0 Members and 1 Guest are viewing this topic.
0
Offline Offline
Newbie
*
Karma: 0
Posts: 30
Arduino rocks
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

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:
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);

  }
}


Logged

California
Offline Offline
Jr. Member
**
Karma: 0
Posts: 62
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

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
Logged

0
Offline Offline
Newbie
*
Karma: 0
Posts: 30
Arduino rocks
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

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.
« Last Edit: January 04, 2009, 03:01:47 pm by cyberheater » Logged

Seattle, WA
Offline Offline
Jr. Member
**
Karma: 1
Posts: 81
Arduino rocks my socks
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

First, the idiom for setting up control registers is to use the constants that avr-libc defines, eg
Code:
TCCR1A =  _BV(COM1B0)      // toggle OC1B on compare match
      | _BV(WGM10)
      | _BV(WGM11);
TCCR1B =  _BV(WGM12)
      | _BV(WGM13);      // Fast PWM mode, OCR1A as TOP
(_BV(x) is defined as (1 << (x)))

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 = smiley-cool) - 1 = 15383

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

ok, so top is going from 15383 to 499, and the prescale is div 8.
Code:
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);
  }
}
Logged

0
Offline Offline
Newbie
*
Karma: 0
Posts: 30
Arduino rocks
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Thanks acleone.

I got your code with some small modifications.
Code:
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.

Logged

0
Offline Offline
Newbie
*
Karma: 0
Posts: 30
Arduino rocks
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

And here's the version which converts hz to microsecond values to set the correct 'top'.
Code:
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);
 }
}

Logged

Pages: [1]   Go Up
Jump to: