PWM resolution


I’m trying to use my Rabbit 3200 to create a 40kHz 50% duty cycle square wave to drive a 40kHz ultrasonic transducer. I have tried to use the hardware PWM but the closest I can get to my frequency is 43.2kHz. Is there some trick to getting closer to 40k? The next lowest frequency I can seen to get is 28.8kHz. While 43.2 will work, the transducer is meant to resonate at 40k, so I would get a stronger signal the closer to 40k i can get.



You might have to change your oscillator to get the frequency you want. The dividers are only going to get you so close. You should look into the CPU specs and change the oscillator to one that will give you the 40KHz you need. However, this may mess up the baud rates for serial.

Another possibility is to build an external oscillator that you can control the duty cycle on. You could do this with a Microchip microcontroller and send commands via a serial protocol. That way you can change the oscillator, but you still need to be able to use a suitable baud rate for serial.

I see. Changing the oscillator in my case in not an option. And I am not really looking to add a PIC to my project either. My assembly is far from par (I have a feeling that might change soon out of necessity). For now, I’m just going to use PWM, but may switch to interrupts and timers in the future.

Since you’re only interested in a 50% duty cycle you should be able to achieve better frequency control using a timer rather than the PWM output. Assuming your board has a 22.12MHz crystal, the clock doubler is enabled and your perclk is set to the main oscilator frequency, then perclk/2 will be 22.12MHz. Using timer B you should be able to generate an interrupt at aprox. 80KHz by loading the match register with the value 276 (or multiple of). Within the interrupt service routine you simply toggle an output pin and then reload the match register of timer B with the next multiple of 276.

The value 276 will give a period of 12.48us (80.145KHz) and the value 277 will give a period of 12.52 (79.856KHz). To be more precise alternating between the two values will give a frequency of 80KHz (40KHz at the output being toggled).

int divisor;
divisor = 276;

// within the ISR
divisor = divisor + 277 - (divisor & 1); // to alternate between the two values

Hope this helps.


Thanks, that is helpful. But, I noticed that your code snippets look to be in C. Wouldn’t I want to use an assembly interrupt service routine to make my pulses? Wouldn’t that be more precise? I hope I’m wrong.