Porting from ATTINY85 to ATTINY24 @ PWM Generation

I´m using this code for generate 25KHZ PWM (for use with SANACE FAN with asks this frequency for speed control wire).
I need more pins for aditional controls. the 85 is limited. I was studing the datasheets and the registers / outputs seems the same. Only in different locations. PB1 on 85 is pin 6. In 24 is 3. The analog input remains A1, pin 7 in 85 that is 12 in 24.

When connecting a LED for monitoring as I did in 85, nothing is output.

Original Code:
/*

  • ATtiny85
  • -------u-------
  • RST - A0 - (D 5) --| 1 PB5 VCC 8 |-- +5V
  • | |
  • A3 - (D 3) --| 2 PB3 PB2 7 |-- (D 2) - A1 → 10K Potentiometer
  • | |
  • A2 - (D 4) --| 3 PB4 PB1 6 |-- (D 1) - PWM → Fan Blue wire
  • | |
  • Gnd —| 4 GND PB0 5 |-- (D 0) - PWM → Disabled

*/

// normal delay() won’t work anymore because we are changing Timer1 behavior
// Adds delay_ms and delay_us functions
#include <util/delay.h> // Adds delay_ms and delay_us functions

// Clock at 8mHz
#define F_CPU 8000000 // This is used by delay.h library

const int PWMPin = 1; // Only works with Pin 1(PB1)
const int PotPin = A1;

void setup()
{
pinMode(PWMPin, OUTPUT);
// Phase Correct PWM Mode, no Prescaler
// PWM on Pin 1(PB1), Pin 0(PB0) disabled
// 8Mhz / 160 / 2 = 25Khz
TCCR0A = _BV(COM0B1) | _BV(WGM00);
TCCR0B = _BV(WGM02) | _BV(CS00);
// Set TOP and initialize duty cycle to zero(0)
OCR0A = 160; // TOP - DO NOT CHANGE, SETS PWM PULSE RATE
OCR0B = 0; // duty cycle for Pin 1(PB1)
}

void loop()
{
int in, out;

in = analogRead(PotPin);
out = map(in, 0, 1023, 0, 160);
OCR0B = out;
_delay_ms(200);
}

Solved My problem.

Considering OC0B (timer, instead of port PB2) @ 85, @24 is PA7. Just changed the code to that pin in 24, and it worked!

I did something similar with a 30KHz Chassis Fan project a while back, but experimented with other Timer/Counter modes ( Phase Correct Mode, Fast PWM Mode, and Asynchronous Mode ). Here’s my code for ATtiny85…

//3D Printer Chassis Fan Project, version 1.0
//
//  Developed for ATtiny85 microcontroller on Digispark Development Board (16.5MHz, 5V)

// Thermister Sensor Defines
#define THERMISTORPIN      1       // which analog pin connected: using pin P2 (analog pin 1)
#define THERMISTORNOMINAL  10000   // resistance at 25 degrees C   
#define TEMPERATURENOMINAL 25      // temp. for nominal resistance (almost always 25 C)
#define NUMSAMPLES      5          // how many samples to take and average, more takes longer
                                   // The beta coefficient of the thermistor (usually 3000-4000)
#define BCOEFFICIENT    4050       // TT Series,10K ohm, TTC05103, beta = 4050
#define SERIESRESISTOR  10000      // the value of the 'other' resistor

// PWM MOSFET Fan Driver
#define MOSFET_PIN        1        // digital output pin for PWM, using pin P1 ( pin 1)
#define HYSTERESIS        1        // Fan Speed Change Temperature Hysteresis

// Define fan speed temperature ranges (degrees C)
#define MIN_TEMP_DETECT     -40    // if less than -40C or sensor short circuit
#define MAX_TEMP_OFF         23    // -40 thru 23C (-40F - 73F), fan off
#define MAX_TEMP_LOW         26    // 24C - 26C(73F - 79F), fan speed low
#define MAX_TEMP_MEDIUM_LOW  30    // 27C - 30C(79F - 86F), fan speed low-medium
#define MAX_TEMP_MEDIUM      35    // 31C - 35C(86F - 95F), fan speed medium
#define MAX_TEMP_MEDIUM_HIGH 40    // 36C - 40C(95F - 104F), fan speed medium-high
                                   // 40C+(104F+), fan speed high, or sensor open circuit

// Define fan speeds (PWM duty cycle values in percent)
#define LOW_SPEED         77       // 30% duty cycle (0.30 * 510/2 = 76.5)
#define MEDIUM_LOW_SPEED  89       // 35% duty cycle (0.35 * 510/2 = 89.25)
#define MEDIUM_SPEED      102      // 40% duty cycle (0.40 * 510/2 = 102)
#define MEDIUM_HIGH_SPEED 178      // 70% duty cycle (0.70 * 510/2 = 178.5)
#define HIGH_SPEED        230      // 90% duty cycle  (0.90 * 510/2 = 229.5)

