
PWM by "Bit-Banging"
Several questions have come into Tech Support asking how to generate programmable periodic pulses that cannot be handled by either the PWM or PPM systems of the Rabbit. In this month's article I want to cover a technique to create periodic pulses known as "bit-banging." I have also included a sample program to help demonstrate my explanation. The details of the code are described in the comments of the attached sample program so I will not repeat them here but I will discuss the methods being used.
First, you always want to do some amount of hardware initialization. For this sample I chose to use parallel port A bit 0 for the output pulse. The definitions which precede the main program have macros that allow you to change the port and bit which are used. However, there are other initializations required which are indicated in the comments at the top of the program. These definitions allow you to change the bit and port without having to edit the Interrupt Service Routine (ISR).
The function CalcTimers is used to calculate the two Timer A values needed to achieve the requested interrupt period. The algorithm first determines the CPU clock speed via one of two methods:
- For "earlier" versions of Dynamic C, it uses the value of freq_divider.
- For the more recent versions of DC 10, there is a function which returns the CPU frequency: get_cpu_frequency
Both of these methods use the 32kHz RTC oscillator to measure the main CPU clock oscillator during the processor boot procedure.
The requested interrupt frequency is developed by dividing the CPU clock by an integer value. This divisor is calculated and tested to determine that it is achievable. Since the maximum divisor achievable with two eight-bit timers in cascade is 65536, the interrupt frequency must be higher than CPUclock/65536. There are many frequencies which cannot be generated exactly so the algorithm finds the pair of values which yields the smallest amount of error. It will exit if it detects that a pair of values will generate the frequency exactly.
You also want to be careful of specifying a frequency that is "too high;" the higher the frequency – the higher the load on the processor. The shortest execution path is about 100 clock cycles. So your frequency had better be <CPUfrequency/100. You can calculate the approximate CPU load with this formula:
Load % = (100 * Interrupt Frequency/CPUfrequency) * 100
For example: if the interrupt is set to 40kHz and the CPU clock is 70MHz, the CPU load is about 5.7%. Note that if the interrupt frequency is 700kHz, the processor load is 100%.
The ISR is relatively short and straight forward. It uses its own working copies of two values which must be set by the user to define the pulse period and width in units of the interrupt period. The period counter is decremented with each interrupt until it reaches 0. At that time, both the period and pulse width counters are reloaded and the output pulse is started. If the decremented period counter is not 0, the ISR jumps to code which decrements the pulse width counter. When this count reaches 0, the pulse is turned off.
If your requirement is to output a series of pulses of varying widths, it would not be too difficult to modify the ISR so that it reads the pulse width from an array instead of a scalar. You could also implement a pulse counter so that the pulses stop after some number of periods. The easiest way to do that is to disable the ISR from within itself by setting the IPL to 0. The main program could monitor this value to determine when the pulse stream has completed. For Rabbit 2000 and 3000 systems, TACR is write-only so you would have to implement a flag. On the Rabbit 4000 and up, TACR is read-write so you can use the register value itself.
Even though "bit-banging" is more processor intensive than using the built-in hardware, it is more flexible. For those applications where neither the PWM nor PPM systems can develop the required pulses, the above technique with the attached program should prove helpful.
- Larry C.
Larry Cicchinelli is Rabbit’s Technical Support Manager. He has 30 years of embedded experience, and is considered one of the foremost authorities on Rabbit products. Larry and his staff offer comprehensive technical support to Rabbit customers.
Submit your questions for Larry via email at AskLarry@rabbit.com
Read more Ask Larry Answers
