November 27, 2013:
Revised: v1.0

Compact PWM Test Oscillator

A tiny 8-pin Atmel ATtiny15 microcontroller generates a wide range of pulse-width modulated (PWM) waveforms. A compact battery-powered unit, this versatile PWM oscillator also includes a sweep frequency capability. It can also output a 1kHz sinusoidal audio test tone.

Compact PWM oscillator


My 8051 chips were just too slow. Snail-pace, really.

This was the conclusion I reached when I was looking for a suitable microcontroller for some new projects. I had noticed that competing PIC and Atmel chips were easily able to outperform any of the 8051 devices I was using at that stage although, for the most part, the limited performance of the 8051 had not previously prevented me achieving any of my design goals.

All of that changed when I started to look at projects requiring generating high speed and high quality digital audio waveforms.

Microcontroller Evaluation

In order to assess the relative merits of these other microprocessors, I decided to try designing a simple project with a low cost device from each of these two microcontroller families. That way, I could evaluate one of the devices in each family at minimal cost, I hoped, and try out the respective programming tools. This way, I could get a first-hand opinion on some of the claimed benefits of each processor as well as any limitations.

This project was the one I used to build to test the AVR family. It uses a low cost ATtiny15 8-pin chip. This device has just 1k of flash memory for programming, 32 8-bit registers, and 64 bytes of EEPROM. This tiny device package was another reason for my search for a new microprocessor – I could not find any 8051 devices in an 8-pin package.

I should warn you right now that the ATtiny15 is no longer a current device in Atmel’s AVR family. It’s been superseded by a variety of other chips, such as the ATtiny25 and ATtiny13. However, this chip is actually still readily available from many suppliers, so I felt it was worthwhile describing this design here. Also, if necessary, the software can be easily adapted to suit, say, the ATtiny25.

Design Goals

Aside from using this design to look more closely at the AVR family, I only had a few objectives in mind at first for this PWM test generator. I had been experimenting with some simple switch-mode power supplies, and testing some circuits would be much easier if I had a basic square-wave generator, something that could generate a TTL-like 5VDC square-wave frequency from a few hundred Hertz up to perhaps 500 kHz. I also wanted to be able to adjust the duty cycle of the waveform.

side view of output connector and power switchI wasn’t too concerned about the exact frequency – I didn’t need a digital readout to the nearest Hz, for example – nor was I particularly interested in the exact duty cycle. Again, just having a continuous adjustment range, ideally from 0 to 100%, I thought would be sufficient.

The PWM generator to be used on my test bench. I don’t know about your situation, but I find I never have enough power supplies for everything. So, from the outset, I wanted this to be battery powered, and as compact as I could reasonably make it.

The Design Goals Expand

As the project developed, and as I became more familiar with writing assembly code for the ATtiny15, I started to add a few more features. I began by just getting the generator to output a simple 1000 Hz square-wave. I then got the duty cycle code working.

Once I got that initial software working correctly, I decided to add a series of frequency ranges. To do that, I needed a switch to select a number of ranges. It had to be compact, however. I happened to have a thin BCD switch in my box of parts and so that got used. This provides a binary coded output on four pins, or five if you include the common pin.

