PWM 16KHz 10bits for 2 lighting Output with Arduino Leonardo

Hello, I present here a challenge that I find myself with.
I am developing a circuit for the control of 2 led lamps (obviously connected through 2 mosfets). The Led, as is well known, has the disadvantage of flicking when it is dimmed, and it is due to the PWM frequency, that is why I wanted to raise the PWM frequency of timer 1 above 2 KHz, in my case I decided to do it at 4KHz. In turn, the number of steps to regulate each output from 0% to 100% is generally 255, but for lower intensities, the intensity jump becomes very visible, so I decided to increase the number of steps to 4096.

Everything is working very well and it is spectacular to see the result in large and bright lamps.

What we have discovered is that when the lamp is of high consumption, for example something less than 150-180W and the source is 200W (the brand does not matter and we have even tried with more powerful power supplies), the source itself generates a unpleasant constant 4KHz beep perfectly audible when the lamp is dimmed between approximately 30% and 80%. This is due to the fact that power supplies are not very friendly to interrupted consumptions, which is essentially what a PWM wave is.

The conclusion I have reached is that perhaps we should raise the working frequency of the PWMs and observe what happens in relation to the noise of the power supplies.

In this case, and to raise the frequency for example to 16KHz (somewhat less) we must sacrifice the number of regulation steps and lower it to 1024, which you will do in order to have no other choice, since it seems to me something important to be able to have the 4096 steps that I would have before.

The problem I find is that I do not understand very well how the registers work to configure the parameters of Timer 1.

I did the program with the help of this forum in another previous post and it worked great for me at 12bits 4KHz. I put the example code here below. Let's see if someone can help me and modify it so that it works at 10bits 16Khz.

Thank you in advance and I hope that all this I have told will help other people.

// 12-Bit 4KHz PWM on PIN9 and PIN10 using direct access to Timer1 in Arduino Leonardo
// Fade in and out 2x LED output
// Markos Ferro 2020/11/26

#define LEDValueWarm OCR1A  
#define LEDValueCool OCR1B

const int PWMMax = 4095;
int value = 1;
int direction = 1;

void setup() { 
  pinMode(9,OUTPUT);
  pinMode(10,OUTPUT);
  
  TCCR1A = (1 << COM1A1) |(1 << COM1B1) | (1 << WGM11); // Enable Fast PWM on OC1A (Pin 9 warm) and in OC1B (Pin 10 cool)
  TCCR1B = (1 << WGM13) | (1 << WGM12) | (1 << CS10);   // Mode 14 Fast PWM/ (TOP = ICR1), pre-scale = 1
    

  ICR1 = PWMMax;  //Set the TOP value for 12-bit PWM
  LEDValueCool = 0;      //Set the PWM output to full off.
  LEDValueWarm = 0;      //Set the PWM output to full off.
}

void loop() {
  //Fade the LED between 0 and PWMMax and then back to 0
  value += direction;
  if (value <=0){
    direction = 1;
  }
  else if (value >= PWMMax){
    direction = -1;
  }
  LEDValueWarm = value;
  LEDValueCool = value;
  delay(25);
}

I did the program with the help of this forum in another previous post and it worked great for me at 12bits 4KHz. I put the example code here below. Let's see if someone can help me and modify it so that it works at 10bits 16Khz.

Change the value of PWMMax from 4095 to 1023.

Frequency will change from 3.906 KHz to 15.625 KHz.

Timer 1 has a special 10-bit Fast PWM mode: WGM 7:

  // Clear the Timer1 Control Registers
  TCCR1A = 0;
  TCCR1B = 0;
  TCIE1 = 0;  

  // WGM 7: 10-bit Fast PWM, 
  TCCR1A |= _BV(WGM11) | _BV(WGM10); 
  TCCR1B |= _BV(WGM12);

  // Enable PWM on OC1A (Pin 9) and OC1B (Pin 10)
  TCCR1A |= _BV(COM1A1) | _BV(COM1B1) ; 

  // Start the clock with prescale of 1
  TCCR1B |=  _BV(CS10);

Thank you very much @cattledog and @johnwasser

I have followed the recommendations of both and that is how the code has remained.
I am going to test it very soon and will be able to tell if it works as I hope.

