Attiny85 Fast PWM on Timer 1

I'm having some trouble actually getting fast PWM to work on Timer 1 (specifically, OCR1B). I understand that the way of doing so for Timer0 is different, and there are many different, well-done tutorials on Fast PWM on Timer0, but I need to use delay() and millis() in my sketch. I am trying to get a 50 kHZ PWM, which according to the data sheet means CS1[3:0] = 0100 and OCR1C = 159. Below is my setup() routine. However, when I do this, I still only get the ~490Hz PWM frequency, which is audible in my circuit. I am using the PWM to generate an LFO signal for driving an LED at the moment. I have been through the data sheet and a vast number of online threads about it, but most all of them deal with Timer0. What am I doing wrong here?

void setup() {

  //Define pin modes
  pinMode(pwm, OUTPUT);
  pinMode(rate, INPUT);
  pinMode(cvIn, INPUT);
  pinMode(cvSw, INPUT_PULLUP);
  pinMode(modeSwitch, INPUT);

  // Initialize pin states
  digitalWrite(cvIn, LOW);
  digitalWrite(pwm,LOW);
  analogWrite(pwm,128);

  lastTime = micros(); // Get an initial value

  // Set up timer/PWM items
  PLLCSR |= (1 << PLLE); //Enable PLL

  //Wait for PLL to lock
  while ((PLLCSR & (1<<PLOCK)) == 0x00)
    {
        // Do nothing until plock bit is set
    }

  // Enable clock source bit
  PLLCSR |= (1 << PCKE);

  // Set prescaler to PCK/8, turn off PWM1A, and set COM1A bits to match the COM1B bits due to attiny bug
  TCCR1 |= (1 << CTC1) | (0 << CS10) | (0 << CS11) | (1 << CS12) | (0 << CS13) | (0<<PWM1A) | (1 << COM1A1);
  
  // Set OCR1B compare value and OCR1C TOP value
  OCR1B = 128;
  OCR1C = 159;
  
  // Enable OCR1B output on PB4, configure compare mode and enable PWM B
  DDRB |= (1 << PB1);
  GTCCR |= (1 << COM1B0) | (1 << COM1B1);
  GTCCR |= (1 << PWM1B);

}

Attiny8 ??
Can you give a pointer to the datasheet?
I did not even know it existed.

I can't find any data on an ATtiny8. Did you mean the ATmega8? Did you mean the ATtiny84 or ATtiny85?

The current microchip catalog contains ATtiny804/806/807/814/816/817/824/826.

Sorry, missed the 5. Fixed the title. Attiny85 is what I'm working with.

Looks to me like there are no Phase Correct or Frequency Correct PWM modes so ALL 'PWM' is Fast PWM.

The Arduino core sets the available timers to PWM mode so you can't be sure the that registers you are writing to are initially set to 0. Try:
PLLCSR = (1 << PLLE); //Enable PLL

Similarly for TCCR1:

I just tried setting the registers you suggested, however I see no change. Still around 500 Hz for PWM frequency. It doesn't seem like it should be difficult, but I just can't seem to get this figured out...

Interesting question.
I played around a bit with this and bumped into getting a 6.3KHz PWM when doing it with Setup and Loop like this

void setup(){
  // Set up timer/PWM items
  PLLCSR |= (1 << PLLE); //Enable PLL

  //Wait for PLL to lock
  while ((PLLCSR & (1<<PLOCK)) == 0x00)
    {
        // Do nothing until plock bit is set
    }

  // Enable clock source bit
  PLLCSR |= (1 << PCKE);

 DDRB |= _BV(PB1);
  // put your setup code here, to run once:
  TCCR1 |= (1<<CS12) | (1<<PWM1A) | (1<<COM1A1); //start timer1, devide timer1 clock by 8, enable pwm, clear on match
  OCR1C = 159; // frequency for 50 KHz
  OCR1A = 10; // approx 8% pulsewidth
}


void loop() {
}

Then I suspected the core is still doing something with the timer1 in it's initialization, so I changed it slightly to avoid using the core initialization

And now it gives me a solid 50KHz PWM on PB1

int main (void){
  // Set up timer/PWM items
  PLLCSR |= (1 << PLLE); //Enable PLL

  //Wait for PLL to lock
  while ((PLLCSR & (1<<PLOCK)) == 0x00)
    {
        // Do nothing until plock bit is set
    }

  // Enable clock source bit
  PLLCSR |= (1 << PCKE);

 DDRB |= _BV(PB1);
  // put your setup code here, to run once:
  TCCR1 |= (1<<CS12) | (1<<PWM1A) | (1<<COM1A1); //start timer1, devide timer1 clock by 8, enable pwm, clear on match
  OCR1C = 159; // frequency for 50 KHz
  OCR1A = 10; // approx 8% pulsewidth
while (1);
}

