Help me please do settings Timer1 for PWM out on NANO

Hi everybody.
I am new to Arduino programming.
I'm trying to get a square wave signal with a variable frequency of 1-200Hz on pin OC1A with Timer1.
I found several threads here on the forum and blogs on the Internet, but the device does not work.
The device is a PWM fan emulator. And I want to send a RPM sensor signal to the motherboard.
I doesn't have oscilloscopes.
I wrote the following example code for setting the timer and pin out.
I ask knowledgeable people to look at this and check where I went wrong.
I myself can not do it yet, I do not understand how it is.
The rest of the code for this question is not important and was not written here.

void setup() {                
  pinMode(hall_mb_out, OUTPUT); 
  pinMode(pwm_mb_in, INPUT); 
  pinMode(pwm_fan_out, OUTPUT); 
  pinMode(hall_fan_in, INPUT_PULLUP); 
  
   cli(); // stop interrupts
  TCCR1A = 0; // set entire TCCR1A register to 0
  TCCR1B = 0; // same for TCCR1B
  TCNT1  = 0; // initialize counter value to 0
  // set compare match register for 31 Hz increments
  OCR1A = 64515; // = 16000000 / (8 * 31) - 1 (must be <65536)
  // turn on CTC mode
  TCCR1B |= (1 << WGM12);
  // Set CS12, CS11 and CS10 bits for 8 prescaler
  TCCR1B |= (0 << CS12) | (1 << CS11) | (0 << CS10);
  // enable timer compare interrupt
  TIMSK1 |= (1 << OCIE1A);
  sei(); // allow interrupts
  prescaler = 8;
}

void loop(){
  if(normalizedtachoFreq >= 31){
    prescaler = 8; 
    TCCR1B |= (0 << CS12) | (1 << CS11) | (0 << CS10);
  }
  else if(21 <= normalizedtachoFreq && normalizedtachoFreq < 31){
    prescaler = 64; 
    TCCR1B |= (0 << CS12) | (1 << CS11) | (1 << CS10);
  }
  else if(normalizedtachoFreq < 21){
    prescaler = 256;
    TCCR1B |= (1 << CS12) | (0 << CS11) | (0 << CS10);
  }
  uint16_t val = 16000000UL/ (normalizedtachoFreq * prescaler) ;
  OCR1A = val;
}

You have a some errors in the code:

  1. To change the prescaler or PWM mode you MUST to stop timer first.

  2. If you don't clear the CS10-CS12 bits first, this line don't setup the prescaler correctly:

because the bit CS12 won't be cleared if it was set.

The common practice to change the timer setting is to clear TCCR1B and TCCR1A first and than setup it from zero state.

1 Like

Thank you!!
I will test it.

Basically you can't do this by just changing the pre-scaler alone because the range of frequencies you get will double with each pre-scaler interval.

And if you want to get a gradual change in frequency the output will not be square.

1 Like

1. From Fig-1, decide which Waveform Mode you wish to select.
Let us choose Mode-4 (Perfect Square Wave Generator with variable frequency).


Figure-1:

Characteristics:
(1) Frequency range is determined by the following equation:
fOC1A = clckSys/2N(1+OCR1A)
clkSys = 16 MHz
N = Clock division factor: 1, 8, 64, 256, 1024
OCR1A = 16-bit Input Output Compare Register of TC1 for Ch-A
fOC1A = 0.20 Hz to 8 MHz

(2) Parameters for 5 Hz PWM Signal (For example, Fig-2):
N = 256
OCR1A = 6249

(3) Example Output Square Wave (Fig-2)


Figure-2:

(4) Changing frequency by changing the content of OCR1A Register (Fig-3).


Figure-3:

2. Tested Sketch (Low frequency 1 - 20 Hz) can be monitored by LED1 of Fig-3.

void setup()
{
  Serial.begin(9600);
  pinMode(9, OUTPUT);
  TCCR1A = 0x00;
  TCCR1B = 0x00;

  bitClear(TCCR1B, WGM13); //Mode-4
  bitSet(TCCR1B, WGM12);
  bitClear(TCCR1A, WGM11);
  bitClear(TCCR1A, WGM10);

  bitClear(TCCR1A, COM1A1); //toggle output pin on compare match
  bitSet(TCCR1A, COM1A0);

  OCR1A = 6249;  //sets the initial frequency at 5 Hz
  TCNT1 = 0x0000;

  bitSet(TCCR1B, CS12); //start TC1 with N = 256
  bitClear(TCCR1B, CS11);
  bitClear(TCCR1B, CS10);
}

