ATTiny85 PWM frequency selection

A bit of background:
I'm looking to control PWM computer cooling fans directly from an ATTiny85, the fans want a ~15-25khz PWM signal. The currant involved in minimal.

At default settings with Coding Badly's excellent arduino port I got 500hz from pin0 and 1khz from pin1.
If I use " TCCR0B = TCCR0B & 0b11111101 | 0x01;" from the Arduino PWM frequency howto, I can get 1khz from pin0 and 2khz from pin1.

However from reading about AVRs in general it appears that far higher is possible, just not doable through the arduino environment.
Is this correct? Am I totally out to lunch?

Any help/advice would be greatly appreciated.
(Sidenote: The 328p's ~32khz output works, but is horrendously inefficient in controlling fans, they don't like it much)

Bobnova:
I'm looking to control PWM computer cooling fans directly from an ATTiny85, the fans want a ~15-25khz PWM signal.

Sounds good.

The currant involved in minimal.

How minimal? It's extremely rare to find a motor that can be driven directly from a microprocessor. Or do the cooling fans have a "control line" separate from the power?

At default settings with Coding Badly's excellent arduino port

Thanks! :smiley:

I got 500hz from pin0 and 1khz from pin1.

Are you running the processor at 16 MHz?

However from reading about AVRs in general it appears that far higher is possible

The ATtiny85 is capable of generating a 64 MHz PWM signal. So "far higher" is a good description.

just not doable through the arduino environment.

The Tiny Core includes a "veneer" that makes changing the timers a bit easier. Confirm the processor speed and I'll try to help.

If I use " TCCR0B = TCCR0B & 0b11111101 | 0x01;" from the Arduino PWM frequency howto, I can get 1khz from pin0 and 2khz from pin1.

I don't know if the ATTiny85 is similar to the 328, but if so that line needs to be:

TCCR0B = TCCR0B & 0b11111000 | 0b001 ; // set to divide-by-1 prescale

(The 328 timer prescaler fields are 3 bits wide)

Minimal currant like <=15ma, the fans are four pin type things: +12v, ground, tach output (you pull it high, it grounds that wire twice per revolution) and PWM input.
The PWM input goes to a controller on the fan circuitboard that figures out when to switch the mosfets to get the fan to run at the speed you selected via PWM. It's a pretty cool setup, though they cost more than the three pin non-PWM fans.

Processor speed wise I've tried both 1mhz and 8mhz, I don't have any spare 16mhz crystals/resonators. I should get some.

I'll try that line in my attiny85 and report back in with what happens.

Thanks for the input!