// Variables
uint16_t samples[NUMSAMPLES];
int fanSpeed = 0;  // fan speed setting: 0 = off, 1 = low, 2 = low-medium, 3 = medium, 4 = medium-high 5 = high

// Function Prototypes
void runFan(int fanSpeed);
void blinkLED(int numBlinks);  // debug code


// Setup
void setup(void) {
  
  // thermister sensor setup
  analogReference(DEFAULT);

  // fan motor output pin setup
  pinMode(MOSFET_PIN, OUTPUT);  // PB1 (PortB, pin 1)
  // DDRB = _BV(DDB1);          // PB1 (PortB, pin 1) sets pin as output, equal to line above

  // Timer/Counter setup
  // Timer0 setup in "Phase Correct PWM Mode", 32.3KHz (16.5MHz/510) on pin 1 {PB1 (OC0B)} 
  TCCR0A = _BV(WGM00)    // Mode 1, PWM,Phase Correct,TOP = 0xFF, datasheet Table 11-5
                         // WGM0[2:0] = 0,0,1
      | _BV(COM0B1);     // OCOB clear up-cnt, set on down-cnt, datasheet Table 11-4
  TCCR0B = _BV(CS00);    // clk/no-prescaling, datasheet Table 11-6
  OCR0B = 0;      // set compare match value, initialize to OFF or 0% duty cycle
  //*/
  
  /*/ Timer0 for "Fast PWM Mode", 32.2KHz (0.25 * 16.5MHz/(8*256) on pin 1 {PB1 (OC0B) } 
  TCCR0B = _BV(CS01)               // clk/8 from prescaler, datasheet Table 11-6
                                   //    CS0[2:0] = 0,1,0
      | _BV(WGM02);                // Fast PWM Mode, OCRA = TOP
  TCCR0A = _BV(WGM01) | _BV(WGM00) // mode 7, datasheet Table 11-5
      | _BV(COM0B1)                // inverting mode, datasheet Table 11-3
      | _BV(COM0B0)   
      //| _BV(COM0A1)
      //| _BV(COM0A0)
      ; 
  OCR0A = 64;  // set TOP for ~30kHz (0.25 *256 = 64)
  OCR0B = 64;  // compare match value, initialized to OFF or 0% duty cycle
  //*/

  /*/ Set Timer1 for "Asynchronous Mode", 30KHz PWM frequency on pin 1 {PB1(OC1A)}
  TCCR1 = _BV(PWM1A)               // PWM A comparator enable,
     | _BV(COM1A1)                 // clear OC1A output line (pin1 on compare match) 
     | _BV(CS12) | _BV(CS10);      // T/C Prescaler bits set to T1CK/16
  OCR1C = 132;                     // set match value of PWM A comparator
  OCR1A = 0;                       // set pin 1 PWM off (0% duty cycle)
  PLLCSR = _BV(PLLE);              // enable timer1 PLL
  while (!( PLLCSR & _BV(PLOCK) ));// pause until PLL stabalizes and PLOCK bit is set (~100usec)
  PLLCSR = _BV(PCKE);              // Timer1 PCK clock enable (64MHz)
  //*/
  
  //*/
  runFan(4);   // fan medium-high
  delay(1500); // for 1/2 second period
  runFan(0);   // fan off
  //*/
}

