Fast PWM - Compare to OCR1A

Hi everyone, so for my application I need to use Fast PWM to output a signal with as close to 2 us pulses with what will eventually be a variable frequency that will be determined by a series of measured voltages.

I am attempting to configure the Fast PWM with Timer1 first, and plan on adding the voltage readings later on. I have been using http://arduino.cc/en/Tutorial/SecretsOfArduinoPWM as a guide along with the ATMega328P datasheet, but without complete success.

In my testing I can get the the pulse with of 2 us correct, but I cannot get the frequency to adapt to whatever value I set OCR1A to. I believe that my errors must be with setting the COM1A0/COM1A1/COM1B0/COM1B1 values in the TCCR1A register.

When I run the following code I get the 2 us pulse width, but the frequency is around 15.6 kHz where I will need to be able to produce frequencies ranging from 450 kHz to 50 kHz. If anyone has any idea as to why the timer isn’t being compared to OCR1A please let me know.

Thank you in advance, and let me know if there is anything I need to clear up in my explanation.

void setup()
{
  // Configure output pins
  pinMode(9, OUTPUT);
  pinMode(10, OUTPUT); 
  TCCR1A = _BV(COM1A0) | _BV(COM1B1) | _BV(WGM11) | _BV(WGM10); // Fast PWM mode - compare to OCR1A
  TCCR1B = _BV(WGM12) | _BV(CS10); // No prescalar
  
  // Value to be compared to the timer value (I believe)
  OCR1A = 64;
  
  // Value to set the pulse width
  // 32 provides approximately a 2 us pulse
  OCR1B = 32;
}

void loop()
{
  
}

You change frequency with a combination of pre-scale and TOP. Depending on the mode you set the TOP value is stored in OCRxA or ICRx. Note that the prescale value will also affect your pulse width. If you have to change the prescale to get the full frequency range you will need to change the PWM value to get your desired pulse width.

The base clock is 16 MHz so 16 clocks per microsecond so 32 counts per pulse with a prescale of 1. If you go to a prescale of 8 you are down to 4 counts per pulse.

50 kHz is 320 counts at a prescale of 1. That means you have to use a 16-bit timer or a prescale. If you use a prescale of 8 you can use an 8-bit timer and would set TOP to 40.

450 kHz is 35.555 counts at a prescale of 1. That will work fine with an 8-bit timer. Of course you can’t use 35.555 so you should use 36 (444.44 kHz) or 35 (457.143 kHz).

Change

  TCCR1B = _BV(WGM12) | _BV(CS10); // No prescalar

to

  TCCR1B = _BV(WGM13) | _BV(WGM12) | _BV(CS10); // No prescalar

and you'll at least be in the right mode!
Table 15.4 in datasheet.

Thank you very much MarkT and johnwasser, everything is working great!

@John: I’m using Timer1 to drive a simple pulse using the setup code:

  TCCR1A = 0;       // Clear TCCR1A/B registers
  TCCR1B = 0;
  TCNT1 = 0;        // Initialize counter to 0

  // No prescaler, period set using the following equation:
  // Compare register for TIMER1: (16mHz / frequency) - 1 = 15999 = 0x3E7F 
  OCR1A = period;
  
  // Timer/Counter Control Register for Interrupt 1 on register B
  TCCR1B |= (1 << WGM12);    // Mode 4, CTC--Clear Timer on Compare
  TCCR1B |= (1 << CS10);     // Clock Select Bit, no prescaling
  TIMSK1 |= (1 << OCIE1A);   // The value in OCR1A is used for compare

I get a great square wave out for 1000Hz. When I want to change the frequency via period at runtime and do the assignment of a new period into OCR1A, things work fine. However, if I raise the frequency to something above 2.5KHz, the frequency doesn’t change. The calculation for period should work well past 2.5KHz. Anything ring a bell here?

What have you set OCR1B to though? Do you ensure TCNT1 is < OCR1A when OCR1A
is changed?

