Pulse width modulation (PWM) is a commonly used technique for generating precisely timed, repetitive digital waveforms. This can be done with dedicated digital ICs, but in our case we will use the Freescale (Motorola) HCS12, which has a built in PWM unit which can generate up to 8 channels of PWM output, at up to 6 MHz. While we can generate any variety of digital waveforms on the HCS12 by periodically writing to the various ports, using timer output capture, etc., these all require direct CPU time for each output pulse. A great advantage of PWM is that once the registers are set up and enabled, the PWM waveforms are generated without any CPU overhead, so we can then use valuable CPU time for other tasks.
PWM waveforms are "on-off" digital waveforms that are specified by two values: the period and the duty cycle, as shown below.
In the above drawing, the period would be a unit of time such as 10 msec. The duty cycle is the proportion of time that the pulse is "on" or "high", and is expressed as a percentage:
duty cycle = 100% * (pulse time high)/ (total pulse period)
In waveforms above, both "a" and "b" have duty cycles of 50% since they are "on" fifty percent of the time. However, their periods are different, with waveform "b" having half the period (twice the frequency) of waveform "a". Waveforms "c" and "d" both have the same periods, but waveform "c" has a duty cycle of 10%, and waveform "d" has a duty cycle of 80%. As a pulse approaches 100% "on" time, the duty cycle approaches 100%, and as it approaches being always "off", the duty cycle approaches 0%.
The PWM waveforms can be left aligned (default) or center-aligned. We use left-aligned in this demo and as shown above. Center-aligned PWM waveforms are fully described in the PWM Manual(S12PWM_8B8CV1.pdf)
The PWM output from the Dragon12 board is from the PP0 - PP7 pins, corresponding to the PWM0 - PWM7 outputs from the PWM unit. The output of each of these pins is taken relative to the chip ground, such as the VSS2 pin (see Dragon12 Schematic 1 for the pin-outs, ground locations, etc dragon12_1_revE.jpg). So, if you generate a logical 0-1-0-1-... sequence for PWM Channel 0 (PWM0) and measure the voltage between pins PP0 and VSS2, you will see that it alternates between 0 and about 5 V.
While PWM can be used to generate digital waveforms, such as used for stepping a stepper motor or controlling other digital circuits, PWM output can also generate analog voltages, albeit at low power. For instance, if you connect a low-current LED (plus resistor of course) to a PWM output, you will find that the intensity increases as the duty cycle goes from 0% to 100%, assuming that you have a relatively fast period, say, 10 msec. As another example, you could use PWM output to control a DC motor. You first hook up the high-frequency PWM output to an H-bridge or some other type of motor driver that can take small input currents and can generate large output currents (there is not enough current from the HCS12 PWM pins to drive a motor). As you increase the duty cycle form the PWM, the motor speeds up in direct proportion to the duty cycle. You can even use PWM to generate audio signals if you use some low-pass filtering to get rid of the high-frequency harmonics present in the PWM waveforms.
In the HCS12, we control the PWM waveforms by writing to different registers. This is discussed in more detail in the DBug12 section below. Note that these are 8 bit registers, giving 8 bit resolution. If finer resolution is needed, PWM registers can be concatenated to give 16 bit resolution. The PWM manual for the PWM module contains full details (S12PWM_8B8CV1.pdf).
*** It will be ESSENTIAL for you to work with the HCS12 manual in this demo to get register addresses, register descriptions, etc. You can use the online manual noted just above, or you can use one of the printed manuals in the lab.***
The main PWM registers we need to use in this demo are noted below:
    * PWME - The PWM Enable Register is at address $00A0 and is used to enable the various PWM channels. Once we enable a PWM channel, it will begin running. To enable a channel, write a binary "1" to its bit position. For instance, if we type ">mm a0 01" at the DBug12 prompt ($01 = %0000 0001), this will write a binary "1" to bit 0 of address $00A0, and will thus start (enable) PWM channel 0. If we wrote ">mm a0 55" at the DBug12 prompt ($55 = %0101 0101), we would enable PWM channels 6,4,2 and 0 . See the manual for full details.
    * PWMCLK - PWM Clock Select Register is at address $00A2. This selects which clock (A, SA, B or SB) will be used as the main time unit for each PWM channel. We use two clocks for each PWM channel: Clocks A and B are for coarse control, and clocks SA and SB are for fine control. We use the SA clock in this demo. For each clock, we will use a "prescaler" or "divider" value, which basically takes our main E clock and slows it down by the divider factor.
    * PWMPRCLK - PWM Prescale Clock Select Register is at address $00A3. This is a prescaler (divider) for the clock A in PWMCLK, and in our case sets the frequency of the A clock. For instance, if we write %00000111 =$07 to address $00A3, we set the A clock to run at a frequency of A = E/128, where E is the E-clock frequency (24 MHz on the Dragon12 in the configuration we use). The clock rate divider is actually given by 2^x, where "x" is the value we wrote to the register (eg, "x" = 7 gives divider of 2^7 = 128, "x" = 5 gives a divider of 2^5 = 32, etc.).
    * PWMSCLA - PWM Scale A Register is at address $00A8. This sets the scaling rate for how clock SA is generated from clock A via a prescaler: SA freq = A/(2*PWMSCLA). For instance, if clock A is set at A=E/128 as above, then writing %00000100 =$04 to the PWMSCLA register divides the A clock rate by (2*4). The SA clock frequency is now SA = A/(2*4) = (E/128)/(2*4) = E/(128*2*4)= 23,437.5 Hz. If we wrote a $00 to the PWMSCLA register, we have a special case where we divide clock A by (2*256).
    * PWMPERx - PWM Channel Period Registers at addresses $00B4 (PWM0) - $00BB (PWM7). This register sets the period of the PWM channel. The PWMx period = Channel Clock Period * PWMPERx, where the Channel clock period is the SA or SB period (inverse of SA/SB freq.) and PWMPERx is the binary value in the register. For instance, if we write %00001000 =$08 to address $00B4, and the SA clock frequency is SA = 23,437.5 Hz as in the example above, then the SA clock period is 1/23,437.5 Hz = 42.7 microseconds. So the period of PWM Channel 0 is then 8 * 42.7 microseconds = 341 microseconds = 0.341 msec. In terms of frequency, the PWM Channel 0 frequency is SA/8 = 23,437.5 Hz/8 = 2930 Hz.
      Note that at this point, we have started with our main system clock, the "E" clock, running at 24 Mhz, and then have slowed it down in several stages. In the first stage, we took the "E" clock and generated an "A" clock running at the slower rate of A = E/128. In the second stage, we took the "A" clock and further slowed it down to generate the "SA" clock running at SA = A/(2*4)= (E/128)/(*2*4) = E/(128*2*4) = 23,437.5 HZ. The SA clock is the base clock for our PWM waveform. For our final PWM waveform, we wrote a value of "8" to the PWMPERx register, further slowing the clock down to SA/8 = 23,437.5 Hz/8 = 2930 Hz. In terms of period, the final PWM waveform has a period of 1/2930 Hz = 0.341 msec. So, we can look at all the A, SA and PWMPERx values as being either dividers in terms of frequency (ie, we divide the starting E fequency by a series of values), or multipliers in terms of period (we multiply the starting E period by a series of values).
    * PWMDTYx - PWM Duty Register is at addresses $00BC (PWM0) - $00C3 (PWM7). This register controls the duty cycle. The formula for our case is:
      Duty Cycle = 100%* [(PWMPERx - PWMDTYx)/PWMPERx]
      Continuing with the above example where we wrote $08 to the PWMPER0 register at address $00B4, if we write %00000100 (=$04) to address $00BC for PWM Channel 0, we set the duty cycle to be: 100% * [(8-4)/8] = 50%.
