The AtMega128 has 4 timers. Each timers can generate PWM signals with the OCR (output compare) registers. The 16 bit timers (TMR1 and TMR3) have 3 OCR (sometimes called HW-PWM) channels, while the 8 bit timers (TMR0 and TMR2) have 1 OCR each. Unofortunately the output pin where OCR2's signal can be routed is conflicting (sharing the pin) with OC1C's pin, so only max 7 HW-PWM signals can be maintained.
There is a restriction: on the same TMR all the PWM signals run from the same timer, just the compare register is different, so the frequency must be the same (well, not necessarily, if some of the signals are 50%).
Sometimes we need more channels, and have slow devices like heaters, or whatever, that can easily be driven from software. There are 2 common ways to make PWM signal from software:
- from a fix-freq interrupt (like handler2k.c in our case) count every time, and change the signal if needed. Eg if you want 5 bits resolution, the 960Hz interrupt will emit a 960/32==30Hz signal. You can cheat it to 6 bits by oscillating between the closest possible values, eg. when 23.5 is commanded, once you switch off the signal at 23, the other time at 24.
- every once in a while (somewhere from mainloop) we integrate the signal: check what state the output was, and for how much time (reading a timer, which is available in every uC). We compare the integrated value to what the signal should be, and set the output accordingly. Simple. The only thing that needs some consideration is when the timer overflows, or the commanded signal is changed. These need to be taken care of by maintaining the remainder (the deviation from optimal level). This is in softpwm_act(). See softpwm.c in the firmware (see OnlineCourse/GettingStarted ).
- softpwm_act_var() is even more sophisticated. It assumes that the amplitude of the digital signal can change (like the supply voltage), but you know the value by measurement. The integration must consider the measured value (called the multiplier in the code). In this case you need to know if you're maintaing a commanded voltage-type signal, or a commanded power. If you care about power dissipated on a resistive load, multiplier should equal (the responsibility of the caller!) the square of the measured voltage signal.
- softelapsed() is not a pwm signal (despite living in softpwm.c), but used for fix freq handlers that have no problem being off a few msec. Eg. if you need an interrupt every 100msec to do sg., you might not care if it's executed with 3..4 msec jitter (max mainloop turnaround, it could be enhanced by a more complex priority scheduler). This way it need not be in interrupt. (another solution would be interrupt cascading).
See softpwm.c in the firmware (see OnlineCourse/GettingStarted ).