I am looking for a way to create an adjustable PWM controller capable of modulation frequency from 0-100 (more preferably 0-1000) Hz, duty cycle from 0-100%, and able to accept a voltage range of 5-30V DC. I have looked through Sparkfun and Adafruit for product or PWM shields capable of this and I have yet to find one. Some servo shields can be programmed down to 50Hz and arduino itself can be slowed to like 30Hz using timer0 and timer1 changes. However none of these options make it easy to vary frequency and none of them can accept much more than 6V.

I have searched far and wide and found a few interesting things:

1) http://www.instructables.com/id/The-ultimate-PWM-driver-for-many-applications/

This instructable shows an analog un-sensored PWM driver. It would get the job done here however, I need a way to read my Frequency and Duty Cycle

2) https://www.electronicsblog.net/arduino-frequency-counterduty-cycle-meter/

Here there is a way using pulsein() and other arduino functions to read both frequency and duty cycle as seen above. But then this is also assuming you're PWM voltage is 5V. So maybe you use a 5V regulator on the signal into the arduino? Would something that simple work or would it skew the signal and prevent accurate measurements?

Obviously, if anyone has a better idea I'm open to it. I've been searching for a solution for some time. The trouble really seems to be the low frequency PWM request.


The ATmega328 in the Arduino Uno has a 16-bit timer (Timer/Counter 1) which can do PWM. It has a prescaler that can divide by 1024. The lowest frequency that it can generate is about 0.12 Hz:

16MHz / 1024 / 65535 / 2 = 0.1192 Hz

We change the PWM frequency by changing the "top" value. For higher frequencies, lower the top value, and/or pick a smaller prescaler.

(And, really, what does 0 Hz PWM even mean?!? :-)

The duty cycle can vary from 0-100%, with the resolution changing with the specific frequency you generate.

As for the high-voltage... use a motor driver, or darlington, or hexfet, or whatever...

I'm confused as to why you're reading the frequency. With the 16MHz crystal, you set it and you get what you asked for (within the crystal tolerances, temperature, etc.)

At least, that's how I think it works. Good luck!

  • So for 440Hz at 25% on pins 9&10, you might choose a fast PWM with Wave Generation Mode 14, 16e6 /1/440 = 36363 TOP with /1 prescaling, so: ICR1=36363 ; TCCR1A=0b10100010; TCCR1B=0b00011001; TCCR1C=0; OCR1A=36363/4 – Dave X Nov 23 '15 at 16:11
  • The PWM Waveform Generation Modes with a settable TOP value (e.g. set with the OCR1A or ICR1) are the ones that can achieve these low, adjustable frequencies. Set the Waveform Generation Mode mode and prescaler with the TCCR1* registers and then the duty cycles would be modified by adjusting OCR?B. For frequencies below the threshhold the timers can handle, one could switch over to toggling based on elapsed millis() and handle frequencies down to 1 cycle per 50 days. – Dave X Nov 23 '15 at 16:29
  • @Mike I appreciate the help. However I truly intend on the PWM frequency to be variable. And yes 0Hz is improper but my point is to make it very low. Using prescale factor like shown in this link you can only reach 30Hz using prescale factors. If you also modulate your TOP values I suppose you get any 16-bit value from 1-65535 in resolution. The 2 indicate difference between the two outputs?? link – Austin McShan Nov 25 '15 at 15:58
  • @Mike Either way this looks like I'll be able to modulate frequency easy enough...Now just the high voltage. I'll have to think more about that. But I definitely think you solved my frequency problem. – Austin McShan Nov 25 '15 at 15:58

Your question is so close to my own project, for a second I thought it was something I posted awhile back, go figure. I don't expect you to gain from my answer, me coming along to find your question some 2 years, 5 months later. It may be of help to others who pass by this way in the future. I am currently working on the exact same variable duty cycle controller and will offer up my findings.

The following programming code is the result of apx. 5 months of study into the Arduino programming as well as a long failing attempt at getting control through a series of 555 timer chip builds. I shit canned the 555 timer chip & Darlington transistor method. I thought it was too crude, and all 'engineering' schematics never allowed me to have anything more than a 50% duty cycle control. I wanted control over all PWM variables. Today I will be purchasing a metal case, or making one that can hold apx. 5-6 potentiometers allowing me to adjust every possible variable within the PWM domain of parameters (there are more variables than what I have in this code below). I will give you some details in the code first, that will give you some idea of the obsessively precise control I'm demanding from my PWM project:

Getting into the heart of the variable HIGH/LOW control. Be sure to read everything you can to understand fully why these functions are needed. Also, know that with 10 bits the 1023 value allows a resolution of .0043 volt increments (really nice):

void loop() 
Potentiometer1 = analogRead(PotHzHIGH); 
Potentiometer1 = map(Potentiometer1, 0, 1023, 1023, 0); 
Potentiometer1 = constrain(Potentiometer1, 500, 1023);

Potentiometer2 = analogRead(PotHzLOW); 
Potentiometer2 = map(Potentiometer2, 0, 1023, 1023, 0); 
Potentiometer2 = constrain(Potentiometer2, 500, 1023);

Potentiometer3 = analogRead(Pot5Volts);     
Potentiometer3 = map(Potentiometer3, 0, 1023, 1023, 1023); //Hold at 12Volts 
Potentiometer3 = constrain(Potentiometer3, 0, 1023); 
//Potentiometer3 can either be locked at 12volts with the map function, or 
//you can use a Potentiometer to varry the voltage. I illistrate the 
//constant at 12 volts with the map function - low/high both set to  
//1023 & 1023

digitalWrite(PotHzHIGH, HIGH); 
//turn it ON delayMicroseconds(PotHzHIGH); 
//Keep ON for Potentiometer1=PotHzHIGH Microseconds 
//1000 microseconds = 1millisecond; 1000ms = 1second

digitalWrite(PotHzLOW, LOW); 
//turn it OFF delayMicroseconds(PotHzLOW); //Keep OFF for 
Potentiometer2=PotHzLOW Microseconds 
//1000 microseconds = 1millisecond; 1000ms = 1second; 
//Largest value within the delayMicroseconds function: 16383microseconds 
//Largest value when converted to milliseconds is 16.383ms, or axp. 1/20th of    
//...a second


//other things to understand about the above program. It is possible to 
//have the LOW written with: delayMicroseconds(PotHzLOW+100); 
//or delayMicroseconds(PotHzLOW*1.1); 
//You can add an additional potentiometer to be the multiplier of the LOW. 
//these added controls prevent the LOW from ever being less than the high. 
//this can come in handy if you wish to work within duty cycles that are 
//between 1% & 50%. It is of interest to me because I am dealing with 
//high frequencies.


Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.