University of Regina Electronic Systems Engineering ENEL487 Real Time Control Winter 2004 Lab #4 1 Objective To gain familiarity with the interrupt system on the Intel PC architecture, and to implement interrupt driven, pulse width modulated speed control of a servo-motor. 2 Background In this lab, motor speed control is the final goal once again, however, rather than using the D/A to produce an analog control voltage, a technique known as pulse width modulation (PWM) will be used. In the previous lab, it was noticed that the control range for the amplifier ranged over a relatively small range. This produced a relatively small number of control values to precisely set the motor’s speed. This problem could be fixed by designing an amplifier with the appropriate gain and offset, or setting the output sensitivity, such that the D/A’s output swing corresponded to the speed control range on the motor. There exists another problem with this technique. When a midrange power setting is desired, a large proportion of the power is dissipated by the drive amplifier. If the motor has an equivalent load of 15 ohms, then the power dissipated in the motor is V 2 /15. If the desired speed setting required 10W be delivered to the motor then the voltage is set to (10∗15)1/2 = 12.25V and the current is 820 mA. If the power supply voltage is 24 volts, the drive transistor is biased such that a 11.75 voltage drop appears across the collector 1 emitter junction. This transistor is therefore dissipating energy in the form of heat (11.75*.820)=9.6 Watts. PWM circumvents this problem by forcing the drive amplifier to be either completely on, or completely off. Clearly, when the transistor is off, no current flows and no power is dissipated. When it is completely on, the drive transistor is saturated and the voltage drop across it is about 0.2 V. The current flowing into the motor will be ((24-0.2)/15) = 1.59A. The power dissipated in the drive transistor will be (0.2*1.59) = 318 mW and the power to the motor will be 23.8*1.59=37.8 Watts. If the amount of power provided to the motor is too high, (i.e. the speed is too high) then the drive transistor can be rapidly switched off and on such that the average power is the correct amount for the appropriate speed. Increasing the percentage of time that the drive transistor is saturated will increase the speed. Note that speed control can be provided by varying the duty cycle of the drive waveform. The goal of this lab is to implement the PWM drive in software by toggling a digital output at an appropriate rate and duty cycle, thereby providing the closed loop speed control similar to the previous lab. 3 Procedure 3.1 Phase 1: Reprogramming the Motherboard Timer The motherboard contains an Intel 82C54 timer, which is used to generate the sytem clock. Essentially all timing functions on the PC are controlled by this chip. (In actuality there is no such chip in modern machines, the function of an 82C54 is incorporated into the PC’s chipset.) The data sheet for the 82C54 timer is available on WebCT and at ftp://download.intel.com/design/periphrl/datashts/23124406.pdf There is also an application note, at http://developer.intel.com/design/periphrl/technote/7203.htm All the registers on the 82C54 are accessed through an 8-bit port. Thus you must use 8 bit I/O instructions ( inp and outp) to access this device. There are three counters on the 82C54, numbered 0, 1 and 2. Note that these are count down timers. Counter 0 is clocked via an onboard crystal running at 1,193,180Hz. This counter is running in mode 2 (rate generator mode) so it’s simply a divide by N counter. By default the counter is programmed with 0, so it effectively divides by 216 = 65, 536, resulting in a signal at 18.206481Hz. This signal is tied to interrupt level 8, and this is the source for the computer’s system clock. 2 The counter can be loaded with any value from 0 to 65535, but doing so naively will result in the system clock running too fast. However, we can correct for this in our ISR. Here’s how: suppose you want to speed up the interrupt rate by a factor of 64 (power of 2 is not required, just cleaner). You would program counter 0 with the number 65536/64 = 1024. Then inside the ISR, you keep track of the interrupts, calling the old dos interrupt once every 64 times. In this way the system clock can continue to run at the proper rate. To implement the PWM the drive voltage is set high for a certain length of time th , then set low for a time tl . The duty cycle is determined by the ratio th /(th + tl ). Suppose you have set the ISR to be called every ∆t seconds. Then both th and tl must be multiples of ∆t. If we arrange it so +tl 0 < th /∆t < 100 and th∆t = 100 then we can set the duty cycle directly in percent. So a crude way of getting this to work would be to set the value of th in the background task. The foreground (ISR) task would then read this value and set the high and low durations accordingly. Then th could be changed by the user to achieve any speed from zero to the maximum. The 82C54 can be found at the base address 0x40. Address TIMER + TIMER + TIMER + TIMER + 0 1 2 3 Access R/W R/W R/W W Ctr 0 1 2 — Function System clock timer Memory Refresh Unused Control We note that an 8 bit I/O port is assigned to a 16 bit counter. In order to load 16 bits we have to perform two successive 8-bit output instructions. The LSB is loaded first. Thus a routine which performs a counter load might look like: void counterLoad(int counterId, unsigned int counterValue) { if (counterId < 0 || counterId >= 3) // counter_id = 0..2 return; // error _outp(TIMER + counterId, counterValue&0xff); _outp(TIMER + counterId, counterValue>>8); } This routine will only work if the control register has been configured properly, which we can assume is the case. For information purposes only, the control register layout is as follows: 3 D7 SC1 D6 SC0 D5 D4 RW1 RW0 D3 M2 D2 D1 M1 M0 D0 BCD • BCD selects binary or decimal counting, we want binary, BCD=0 • M2,M1,M0 select a variety of modes. We want the rate generator mode=010 for counter 0. • RW1,RW0 controls the read/load operation. We want LSB, followed by MSB, so RW1:RW0 = 11 • SC1,SC0 selects the counter (0, 1, or 2) we are configuring. • We would configure counter 0 with 00110100 = 0x34. The routine to perform this configuration is (which, again, we shouldn’t have to do): void counterConfig() { outp(TIMER + 3, 0x34); } To perform the PWM code in s/w we must first select a frequency to drive the motors. We select a frequency that is sufficiently high as to provide smooth motor operation, but not so high that the interrupt service routine can’t keep up. Somewhere around 10 Hz is about as low as we can go. (That means that a single PWM cycle will be 100ms.) The counter load value is determined by this frequency. We want our ∆t to be 1/100 of the PWM cycle time. So we set the 82C54 counter 0 value accordingly. What value do you think would be appropriate? 3.2 Phase 2: PWM Logic The interior of the ISR is still a mysterious beast. How can we accomplish all the things that are required? The ISR must: • keep calling the old dos interrupt at 18.206Hz • track a global variable, th , which is a number between 0 and 100 and which represents the duty cycle percentage. 4 • hold the motor voltage at a maximum for th seconds and hold it at zero for tl seconds, keeping the sum th + tl a constant (100ms in the above example, but you’ll pick your own). • keep reading the motor speed using the A/D so it can be controlled. A reasonable idea is to read the A/D once every PWM cycle. You should now come up with some program logic which will do this. 3.2.1 The EOI Command Up to now, we’ve always chained our interrupt service routine onto the preexisting dos ISR. In doing this we obscured a small detail about the PC’s interrupt hardware. As mentioned in class, the programmable interrupt controller (Intel 8059) feeds several interrupt lines into the CPU’s single interrupt input. When the 8059 receives an interrupt it informs the CPU of this fact and the CPU can then jump to the appropriate vector address (15 in the case of a timer interrupt). At the end of the ISR, the CPU must reset the 8059 by sending it a non-specific End of Interrupt (EOI) command. This is accomplished by writing the value 0x20 to the 8059’s base address. This base address happens to be 0x20, as well. So you must write the number 0x20 to I/O address 0x20, at the end of your ISR. If you don’t do this the system will hang. 3.3 Phase 3: Speed control The speed control algorithm is not much different in this lab than in lab #2. The A/D value is read, but instead of reading directly from the hardware, the background task reads it from the memory location stored by the ISR. Based on the read value and user input to set the value, a % duty cycle is determined. The duty cycle is stored with the speed set() command, a function that sets the value of th . When an interrupt occurs, the new speed value is used in determining the timer load value. Write the background routine which takes the user input and the A/D value to determine a % duty cycle thereby producing closed loop speed control. You may find that one PWM cycle every 100ms is too slow and results in a noticable oscillation. How would you speed up the PWM cycle to improve this situation? 3.4 Phase 4: Questions to answer We note that the there are only 101 speed settings: from 0% to maximum at 100%. 5 • If higher precision is required, how should the code be modified to achieve this? • How would you shorten the duration between interrupts? • Is there a minimum possible time between interrupts? If so, what happened when you went beyond the limit? 6