This opens up many expectations for me and from what you say, if by changing the maximum value to 1023, it could be understood that I can set the value I want, and the consequence will vary the frequency? or it would have to be n ^ x-1 like 255, 511, 1023, 2047, 4095 ... I mean, I could put 1000 so that if I send to PWM 1 it is 0.1%, 20 is 2%, 1000 be 100% for example?

Thanks !!

// 12-Bit 4KHz PWM on PIN9 and PIN10 using direct access to Timer1 in Arduino Leonardo
// Fade in and out 2x LED output
// Markos Ferro 2020/11/26

#define LEDValueWarm OCR1A  
#define LEDValueCool OCR1B

const int PWMMax = 1023;  // *****  thanks to cattledog  ********

int value = 1;
int direction = 1;

void setup() { 
  pinMode(9,OUTPUT);
  pinMode(10,OUTPUT);
  
                      // *****  thanks to johnwasser  ********
  // Clear the Timer1 Control Registers
  TCCR1A = 0;
  TCCR1B = 0;
  TCIE1 = 0;  

  // WGM 7: 10-bit Fast PWM, 
  TCCR1A |= _BV(WGM11) | _BV(WGM10); 
  TCCR1B |= _BV(WGM12);

  // Enable PWM on OC1A (Pin 9) and OC1B (Pin 10)
  TCCR1A |= _BV(COM1A1) | _BV(COM1B1) ; 

  // Start the clock with prescale of 1
  TCCR1B |=  _BV(CS10);
  
  

  ICR1 = PWMMax;  //Set the TOP value for 12-bit PWM
  LEDValueCool = 0;      //Set the PWM output to full off.
  LEDValueWarm = 0;      //Set the PWM output to full off.
}

void loop() {
  //Fade the LED between 0 and PWMMax and then back to 0
  value += direction;
  if (value <=0){
    direction = 1;
  }
  else if (value >= PWMMax){
    direction = -1;
  }
  LEDValueWarm = value;
  LEDValueCool = value;
  delay(25);
}

This opens up many expectations for me and from what you say, if by changing the maximum value to 1023, it could be understood that I can set the value I want, and the consequence will vary the frequency? or it would have to be n ^ x-1 like 255, 511, 1023, 2047, 4095 ... I mean, I could put 1000 so that if I send to PWM 1 it is 0.1%, 20 is 2%, 1000 be 100% for example?

You understand correctly. In the Fast PWM modes the timer counts up to a TOP value and then resets to 0. The TOP value and the prescaler determine the frequency. For the higher frequencies you are going to use a prescaler of 1, and each timer count is .0625 microseconds. There is no requirement that the TOP value be a power of 2. There are Timer modes for Fast PWM where the TOP is 255, or 511, or 1023, or ICR1 or OCR1A. When using OCR1A as TOP, you sacrifice one of the output pins.

When you use one of the modes with a fixed top value, like 511 or 1023 (Mode 7 which John has pointed out) The value of ICR1 in not used.

MarkosFerro:
I mean, I could put 1000 so that if I send to PWM 1 it is 0.1%, 20 is 2%, 1000 be 100% for example?

Well, you'd use 999 to get 1000 PWM options: 0 to 999. That would give you an exact 16 kHz output.

johnwasser:
Timer 1 has a special 10-bit Fast PWM mode: WGM 7:

johnwasser,
when I compile your code as I wrote it in my post I get an error: 'TCIE1' was not declared in this scope
on the other hand, I think I have not understood it, with this configuration, would it be or not possible to change the TOP to 1000 or another value? I don't know very well what WGM 7 is and that's how you indicate how I have to program it to be able to chose the TOP value.

Thanks a lot

cattledog:
You understand correctly. In the Fast PWM modes the timer counts up to a TOP value and then resets to 0. The TOP value and the prescaler determine the frequency. For the higher frequencies you are going to use a prescaler of 1, and each timer count is .0625 microseconds. There is no requirement that the TOP value be a power of 2. There are Timer modes for Fast PWM where the TOP is 255, or 511, or 1023, or ICR1 or OCR1A. When using OCR1A as TOP, you sacrifice one of the output pins.

When you use one of the modes with a fixed top value, like 511 or 1023 (Mode 7 which John has pointed out) The value of ICR1 in not used.

This has been very informative, it opens my eyes a lot and gives me infinite possibilities to know the concept. Now it remains for me to clarify that it is WGM 7, although I understand that it can be a preconfiguration to something like that? I don't know if WGM 7 is incompatible with my purpose.

