![]() |
|
By Kevin Ross
You have seen some information about the 68HC12 in the Encoder before ( An Overview of the 68HC12, The 68HC912B32: An Overview, and Programming the 68HC12). This article is going to describe the basics of how to use the Pulse Width Modulation module of the 68HC912B32
I will go through the basics of the 68HC912B32 PWM module assuming you have no prior knowledge. It would be very helpful, however, if you had the 68HC912B32TS document handy. You can grab it from the web. This article is intended to help you understand what is in that document.
The purpose of the PWM module is to allow for time critical waveform operations to be handled by the hardware, instead of trying to accomplish everything in software. For example, generating wave forms with fairly precise duration and duty cycles is extremely easy with the PWM module. A good overall description of the B32's PWM module can be found in the Technical Summary Document:
The pulse-width modulator (PWM) subsystem provides four independent 8-bit PWM waveforms or two 16-bit PWM waveforms or a combination of one 16-bit and two 8-bit PWM waveforms. Each waveform channel has a programmable period and a programmable duty-cycle as well as a dedicated counter. A flexible clock select scheme allows four different clock sources to be used with the counters. Each of the modulators can create independent, continuous waveforms with software-selectable duty rates from 0 percent to 100 percent. The PWM outputs can be programmed as left-aligned outputs or center-aligned outputs.
That is a mighty dense, but pretty accurate description of the PWM module. The reset of this article will explain in a little more depth what these features mean and how to use them. Near the end, I will show the SBASIC code needed to make the PWM mechanism work.
Pulse width modulation is a technique of outputing a waveform. A waveform in this case means that the electrical state of an output pin is going to change in a repeating pattern. The PWM module on the B32 outputs a digital signal, meaning it takes on only one of two states: Logical high, and logical low (ie +5 volts, and 0 volts on the B32).
There are two parts to the waveform that are important to understand. The Period is the amount of time between the start of each waveform. The Duty is the amount of time that the signal is high. The Duty is always less than the Period. This is an important point to keep in mind. If you ever end up with the Duty value being greater than the period, then the result is a non-changing waveform!
Left Aligned Duty and Period
The B32 PWM module allows you to specify width of the Duty and the width of the Period. There are many different uses for PWM, such as controlling the position of servo motors, dimming the brightness of a lamp, or controlling the speed of a DC motor. The PWM module makes this an easy task. The PWM module is flexible enough to output a very wide range of periods. For example, its possible to output both a 40khz waveform for modulating an IR remote and output a waveform that causes an LED to flash once or twice a second!
A quick note about period and frequency. They are mathmatically related. The period of a waveform is the amount of time between the starts of each wave. The frequency of the same waveform tells you how many times the waveform is going to happen per some unit of time, usually per second. The frequency is the inverse of the period. In other words,
For example, a waveform that has a 40khz frequency has a period of 1 / 40,000 = .000025 seconds, or 25 microseconds. Sometimes, the terms frequency and period get used interchangably. For example, one waveform might have a 40khz frequency, while we might describe another signal as having a 2 second period. It is just as valid to claim they have a period of 25 microseconds and a frequency of 0.5 hz as well. You will find that it is easier to describe very short periods in terms of frequency. Saying a processor has a 16mhz clock is more common than saying it has a 62.5 nanosecond period. On the other hand, longer periods (which means lower frequencies) are typically expressed by period rather than frequencies.
The PWM module allows you to generate two types of wave forms. Left aligned, and Center Aligned. The graph above was of the Left Aligned variety. The duty cycle is aligned on the left side of the period. The other option is the Center Aligned Duty and Period.
Center Aligned Duty and Period
With Center aligned mode, the actual period ends up being twice the contents of the period register. The Center aligned mode works well, but is a little more complicated to explain. I will get into more details later. The alignment mode is global for all of the PWM channels, and is set in the PWCTL ($0054) register. Therefore, they are all left aligned or all center aligned.
Another option that controls the appearance of the waveform is the Polarity control. In the PWPOL register ($0041), you can control for each channel what the polarity of the waveform is. This means you can choose to have the duty portion of the period be low or high, as in the diagram below. Each channel has its own polarity bit.
The Polarity Bits
Last, but not least, I should tell you that the output pins of the PWM module are all in PORTP. There are 4 channels, which use Bits 0-3 of PORTP for their output.
The PWM module is pretty flexible. It allows you to control the Duty cycle, Period, Polarity, and the Alignment of the waveform. Armed with these basic ideas, I will now explain how to put it all together. There are lots of related parts, but it is all really pretty simple.
There are four 'independent' channels in the PWM module. Each channel can be programmed with an 8-bit channel period and an 8-bit channel duty. There are actually two groups of PWM channels. Channels 0 and 1 form one pair, and channels 2 and 3 create the other. Channels 0 and 1 share the same basic clock source, as do channels 2 and 3.
Another great trick allows you to concatenate these channel pairs. Instead of two seperate 8 bit channels, you can combine the channels in a pair into a single 16 bit channel. That allows you to have a 16 bit Duty and 16 bit Period register. Very useful for generating extremely long waveforms. The waveforms can generated with periods as fast as 500ns to as slow as over 17 minutes!
The size of the channels is determined by the high 2 bits of the PWCLK ($0040) register. Bit 7 controls the concatenation of channels 2 and 3. Bit 6 controls channels 0 and 1. Setting a 1 in these bits causes the channels to form a single 16-bit channel. The resulting output of the 16-bit channels are on PORTP Bit 0 for channel 0/1 and PORTB Bit 2 for channel 2/3. In addition, the clock control bits for the 16-bit channels are associated with channels 1 and 3 respectively. This means if you wish to set options, such as the polarity, on a 16-bit channel, then you need to set them in channel 1 or 3.
Lets have a look at what is actually driving the PWM system. There are a set of clocks that are defined for the PWM system. It is important to see how these are derived since the speed of the clock is critical to the generation of a proper waveform.
The PWM system uses the E Clock (the main bus clock for the CPU) as its primary clock source. From this primary source, it branches off into two seperate clock chains. ClockA and ClockB. ClockA drives channels 0 and 1, while ClockB is fed into channels 2 and 3. ClockA and ClockB are connected to E through a prescalar chain. The prescalars job is to divide the E clock, thus making ClockA and ClockB slower than or equal to E. You can program this with some of the bits in PWCLK register, which is at address $0040. PCKA0-PCKA2 (Bits 3-5) select the prescale for ClockA, and PCKB0-PCKB2 (bits 0-2) for ClockB. Each three bit group defines the power of 2 that the clock is divided by. 0 = E / 1, 1 = E / 2, 2 = E / 4, ..., 7 = E / 128
The PWM Clock Chains
As you can see by the diagram, PCLK (which is supposed to read ECLOCK) is fed into the system at the top left. Bits PCKA0-PCKA2 control the division for ClockA. Bits PCKB0-PCKB2 control the division for ClockB. Note that E usually runs at 8mhz. You can divide that down using these bits to being 62500 hz, or 62.5khz. That is quite a range! Since they are independently prescaled, ClockA and ClockB can have drastically different rates.
Each of the clocks is run to into an optional second prescalar mechanism. You can select, in register PWPOL ($0041) which clock source each PWM channel will use. Your options for each channel are its preassigned clock (ClockA/ClockB), or you can select a secondary prescaled clock (ClockS0 or ClockS1). If you select ClockS0/1, then ClockA can be further reduced in speed by setting a value in the appropriate PWSCAL register. PWSCAL0 ($0044) controlling ClockS0, and PWSCAL1 ($0046) controlling ClockS1. The mathmatics for this scaling are interesting.
ClockS0 = ClockA / (2 * (PWSCAL0 + 1))
That means you can divide the clock by any multiple of 2 (0,2,4,6,8,10,12,...,512). When combined with the ability to have ClockA derived from another divider chain, it is possible to have some pretty slow (relatively!) clock frequencies.
Now we have the clock sources that will feed into the PWM module itself. That is the next topic.
(A little optional section:)
Here is a partial spreadsheet showing some of the interesting values.
Frequency in Hz of ClockS0 at different scalars
How to interpret this chart. The top row is ClockA. At 8mhz (no dividing of E), the range of frequencies for ClockS0 is 4mhz to 15.625khz, depending on the value of PWSCAL. If ClockA has full prescaling from E, then the range of frequencies is from 31.25khz all the way down to 122 HZ. Thats mighty slow.
There are two versions of the diagram for the PWM channels. I am going to start with the Left Aligned channel first. Most everything will apply to both. I will explain Center Aligned channels in the next section.
As you can see in the diagram above, there isn't too much to the actual PWM channels. A clock source (shown in the upper left) is fed into a counter register (PWCNTx). Each channel has its own counter and compare registers (PWDTYx and PWPERx). (hint: the 'x' could be 0,1,2, or 3 depending on the channel number!). PWENx is a bit in the PWEN register ($0042) which corresponds to the channel. This enables the clock to drive the PWCNT counter and the output to PORTP.
To use the channel, you first set the PWDTYx register with the duration of the duty. Then you set the PWPERx with the period that you desire. Set the appropriate bit in the PWPOL register to enable the polarity you desire. Then, for safety, write a ZERO into the PWCNTx register to insure you start with a fresh counter. Last, but not least, write a 1 to the appropriate bit in PWEN.
The channel works as follows. The PWCNTx register will start counting up. When PWCNTx is equal to PWDTYx, the output bit is set accordingly (according to the PPOLx bit). PWCNTx continues counting up. When PWCNTx reaches PWPERx, PWPERx causes the output bit to be reset, and also causes PWCNTx to be reset to zero. Then the whole thing starts again!
Keep in mind that while the picture shows 8-bit registers, in 16-bit channel mode, these registers are all 16-bits in size. That means you can set PWPERx to be $FFFF (65535). Doing a little math: 65535 / 122.0703 (hz) = 536.86 seconds. A 16-bit counter using all maximum prescalar values can handle a period of 536.86 seconds (8.94 minutes). Not bad!
The calculations for the period of the channel is dependent on the alignment mode. For left aligned mode, the formula is:
Channel Period = ClockTickPeriod * (PWPER + 1) ; Left Aligned Mode ONLY!
For example, if the ClockA is running at 8mhz (8,000,000), then the ClockTickPeriod is 1 / 8mhz = .000000125 seconds, or 125 nanoseconds. If PWPER is set to 99, then
ChannelPeriod = 125ns * ( 99 + 1) = 12500ns or 12.5us
The calculation for the duty period of the channel is exactly the same:
Channel Duty Period = ClockTickPeriod * (PWDTY + 1)
Hence, a PWDTY set to 49 would result in
ChannelDutyPeriod = 125ns * (49 + 1) = 6250ns or 6.25us
Pulse Width Modulation is usually expressed in terms of the Period and the Duty Cycle. Duty Cycle is a percentage based on the ratio of the DutyCycle to Period. Thus, to calculate the duty cycle, in Left Aligned Mode, the calculation is
Duty Cycle = ( (PWDTY + 1) / (PWPER + 1) ) * 100%
In the above example,
Duty Cycle = ( ( 49 + 1) / ( 99 + 1) ) * 100% = 50/100 = 50% Duty Cycle
Center aligned mode is an interesting feature. It affects both the channel period and duty period calculations, as you will see in a moment. Here is a block diagram of the Center Aligned Mode:
The operation in center aligned mode is different than left mode. The counter starts out at zero, and counts up just like left aligned mode. When PWDTY matches, the output pin is toggled. The big difference is what occurs when PWPER compares to the counter. When PWPER matches PWCNT, instead of changing the output pin, it changes the direction of the counter. The counter starts counting back down to zero. On its way to zero, it will cause PWDTY to compare again, and the state of the pin is toggled once again. When PWCNT reachs zero, the direction bit is reset and the counter starts counting up once again.
The net effect of this mechanism is that the waveform is stretched by a factor of 2. As you can see in the diagram, the period of the waveform is actually PWPER * 2. The duty period is also seen twice. In a continuous waveform, it results in the percieved duty period being PWDTY * 2. This is an interesting mode that, while I understand how it works, I am not completely sure why Motorola included it in the design. It does allow you to create yet another prescale mode. The longest period in this mode is 17.88 seconds!
Again, Center aligned mode is selected in the PWCTL registers ($0054) and it applies to globally to all of the channels.
Nothing like a code example, right! Here is an annotated version of the code sequence to setup the PWM system for the following specifications: Channel 0 will output a 40khz square wave with a 50% duty cycle, such as would be used to generate a modulated 40khz IR signal. Channel 2/3 will output a 20 millisecond square wave with a 1.5ms duty period, such as would be used on a servo motor. Channel 2/3 will be setup as a 16-bit channel outputting to show a 16-bit channel, but also to demonstrate how to create as accurate a pulse as possible. We will use left aligned mode, and all of the polarities will be for a high duty cycle. Last, but certainly not to be forgotten, we are going to assume an E-Clock of 8mhz. That would be a chip with a 16mhz crystal.
First, we need to calculate the prescalar values. Since SBASIC doesn't do floating point and cannot handle large calculations, we need to precalculate the timer values. Lets start by determining if we need a prescale for ClockA and ClockB.
Channel 0 will be setup as a 40khz square wave. That means that the period will be 25 microseconds. To do these calculations, I am going to use frequency instead of period. Remember that the relationship is Period = 1 / Frequency.
Channel 0 is going to be an 8 bit counter. Therefore, the period calcuations need to take that into account. The larger the PWPER value, the more accurate your duty cycle can be adjusted. As a rule of thumb, you should make your period as large as you can fit into the register without loosing accuracy. We have an 8mhz clock, and a 40khz signal to generate. A quick stab at the calculation yields:
8mhz / 40khz = 8,000,000 / 40,000 = 200
At full clock speed, 200 clock periods are required to create a 40khz signal. 200 fits easily into an 8 bit register, and will be a good choice.
Consider the uses for channel 2/3. It is going to be used for positioning a servo motor. Therefore, we would like to have as much accuracy as possible. Again, we choose a prescalar that will allow as fine a resolution as possible. A 20 millisecond period works out to be 50hz. Using the same formula as before:
8mhz / 50hz = 160,000
This calculation shows that 160,000 clock periods are required to create a 50hz signal. Since the 16-bit PWM channel can only handle 65535 clocks, we are going to require a prescale to make this work. A minimal prescalar would be 160,000 / 65535 = 2.44 which is a value we cannot generate. The closest we are going to get is to prescale by a factor of 3. There are two options here. First is to prescale ClockB. This divides the EClock by a power of 2. In this case, we would have to choose to divide by 4, resulting in ClockB equalling 2mhz:
2mhz / 50hz = 40,000
We can easily fit 40,000 into the 16-bit registers for the PWM channel. The other option is to use the prescale ClockS1, which can divide ClockB by 3, resulting in 53333 being the period value needed to generate a 50hz period on the waveform. Here is where a little forethought is good. Option A is to prescale ClockB as EClock / 4. Option B is to prescale ClockB by using ClockS1 to divide by 3. Option A results in a nice clean value of 40,000 for the period, and the resolution for each step of the value is 500 nanoseconds. Option B allows for finer resolution, but each step ends up being an ugly number of 375 nanoseconds. Personally, I would prefer to loose a slight amount of resolution in order to have cleaner math of 500ns increments.
Sorry, I know that was an ugly bit of explaination, but that is how I determined the proper clock values for the code. Oh yeah, here comes the code:
' ' A small test program to exercise the PWM functions on ' a 68HC912B32 ' ' This file is written for the 68HC12 by Kevin Ross (kevinro@nwlink.com) ' It is written in SBASIC. ' To compile this file, you need to invoke the following commands: ' ' sbasic adtest.bas /c8000 /v0000 /m6812 > adtest.asm ' as12 adtest.asm ' declare i
include "regs12.lib"
main: ' Turn off the COP pokeb copctl,0
' Enable serial port poke sc0bdh, 26 ' Set for 19200 baud pokeb sc0cr2,$0c ' Enable transceiver
print print "****####****" print "pwmtest running"
' Set the concatenate PWM channel bits so we have channels 0 and 1 acting ' as 8 bit counters, and channels 2/3 acting as a single 16-bit counter ' ' There are two independent clocks on the PWM. Clock A and Clock B. ' The low 6 bits of PWCLK set the prescalar values (3 bits each). ' ClockA is going to run at full speed. ClockB is to be divided by 4 ' pokeb PWCLK, $82
' ' PWPOL is the Clock Select and Polarity control register. Clock A ' runs channels 0 and 1, and CLOCK B is going to run channels 2 and 3 ' Polarities are set so that the signal is low at the start of the ' cycle, and flips high when the duty count is reached on channel 0. ' ' Channel 2/3 is set to be high at the start, and go low at the end ' of the duty period. ' pokeb PWPOL, $0C
' For now, we will assume all of the prescalars should be set to zero pokeb pwscal0, $0 pokeb pwscal1, $0
' Lets setup a 40khz signal on the 8-bit PWM channel 0 with a 50% duty cycle. ' If you do the math, 8mhz/40k turns out to be 200, ' which means that our period is 200 clocks. If you check the documentation, ' it turns out that the correct value is 200 - 1 because of the way that ' PWPER is compared in left aligned mode. '
pokeb pwper0, 200 - 1 pokeb pwdty0, 100 - 1
' Now setup channel 2/3 with a 20 millisecond period, and a 1.52 millisecond duty ' period. This should center a servo motor. 1.52ms / 500ns = 3040 ' poke pwper2,40000 - 1 poke pwdty2,3040 - 1
' Now enable channel 0 and channel 2/3 in the PWEN register. Due to a bug in the ' XC and previous versions of the B32, you need to turn on both channel 2 and 3 ' to get this to work.
pokeb pwen, $0D
do i = inkey() i = i AND $ff
loop
You can download the code for this article from the following file: pwmtest.bas
The PWM module of the B32 is a very powerful system. It allows you extreme flexibility in generating waveforms of widely different periods with no software interrupts or CPU overhead. Be sure to get a copy of the B32 technical specification.
Here are links and instructions for getting 68HC12 resources
as12 assembler is available from Karl Lunt, and is the freeware assembler for the HC12 family.
SBasic is Karl Lunts version of BASIC for the 68HC12. This is a good high level language.
Robotic and Microcontrollers Products is my page that allows you to order the SRS 68HC812A4 Development board, as well as my BDM12 interface board, and the new 68HC912B32 development board.
Imagecraft has a great C compiler for the 68HC12. The compiler runs around $150, but is a great bargin if you would like to write software in C for the HC12.
You can get online versions of the 68HC12 documentation from Motorolas website. Here are the files to grab. You can also get printed versions as described below.
CPU12RM-AD Index is an index to a series of .PDF files that create the Motorola CPU12 Reference Manual. This describes the instruction set and programming model for the chip. It is definitely a must have. There is also an Online Version at CPU12RM-AD or http://www.mcu.motsps.com/lit/manuals/cpu12/outline1.html
a4.pdf is the .PDF file of the 68HC812A4 Technical Summary that contains information specific to the 68HC812A4 version of the chip. This is the one with 4k of EEPROM, 1k RAM, and tons of I/O. This explains all of the systems on the chip, where the registers are, etc. You should definitely have one of these if you are using the 68HC812A4.
a4-ec.pdf
(250K bytes) is a PDF file with the electrical characteristics of the 68HC812A4.
Timing diagrams, electrical capacities and absolutes, accuracies, etc.
b32ts.pdf is the .PDF file of the 68HC912B32 Technical Summary that contains information specific to the 68HC912B32 version of the chip. This is the one with 32k of Flash, 768 bytes EEPROM, 1k RAM, and still lots of I/O. This explains all of the systems on the chip, where the registers are, etc. You should definitely have one of these if you are using the 68HC912B32.
b32-ec.pdf
(272K bytes) is a PDF file with the electrical characteristics of the 68HC912B32.
Timing diagrams, electrical capacities and absolutes, accuracies, etc.
Motorola will also allow you to order printed documentation online at http://design-net.com/home2/lit_ord.html where you will find a form to fill out your name and address, then a spot on the bottom for document numbers. The following are the documents that you might be interested in
CPU12RM/AD | The CPU12 Reference Manual (Must Have) | |
MC68HC812A4EC/D | 68HC812A4 Electrical Characteristics | |
MC68HC812A4PP/D | 68HC812A4 Product Preview (Not worth the effort!) | |
MC68HC812A4TS/D | 68HC812A4 Technical Summary (Must Have) | |
MC68HC912B32ECD | 68HC912B32 Electrical Characteristics | |
MC68HC912B32PPD | 68HC912B32 Product Preview (Not worth the effort!) | |
MC68HC912B32TSD | 68HC912B32 Technical Summary (Must Have)
|