Am I reading this all wrong? If it takes a 16 bit value, I thought OCR1A and OCR1B are treated as a single register. Doesn't the ISR, TIMER1_COMPA_vect, only get called when TOV1 reaches MAX? It's in the ISR that I put in the new OCR1A value for the new frequency. For some reason I just can't seem to get the penny to drop on this one when it seems to be a simple thing...change the frequency.

econjack:
@John: I'm using Timer1 to drive a simple pulse using the setup code:

I get a great square wave out for 1000Hz. When I want to change the frequency via period at runtime and do the assignment of a new period into OCR1A, things work fine. However, if I raise the frequency to something above 2.5KHz, the frequency doesn't change. The calculation for period should work well past 2.5KHz. Anything ring a bell here?

It should be a problem for frequencies BELOW 2.5 kHz. Frequencies too low without a prescale will give you a value of TOP greater than 65535, the largest number that fits in a 16-bit register.

Calculate TOP using unsigned long values. If TOP > 65535 set the prescale to 8 and use TOP/8. Otherwise use a prescale of 1 and use TOP directly. For even lower frequencies expand that to higher prescales.

@John: Thanks for the reply. Let me check my math. If I want a 1KHz signal, and no prescaler, I use:

OCR1A = (16000000 / 1000) - 1;
= 16000 - 1;
= 15999;

When I do this, I get a beautiful square wave on the scope. However, changing the frequency to 5KHz, and the most I get out is 2.38KHz. I have a rotary encoder on Int0 and Int1 that allows me to change the frequency. When I see the frequency I want, I use the rotary switch to select the current frequency (as displayed on an LCD). All that stuff is working fine. When I shove the new frequency into the equation above (cli() before I reset and sei() after), it does not change the scope output. As I mentioned, I can't get anything out above 2.38Khz. Could it be too much code in the ISR? I do have a small switch with a few statements in each, all volatile variables.

I know this has to be a "flat-forehead" moment, where someone points out my error and I slap my forehead with a loud "Duh", but so far, no joy.

johnwasser:
It should be a problem for frequencies BELOW 2.5 kHz. Frequencies too low without a prescale will give you a value of TOP greater than 65535, the largest number that fits in a 16-bit register.

Oops. I got 6,400 AND 64,000 confused. Ignore this part.

16,000,000 / 2,500 = 6,400 so that should work fine in a 16-bit counter.
16,000,000 / 5,000 = 3,200 so that should work fine in a 16-bit counter.

With a 16-bit timer you should be able to get down to 244 Hz and up to 8 MHz.

Perhaps it is time to show your code.

OK. It’s fairly long, so I’ll have to split it:

#include <avr/io.h>
#include <avr/interrupt.h>
#include <avr/pgmspace.h>
#include <pins_arduino.h>
#include <rotary.h>
#include <digitalWriteFast.h>
#include <digitalWriteFast.h>
#include <LiquidCrystal.h>


#define ENCODERSWITCH           4
#define ENCODERDELAY           20
#define ENCODERSETTOFREQ        1
#define ENCODERSETTOSELECT      0

#define TRIANGLE                0
#define SAWTOOTH                1
#define SINE                    2
#define SQUARE                  3

#define LCDCOLUMNOFFSET         6
#define LCDCOLUMNS             16
#define LCDROWS                 2

#define MAXFREQUENCY      2000000
#define CLOCKFREQUENCY   16000000
#define MINFREQUENCY          100
#define DEFAULTFREQ          1000

PROGMEM byte sineWave[] = {
  1, 2, 3, 5, 6, 8, 11, 13, 
  16, 18, 21, 24, 27, 30, 32, 35, 38, 41, 
  43, 46, 49, 51, 54, 56, 57, 59, 60, 61, 
  62, 63, 63, 63, 63, 62, 61, 60, 59, 57, 
  56, 54, 51, 49, 46, 43, 41, 38, 35, 32, 
  30, 27, 24, 21, 18, 16, 13, 11, 8, 6, 
  5, 3, 2, 1, 0, 0, 0, 0, 1, 2, 
  3, 5, 6, 8, 11, 13, 16, 18, 21, 24, 
  27, 30, 32, 35, 38, 41, 43, 46, 49, 51, 
  54, 56, 57, 59, 60, 61, 62, 63, 63, 63, 
  63, 62, 61, 60, 59, 57, 56, 54, 51, 49, 
  46, 43, 41, 38, 35, 32, 30, 27, 24, 21, 
  18, 16, 13, 11, 8, 6, 5, 3, 2, 1,

};