As the code that I wrote above gives me an error when compiling and not and I do not know if it is compatible with my purpose, I do not know if my previous code would be valid or how it would go well

Code with WGM 7 (which now gives error...):

// 10-Bit 16KHz PWM on PIN9 and PIN10 using direct access to Timer1 in Arduino Leonardo
// Fade in and out 2x LED output
// Markos Ferro 2020/11/26

#define LEDValueWarm OCR1A  
#define LEDValueCool OCR1B

const int PWMMax = 1000;  // *****  thanks to cattledog  ********

int value = 1;
int direction = 1;

void setup() { 
  pinMode(9,OUTPUT);
  pinMode(10,OUTPUT);
  
                      // *****  thanks to johnwasser  ********
  // Clear the Timer1 Control Registers
  TCCR1A = 0;
  TCCR1B = 0;
  TCIE1 = 0;  

  // WGM 7: 10-bit Fast PWM, 
  TCCR1A |= _BV(WGM11) | _BV(WGM10); 
  TCCR1B |= _BV(WGM12);

  // Enable PWM on OC1A (Pin 9) and OC1B (Pin 10)
  TCCR1A |= _BV(COM1A1) | _BV(COM1B1) ; 

  // Start the clock with prescale of 1
  TCCR1B |=  _BV(CS10);
  
  

  ICR1 = PWMMax;  //Set the TOP value for 12-bit PWM
  LEDValueCool = 0;      //Set the PWM output to full off.
  LEDValueWarm = 0;      //Set the PWM output to full off.
}

void loop() {
  //Fade the LED between 0 and PWMMax and then back to 0
  value += direction;
  if (value <=0){
    direction = 1;
  }
  else if (value >= PWMMax){
    direction = -1;
  }
  LEDValueWarm = value;
  LEDValueCool = value;
  delay(25);
}

Previous code:

//  PWM on PIN9 and PIN10 using direct access to Timer1 in Arduino Leonardo
// Fade in and out 2x LED output
// Markos Ferro 2020/11/26

#define LEDValueWarm OCR1A  
#define LEDValueCool OCR1B

const int PWMMax = 1000;
int value = 1;
int direction = 1;

void setup() { 
  pinMode(9,OUTPUT);
  pinMode(10,OUTPUT);
  
  TCCR1A = (1 << COM1A1) |(1 << COM1B1) | (1 << WGM11); // Enable Fast PWM on OC1A (Pin 9 warm) and in OC1B (Pin 10 cool)
  TCCR1B = (1 << WGM13) | (1 << WGM12) | (1 << CS10);   // Mode 14 Fast PWM/ (TOP = ICR1), pre-scale = 1
    

  ICR1 = PWMMax;  //Set the TOP value for 12-bit PWM
  LEDValueCool = 0;      //Set the PWM output to full off.
  LEDValueWarm = 0;      //Set the PWM output to full off.
}

void loop() {
  //Fade the LED between 0 and PWMMax and then back to 0
  value += direction;
  if (value <=0){
    direction = 1;
  }
  else if (value >= PWMMax){
    direction = -1;
  }
  LEDValueWarm = value;
  LEDValueCool = value;
  delay(25);
}

Thanks a lot

johnwasser:
Well, you'd use 999 to get 1000 PWM options: 0 to 999. That would give you an exact 16 kHz output.

Yes, it is true, and I had already thought about it. In this case the important thing is to have the value from 0 to 1000, knowing that there are 1001 positions, to be able to graphically show its direct equivalent in percentage (for example 0 = 0.0%, 101 = 10.1%, 605 = 60.6%, 1000 = 100.0 %). it does not matter that the frequency is a little lower accordingly.

Thank you very much for all the help!

I don't know if WGM 7 is incompatible with my purpose.

Mode 7 has a fixed top value of 1023. For your more flexible purposes, you are better with what you previously were using, Mode 14 Fast PWM to ICR1, where ICR1 is a changeable TOP value.

The second version of code ("Previous code") with a TOP value of 1000 shown in reply #7 should serve you well.

TCIE1 = 0;

I don't know what John had in mind, but this may be a typo. I'm not aware of a Timer1 register or register bit with that name.

Oops! Sorry. I was thinking TIMSK1 (Timer/counter Interrupt MaSK register 1) and remembered it as the TCIE1 (Timer/Counter Interrupt Enable register 1). Just wanted to make sure all the Timer1 interrupts were disabled.