And looking at your code I was unsure if clearing bits in a register can be done like
| (0 << CS10) | (0 << CS11)
But if you clear TCCR1 first it works with setup and loop.
Below code also gives me also 50KHz PWM

void setup(){
  // Set up timer/PWM items
  PLLCSR |= (1 << PLLE); //Enable PLL

  //Wait for PLL to lock
  while ((PLLCSR & (1<<PLOCK)) == 0x00)
    {
        // Do nothing until plock bit is set
    }

  // Enable clock source bit
  PLLCSR |= (1 << PCKE);

 DDRB |= _BV(PB1);
  // put your setup code here, to run once:
  TCCR1 = 0;
  TCCR1 |= (1<<CS12) | (1<<PWM1A) | (1<<COM1A1); //start timer1, devide timer1 clock by 8, enable pwm, clear on match
  OCR1C = 159; // frequency for 50 KHz
  OCR1A = 10; // approx 8% pulsewidth
}

void loop() {
}

Thanks so much! I used your code and then looked at things with my logic analyzer. I noticed that the frequency was 50 kHz whenever my specified duty cycle was equal to the OCR1A value (in my loop I was changing the duty cycle based on an external input and was updating duty cycle using analogWrite(pwmPin,dutyCycle), which was apparently my root issue). Using your setup suggestions and then setting OCR1A to my new duty cycle gives me 50 kHz throughout my entire capture. I'm going to do some more thorough testing, but things are looking up right now! Thanks!

OK, after further testing, it looks like it's all working! It turns out that the analogWrite was, in fact, making it so that the frequency was reverting to 500 Hz until the duty cycle again reached the OCR1A value that had been previously set, at which point it would briefly resume 50 kHz operation. Instead of using analogWrite, direct assignment to OCR1A in conjunction with the other settings results in desired behavior. Thanks!

In my opinion the Arduino API's are nice if you stay far from manipulating the registers directly. As soon as I have a need for programming the hardware features of a chip directly, I Try to stay away from Setup and Loop, accept that I need to all the timings and configurations myself and be selective about what Arduino commands to use.
Usually I then avoid commands that are relying on timekeeping or interrupts as they may be unpredictable if I don't stick to the Arduino API rules.

And following @johnwasser suggestion to use "=" instead of "|=" you don't need to clear TCCR1 first

TCCR1 = (1<<CS12) | (1<<PWM1A) | (1<<COM1A1); will suffice

This does NOT clear bits CS10, CS11, CS13, or PWM1A. The 'or=' operator (|=) will only SET bits, never CLEAR them.

How are the CKDEL fuses set on your ATtiny85?

The ATtiny15 compatibility mode is selected by writing the code “0011” to the CKSEL fuses (if any other code is written, the Timer/Counter1 is working in normal mode). When selected the ATtiny15 compatibility mode provides an ATtiny15 backward compatible prescaler and Timer/Counter. Furthermore, the clocking system has same clock frequencies as in ATtiny15.

You are very correct about not clearing bits. I had forgotten that I was using the | operator which only ors, which means 0's are meaningless. Thanks for the reminder!

Hoping this isn't 'stealing' the thread, but it seems related...

I have the fast pwm working on my breadboard but I have a use for the pwm and it's inverted output.

From what I can see in the data sheet, I can enable pin PB1 for OC1A output and for it's inverted (OC1A) on PB0.

I get the PB0 output but not the PB1 output... I've been struggling if I've mis-read it or have it incorrectly configured, as I suspect?

It's on a breadboard...

void setup() {

/*
 * 
 * PB0 ^OC0B  pin 5  DDB0   PWM_INV
 * PB1  OC0B   pin 6  DDB1   PWM
 * PB3  LED      pin 2  DDR3   LED
 * 
 */

  DDRB    = _BV(DDB0) | _BV(DDB1) | _BV(DDB3);
  TCCR0B = _BV(CS00);
  TCCR0A = _BV(WGM00) | _BV(WGM01) | _BV(COM0A1) | _BV(COM0A0);
  
  OCR0A = 128;

}

I can change the state of PB1 in the program and the data sheet advises that when it set up for pwm it automatically uses the pins... so something isn't right...

Suggestions?

:smiley_cat:

I am far from an expert, but I'm not certain that OC1A and OC1A inverted can be used that way at the same time. Of course, I may be totally wrong. Hopefully someone more knowledgeable comes along...

http://w8bh.net/avr/TrinketTimers.pdf

Thanks... it had what I needed, left out a register...

:smiley_cat:

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.