1
\$\begingroup\$

I'm doing a project that can draw an analog clock on the XY mode of an oscilloscope using the Atmega88-20PU with the default clock frequency of 1MHz. To display the image I use a PWM signal applied to a low-pass filter (working as a DAC) for channels 1 and 2 of the oscilloscope.

The problem I'm facing is that, even though the PWM signals are being generated correctly, the output of the low-pass filter isn't working correctly. Just for testing purposes, I tried drawing a simple rectangle (4 vertices) but it only displays 1 vertex.

My intuition tells me that the problem might be that the clock frequency is too low, or that the values of the RC low-pass filter are not the most appropriate (R = 4.7KOhm, C = 100nF).

After some research, I found another similar project where the creator used the same RC values, but the code was running on a 16MHz µC (https://www.youtube.com/watch?v=nlCADL5_g-I).

Here is some of my code:

void Clock::display() volatile
{
    for(uint8_t i = 0; i < 4; i++)
    {
        display_point(points[i]);
        _delay_us(point_delay);
    }
}

void Clock::display_point(Point point) volatile
{
    OCR2A = point.x;
    OCR2B = point.y;
}

void Clock::calc_points() volatile
{
    points = (Point *) malloc(4 * sizeof(Point));
    points[0].x = 20;
    points[0].y = 20;
    points[1].x = 100;
    points[1].y = 20;
    points[2].x = 100;
    points[2].y = 200;
    points[3].x = 20;
    points[3].y = 200;
}

clock.display() is called every frame of the main loop.

clock.calc_points() is called every 1 second (on an ISR).

EDIT #1: I have already slowly tested the code and the PWMs generate correctly accordingly to the points I tell it to run. Therefore the problem must only be on the low-pass filter.

EDIT #2: The frequency of the PWM waves is f_clk / 256 = 3.9 kHz. What should be the value of the cutoff frequency of the filter?

EDIT #3: I added a 20 MHz crystal oscillator (PWM frequency of 78 kHz) and changed the filter to have a cutoff frequency of about 200 Hz and got the following output (channel 1 shows one of the PWM outputs and channel 2 shows the filter of that same PWM):

enter image description here

\$\endgroup\$
12
  • 2
    \$\begingroup\$ Use the scope in normal time domain mode to examine your output. Is it smooth or does it show pulses? Dropping the clock rate by a factor of 16 likely would require a change in filter components. \$\endgroup\$ Dec 16 '20 at 16:21
  • 1
    \$\begingroup\$ Program a steady mid range output, remove the filter (or simply look at the pin upstream of the resistor) and make sure you see PWM pulses. Then replace the filter, program it to slowly ramp and see that it does. Basically, don't jump to the end goal, test each step along the way. For that matter, what proof do you have that your code is even running? \$\endgroup\$ Dec 16 '20 at 16:24
  • 1
    \$\begingroup\$ You could try making something that cycled between different settings a few seconds apart and using a voltmeter. Also order a cheapie USB logic analyzer, they're very useful, in this case you could only use it on the raw unfiltered output, but you should be able to debug most of your system that way - if you really wanted to you could use the sigrok python API and decode PWM to live XY plotting on your computer ;-) \$\endgroup\$ Dec 16 '20 at 16:29
  • 2
    \$\begingroup\$ Why use dynamic allocation for points? If that is called every second, where is it freed \$\endgroup\$
    – Colin
    Dec 16 '20 at 16:33
  • 1
    \$\begingroup\$ @frizd For something small and necessary I'd probably just make the array a member, rather than having a pointer to it, if you need to keep it that way though, allocate the space once in your constructor and delete it in your destructor, use new [] and delete [] too if you're writing c++ \$\endgroup\$
    – Colin
    Dec 16 '20 at 16:48
0
\$\begingroup\$

I don't know for sure what's wrong with your setup, but I'll tell you the things I can see from your description.

The cutoff of your RC filter is around 340Hz. As a simple RC filter, it has an attenuation of 20 dB per decade - that is, at ten times the frequency you have 1/10 the amplitude.

Your PWM frequency (3.9kHz) is about ten times the cutoff, so you can expect the pulses to be attenuated from 5V peak to peak to about 500 millivolts peak to peak.

For the midrange PWM values, you'll have the 500 mV as zig-zags in your display - big zig-zags. At high and low PWM duty cycles it will be smoother.

Since your filter cutoff is 340 Hz, if you change the output any more often than 340 times per second the output won't be able to follow. At 340 times a second the output will be noticeably wrong. As you go faster, the distortion will be greater. At some point, the filter output will appear to not change anymore in response to the changing PWM.

You probably need a much higher PWM frequency and a higher cutoff for your filter. You want a very wide separation between the cutoff and the PWM frequency.

You want the cutoff high enough that you can quickly draw your display, but low enough that it can effectively remove the PWM frequency.

You can get familiar with RC low pass filters using this online calculator.


Make sure you use DC coupling on your scope inputs.


I've been working on a project that also uses a filtered PWM signal as a DAC.

The cutoff I used is about half of yours (159Hz,) and the PWM frequency is over twice as high (10kHz.)

I still have a pretty jagged output from the filter:

enter image description here

That's 1 volt per division, so the jaggies are about 200 to 300 millivolts peak to peak.

That is better than the results you will get with your PWM frequency and low pass filter.


The picture you posted of your filtered PWM looks wrong. It looks to me almost like you have a high pass filter instead of a low pass filter.

You would wire a PWM low pass like this:

enter image description here

The pin marked "D9" is the PWM signal, VBias is the filtered output.

Check your circuit and see what you've really got.

\$\endgroup\$
5
  • \$\begingroup\$ I have a 20 MHz crystal oscillator that I can use as the clock. I tried using it once but I accidentally set the wrong fuses, which blocked me from programming that chip again. So, which fuses should I set on my Atmega88P? \$\endgroup\$
    – frizd
    Dec 23 '20 at 0:11
  • 1
    \$\begingroup\$ I don't know anything about fuses on the ATmega chips. \$\endgroup\$
    – JRE
    Dec 23 '20 at 6:34
  • \$\begingroup\$ I updated my original question to have a picture of the output. It doesn't have a ripple effect like yours. \$\endgroup\$
    – frizd
    Dec 23 '20 at 20:27
  • \$\begingroup\$ Do you actually get any DC out of it? It looks to me like you've got a high pass instead of a low pass. \$\endgroup\$
    – JRE
    Dec 23 '20 at 20:51
  • \$\begingroup\$ The output of the filter moves up and down a lot, so I think I am getting DC out of it. It is a low pass filter. \$\endgroup\$
    – frizd
    Dec 23 '20 at 23:47

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.