(Sidenote: I got 15.4khz PWM out of a 328p by using the internal oscillator at 8mhz and running the timer at full speed, but attiny85's are cheaper and easier to find)

EDIT:
That worked beautifully! I now have a pair of 15.6khz PWM outputs from my attiny85.
Thank you very much!

Bobnova:
EDIT:
That worked beautifully! I now have a pair of 15.6khz PWM outputs from my attiny85.
Thank you very much!

how did you get it to work ? :slight_smile:

how did you get it to work ? :slight_smile:

Adding the "TCCR0B = TCCR0B & 0b11111000 | 0b001 ;" line to Setup() results in 32.35kHz on Digispark ATtiny85 module (pin 0 as well as pin 1):

The ATtiny85 is capable of generating a 64 MHz PWM signal. So "far higher" is a good description.

How to configure ATtiny85 for MHz PWM frequencies?

Found "Setting High Speed PWM on ATTiny85":

Adapted for Arduino IDE I measured up to 676.7kHz with "OCR1C = 123;".

123*676.7
83234.1
127*648
82296
141*590.8
83302.8
159*524.4
83379.6

This is from comment of sketch below, not sure why I measured 1.3 times as much:

    // Set PWM TOP value - counter will count and reset
    //  after reaching this value
    //          OCR1C
    // 400Khz   159     
    // 450khz   141
    // 500khz   127
400*159
63600
450*141
63450
127*500
63500

Hermann.

//#include <avr/io.h>
//#include <avr/interrupt.h>


void setup()
{
    PORTB = 0;      //Reset values on port B

    // After setting up the timer counter, 
    // set the direction of the ports to output
    DDRB |= (1<<PB1) | (1<<PB0); // Set the direction of PB1 to an output

    // PLLCSR - PLL control and status register:
    // PLL is a clock multiplier - multiplies system     8Mhz by 8 to 64Mhz
    // PLL is enabled when:PLLE bit is enabled, 
    // CKSEL fuse is programmed to 0001.  This clock is 
    //   switched off in sleep modes!
    PLLCSR |= (1<<PLLE);    // PLL enable

    // Wait until the PLOCK bit is enabled 
    //  before allowing the PCK to be enabled
    // WaitForPLOCK();
    // unsigned int i = 0;


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

    PLLCSR |= (1<<PCKE); // Enable asynchronous mode, sets PWM clock source


    TCCR1 =
            (1<<CTC1) |  //enable pwm
            (1<<PWM1A) | // set source to pck
            (1<<(CS10)) | // clear the pin when match with ocr1x
            (1<<COM1A1);
    GTCCR =   (1<<PWM1B) | (1<<COM1B1);


    // Set PWM TOP value - counter will count and reset
    //  after reaching this value
    //          OCR1C
    // 400Khz   159     
    // 450khz   141
    // 500khz   127
    OCR1C = 159;


    //enable Timer1 OVF interrupt
    TIMSK = (1<<OCIE1A) | (1<<TOIE1);   

    sei();

    //This should set the duty cycle to about 75%
    OCR1A = 120;
}

void loop() {
}

Some measurements are strange, but I was able to measure 10MHz PWM frequency with these changes:

...
    OCR1C = 5;
...
    OCR1A = 3;
...

Nice clean 10MHz PWM signal from 1$ ATtiny85. 10MHz is maximum that I was able to measure with 24Msps logic analyzer. Will try with 400Msps analyzer when back at home.

I had problems determining average frequency for measurement with 400Msps logic analyzer DSView GUI.

Therefore I used 100Msps logic analyzer. Salea Logic 1.2.10 GUI allows to determine average frequency as one of many options for measurements.

Maximal frequency is achieved with

...
    OCR1C = 2;
...
    OCR1A = 1;
...

Interestingly maximal PWM frequency is different depending on "board" used in Arduino IDE to program ATtiny85:

  • 22.12MHz for "Digispark (Default - 16.5mhz)"
  • 28.27MHz for "Digispark (16mhz - No USB)"

Hermann.

Using your code I can reach the high frequency as I want, but I cannot modulate the Duty Cycle by an analog road for example, it is fixed at the value selected in the code. How could I do that? it seems that other functions are disabled with this code.

HermannSW:
Found "Setting High Speed PWM on ATTiny85":
c - Setting high speed PWM on ATtiny85 - Stack Overflow

Adapted for Arduino IDE I measured up to 676.7kHz with "OCR1C = 123;".

123*676.7

83234.1
127648
82296
141
590.8
83302.8
159*524.4
83379.6




This is from comment of sketch below, not sure why I measured 1.3 times as much:


// Set PWM TOP value - counter will count and reset
    //  after reaching this value
    //          OCR1C
    // 400Khz  159   
    // 450khz  141
    // 500khz  127





400159
63600
450
141
63450
127*500
63500




Hermann.


//#include <avr/io.h>
//#include <avr/interrupt.h>

void setup()
{
    PORTB = 0;      //Reset values on port B

// After setting up the timer counter,
    // set the direction of the ports to output
    DDRB |= (1<<PB1) | (1<<PB0); // Set the direction of PB1 to an output

// PLLCSR - PLL control and status register:
    // PLL is a clock multiplier - multiplies system    8Mhz by 8 to 64Mhz
    // PLL is enabled when:PLLE bit is enabled,
    // CKSEL fuse is programmed to 0001.  This clock is
    //  switched off in sleep modes!
    PLLCSR |= (1<<PLLE);    // PLL enable

// Wait until the PLOCK bit is enabled
    //  before allowing the PCK to be enabled
    // WaitForPLOCK();
    // unsigned int i = 0;

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

PLLCSR |= (1<<PCKE); // Enable asynchronous mode, sets PWM clock source

TCCR1 =
            (1<<CTC1) |  //enable pwm
            (1<<PWM1A) | // set source to pck
            (1<<(CS10)) | // clear the pin when match with ocr1x
            (1<<COM1A1);
    GTCCR =  (1<<PWM1B) | (1<<COM1B1);

// Set PWM TOP value - counter will count and reset
    //  after reaching this value
    //          OCR1C
    // 400Khz  159   
    // 450khz  141
    // 500khz  127
    OCR1C = 159;

//enable Timer1 OVF interrupt
    TIMSK = (1<<OCIE1A) | (1<<TOIE1);

sei();

//This should set the duty cycle to about 75%
    OCR1A = 120;
}

void loop() {
}

claudiocfv:
Using your code I can reach the high frequency as I want, but I cannot modulate the Duty Cycle by an analog road for example, it is fixed at the value selected in the code. How could I do that? it seems that other functions are disabled with this code.

Another way of doing this, is using an external XOR or XNOR gate.
With adjusting the delay on the 2nd output, you can generate a PWM signal.

As a bonus, the XOR will DOUBLE the frequency as well.