Example Calculations
We can use either frequency and period in setting up clocks, depending on convenience and the design situation. Most formula in the HCS12 manuals refer to frequency, but often it is easier to work in terms of periods. Assuming we are using the SA clock and that the E-clock is 24 MHz, the basic formula for the PWM channel period and frequency is:
In terms of period: PWMx period (seconds) = 2 * (PWMPRCLK) * (PWMSCLA) * (PWMPERx) / 24x10^6
In terms of frequency: PWMx frequency (Hz) = 24x10^6/ [2 * (PWMPRCLK) * (PWMSCLA) * (PWMPERx)]
where PWMx refers to the PWM Channel (PWM0 = PWM Channel 0, etc). The values in parentheses refer to the NUMERICAL values set up in the registers. For the PWMSCLA, PWMPERx and PWMDTYx registers, the numerical values correspond directly to the hex values we write there. For instance, if we write a $80 to the PWMPER0 register (x=0), we have a numerical value of 128 ($80 = 128). If we write a $05 to the PWMDTY0 register, we have a numerical value of 5. The one exception is that a write of $00 to the PWMSCLA register sets the numerical value to 256 (see manual). But for the PWMPRCLK register, the hex values DO NOT correspond to direct numerical values. Rather, they are essentially binary exponents. So, writing a %00000101 = $07 to the PWMPERCLK register sets the numerical value to 128 = 2^7, meaning that clock A is running at a frequency of A = E/128 (E= 24 MHz). If we wrote a $05 to the PWMPRCLK, we would have a frequency divider of 2^5 = 32, so that A = E/32. You can see this in Table 3-3 of the PWM manual. You MUST use this manual to really understand what is going on with the PWM registers, so make sure to have a look at it.
The examples below show the steps in calculating the register values for the PWM0 register.
Example 1 (0.5024 sec period, 50% duty cycle)
write $01 to PWMCLK at address $A2 to use the SA clock
write $07 to PWMPRCLK at address $A3, then (PWMPRCLK) = 128 = 2^7
write $00 to PWMSCLA at address $A8, then (PWMSCLA) = 256 (see manual for why $00 = 256)
write $B8 to PWMPER0 at address $B4, then (PWMPER0) = 184
write $5C to PWMDTY0 at address $BC, then the duty cycle is ($b8-$5c)/$b8 = (184-92)/184  = 50%
PWM0 period = 2 * 128 * 256 * 184 / 24x10^6 = 0.5024 seconds
PWM0 frequency = 24x10^6 / (2 * 128 * 256 * 184) = 1.99 Hz
To achieve the above results in DBug12 code we would write:
>mm a2 01
>mm a3 07
>mm a8 00
>mm b4 b8
>mm bc 5c
>mm a0 01 ; to start the PWM on Channel 0
Example 2 (4.95 m sec period, 75% duty cycle)
write $01 to PWMCLK at address $A2 to use the SA clock
write $07 to PWMPRCLK at address $A3, then (PWMPRCLK) = 128 = 2^7
write $04 to PWMSCLA at address $A8, then (PWMSCLA) = 4
write $74 to PWMPER0 at address $B4, then (PWMPER0) = 116
write $1D to PWMDTY0 at address $BC, then the duty cycle is ($74-$1D)/$74 = (116-29)/116  = 75%
PWM0 period = 2 * 128 * 4 * 116 / 24x10^6 = 0.00495 sec = 4.95 msec
PWM0 frequency = 24x10^6 / (2 * 128 * 4 * 116) = 202 Hz
To achieve the above results in DBug12 code we would write:
>mm a2 01
>mm a3 07
>mm a8 04
>mm b4 74
>mm bc 1d
>mm a0 01 ; to start the PWM on Channel 0
For further details of using PWM, see the pages PWM Using DBug12 and PWM Using The Logic Analyzer.
Introduction of PWM