Once I had the generator operating over a series of ranges, I still had some positions free on the switch. The next feature I decided to try to add was a frequency sweep where the generator would automatically step (or “sweep") across a range of frequencies, and then repeat this sweep of frequencies until another range was selected. I couldn’t think of an application for it at the time, but I thought it was a feature worth adding.
Three AAA batteries power the PWM generator
Finally, and just before calling a halt to all of this, I decided to try to generate a sine wave using a much higher PWM waveform. The addition of a very simple RC filtr will remove the much higher PWM “carrier” frequency, leaving the much lower audio frequency sine wave.

At that point, I stopped writing more code. The range selection switch actually has ten positions (0 - 9) so the last two are free for any extra features you'd like to add. Of course, you'll have to write the code. I'm too busy with my day job, and my other projects in the evening and weekends.

In the end, what I had developed in a compact box was a simple variable PWM test generator with an audio tone and sweep frequency generator features.

The Schematic

The diagram below shows the final circuit. As you can see, there are very few parts.

PWM test oscillator circuit diagram
Figure 1 : The circuit diagram of the ATtiny15 PWM test generator

Three AAA cells provide a nominal 4.5V supply for the oscillator. A small yellow LED is used to remind me that the power is turned on. (I could have added a power-down feature, I guess…) The current drain of the ATtiny15 is very low, about 3mA, and the entire generator draws less than 15 mA in use. I’ve had it on my bench for over three years and I’ve still not needed to change the batteries.

Two potentiometers are used to input the required frequency and duty cycle. The resulting variable DC voltage on each pot's wiper ranges from zero to the supply rail. These voltages go into two of the ATtiny15’s 10-bit analog to decimal converter (ADC) inputs. Just note for the moment that one pot is a LOG (logarithmic) type while the other is a LIN (linear) type. I’ll come back to that in a moment.

The PWM generator’s output comes from a pin which is connected to the ATtiny15’s internal PWM generator. This feeds a pair of small signal transistors which buffer this before the signal is fed to the generator output connector via a switch.
Inside the pwm generator
A resistor and capacitor (R3 and C4) is also connected to the output of the buffer and is connected to the other side of the output switch. This RC pair forms a very simple RC PWM filter used with the range #7 to give the 1kHz sine-wave audio output. IThe output switch is just a PCB jumper in my prototype because at the time, I didn’t have a suitable switch at the time. I’ve never got around to changing it.

If you look really carefully at the board in this photo, you may see a small diode next to the yellow LED that is not shown on the schematic. This is a 5.6V zener diode that was used to limit the voltage from a DC-DC inverter generating 5V from a single AA cell. I didn't like the final result, so the inverter was removed, but that diode, which serves no purpose now (but affects nothing either) was left in place.


The key features of the ATtiny15 used in this design are the ADC inputs, which allow the analog values of the variable control pots to be determined with 10-bit accuracy, and the powerful high speed PWM oscillator.

The main core of the software simply reads the value of the control pots and uses them to program the PWM oscillator registers. The required tuning range is determined by reading the value of the three pins connected to the BCD switch.

The PWM oscillator response is near-logarithmic for a linear selection of PWM register settings. Using a log pot for the frequency control gives a roughly linear frequency output from the generator. This is the reason behind the log and linear potentiometers.

The fully commented source code, for those wanting to change or add features, and the hex file, generated by the assembler for programming the ATtiny15, are both provided for download further down this page.


As the photos here show, the generator was built on a small prototyping board. The battery holder was hot-glued to the underside of the board.

A small wooden box was made from a thin strip of hard wood, and threaded nylon spacers were glued with epoxy into each inner corner. Top and bottom covers were then made for the box, and these are screwed into place using bolts on each corner screwed into the threaded spacers. The entire enclosure measures about 80(w) x 50(d) x 25mm(h).

coverThe LED power indicator is mounted down on the board. In order to see it with the covers in place, a 3mm hole was drilled in the top cover, and a 10mm length of 3mm diameter clear plastic rod was pushed into the hole, the top flush with the top of the lid. This sits directly over the power LED. Its yellow glow shows clearly on the top cover.

The range selector switch fits through a hole in the woodwork on one side, and the power switch and output connect fit through other similar holes. A label showing the details of each range (See one of the photos towards the top of this page) was covered in clear plastic and glued to the bottom cover. Another label was made for the controls on another side of the box.

While these labels may be of limited use, I have provided the artwork in the download section below.


Some Thoughts about the AVR Family

Since much of this design was done in order to see if the AVR family would work for me, I guess I should add just a few thoughts about that here. If it's a topic of little interest, feel free to stop reading here.

In practice, I found the ATtiny15 microcontroller was generally easy to program and it was certainly fast, as promised. That's also proven to be true as I've gone on to use other devices in the family. (I began programming the ATtiny15 with a homemade programmer, and then moved on later to PonyProg which uses an RS232 interface with the PC. More recently, I've made this USB programmer which works very well) It was also very nice not to have to worry about adding a crystal to the ATtiny microprocessor to get it to run, as you must with every 8051 chip even when the application doesn't need that level of clock accuracy. So, the internal Atmel clock is a nice feature.

The software provided by Atmel was also easy to install, and it worked much as expected. The datasheet was clear and, once I read it carefully in a few areas, the operation of the chip became clear. I tend to print out a copy of the datasheet and put it into a ring binder. It makes it easier for me to add notes on specific features as I go, or add highlights and underlining to remind myself about certain issues. However, now that I’m using some of the current 8-pin chips, like the ATtiny85, I do find it amusing to find the printed datasheet, which runs to hundreds of pages in length, weight hundred of time the weight of the chip itself. As I glance across at my bench on one side today, I see the tiny chip over there, and the huge pile of datasheet paper in the folder on the shelf opposite. Huh.

I did find some odd things about a few AVR instructions, and a few basic instructions missing. Why can’t you invert a bit in one instruction anywhere in memory or on an output pin? Weird. Also, coming from the 8051 background where flags can be set and tested so easily, yet doing the same with the AVR chips is just a continual pain in the, um, well…

I also encountered a few issues initially with the programmable fuses – These are some internal separately programmed settings which provide some basic operating settings for the chip. I just had to think some more about these factors, and documenting these is also important.

Generally, however, I don’t see any reason to go back to using the 8051 family, especially now I’ve got some higher level compliers for the AVR family. That eases my complaints about missing instructions, and speeds up my programming.

The PIC tests? Well, I did build a little binary clock designed by Roman Black to test one of the current PIC chips. Of course, I designed my own tiny PCB for it and made a very compact hardwood box for it. Then I messed about with the software to add automatic nighttime dimming. Despite this limited exposure to PICs, I found the entire PIC experience at assembly code level so primitive, and just so awful, that I’ve never bothered with a PIC chip again. Using high level compilers (and I don’t include C in that category!) would protect you from such things, of course. However, for me, my periodic descent into assembly code on a number of my projects, in some cases just for one or two critical routines, makes it impossible to consider the PIC family.

"Your mileage may vary", as they say. But one day, that PIC chip is going to be ripped out of that clock and replaced with something more modern. Maybe an Intel 4040.


Want to go back to the main page? Click here to return directly.