PROGMEM byte triangle[] = {
  32,34,36,38,40,42,44,46,48,50,
  52,54,56,58,60,62,63,62,60,58,
  56,54,52,50,48,46,44,42,40,38,
  36,34,32,30,28,26,24,22,20,18,
  16,14,12,10,8,6,4,2,0,2,
  4,6,8,10,12,14,16,18,20,22,
  24,26,28,30,
  32,34,36,38,40,42,44,46,48,50,
  52,54,56,58,60,62,63,62,60,58,
  56,54,52,50,48,46,44,42,40,38,
  36,34,32,30,28,26,24,22,20,18,
  16,14,12,10,8,6,4,2,0,2,
  4,6,8,10,12,14,16,18,20,22,
  24,26,28,30
};
PROGMEM byte sawTooth[] = {
  0,1,2,3,4,5,6,7,8,9,
  10,11,12,13,14,15,16,17,18,19,
  20,21,22,23,24,25,26,27,28,29,
  30,31,32,33,34,35,36,37,38,39,
  40,41,42,43,44,45,46,47,48,49,
  50,51,52,53,54,55,56,57,58,59,
  60,61,62,63,
  0,1,2,3,4,5,6,7,8,9,
  10,11,12,13,14,15,16,17,18,19,
  20,21,22,23,24,25,26,27,28,29,
  30,31,32,33,34,35,36,37,38,39,
  40,41,42,43,44,45,46,47,48,49,
  50,51,52,53,54,55,56,57,58,59,
  60,61,62,63};

PROGMEM byte squareWave[] = {
  63, 63, 63, 63, 63, 63, 63, 63, 63, 63,
  63, 63, 63, 63, 63, 63, 63, 63, 63, 63,
  63, 63, 63, 63, 63, 63, 63, 63, 63, 63,
  63, 63, 
  0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
  0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
  63, 63, 63, 63, 63, 63, 63, 63, 63, 63,
  63, 63, 63, 63, 63, 63, 63, 63, 63, 63,
  63, 63, 63, 63, 63, 63, 63, 63, 63, 63,
  63, 63, 
  0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
  0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
};

static  long incrementTable[] = {
  10,20,100,500,1000,2500,5000,10000};
static char waveForms[][10] = {
  "10 Hz    ", "20 Hz    ", "100 Hz   ", "500 Hz   ", "1 kHz    ", 
  "2.5 kHz  ", "5 kHz    ", "10 kHz   ", 
  "Triangle ", "Saw Tooth", "Sine     ", "Square   "};
#define MAXENCODERVALUES    (sizeof(incrementTable) / sizeof(incrementTable[0]))
#define MAXWAVETYPES        4

int tableSize = sizeof(sineWave) / sizeof(sineWave[0]);  // Waveforms all the same
int buttonRead;

volatile byte tableVals = tableSize;
volatile boolean buttonState;
volatile byte *waveTypePtr;
volatile byte index = 0;
volatile int increIndex = 0;
volatile int waveType;
volatile float increment;
volatile long frequency;
volatile long oldFrequency;
volatile long period;

Rotary myEncoder(2, 3);
LiquidCrystal lcd(12, 11, 9, 8, 7, 6);