void loop(void) {
 
  /*/ Timer0, "Phase Correct PWM Mode" ...
  //OCR0B = 230;  // 90% duty cycle (0.90 * 510/2 = 229.5)
  //delay(5000);
  //OCR0B = 178;  // 70% duty cycle (0.70 * 510/2 = 178.5)
  //delay(5000);
  OCR0B = 102;  // 40% duty cycle (0.40 * 510/2 = 102)
  delay(8000);
  OCR0B = 89;  // 35% duty cycle (0.35 * 510/2 = 89.25)
  delay(8000);
  OCR0B =  77;     // 30% duty cycle (0.30 * 510/2 = 76.5)
  delay(8000);
  OCR0B = 0;  // PWM off
  delay(5000);
  // end - Timer0 "Phase Correct PWM Mode" ...
  //*/
  
  /*/ Timer0, "Fast PWM Mode" ...
  // OCR0B = 32;  // 50% duty cycle (0.50 * 64 = 32)
  OCR0B = 48;     // 25% duty cycle {64 * (1 - 0.25) = 48}
  delay(5000);
  OCR0B = 64;     // PWM off
  delay(5000);
  // end - Timer0 "Fast PWM Mode" ...
  //*/
  
  /* // Timer1 "Asynchronous Mode" ...
  OCR1A = 66;  // 50% duty cycle
  delay(50000);
  OCR1A = 0;  // PWM off
  delay(50000);
  // end - Timer 1 "Asynchronous Mode" ...
  //*/

  // Chassis Fan Main Loop ...
  uint8_t i;
  float average; 
 
  // take N thermistor samples in a row, with a slight delay
  for (i=0; i< NUMSAMPLES; i++) {
   samples[i] = analogRead(THERMISTORPIN);
   delay(10);
  }
 
  // average all the samples out
  average = 0;
  for (i=0; i< NUMSAMPLES; i++) {
     average += samples[i];
  }
  average /= NUMSAMPLES;
 
  // convert the average sampled value (0-256) to average sensor resistance
  average = 1023 / average - 1;
  average = SERIESRESISTOR / average;

  //Serial.print("Thermistor resistance "); // breadboard test with UNO
  //Serial.println(average);                // breadboard test with UNO
 
  float steinhart;
  steinhart = average / THERMISTORNOMINAL;     // (R/Ro)
  steinhart = log(steinhart);                  // ln(R/Ro)
  steinhart /= BCOEFFICIENT;                   // 1/B * ln(R/Ro)
  steinhart += 1.0 / (TEMPERATURENOMINAL + 273.15); // + (1/To)
  steinhart = 1.0 / steinhart;                 // Invert
  steinhart -= 273.15;                         // convert to C
 
  delay(1000);

  // Adjust cooling fan speed by defined temperature ranges
  float temp = steinhart;
  int newFanSpeed;

  if ( temp >= MIN_TEMP_DETECT ){
      if ( (temp < MAX_TEMP_OFF + HYSTERESIS && fanSpeed == 0) || (temp < MAX_TEMP_OFF - HYSTERESIS && fanSpeed > 0) ) newFanSpeed = 0;  // fan off
      else if ( (temp < MAX_TEMP_LOW + HYSTERESIS && fanSpeed <= 1) || (temp < MAX_TEMP_LOW - HYSTERESIS && fanSpeed > 1) ) newFanSpeed = 1;  // low
      else if ( (temp < MAX_TEMP_MEDIUM_LOW + HYSTERESIS && fanSpeed <= 2) || (temp < MAX_TEMP_MEDIUM_LOW - HYSTERESIS && fanSpeed > 2) ) newFanSpeed = 2;  // low-medium
      else if ( (temp <= MAX_TEMP_MEDIUM + HYSTERESIS && fanSpeed <= 3) || (temp < MAX_TEMP_MEDIUM - HYSTERESIS && fanSpeed > 3) ) newFanSpeed = 3;  // medium
      else if ( (temp <= MAX_TEMP_MEDIUM_HIGH + HYSTERESIS && fanSpeed <= 4) || (temp < MAX_TEMP_MEDIUM_HIGH - HYSTERESIS && fanSpeed > 4) ) newFanSpeed = 4;  // medium-high
      else newFanSpeed = 5; // temp > MAX_MEDIUM_HIGH, sensor short-circuit
  }
  else newFanSpeed = 5; // temp < MIN_TEMP_DETECT, in case sensor is open-circuit
  
  if (newFanSpeed != fanSpeed){
    fanSpeed = newFanSpeed;
    runFan(fanSpeed);  // disable this line for debug code to blink LED to indicate fan speed
  }
  //blinkLED(fanSpeed);  // debug code, test fanSpeed changes with temperature using LED blink count
}

//  runFan function, Timer0 set to "Phase Correct PWM Mode"
void runFan(int fanSpeed){
  switch (fanSpeed){ 
    case 0: OCR0B = 0; // Fan off 
            break;
    case 1: OCR0B = MEDIUM_SPEED; // kickstart fan
            delay(100);
            OCR0B = LOW_SPEED;
            break;
    case 2: OCR0B = MEDIUM_LOW_SPEED;
            break;
    case 3: OCR0B = MEDIUM_SPEED;
            break;
    case 4: OCR0B = MEDIUM_HIGH_SPEED;
            break;
    default:OCR0B = HIGH_SPEED;
  }
}

ChassisFan.ino (9.04 KB)

1 Like

Timer0 is the same 8-bit timer with 2 PWM channels across all classic AVR chips (both attiny and atmega). It's used for millis() timekeeping, so millis() and delay() are broken when you take it over, hence the use of _delay_ms() from the avr util/delay.h library, instead of normal delay().

Timer1 is the same 16-bit timer with 2 PWM channels (used as 8-bit in Arduino land, unless you take over the timer) across most classic AVR chips, but the tiny85/45/25 and tiny861/461/261 have a weird high-speed timer in it's place (a very lovely timer, but rather confusing to use). Converting the 24 code to use timer1 would keep millis() usable, but with a '24 I think you might as well leave it as is if it works; with 2k of flash, you're not going to be adding additional functionality where you'd want the arduino timekeeping functions.