I currently can set four PWM pins to around 31 kHz with the following code:
void setup()
{
TCCR1B = TCCR1B & B11111000 | B00000001; // Set PWM frequency for D9 & D10:
pinMode(pwmPin9, OUTPUT); // Sets the pin as output
pinMode(pwmPin10, OUTPUT); // Sets the pin as output
TCCR2B = TCCR2B & B11111000 | B00000001; // Set PWM for D3 & D11
pinMode(pwmPin3, OUTPUT); // Sets the pin as output
pinMode(pwmPin11, OUTPUT); // Sets the pin as output
}
I found this setup somewhere, but I don't know how I can set these four PWM pins to around 25 kHz instead. How is that possible?
-
3Do you understand how the AVR timers work?Ignacio Vazquez-Abrams– Ignacio Vazquez-Abrams2016年06月24日 01:04:33 +00:00Commented Jun 24, 2016 at 1:04
-
3See my page about timers.Nick Gammon– Nick Gammon ♦2016年06月24日 01:06:55 +00:00Commented Jun 24, 2016 at 1:06
-
1@IgnacioVazquez-Abrams Im not familiar and I need to set those four pins to around 25kHz at the beginning . I have hurry to finish a project and I would be glad any help. The code I have sets to 31kHz. Can I modify it to 25kHz? DC motors require that freq.user16307– user163072016年06月24日 01:08:57 +00:00Commented Jun 24, 2016 at 1:08
-
1@NickGammon Thanks but I really dont have enough time to study these at the moment. Could you provide me the code part to setup 25kHz. Im lostuser16307– user163072016年06月24日 01:12:01 +00:00Commented Jun 24, 2016 at 1:12
-
2I need to tune their exact rpm so their duty cycles will be slightly different. How about is it possible to set 2 pins only to 25kHz?user16307– user163072016年06月24日 07:26:58 +00:00Commented Jun 24, 2016 at 7:26
2 Answers 2
I am posting this second answer since I realized it is possible to have
4 PWM channels at 25 kHz with 161 steps on a single Arduino Uno. This
involves changing the main clock frequency to 8 MHz, which
has some side effects since the whole program will run half as fast. It
also involves reconfiguring the three timers, which means loosing the
Arduino timing functions (millis()
, micros()
, delay()
and
delayMicroseconds()
). If these trade-offs are acceptable, here is how
it goes:
void setup()
{
// Set the main system clock to 8 MHz.
noInterrupts();
CLKPR = _BV(CLKPCE); // enable change of the clock prescaler
CLKPR = _BV(CLKPS0); // divide frequency by 2
interrupts();
// Configure Timer 0 for phase correct PWM @ 25 kHz.
TCCR0A = 0; // undo the configuration done by...
TCCR0B = 0; // ...the Arduino core library
TCNT0 = 0; // reset timer
TCCR0A = _BV(COM0B1) // non-inverted PWM on ch. B
| _BV(WGM00); // mode 5: ph. correct PWM, TOP = OCR0A
TCCR0B = _BV(WGM02) // ditto
| _BV(CS00); // prescaler = 1
OCR0A = 160; // TOP = 160
// Same for Timer 1.
TCCR1A = 0;
TCCR1B = 0;
TCNT1 = 0;
TCCR1A = _BV(COM1A1) // non-inverted PWM on ch. A
| _BV(COM1B1) // same on ch. B
| _BV(WGM11); // mode 10: ph. correct PWM, TOP = ICR1
TCCR1B = _BV(WGM13) // ditto
| _BV(CS10); // prescaler = 1
ICR1 = 160;
// Same for Timer 2.
TCCR2A = 0;
TCCR2B = 0;
TCNT2 = 0;
TCCR2A = _BV(COM2B1) // non-inverted PWM on ch. B
| _BV(WGM20); // mode 5: ph. correct PWM, TOP = OCR2A
TCCR2B = _BV(WGM22) // ditto
| _BV(CS20); // prescaler = 1
OCR2A = 160;
}
void loop()
{
analogWrite( 3, 1); // duty cycle = 1/160
analogWrite( 5, 53); // ~ 1/3
analogWrite( 9, 107); // ~ 2/3
analogWrite(10, 159); // 159/160
}
Unlike the other answer, this does not need a modified version of
analogWrite()
: the standard one will work fine. Only care should be
taken that:
- The value written should be between 0 (meaning always LOW) and 160 (always HIGH), inclusive.
- Only pins 3, 5, 9 and 10 are available. Attempting to
analogWrite()
to pins 6 or 11 will not only fail to deliver a PWM output, it will also change the frequency on pin 5 or 3 respectively.
-
Been very long time and now I'm stuck with the same thing with Arduino Due which uses another processor. I would be glad if you hae any input here arduino.stackexchange.com/questions/67053/…user16307– user163072019年07月14日 16:34:25 +00:00Commented Jul 14, 2019 at 16:34
You can configure Timer 1 to cycle at 25 kHz in phase correct PWM mode, and use it's two outputs on pins 9 and 10 like so:
// PWM output @ 25 kHz, only on pins 9 and 10.
// Output value should be between 0 and 320, inclusive.
void analogWrite25k(int pin, int value)
{
switch (pin) {
case 9:
OCR1A = value;
break;
case 10:
OCR1B = value;
break;
default:
// no other pin will work
break;
}
}
void setup()
{
// Configure Timer 1 for PWM @ 25 kHz.
TCCR1A = 0; // undo the configuration done by...
TCCR1B = 0; // ...the Arduino core library
TCNT1 = 0; // reset timer
TCCR1A = _BV(COM1A1) // non-inverted PWM on ch. A
| _BV(COM1B1) // same on ch; B
| _BV(WGM11); // mode 10: ph. correct PWM, TOP = ICR1
TCCR1B = _BV(WGM13) // ditto
| _BV(CS10); // prescaler = 1
ICR1 = 320; // TOP = 320
// Set the PWM pins as output.
pinMode( 9, OUTPUT);
pinMode(10, OUTPUT);
}
void loop()
{
// Just an example:
analogWrite25k( 9, 110);
analogWrite25k(10, 210);
for (;;) ; // infinite loop
}
Writing a value of 0 with analogWrite25k()
means the pin will be
always LOW, whereas 320 means always HIGH. The regular analogWrite()
should almost work, but it will interpret 255 the same as 320 (i.e.
always HIGH).
This code assumes an Arduino Uno or similar board (ATmega168 or 328 @ 16 MHz). The method used here requires a 16-bit timer, and thus it uses Timer 1 as it's the only one available on the Uno; that's why only two outputs are available. The method could be adapted to other AVR-based boards with a 16-bit timer. As Gerben noted, that timer should have a corresponding ICRx register. There are 4 such timers on the Arduino Mega, each with 3 outputs.
-
1It might be useful to explain that this method only works for timer1, as the other timers don't have a
ICRx
register. At most, you can only have one PWM pin per timer, for timers 0 and 2.Gerben– Gerben2016年06月24日 13:04:19 +00:00Commented Jun 24, 2016 at 13:04 -
1@Gerben: Don't all the 16-bit timers have that register? At least on the Mega they do.Edgar Bonet– Edgar Bonet2016年06月24日 13:09:20 +00:00Commented Jun 24, 2016 at 13:09
-
1Yes, but only timer1 is 16-bit on the ATMega328. The rest are 8-bit. And the OP wants 4 PWM output, and your solution only provides 2. Or am I mistaken?Gerben– Gerben2016年06月24日 15:12:53 +00:00Commented Jun 24, 2016 at 15:12
-
1@Gerben: No, you are right. I just say that requiring ICRx seems to be redundant with requiring the timer to be 16-bits. For the Uno and Mega at least, not sure about other AVR-based Arduinos. The OP understand this only provides 2 PWM channels: see my comment on his question and his answer.Edgar Bonet– Edgar Bonet2016年06月24日 15:48:45 +00:00Commented Jun 24, 2016 at 15:48
-
2@techniche: 1. Works for me. Maybe you forgot to set
COM4C1
inTCCR4A
? 2. If that's not the problem, then read How do I ask a good question?, then update your question by including your full source code and clearly stating what you expect the program to do and what it does instead ("I dont see any success" is not considered a valid problem statement).Edgar Bonet– Edgar Bonet2017年09月15日 08:43:10 +00:00Commented Sep 15, 2017 at 8:43