void setup() 
{
  pinMode(ENCODERSWITCH, INPUT);
  digitalWrite(ENCODERSWITCH, HIGH);
  
  frequency = DEFAULTFREQ;  // Start off with 1000Hz
  
  cli();                    // Turn interrupts off

  InitEncoder();            // Set up encoder interrupt
  InitTimer();              // Set up timer interrupt

  sei();                    // Turn interrupts on
  
  lcd.begin(LCDCOLUMNS, LCDROWS);
  
  buttonRead = ENCODERSETTOFREQ;
  buttonState = false;
  oldFrequency = frequency - 1;
  increment = incrementTable[increIndex];
  waveType = SQUARE;
  waveTypePtr = squareWave;

  UpdateDisplayFrequency();

  lcd.setCursor(14, 0);
  lcd.print("Hz");
  lcd.setCursor(0, 1);
  lcd.print("Incr: 10 Hz");

}

Part 2:

/*****
  This function is used to set up the interrupt for the Timer1/Couner interrupt.
  Timer1 is a 16 bit timer and used here with no prescaling. This function also
  sets the state for the three ports used by the sketch.

  Paramter list:
    void
    
  Return value:
    void
*****/
void InitTimer()
{
  // Clear Timer/Counter Control Register for Interrupt 1, bytes A and B (TCCR1?)
  TCCR1A = 0;       // Clear TCCR1A/B registers
  TCCR1B = 0;
  TCNT1 = 0;        // Initialize counter to 0

  // Compare register for TIMER1: (16mHz / frequency - `) = 15999 = 0x3E7F / 2^6
  period = (CLOCKFREQUENCY / frequency - 1) / 64;
  OCR1A = period;
  
  // Timer/Counter Control Register for Interrupt 1 on register B
  TCCR1B |= (1 << WGM12);    // Mode 4, CTC--Clear Timer on Compare
  TCCR1B |= (1 << CS10);     // Clock Select Bit, no prescaling
  TIMSK1 |= (1 << OCIE1A);   // The value in OCR1A is used for compare
 
  DDRB = 0b11111111;         // PORTB all outputs
  DDRC = 0b00111111;         // PORTC all inputs except PC6, which is RESET ** DANGER, and PC7
  DDRD = 0b11111111;         // PORTD all outputs
}


/*****
  This function is used to set up the interrupt for the rotary encoder on pins
  2 (PCINT18) and 3 (PCINT19).
  
  Paramter list:
    void
    
  Return value:
    void
*****/
void InitEncoder()
{
  PCICR |= (1 << PCIE2);  // Turn on Port Control Interrupt Enable for Timer2 for 
                          // Pin Change Interrupt Control Register
  //PCMSK2 |= (1 << PCINT18) | (1 << PCINT19); // Look for Pin Control Interrupts on pins 2 and 3
  PCMSK2 |= (1 << PCINT18) | (1 << PCINT19) | (1 << PCINT20); // Look for Pin Control Interrupts 
                                                              // on pins 2, 3, and 4
}  

void loop() 
{ 
  buttonRead = digitalReadFast2(ENCODERSWITCH);
  
  if (buttonRead == LOW) {
    deBounce(ENCODERSWITCH);
    buttonRead = ENCODERSETTOSELECT;
    UpdateDisplayIncrement();
  }
   
  if (frequency != oldFrequency) {    // Did frequency cahnge?
    if (frequency > MAXFREQUENCY) {   // New one too hihg??
      frequency = MAXFREQUENCY;
    }
    if (frequency < MINFREQUENCY) {   // New one too low??
      frequency = MINFREQUENCY;
    }
    UpdateDisplayFrequency();
    oldFrequency = frequency;    // So we don't update unless needed
  }
  CheckFrequency();
}