void loop()
{
  unsigned int y = analogRead(A0);
  OCR1A = map(y, 0, 1023, 0, 65535);      //rotate Pot of Fig-3 to change frequency
  delay(1000);  //test interval
}
1 Like

Why?
You can use compare match toogle mode with MAX counter value = OCR1A or ICR1 (modes 4 and 8-14), that give you gradual change in frequency with square output.

1 Like

Perfect square wave can only be found in Mode-4 (CTC) operation. In Mode-12 (CTC), the duty cycle can be varied by OCR1A Register.

1 Like

If you setup COM1A0/COM1A1 to toogle channel A output, duty will be always 50% and not affected by OCR1A value.

1 Like

In which Mode -- 4 or 12?

OP may exercise the following sketch of Mode-12 (CTC) and the setup (Fig-1) to see if duty cycle could be varied by changing the value of OCR1A.

void setup()
{
  Serial.begin(9600);
  pinMode(9, OUTPUT);   //Ch-A
  pinMode(10, OUTPUT); //Ch-B
  //-Mode-12
  TCCR1A = 0x00;  //reset register
  TCCR1B = 0x00;
  TCCR1B |= bit(WGM13) | bit(WGM12); //
  TCCR1A |= bit(COM1A0) | bit(COM1B0); //toggle at DPin-9;
  TCNT1 = 0x0000;
  ICR1 = 31249; //1 Hz
  OCR1A = 104;// 1666 us
  OCR1B = 5; //8333 us
 
  TCCR1B |= bit(CS12); //CTC mode; clkTC1 = 16 MHz/256 = 62500
}

void loop()
{
  ICR1 = map(analogRead(A1), 0, 1023, 0, 65535); //frequecy change
  OCR1A = map(analogRead(A0), 0, 1023, 0, 65535); //duty cycle change
  delay(1000);
}

pwmAppx
Figure-1:

1 Like

Thanks everyone for these tips!!!
I'll try them out and post here.

in all modes where TOP value is set by OCR1A or ICR1

The duty will always 50% because the state of the pin will be TOOGLED once on the period - from HIGH to LOW in first cycle and than from LOW to HIGH in the next cycle.
So the duration between the toogles will be always equal to full timer cycle and freq of signal will be half of timer freq independent from OCR1A value.

Also, FYI, your frequency is going to have to be twice the frequency you are trying to mimic. PWM fans send a signal twice per revolution on the tachometer line. So, if you are trying to mimic a 1000RPM fan, you would need a signal that is 2000Hz. You'll also want to be careful because the motherboard is going to have a pull-up to 12v on the Tach line of a 4 wire PWM fan.
There's a spec for those fans you might want to look at.

The fan is actually an open drain collector. That means it's an inverted PWM signal or an inverse square wave. You don't need to bring the voltage up to simulate a pulse. You need to bring it down.

Then I am confused to interpret the meaning of "Update of OCR1x at" of Fig-1 in Mode-12 (CTC). When ICR1 sets the frequency, then what is the use of OCR1A Register.

Figure-1:

I have a 6000 rpm / 5V power fan.
And my calculations are: 6000 * 2 = 12000 pulses / min = 12000 / 60 pulses / second = 200 pulses / second = 200Hz.
Am I wrong in these calculations?

The background to the creation of this emulator is as follows: I bought a new Dell Optiplex Micro computer, and unfortunately the BIOS does not have a fan speed setting. And this CPU fan often picks up high speed and makes a lot of noise. At the same time, the processor temperature never rises above 65 degrees Celsius, and most often 45-55. I want to deceive the BIOS with the help of Arduino, and lower the cooler speed. Even if the processor heats up to 80-85 degrees, then this will suit me.
The electrical connections have already been made by me. With a simple repetition of the signals, the fan operates normally with the following code:

  digitalWrite(hall_mb_out,digitalRead(hall_fan_in));
  digitalWrite(pwm_fan_out,digitalRead(pwm_mb_in));

OCR1A and (if applicable) OCR1B controls the phase of the generated signals

Interesting! I will do the experiment. Which one is to be taken as reference -- OC1A or OC1B?

There is no predefined "referemce point". The phase is relative one from the other.
Take the test, you will see.

1 Like

If you are not tied to writing your own code then this may be helpful: PWM on any I/O pin

1 Like