/*****
  This function is used to update the LCD display for any changes in the increment index
  which is used to display the second LCD line.
  
  Parameter List:
    void
    
  Return value:
    void
*****/  
void UpdateDisplayIncrement()
{
  increIndex++;

  if (increIndex >= (MAXENCODERVALUES + MAXWAVETYPES)) { // Increment too far??
    increIndex = 0;                                      // Yep, start over.
  }
  
  lcd.setCursor(0, 1);                                 // Start second line
  if (increIndex < MAXENCODERVALUES) {
    lcd.print("Incr: ");
    increment = incrementTable[increIndex];
  } else {
    lcd.print("Wave: ");
    waveType = increIndex - MAXENCODERVALUES;
    switch (waveType) {
      case TRIANGLE:  
        waveTypePtr = triangle;
        break;
      case SAWTOOTH:
        waveTypePtr = sawTooth;
        break;
      case SINE:
        waveTypePtr = sineWave;
        break;
      case SQUARE:
        waveTypePtr = squareWave;
        break;
      default:
        waveTypePtr = sineWave;
        break;
    }
  }  
  lcd.print(waveForms[increIndex]);
}

/*****
  This function is used to move the new frequency to the interrupt routines
  
  Parameter List:
    void
    
  Return value:
    void
*****/  
void CheckFrequency()
{
  if (frequency == oldFrequency)  // Nothing's changed...
    return;
  // The clock speed / frequency less 1 clock cycle for update divided by
  // wavefrom bits, 2^6 = 64.
  period = (CLOCKFREQUENCY / frequency - 1) / 64;
  cli();
  OCR1A = period;
  sei();
}

/*****
  This function is used to update the LCD display for any changes in the frequency.
  
  Parameter List:
    void
    
  Return value:
    void
*****/
void UpdateDisplayFrequency()
{
  lcd.setCursor(0, 0);
  lcd.print("Freq:        ");            // Faster than clear
  lcd.setCursor(LCDCOLUMNOFFSET, 0);  
  lcd.print(frequency);
}

/*****
  This function is the Interrupt Service Routine (ISR) for interrupt 2. This uses the 
  rotary encoder to increment/decrement the frequency.
  
  Parameter List:
    N/A
    
  Return value:
    N/A
*****/
ISR(PCINT2_vect) 
{
  volatile unsigned char result = myEncoder.process(); // What did they do with encoder?
  
  switch (result) {
  case 0:      
    return;
  case DIR_CW:     // Turning Clockwise, going to higher frequencies
    frequency += increment;
    break;
  case DIR_CCW:    // Lower frequencies
    frequency -= increment;
    break;
  default:
    frequency = DEFAULTFREQ;    // Default
    break;
  }

}

/*****
  This function is the Interrupt Service Routine (ISR) for Timer1. This sets
  the waveform to be used and routes the value to PORTC.
  
  Parameter List:
    N/A
    
  Return value:
    N/A
*****/
ISR(TIMER1_COMPA_vect)//timer 1 interrupt
{

  if (index >= tableVals) {
    index = 0;
  }
  PORTC = (byte)( pgm_read_byte_near(waveTypePtr + index));  
  index++; 
}

/*****
  This function is a debouce routine used to let the affected switch to stablize.
  This is based on the function by Nick Gammon: http://www.gammon.com.au/forum/?id=11488
 
  Parameter List:
    int button      the switch to settle
    
  Return value:
    void
*****/
void deBounce(int buttonPin)
{
  unsigned long now = millis ();
  do
  {
    if (digitalRead(buttonPin) == LOW)  // on bounce, reset time-out
      now = millis ();
  } while (digitalRead(buttonPin) == LOW || (millis() - now) <= ENCODERDELAY);
}

I’m embarrassed to tell you how long I’ve been looking at this, as it’s my first use of Arduino interrupts.

The output is derived from A0 through A5 on PORTC using an R2R ladder. Taking the ladder values and using the tables gives the output wave form. All four look pretty good, but I cannot change the frequency above 2.380KHz. Like I said, I'm doing something dumb, but I simply cannot see it.

It wil be very rare for “CheckFrequency()” to do anything since it is called when frequency and oldFrequency are almost always equal.

 if (frequency != oldFrequency) {    // Did frequency cahnge?
  // DO SOME STUFF
    oldFrequency = frequency;    // So we don't update unless needed
  }
  CheckFrequency();
void CheckFrequency()
{
  if (frequency == oldFrequency)  // Nothing's changed...
    return;
}

Dooooo! Can't thank you enough, John, even though I feel like an idiot!