Sie sind auf Seite 1von 42

Fuel Cell Powered Go-Kart Final Report ECE 291: Spring 2002 Team 2: Peter Prizio (EE) Brian

Stevens (EE) Anhtuan Truong (EE) Kenneth Cappola (ME) James OBrien (ME) Nathan Snape (ME) Advisor: Professor Martin D. Fox

Sponsored by the University Of Connecticut Contact: Professor James M. Fenton, Chemical Engineering

Table of Contents Executive Summary3 Statement of Need...3 Project Description..4 Inverter......5 Power Supply5 Buck Converter.6 Battery charger......8 Battery.....14

Data and Results16 Budget...21 Timeline23 Conclusion.....23 References.....24 Appendix...25

Executive Summary Fuel cells are becoming an increasingly important fuel supply for portable power applications. Their environmentally friendly emissions and high efficiency make them a good solution for renewable energy source vehicles. The project involves converting an electric go-kart into a fuel cell electric vehicle. The project research and design is sponsored by the University of Connecticut, Professors James M. Fenton, and Martin D. Fox. The power source provided was a 12-volt, 42-amp fuel cell, which was not powerful enough to directly drive a 36-volt, 38-amp motor. The fuel cell was utilized as a power supply for a battery charger to dynamically charge the batteries during the operation of the kart. The design of this charger was the crux of the design effort this semester. The best and most feasible way to charge the three 12-volt batteries was to isolate each battery while sensing current, voltage, and temperature. The approach proposed applied a micro-controller to create a pulse width modulation (PWM) battery charger, due to its ability to change parameters quickly and efficiently. Statement of Need Designing a fuel cell powered go-kart will help explore the potential for alternative fuel vehicle transportation. According to the Sustainable Energy Coalition, a coalition that promotes renewable energy technologies, a 10% penetration for fuel cells into the automobile market can reduce petroleum imports by 800,000 barrels per day, or roughly 10% of the U.S. daily usage. Fossil fuels also have many emissions that are harmful to the environment. Fuel cells are good, clean alternatives to conventional fossil fuel power plants. They can run on pure hydrogen gas or any other gaseous fuel from natural gas to landfill waste methane gas. Fuel cells also operate with high efficiencies, and using hydrogen gas, produce no harmful emissions. The go-kart project takes an electric go-kart and attaches a fuel cell with its subsystems to power the kart in some way. The project will be considered a success if the kart can be operated for a significantly longer time than running the kart off the battery pack alone. The bulk of the problem lies in creating an intelligent battery charger that can handle the input and output voltage and current levels. Todays battery chargers have many limitations. Many chargers run one charging algorithm tailored for one specific type of battery and do not monitor the battery for optimal charging conditions. Smart battery chargers are mostly micro-controller based and monitor battery conditions while charging, but are not portable. Thus, a portable, smart battery charger that can monitor multiple batteries at a time and charge only the most discharged battery is necessary. Many applications for portable battery chargers are present today, and having the ability to charge the batteries during discharge will extend the life cycle of the go-karts batteries.

Project limitations are regulated by budget, time, and weight. The fuel cell is very expensive. Thus, the project must be completed without spending in excess of the allotted budget. The kart must also be completed by May 2002. The final limitation is the weight. The kart is to be fitted with all of the original kart components as well as the fuel cell, hydrogen tank, water tank, some type of chiller, and a full-grown person rather than a child. It can potentially be a difficult task for the motor to push all of the extra weight. The focus of the electrical engineering group will be limited to the fuel cell-battery interface while the mechanical engineers will concentrate on the fuel cell and its operation. Project Description The solution to the problem given to the Fuel Cell Go-Kart Electrical Engineering Team by the University of Connecticut has focused on implementing the fuel cell to charge the battery while under continued load. The blocks that will be involved include the DC-AC Inverter, the 48VDC Power Supply, the Buck Converter, the Battery Charger, and the Battery. Each section will be discussed using block diagrams and schematics in order to understand the full scope of the problem.

Figure 1 Project Block Diagram

Figure 2 Battery Charger Overall Circuit Diagram Inverter The inverter is made by SUPEREX and has a rated input of 10-15VDC and an output of 115VAC 1000W continuous. The inverter simply converts DC to AC in order to drive the power supply. A commercially available inverter was chosen due to its low cost, high efficiency, and convenient packaging. Efficiency calculations are shown in the Data and Results section of the report. Power Supply The power supply takes an AC input and produces a DC output. The circuit is rated for 48VDC, 10 Amps and 500 Watts. It provides a DC input for the charger and buck converter circuits. It always produces a constant current and voltage. Since it is unregulated, the voltage may increase or decrease inversely proportional to the current. The power supply is approximately 80% efficient per the manufacturer at a full load of 48VDC and 10 Amps.

Buck Converter One of the most important parameters in charging batteries is the control of the power source. Whether a current or voltage-based charging is used, control of the power source is imperative to prolonged battery life. The power source for this design was a 48VDC power supply. A buck converter was chosen because of its simplicity, efficiency, and low heat dissipation. A simplified diagram of the converter circuit is shown below.

Figure 3 Buck Converter Circuit The Pulse-width modulation (PWM) controlled buck DC/DC converter consists of an input voltage source, transistor driver, high power P-channel HEXFET, inductor, and an output capacitor used for filtering. The buck converter is a dc-to-dc step down converter in which PWM is applied to the Power MOSFET Switch. When the output of the PWM is high, current passes through the inductor to the battery. The inductor energizes and the capacitor charges. When the PWM is low, the capacitor discharges through the diode as the inductor voltage reverses to resist the change in current. The filter at the output of the converter is used for eliminating harmonics and noise. The varying duty cycle of the PWM controls the on and off times of the MOSFET. As the duty cycle changes, the output voltage from the buck converter changes per the equations below. The micro-controller PWM output is shown in Figure 2.

ton T
Figure 4 PWM Output The relationship between PWM output and the output voltage is:
tOn

T
Where:

(VO + VD ) (Vi - Vsat + VD )

ton = On time of PWM T = PWM period Vi = Input voltage VO = Output voltage VSAT =Saturation voltage of transistor VD = Diode forward-bias voltage drop

VD For VD , Vi , VSAT , and T constant, increasing or decreasing ton will increase or decrease the output voltage Vo. Therefore, for different battery technologies requiring different charging voltage, Vo may be varied by varying ton .
The buck converter inductor value is determined by: L=

(Vi - VSAT - VO )tOn


I PK

Where I PK = 2 I OMAX and I OMAX is Maximum output current.

Below is a graph of the efficiency of the buck converter and charger circuit. As battery voltage increases towards 43.5VDC, the efficiency increases from a minimum of 78% to a maximum of 93%. The circuit is more efficient when the P-channel MOSFET is on since that is the normal operating condition.
Charger Efficiency versus Charger Output Voltage 94 92 90 Charger Efficiency (%) 88 86 84 82 80 78 76 36 37 38 39 40 41 42 43 44 Charger Output Voltage (Volts)

Figure 5 Buck Converter/Charger Efficiency

Battery Charger An intelligent battery charger is necessary for this project to maximize the life of the batteries. What makes a charger intelligent is its ability to monitor multiple battery characteristics and adjust the parameters accordingly, thus providing efficient safe charging. Unlike intelligent chargers, dumb chargers require the battery to be disconnected from its application and they do not sense current, voltage, and temperature. These parameters are required for safe operation and charging. The brain of the battery charger is the PIC16F874 micro-controller. This micro-controller was chosen for its versatility. It has eight, ten bit A/D inputs, which are used for accurate voltage and current sensing. It also has two capture compare modules, which allow for an accurate and easy decoding of an Analog Devices TMP04 temperature sensor and an easy generation of an adjustable pulse width modulation pulse train. Currently, few of the inputs are being used, but there are more available for future expansion and refinement of the project.

Since the battery voltage is high when compared to the limits of the micro-controller, it must be scaled down to allow for sensing. The voltage is sent through a voltage divider, which brings a maximum of 20 volts to the 5-volt micro-controller threshold. The current is sensed through a current shunt resistor, the voltage drop is read across this resister. This voltage is very small and will only translate to about 20 steps of accuracy within the micro-controller. Since current is a very important parameter, this is not accurate enough. To solve this problem, the voltage is amplified utilizing a non-inverting amplifier circuit. This voltage now has a 10A to 5V correlation. When input into the A/D of the PIC, this results in about a 10mA per bit resolution. The PIC can then display the readings on a liquid crystal display (LCD) so the user can check the level of charge and the charging current. The basic functions performed by the micro-controller are generating a pulse width modulation (PWM) pulse train and parameter sensing. The functionality of a PWM intelligent charger is to charge the battery while the PWM output is a 1 and sense the voltage, current, and temperature on the 0. This is accomplished by using one of the hardware PWM modules of the PIC. The reason a hardware PWM was chosen was for accuracy. The PWM has up to a 10-bit resolution and is not affected by interrupts or the timing of the other subroutines in the program. Writing to the PR2 register period controls the PWM frequency. The frequency also depends on the clock frequency and the timer 2 pre-scale value, but they are not addressable once the program has been built. The battery charger runs at a constant frequency of 15kHz and is not varied in operation. The PWM duty cycle is what gets changed to vary the charging voltage and maintain a constant current. Like the frequency, the duty cycle also depends on the clock frequency and the timer 2 pre-scale value, but is also dependant on the CCPR1L register for the 8 most significant bits and the CCP1CON bits 5 and 4 for the 2 least significant bits. This 10-bit value is varied depending on the current sensing input to maintain a constant current charging.

Figure 6 Battery Charger Flowchart The beginning cycle of the charger initializes all of the necessary inputs and outputs of the PIC (figure 1). It also sets up the PWM frequency and sets all initial variable values. Next, the initial PWM duty cycle is calculated. In the PWMON subroutine, an initial reading of the battery voltage is taken. This enables the PIC to calculate the starting duty cycle so the output voltage is slightly higher than the voltage of the battery (figure 2). Charging can now begin normally and the battery charger becomes fully autonomous.

10

Figure 7 PWMON Subroutine Flowchart Next, the TestMode subroutine is entered (figure 3). Here the PIC reads all of its inputs and determines what to do to control the charging. The first parameter sensed on the low PWM cycle was the voltage. This is controlled by the duty cycle and is compared to 43.5 volts. If the voltage is under the threshold, the charging continues and the program returns to the main loop and the state variable is set to test current next. If the value is over 43.5, the charging is complete and the duty cycle is set to 0% and charging ceases. The battery temperature is also calculated. This parameter has no effect on the charging scheme. Tests run on charging the battery pack has shown that the temperature does not rise uncontrollably and would not cause damage to the batteries. Also the air temperature would cause for erroneous charging due to the fact that the sensor would not only be reading the temperature of the battery, but the ambient temperature. Ideally, the temperature of the cell would be the internal battery temperature. Only then would the temperature be a valid charging parameter. The temperature sensor is a pulse train input to a capture compare module. Once the sensor detects a temperature reading is available,

11

the program interrupts and the input times are captured. After sensing the temperature the program again sets the state variable to test the charging current. After temperature or voltage has been calculated, the next subroutine is to check the current. The charging current is limited to between 4 and 4.5 amps. Reading an A/D input and performing some scaling calculations provided an accurate reading of the current to within 0.1A. The reading is firs checked for the low current value. The 10-bit A/D input is subtracted from a value that corresponds to 4A. If this action does not cause a borrow, the current is lower than 4A and the program moves to an increase duty cycle subroutine. Here, 2 is added to the previous 10-bit value and the program returns to the main loop to sense other parameters. If the current is above 4A, the input value will get subtracted from a value that corresponds to 4.5A. If a borrow occurs here, the current is too high and the duty cycle is decreased by subtracting 2 from the previous 10-bit value and returns to the main loop. If the current is now determined to be below 4.5A, the duty cycle remains unchanged and the program returns to the main loop. The reason for checking current twice is to maximize the controllability of the current so it always remains within the 0.5A range. Finally, the values sensed are all displayed simultaneously in the DisplayAll subroutine.

12

Figure 8 TestMode Subroutine Flowchart

13

It was decided that the battery charger should require little user/charger interaction. The battery charger is completely stand-alone. Once activated, it will not need any user inputs. The LCD is there to display the parameters to the driver so they can view the condition of the batteries. The charging and discharging will be completely controlled by the micro-controller in order to provide the most efficient and safe battery charging available. The code for the intelligent battery charger is located in the Appendix. Battery The go-kart utilizes three PC12330 Sealed Absorbed Glass Mat (AGM) Deep Cycle Interstate Batteries; each rated at 12V and 33-Ampere Hours output. In AGM batteries, the acid is absorbed between the plates and immobilized by a very fine fiberglass mat, keeping the acid available to the plates. A fast reaction between the acid and plate material results from this construction. Coupled with low internal electrical resistance, the battery can deliver and absorb higher rates of current than other sealed batteries during discharging and charging. In addition, AGM batteries can utilize the same chargers used for wet cell batteries. The battery is also valve regulated, meaning there is no gassing, no electrolyte spillage, no corrosion, and no special handling precautions. This makes the battery the best choice for an environment consisting of constant stress and motion, as in a go-kart. The absorbed glass mat is porous. The oxygen gas generated at the positive plate will diffuse to the negative plate, contact the lead, and form lead oxide. The lead oxide reacts with the sulfuric acid to form lead sulfate. If the voltage used during charging is excessive, considered overcharging, oxygen gas develops at a rate faster than what the separator can diffuse. Thus, the battery will act like a regular vented battery, releasing hydrogen and using water, drying out a cell and causing it to fail. Undercharging occurs when a battery is discharged and less than the 107% - 115% of the removed amp-hours is not supplied to the battery during recharge. As a result, lead sulfate stays on the plates, eventually hardening. The lead sulfate may not be converted back to lead oxide as it should, the battery will suffer permanent capacity loss. To optimize life, overcharging and undercharging must be avoided. The large amount of discharging and charging cycles of the batteries dictates that any type of trickle or float charging is undesirable. The trickle charges are used to maintain a battery fully charged without a state of discharge. The best method for charging, according to the battery manufacturer, is to charge the battery using cycling applications. The maximum current of the charger should be less than 20% of amp-hour capacity of the battery. The 20% value of the amp-hour capacity is 6.6Amps. The battery is then charged to 14.40 to 14.70 Volts at approximately 68F (20C) under load. The voltage reaches 14.40 to 14.70 Volts until the current drops to approximately 3.3 Amps. At this point, the battery is fully charged and will be disconnected from the charger. Voltage, current, and temperature readings would be sent to the micro-controller. The microcontroller uses the feedback to stop charging the battery pack. The charge is considered a constant current charge in that the current is limited to a certain value while the voltage

14

changes until the battery is fully charged. The starting charging voltage is chosen based upon a value, which leaves a proper safety margin to allow proper operation of the gokart. In other words, it is undesirable for the batteries to go dead before the charging begins. Below is a diagram typical of a constant current charge for a valve-regulated battery. The current value shown is a generic value. The PC12330 deep cycle battery limits the charge to a maximum of 6.6 Amps.

Figure 9 Constant Current Charging Curve Depending upon the response of the battery to charging and continued cycling, voltage values may be adjusted within the limits above to obtain the best charging rates for the battery. Although the extended range of the go-kart is an important goal, the integrity of the battery and the safety of the passenger must not be compromised. Due to the above and the application, the constant current charging scheme was chosen. This will allow the charger to control the current during the charging cycle and prevent a difference in potential between the battery and the charger from causing current to rise out of control. It also keeps the difference in potential from allowing current to flow back into the charger circuit and damaging the output diode. The current limit for charging was based upon the limits of the components, circuit board, and the fuel cell. The resistors in the circuit were the limiting factor in keeping the current between 4 to 4.5 amps. The power dissipation of the majority of the resistors in 15

the circuit was a watt. Thus, to keep the safety margin large, we ensured that the product of the voltage and current was well within the limits. The circuit board was limiting as a result of the size of the current runs. As the circuit runs became larger and the board area increased, the price of the board increased dramatically. Thus, we were able to limit the cost and size of the board. Lastly, the fuel cell limits dictated that we should keep the current low. The combination of the battery and fuel cell limits made 4 to 4.5 amps an appropriate choice. Data and Results The best measure of how well the project worked is to follow the level of efficiency throughout the system. The efficiency was measured between each module and combined to obtain the overall efficiency. The results were then compared with the battery charger delivered with the go-kart. The charging test with the intelligent battery charger was performed and the results are posted below. The battery pack was successfully charged using the intelligent battery charger. The fuel cell was not used due to its low power output. The power would have been insufficient for our needs. The fuel cell will be explained in the Discussion section. Inverter An experiment was performed in which the battery was charged using the inverter and the battery charger that was delivered with the go-kart. The efficiency of the inverter is calculated below from the experiment: DC Power into Inverter: P = IV = (12V)(14.3A) = 171.6W AC Power out of Inverter: P = VRMSIRMS = (105.1VAC)(1.5) = 157.65W VRMS = VP / 2 = 105.1VAC IRMS = IP / 2 = 1.5A Efficiency: Inverter: (100)*157.65W/171.6W = 91.9% The AC Power was calculated using the RMS value or root mean square value of current and voltage. Assuming a power factor of unity and the voltage and current in phase, then P = (VRMS)(IRMS)(pf) = VRMSIRMS. The efficiencies were measured by dividing the output power by the input power. The charging current and voltage of the battery pack is shown below. These numbers were used to calculate the power. The changes in current and voltage are due to the algorithm of the battery charger and the state of charge of the battery.

16

Battery Charging Current vs. Time Using Inverter 3.5 3 2.5 2 1.5 1 0.5 0 0 50 100 150 200 Time (Minutes) 250 300 350 400

Figure 10 Battery Charging Current vs. Time In the charging current plot, it can be observed that the current initially ramps up from 2.7 to 3.0 Amps and then slowly decreases over time, especially once the time exceeds 300 minutes or 5 hours.
Total Battery Charge vs. Time using Inverter 50 45 40 35 30 25 20 15 10 5 0 0 50 100 150 200 Time (Minutes) 250 300 350 400

17

Figure 11 - Total Battery Voltage vs. Time The voltage increased linearly for the first 5 hours. At the five-hour point when current started to drop more rapidly, the voltage increased more quickly and then stabilized at about 43.5 Volts for the pack and 14.3 Volts for each individual battery. Once the current flow was secured, the battery voltage dropped to a steady-state open circuit voltage of about 39.9 Volts for the pack and 13.38 Volts for each individual battery. Power Supply The power supply is rated for an efficiency of 80% at 48VDC and 10 Amps according to the manufacturer data sheet. Below is the calculated efficiency of the power supply.
Power Supply Current vs. Efficiency 4.5 4 3.5 3 2.5 2 1.5 1 0.5 0 50 51 52 53 54 55 56 57 Efficiency (%)

Figure 12 Power Supply Efficiency The disparity between the data sheet and the calculated efficiency results from the limited current setting. The changing efficiency is due to the changing voltage of the battery input as it is depleted. A higher efficiency would be observed if the current was closer to 10 Amps. Intelligent Battery Charger The intelligent battery charger includes the buck converter, microcontroller, and voltage/current sensing circuits. The efficiency of the battery charger is shown below.

18

Charger Efficiency versus Charger Output Voltage 94 92 90 88 86 84 82 80 78 76 36 37 38 39 40 41 42 43 44 Charger Output Voltage (Volts)

Figure 13 Charger Efficiency The efficiency of the charger increased as the voltage became closer to the designed final full battery charge. Below is the overall efficiency of the entire circuit.
Overall Efficiency versus Charger Output Voltage 44 43 42 41 40 39 38 37 36 0 10 20 30 Charger Voltage (Volts) 40 50 60

Figure 14 System Efficiency

19

The discrepancy at 41VDC is due to the switching of the battery source. The inverter would not operate at such a low input voltage near 11VDC. Overall, the efficiency was low. The main problem is limiting the power supply to such a low current when it was designed for a higher efficiency at a higher current. The overall goal of charging the battery pack was accomplished and the result is shown in Figure 15.
Battery Voltage vs. Time 44 43 42 41 40 39 38 37 36 0 50 100 150 200 250 300 350 Time (Minutes)

Figure 15 Battery Charge Results Discussion The major problem encountered during the project was the fact that the fuel cell could not produce a voltage high enough to supply the needs of our circuit. At the required 28 amps for our inverter input, the voltage was less than 5 volts. In order to compensate, we used a 70 amp-hour 12VDC battery donated by Douglas Batteries Inc. in order to mimic the high current needed by the project.

20

Voltage vs. Current


20 Voltage (V) 15 10 5 0 0 5 10 Current (A) 15 20

Figure 16 Fuel Cell Voltage versus Current


Cell Power
120 100 Power (W) 80 60 40 20 0 0 5 10 Current (A) 15 20

Figure 17 Fuel Cell Power versus Current The low voltage and current developed by the fuel cell was not sufficient to drive the battery charging circuit as shown above. The power supply was rated at a higher efficiency at a higher output current. As a result of the limited current used, the overall efficiency of the project suffered. Improvements in the power supply can possibly be made if a bridge rectifier was used in place of the power supply bought commercially. Budget The following is an approximation of the cost of the device. The budget is based upon the actual cost of purchased items for the end product. The following is an estimate and may deviate from stated values if additional parts are necessary.
Component Express-PCB Circuit Board Quantity 1 Price (each) $73.00 Total $73.00

21

PIC Micro-controller TMP04 Temperature Detector IRF9540 P-Channel MOSFET LF353N Operational Amplifier DSEI8-6A FRED Diode M9908 Inductor DN1185-140V Diode 4MHz Crystal LCD Screen 48V/10A Power Supply Digital Multi-meter Voltage Inverter Current Shunt Resistor 12VDC, 0.12A Fan Project Enclosure Boxes Various Components Total

1 1 1 1 1 1 1 1 1 1 2 1 1 1 2 25

$4.00 $3.25 $1.68 $0.69 $1.41 $4.73 $3.44 $0.88 $18.00 $147.85 $17.95 $140.00 $4.38 $12.00 $15.00 $0.05

$4.00 $3.25 $1.68 $0.69 $1.41 $4.73 $3.44 $0.88 $18.00 $147.85 $35.90 $140.00 $4.38 $12.00 $30.00 $1.25 $482.46

Note: Budget for project was not limited and includes amounts for final design.

Table 1 Budget

22

Timeline The project timeline shown below has been followed since the beginning of the project. All steps have been completed.

Figure 18 Project Timeline Conclusion The intelligent battery charger has revolutionized the industry. Batteries are lasting longer, charging is safer, and chargers are becoming more versatile. The intelligent battery charger can be specifically tailored to a situation where a conventional charger cannot. In the case of the fuel cell powered go-kart, a conventional charger can only charge the battery pack when the kart is not being used. Even commercially available intelligent chargers cannot be used while the kart is in operation. Since most intelligent chargers are micro-controller based, the charger was coded in a way that allows for charging under the circumstances required by the fuel cell and the kart during kart operation. A product of this nature also has applications outside the fuel cell powered gokart industry and can prove to be a valuable product for electric vehicle applications as well as other portable electric power applications.

23

References Renewable Energy Resources Micro-Controller Motor Battery MAXIM National Instruments: ETID (Texas A&M): ExpressPCB http://www.sustainableenergy.org http://www.diversifiedengineering.net http://www.microchip.com http://www.kangoev.com http://www.interstatebatteries.com http://www.maxim-ic.com http://www.zone.ni.com http://www.entcweb.tamu.edu http://www.expresspcb.com and

24

Appendix Below is the computer code for the intelligent battery charger.
;****************************************************************************** ;****************************************************************************** ;************************* Battery Charger ********************************** ;****************************************************************************** ;********************* Fuel Cell Design Group ******************************* ;********** Peter Prizio, Anhtuan Truong, Brian Stevens ********************* ;****************************************************************************** ;****************************************************************************** ;***************** May 4, 2002 - Final Version ****************************** ;****************************************************************************** ;* * ;* Program Description: * ;* This program utilizes a current controlled duty cycle to charge a 36V * ;* battery pack from a 12V fuel cell. The charging algorithm mimics constant * ;* current charging by limiting the charging current between 4.3 and 4.5 * ;* Amps. This is accomplished by controlling the charging voltage. * ;* * ;* This program reads the temperature, voltage and current of the batteries. * ;* Once the desired voltage has been achieved, the charging stops. * ;* * ;****************************************************************************** ;****************************************************************************** ;****************************************************************************** ;* Program hierarchy ********************************************************** ;* * ;* Mainline * ;* Initial * ;* PWMON * ;* * ;* MainLoop * ;* TestMode * ;* Current * ;* Voltage * ;* Temperature * ;* DisplayAll * ;* goto MainLoop * ;* * ;* IntService * ;* Capture * ;* * ;****************************************************************************** list P=PIC16F874, F=INHX8M, C=160, N=77, ST=OFF, MM=OFF, R=DEC, X=OFF #include P16F874.inc __config(_CP_OFF & _PWRTE_ON & _XT_OSC & _WDT_OFF & _BODEN_OFF) errorlevel -302 ;****************************************************************************** ;******* Equates ************************************************************** ;****************************************************************************** Bank0RAM MaxCount equ equ H'20' 50 ;Start of Bank 0 RAM area ;Number of loops in half a second

;****************************************************************************** ;******* Variables ************************************************************ ;****************************************************************************** cblock Bank0RAM W_TEMP ;Temporary storage for W during interrupts STATUS_TEMP ;Temporary storage for STATUS during interrupts TEMP1 ;Temporary variable for BarChart subroutine TEMP3 ;temp stor for display TEMP4 ;temp store for display INT_FLAG ;keep track of alternate interrupts

25

LCD_TEMP CHARCODE TEMPSTG:10 CURRSTG:10 VOLTSTG:10

;keep track of LCD vector ;address location for RPG output ;temperature string ;Current String ;Voltage String

T1BEGINH ;temp for begin of T1 high T1BEGINL ;temp for begin of T1 high T1ENDH ;temp for end of T1 high T1ENDL ;temp for end of T1 low T1HIGH ;Temp time1 high byte T1LOW ;Temp time1 low byte T2HIGH ;Temp time2 high byte T2LOW ;Temp time2 low byte T2ENDH ;temp stor for t2 high end T2ENDL ; temp store for t2 low end CAP_FLAG ;Capture Flag ADCHARGETIME ;var to allow ad to charge up CHANGE CHANGE1 PWMFLG STATEVAR VOLTLOW VOLTHIGH SETDUTYH SETDUTYL OLDDUTYL OLDDUTYH DutyFlag WASTE1 WASTE2 WASTEDONE endc #include ;======= #define #define #define #define math.inc ;include math include file

#DEFINE rdAN0 rdAN1 startAD offAD b'11000001',ADCON0 b'01001001',ADCON0 ADCON0,2 ADCON0,0 ;set internal ad to read AN0 ;set internal ad to read AN1 ;start internal ad ;turn ad off

;****************************************************************************** ;****** Macro definitions ***************************************************** ;****************************************************************************** MOVLF macro movlw movwf endm macro movf movwf endm macro bsf endm literal,dest literal dest source,dest source,W dest

MOVFF

BANK1

STATUS,RP0

BANK0

macro bcf STATUS,RP0 endm ;****************************************************************************** ;****** Program Code ********************************************************** ;******************************************************************************

26

org nop goto org goto

H'000' Mainline H'004' IntService

;Reset vector ;Branch past tables ;Interrupt vector ;Branch to interrupt service routine

;****************************************************************************** ;****** LCDinit_Table subroutine ********************************************** ;****************************************************************************** LCDinit_Table movf addwf retlw retlw retlw retlw retlw retlw retlw LCD_TEMP,W PCL,F H'33' H'32' H'28' H'01' H'0C' H'06' H'00' ;Use LCD_TEMP as offset into table ;Send 3, 3, 3, 2 to obtain 4-bit interface ;Display has two rows ;Clear display ;Turn off display of cursor ;Increment cursor position automatically ;End-of-table designator

;****************************************************************************** ;****** DisplayC_Table subroutine ********************************************* ;****************************************************************************** ; ; This subroutine is called with LCD_TEMP containing the offset from CDS ; to the desired byte. DisplayC_Table movf LCD_TEMP,W addwf PCL,F CDS _ClrRow1End retlw dt retlw _ClrRow2End retlw dt retlw _ClrRow2Begin retlw dt retlw _Done1 retlw dt retlw _Done2 retlw dt retlw _Initial1 retlw dt retlw _Initial2 H'80' " Calibrating Initial Duty Cycle 0 " H'C0' " 0 Batteries Full " H'80' " 0 Charging Stopped " H'C0' " 0 " H'CF' " 0 " H'8F' " 0 " ;Copy LCD_TEMP to W ;add adequate offset to PCL

27

retlw dt retlw

H'C0' " For Correct Charging Current Level 0

"

;****************************************************************************** ;****** End of Tables ********************************************************* ;****************************************************************************** ;****************************************************************************** ;****** Mainline program ****************************************************** ;****************************************************************************** Mainline call call MainLoop call call goto Initial PWMON TestMode DisplayAll MainLoop ;Initialize everything

;*********************************************************************************** ; 32/16 Bit Unsigned Fixed Point Divide 32/16 -> 32.16 ; Input: 32 bit unsigned fixed point dividend in AARGB0,AARGB1,AARGB2,AARGB3 ; 16 bit unsigned fixed point divisor in BARGB0,BARGB1 ; Use: CALL FXD3216U ; Output: 32 bit unsigned fixed point quotient in AARGB0,AARGB1,AARGB2,AARGB3 ; 16 bit unsigned fixed point remainder in REMB0,REMB1 ; Result: AARG, REM <-- AARG / BARG ; Max Timing: 2+699+2 = 703 clks ; Max Timing: 2+663+2 = 667 clks ; PM: 2+240+1 = 243 DM: 9 FXD3216U CLRF REMB0 CLRF REMB1 UDIV3216L RETLW 0x00 ;************************************************************************************ ; 16x16 Bit Unsigned Fixed Point Multiply 16x16 -> 32 ; Input: 16 bit unsigned fixed point multiplicand in AARGB0,AARGB1 ; 16 bit unsigned fixed point multiplier in BARGB0,BARGB1 ; Use: CALL FXM1616U ; Output: 32 bit unsigned fixed point product in AARGB0,AARGB1,AARGB2,AARGB3 ; Result: AARG <-- AARG x BARG ; Max Timing: 6+248+2 = 256 clks ; Min Timing: 6+101 = 107 clks ; PM: 6+51+1 = 58 DM: 9 FXM1616U CLRF ACCB2 CLRF ACCB3 MOVF AARGB0,W MOVWF TEMPB0 MOVF AARGB1,W MOVWF TEMPB1 UMUL1616L RETLW 0x00 ;****************************************************************************** ;****** Initial subroutine **************************************************** ;****************************************************************************** ; ; This subroutine performs all initializations of variables and registers. ; Note: do Bank 1 initializations first, then set back to Bank 0 for ; remainder of program. Enable interrupts last. Initial BANK1 bsf ;Set register access to bank 1 PIE2,CCP2IE ;set CCP2 interrupt

28

MOVLF MOVLF MOVLF MOVLF clrf MOVLF MOVLF MOVLF BANK0 clrf clrf MOVLF MOVLF clrf clrf clrf clrf bsf MOVLF MOVLF MOVLF MOVLF MOVLF MOVLF MOVLF MOVLF MOVLF MOVLF MOVLF MOVLF MOVLF MOVLF MOVLF MOVLF MOVLF MOVLF MOVLF MOVLF MOVLF MOVLF clrf clrf clrf clrf clrf clrf clrf clrf clrf clrf clrf clrf clrf clrf clrf clrf

B'10000010',ADCON1 B'00001011',TRISA B'00000000',TRISB B'11010011',TRISC TRISD B'00000100',TRISE

;Select PORTA pins for ADC or digital I/O ;an4-an0 on ;Set I/O for PORTA ;Set I/O for PORTB; 1-4=out ;Set I/O for PORTC-bit 1 makes CCP2 an input ;Set I/O for PORTD ;Set I/O for PORTE

B'11000001',OPTION_REG ; Initialize option register for 32:1 ;prescale 66,PR2 ;Set register access back to bank 0 CCP1CON TMR2 ;initialize timer 2 B'00001100',CCP1CON ;Set CCP1 for PWM B'00000100',T2CON ;Initialize Timer 2 for a 1:1 prescale TMR0 ;initialize timer 0 TMR1L ;initialize to zero low and high bytes of timer 1 TMR1H ; T1CON ;to turn on set bit 0 to 1 (TMR1ON bit) T1CON,TMR1ON ;start timer1 b'00000101',CCP2CON ;init capture to rising B'00000000',INT_FLAG H'30',CHARCODE ;initialize interrupt flag ;initialize CHARCODE

H'C8',TEMPSTG ;POSITION OF beginning of temp str B'00101110',TEMPSTG+4 ; ASCII code for a decimal point H'DF',TEMPSTG+6 ;ASCII code for the degree symbol H'46',TEMPSTG+7 ; ASCII code for F A' ',TEMPSTG+8;put space here initially H'00',TEMPSTG+9 ; serve as the end-of-string designator. H'88',CURRSTG ;POSITION OF beginning of temp str B'00101110',CURRSTG+4 ; ASCII code for a decimal point H'DF',CURRSTG+6 ;ASCII code for the degree symbol H'46',CURRSTG+7 ; ASCII code for F A' ',CURRSTG+8;put space here initially H'00',CURRSTG+9 ; serve as the end-of-string designator. H'80',VOLTSTG ;POSITION OF beginning of temp str B'00101110',VOLTSTG+4 ; ASCII code for a decimal point H'DF',VOLTSTG+6 ;ASCII code for the degree symbol H'46',VOLTSTG+7 ; ASCII code for F A' ',VOLTSTG+8;put space here initially H'00',VOLTSTG+9 ; serve as the end-of-string designator. B'00000001',STATEVAR ;set variable to measure voltage PWMFLG AARGB0 AARGB1 AARGB2 AARGB3 CCPR2L ;init CCPR2 registers CCPR2H T1LOW T1HIGH T2HIGH T2LOW CAP_FLAG ;initialize capture flag PORTD ;Turn off LEDs PORTB PORTE VOLTLOW

29

clrf clrf clrf clrf clrf MOVLF clrf bsf bsf bcf bsf call bcf return

VOLTHIGH SETDUTYH SETDUTYL OLDDUTYH OLDDUTYL 1,DutyFlag INTCON INTCON,T0IE INTCON,PEIE INTCON,INTE ADCON0,0 InitLCD INTCON,GIE

;initialize interrupt control register ;enable timer 0 [bit5] ;enable peripheral interrupts for capture ;disable port b interrupts for RPG ;turn on a/d module ;set up lcd for Nibble mode ;global interrupt disable [bit7]

;****************************************************************************** TestMode btfsc goto nop btfsc goto nop btfsc goto nop btfsc goto btfsc goto nop Temperature bcf bsf bsf TEMPWAIT btfss goto call STATEVAR,2 STATEVAR,3 INTCON,GIE CAP_FLAG,2 TEMPWAIT CalcTemp ;Set state to test current again ;enable interupts ;stay in main loop ;until temp available PORTC,6 TestMode STATEVAR,0 VoltageTest STATEVAR,1 CurrentTest STATEVAR,2 Temperature STATEVAR,3 CurrentTestAgain ;check pwm if low, continue testing values

bcf INTCON,GIE ;disable interupts return ;go back to normal loop until temp avail. ;;;;;;;;;;Calculation;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; AARGB0, AARGB1,... where the B0 byte represents the ;most-significant byte. ; You need to take the 16-bit result from ;AARGB2 and AAARGB3, not from AARGB0 and AARGB1. ;Temperature = 4550 - [7200xT1/T2] ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; CalcTemp nop MOVFF MOVFF MOVLF MOVLF CALL MOVFF MOVFF CALL T1HIGH,AARGB0 T1LOW,AARGB1 H'1C',BARGB0 H'20',BARGB1 FXM1616U T2HIGH,BARGB0 T2LOW,BARGB1 FXD3216U ;move to most sign bytes for mult. ;move hex equiv of D'7200' ;H'1C20' to BARG regs. ;do multiply,result in AARGB0,1,2,3 ;BARG is only 16 bits ;0 is most sign. ;do division

;****** 16 bit literal subtract ********************************************** movf AARGB3,W ;D'4550'= H'11C6' now need 16 bit subtract.

30

sublw movwf movf btfss addlw sublw movwf

H'C6' AARGB3 AARGB2,W

;subtract lit-w, result in w ;put result in AARGB3 ;put high byte into w

STATUS,C ;if C=0->Carry from low byte 1 ;if C set add 1 back to high byte ;if carry bit=0 (not set) reduce high byte by 1 ;if carry bit=0 (not set) reduce high byte by 1 to 10 H'11' ;H'11'-w(+1), result in w AARGB2 ;put result back in AARGB2

;****************************************************************************** ;;;;;;;;;;Binary to ASCII Conversion;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;Peatman 154 ;After you have completed the calculation ; of the temperature, you need to take the two-byte binary result ;and break it into a three-ASCII-character result. ;If you divide the two-byte binary value by ten, the remainder ;will be the units digit value. Add H'30' to this to convert ;it to the ASCII code for the digit. ;Dividing the quotient by ten will yield the tens digit in the ;new remainder and the hundreds digit in the new quotient. ; ;Divide; Output: 32 bit unsigned fixed point quotient in ; AARGB0,AARGB1,AARGB2,AARGB3 ; 16 bit unsigned fixed point remainder in REMB0,REMB1 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; MOVLF MOVLF CALL movlw addwf MOVFF CALL movlw addwf MOVFF CALL movlw addwf MOVFF CALL movlw addwf movf sublw btfsc goto goto Blank Digit MOVLF MOVFF MOVLF MOVLF 10,BARGB1 ;divide result by 10 0,BARGB0 ;put 0 into BARGB0 FXD3216U ;remainder in BARG is units H'30' REMB1,F ;add H'30' to remainder in REMB1 REMB1,TEMPSTG+5 ;put in tenths of a degree slot ;divide again by 10 FXD3216U ;remainder in REMB1 =degrees H'30' REMB1,F ;add H'30' to remainder in REMB1 REMB1,TEMPSTG+3 ;put in degrees slot ;divide result by 10 again FXD3216U ; H'30' REMB1,F ;add H'30' to remainder in REMB1 REMB1,TEMPSTG+2 ;put in 10s degrees slot FXD3216U H'30' REMB1,F REMB1,W H'30' STATUS,Z Blank Digit H'20',REMB1 REMB1,TEMPSTG+1 H'02',REMB1 ;put in 100s degrees slot ;divide result by 10 again ; ;add H'30' to remainder in REMB1

;move space into 100's if = 0

H'DF',TEMPSTG+6

31

MOVLF MOVLF NewValue

A'F',TEMPSTG+7 A' ',TEMPSTG+8

clrf MOVLF bcf

CAP_FLAG

;reset capture flag to get new value ;set for rising edge

b'00000101',CCP2CON PIR2,CCP2IF

;clear CCP2 interrupt flag ;PIE2=H'8D'use indir. addr. to set interrupt ;enable [ CCP2IE bit].

MOVLF PIE2,FSR bsf INDF,CCP2IE return

;****************************************************************************** ;****** InitLCD subroutine **************************************************** ;****************************************************************************** ; ; Initialize the FEMA 16x2 character LCD display. ; (Initialize PIC ports prior to calling this subroutine.) ; This subroutine uses a one-byte RAM variable called LCD_TEMP. InitLCD MOVLF InitLCD_1 call decfsz goto bcf InitLCD_2 call iorlw btfsc goto movwf movwf swapf rlf movwf bsf bcf call rlf movwf bsf bcf call 25,LCD_TEMP LoopTime LCD_TEMP,F InitLCD_1 PORTE,0 ;Wait 1/4 second ;Call LoopTime 25 times ;LCD_TEMP now equals zero ;RS=0 for command

LCDinit_Table ;Get next byte, pointed to by LCD_TEMP H'00' ;Set Z flag if W=0 STATUS,Z InitLCD_done TEMP4 ;Save copy of byte TEMP3 ;save another copy TEMP4,F ;position for xfer part1 TEMP4,W ;position for xfer part2 PORTB PORTE,1 PORTE,1 LoopTime ;Send upper nibble ;Drive E high ;Drive E low so LCD will process input ;Wait ten milliseconds

TEMP3,W ;rotate and put into W for Xfer PORTB ;Send lower nibble PORTE,1 ;Drive E high PORTE,1 ;Drive E low so LCD will process input LoopTime ;Wait ten milliseconds ;Point to next byte ; and deal with it

incf LCD_TEMP,F goto InitLCD_2 InitLCD_done return

;****************************************************************************** ;****** DisplayC subroutine *************************************************** ;****************************************************************************** ; ; This subroutine is called with W containing the offset from CDS to the ; beginning of the desired constant display string. It uses a one-byte LCD_TEMP ; to the next byte in the display string DisplayC movwf bcf LCD_TEMP PORTE,0 ;Save pointer ;Drive RS pin low for cursor positioning code

32

DisplayC_1 call incf iorlw btfsc goto movwf movwf swapf rlf movwf bsf bcf rlf movwf bsf bcf call bsf goto DisplayC_done return

DisplayC_Table ;Get byte from string into W LCD_TEMP,F ;Point to the next byte 0 ;Set Z if end of string STATUS,Z ;if not, then go on DisplayC_done TEMP4 ;Save copy of byte TEMP3 ;save another copy TEMP4,F ;position for xfer part1 TEMP4,W ;position for xfer part2 PORTB ;Write upper nibble PORTE,1 ;Strobe E PORTE,1 TEMP3,W ;rotate and put into W for Xfer PORTB PORTE,1 PORTE,1 T40 PORTE,0 DisplayC_1 ;Write lower nibble ;Strobe E ;Wait 40 usec ;Drive RS pin high for displayable characters

;****************************************************************************** ;****** DisplayV subroutine *************************************************** ;****************************************************************************** ; ; This subroutine is called with W containing the offset from CDS to the ; beginning of the desired constant display string. ; This extracts the bytes of a display string from ram. ; It saves pointer in w to FSR ; to the next byte in the display string DisplayV movwf bcf DisplayV_1 movf incf iorlw btfsc goto movwf movwf swapf rlf movwf bsf bcf rlf movwf bsf bcf call FSR ;Save pointer PORTE,0 ;Drive RS pin low for cursor ;positioning code INDF,w ;Get byte from string into W FSR,F ;Point to the next byte 0 ;Set Z if end of string STATUS,Z ;if not, then go on DisplayV_done TEMP4 ;Save copy of byte TEMP3 ;save another copy TEMP4,F ;position for xfer part1 TEMP4,W ;position for xfer part2 PORTB ;Write upper nibble PORTE,1 ;Strobe E PORTE,1 TEMP3,W ;rotate and put into W for Xfer PORTB ;Write lower nibble PORTE,1 ;Strobe E PORTE,1 T40 ;Wait 40 usec ;Drive RS pin high for displayable characters

bsf PORTE,0 goto DisplayV_1 DisplayV_done return

;****************************************************************************** ;****** T40 subroutine ******************************************************** ;****************************************************************************** ; ; Pause for 40 microseconds (assumes 4 MHz crystal). T40

33

T40_1

movlw movwf

12 TEMP1

decfsz TEMP1,F goto T40_1 return ;****************************************************************************** ;****** LoopTime subroutine *************************************************** ;****************************************************************************** ; ; This subroutine waits for Timer2 to complete its ten millisecond count ; sequence. LoopTime clrf bcf LoopTimeWait btfss goto return ;****************************************************************************** ;****** IntService interrupt service routine ********************************** ;****************************************************************************** ; ; This interrupt service routine fields all interrupts. It first sets aside W ; and STATUS. It assumes that direct addressing will not be used in the ; mainline code to access Bank 1 addresses (once the Initial subroutine has ; been executed and interrupts enabled). It polls each possible interrupt ; source to determine whether it needs service. IntService nop movwf swapf movwf Poll btfsc goto Poll2 btfsc goto btfsc bcf Back ; Restore STATUS and W and return from interrupt swapf STATUS_TEMP,W ;Restore STATUS bits (unswapping nibbles) movwf STATUS ; without affecting Z bit swapf W_TEMP,F ;Swap W_TEMP swapf W_TEMP,W ; and swap again into W without affecting Z bit retfie ;Return from mainline code; reenable interrupts ;****** Capture Interrupt Handler ********************************************* ; ;Captures times for a pulse train input into RC1 [CCP2] ;Returns Delta T1 in T1High and T1Low, Delta T2 in T2LOW and T2HIGH ; Capture bcf PIR2,CCP2IF ;software reset of interrupt flag btfsc goto CAP_FLAG,1 CaptureT2 ;if second cycle,get T2 INTCON,INTF RPG INTCON,T0IF INTCON,T0IF ;Test PB0/INT flag ;increment or decrement CHARCODE ;test for timer 0 overflow ; clear T0 flag PIR2,CCP2IF Capture W_TEMP STATUS,W STATUS_TEMP TMR0 INTCON,T0IF INTCON,T0IF LoopTimeWait ;Check whether ten milliseconds are up

; Set ;Copy ;Move ;Copy

aside W and STATUS W to RAM STATUS to W without affecting Z bit to RAM (with nibbles swapped)

; Execute polling routine ;Check for interrupt (CCP2IF) ;save values and return

34

btfss goto btfsc goto goto

CAP_FLAG,0 ;get time of rising edge of T1 CaptureT1Begin CAP_FLAG,0 ;get time of falling edge of T1 CaptureT1End Poll ;go back to poll, execute return from int

CaptureT1Begin MOVFF MOVFF MOVLF bcf bsf goto CaptureT1End bcf MOVFF MOVFF MOVLF bcf bsf MOVLF goto CaptureT2 MOVFF MOVFF MOVLF CAP_FLAG,0 ;clear bit 0 of capture flag CCPR2L,T1ENDL ;save low byte CCPR2H,T1ENDH ;save high byte b'00000101',CCP2CON PIR2,CCP2IF CAP_FLAG,1 ;set for rising edge CCPR2L,T1BEGINL ;save low byte CCPR2H,T1BEGINH ;save high byte b'00000100',CCP2CON ;set back to falling PIR2,CCP2IF CAP_FLAG,0 Poll ;software reset of interrupt flag ;set cap_flag to get falling next

;clear CCP2 interrupt flag

;set bit 1 of capture flag so next pass ;gets T2 b'00000101',CCP2CON ;set back to rising Poll CCPR2L,T2ENDL CCPR2H,T2ENDH ;save low byte ;save high byte ;set for rising edge

b'00000101',CCP2CON

bcf bsf MOVLF clrf bcf

;clr bit 1 so we go thru nl sequence ;set bit 2 of capture flag so ;temp calc can proceed PIE2,FSR ;PIE2=H'8D'use indir. addr. to clear interrupt INDF ;enable [ CCP2IE bit]. ;i.e. no interrupts while calculating PIR2,CCP2IF ;clear CCP2 interrupt flag

CAP_FLAG,1 CAP_FLAG,2

;****************************************************************************** ;****** 16 bit subtract; T1END[B] - T1BEGIN[A] ******************************** ;;result in T1LOW,T1HIGH movf subwf movwf T1BEGINL,w ;move t1beginh to w T1ENDL,w ;sub f-w, result in w T1LOW

movf T1BEGINH,w ;move high byte into w btfss _C ;check carry bit from prev. subtr. incfsz T1BEGINH,w ;if carry NOT set incr subwf T1ENDH,w movwf T1HIGH ;******************************************************************************

35

;***** 16 bit subtract; T2END[B] - T1END[A] *********************************** ;;result in T2HIGH,T2LOW movf subwf movwf T1ENDL,w ;move t2 end to w T2ENDL,w ;sub f-w, result in w T2LOW

movf T1ENDH,w ;move high byte into w btfss _C ; check carry bit from first calc. incfsz T1ENDH,w subwf T2ENDH,w ;sub high bytes result in w movwf T2HIGH ;move from w to T2HIGH ;****************************************************************************** goto Back

;****************************************************************************** ;****** Voltage Test Subroutine *********************************************** ;****************************************************************************** VoltageTest bcf bsf STATEVAR,0 STATEVAR,1 ;set state to test current next

FirstVoltageTest Start MOVLF movlw movwf rdAN1 d'15' ADCHARGETIME ;load control for AN1

Wait4Settle decfsz ADCHARGETIME,F ;delay 30 usec for settling goto Wait4Settle bsf ADWait btfsc goto BANK1 movf BANK0 movwf MOVFF MOVFF MOVFF nop MOVLF MOVLF CALL nop MOVLF MOVLF CALL nop H'3',BARGB0 H'E8',BARGB1 FXM1616U H'7',BARGB0 H'91',BARGB1 FXD3216U startAD ;start conversion

ADCON0,GO_DONE ;check GO_DONE bit and continue if clear ADWait ADRESL,w AARGB1 AARGB1,VOLTLOW ADRESH,AARGB0 AARGB0,VOLTHIGH

;;;;;;;;;;Binary to ASCII Conversion;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;Peatman 154 ;After you have completed the calculation ; of the temperature, you need to take the two-byte binary result ;and break it into a three-ASCII-character result. ;If you divide the two-byte binary value by ten, the remainder ;will be the units digit value. Add H'30' to this to convert ;it to the ASCII code for the digit.

36

;Dividing the quotient by ten will yield the tens digit in the ;new remainder and the hundreds digit in the new quotient. ; ;Divide; Output: 32 bit unsigned fixed point quotient in ; AARGB0,AARGB1,AARGB2,AARGB3 ; 16 bit unsigned fixed point remainder in REMB0,REMB1 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; MOVLF MOVLF CALL movlw addwf MOVFF CALL movlw addwf MOVFF CALL movlw addwf MOVFF CALL movlw addwf movf sublw btfsc goto goto Blank1 MOVLF Digit1 MOVFF MOVLF MOVLF MOVLF MOVLF 10,BARGB1 ;divide result by 10 0,BARGB0 ;put 0 into BARGB0 FXD3216U ;remainder in BARG is units H'30' REMB1,F ;add H'30' to remainder in REMB1 REMB1,VOLTSTG+5 ;put in tenths of a degree slot ;divide again by 10 FXD3216U ;remainder in REMB1 =degrees H'30' REMB1,F ;add H'30' to remainder in REMB1 REMB1,VOLTSTG+3 ;put in degrees slot ;divide result by 10 again FXD3216U ; H'30' REMB1,F ;add H'30' to remainder in REMB1 REMB1,VOLTSTG+2 ;put in 10s degrees slot FXD3216U H'30' REMB1,F REMB1,W H'30' STATUS,Z Blank1 Digit1 H'20',REMB1 REMB1,VOLTSTG+1 H'02',REMB1 A' ',VOLTSTG+6 A'V',VOLTSTG+7 A' ',VOLTSTG+8 ;put in 100s slot ;divide result by 10 again ; ;add H'30' to remainder in REMB1

;move space into 100's if = 0

;****************************************************************************** ;****** 16 bit literal subtract *********************************************** movf sublw movf btfss addlw sublw btfss goto return VOLTLOW,W H'7C' VOLTHIGH,w STATUS,C 1 ;subtract lit-w, result in w ;put high byte into w

;if C=0->Carry from low byte ;if C set add 1 back to high byte ;if carry bit=0 (not set) reduce high byte by 1 ;if carry bit=0 (not set) reduce high byte by 1 to 10 H'03' ;H'11'-w(+1), result in w

STATUS,C TurnChargingOff2

37

;****************************************************************************** ;****** Turn On PWM If First Time Thru Program ******************************** ;* First check if first time thru and then check the initial voltage of the *** ;* battery and set initial duty cycle accordingly ***************************** ;****************************************************************************** PWMON ;****************************************************************************** movlw _Initial1 - CDS ;Put initial messages on the screen call DisplayC movlw call bcf bcf call nop MOVFF MOVFF nop MOVLF MOVLF CALL nop MOVFF MOVFF nop MOVLF MOVLF CALL nop btfsc bsf btfsc bsf rrf rrf movlw andwf swapf rlf rlf movlw andwf movf iorwf _Initial2 - CDS DisplayC CCP1CON,5 CCP1CON,4 FirstVoltageTest VOLTLOW,AARGB1 VOLTHIGH,AARGB0 0,BARGB0 57,BARGB1 FXD3216U AARGB2,AARGB0 AARGB3,AARGB1 0,BARGB0 4,BARGB1 FXM1616U AARGB3,0 CCP1CON,4 AARGB3,1 CCP1CON,5 AARGB3,f AARGB3,f B'00111111' AARGB3,f AARGB2,f AARGB2,f AARGB2,f B'11000000' AARGB2,f AARGB2,w AARGB3,f ;Clear bits of duty vyvle control ;Take Initial voltage values ;Move variables into arguments for ;calculations ;divide by frequency of PWM ;to get dutycycle value ;needed by PIC ;Move arguments to get proper ;inputs for 16x16 multiply ;multiply by frequency of clock ;also needed by PIC ;see page 60 ;set least sig bits for ;duty cycle control

;get rid of 2 least sig bits ;Clear two most sig bits to get rid of ;of any errors from the rotation and carry ;Set up the upper two bits in the 10-bit ;control. ;clear all other bits

MOVFF AARGB3,CCPR1L call WaitSeconds return

;or lower 6 and upper 2 bits together ;to get 8 bit value ;move into upper 8 bit duty cycle control

;****************************************************************************** ;****** Increase Duty Cycle Routine ******************************************* ;* Called from current test if current is too low. Need to slowly increase **** ;* the duty cycle to increase the output voltage to get desired current. ****** ;* Increases duty cycle by adding 2 to the CCP1R,CCP1CON<5:4> register. ******* ;****************************************************************************** IncreaseDutyCycle bcf STATEVAR,6 ;Clear flag that sets duty cycle to 0 ;In decrease routine movf CCPR1L,w ;check to see if duty cycle is too high

38

sublw btfsc goto btfss goto return Continue movlw btfsc goto goto AddHigh addwf bcf goto bsf goto

B'00111111' _C Continue CCP1CON,5 Continue

;If so put max suty cycle value in.

1 CCP1CON,5 AddHigh AddLow CCPR1L,f CCP1CON,5 CheckAgain CCP1CON,5 CheckAgain

;Check to see if bit 1 is high ;if so, goto AddLow ;else goto AddHigh

;Add 1 to upper 8 bits then clear bit ;1 of the 10 bits. i.e. add 2, but ;there is a carry to bit 3 ;set bit 1, (add 2 to bit 1, no carry)

AddLow

CheckAgain call WaitSeconds return ;****************************************************************************** ;****** Decrease Duty Cycle Routine ******************************************* ;* Called from current test if current is too high. Need to slowly decrease *** ;* the duty cycle to decrease the output voltage to get desired current. ****** ;* Decreases duty cycle by subtracting 2 to the CCP1R,CCP1CON<5:4> register. ** ;****************************************************************************** DecreaseDutyCycle btfsc STATEVAR,6 ;Check flag to see if duty cycle low goto CheckAgain btfsc CCP1CON,5 ;Check to see if bit 1 is high goto SubLow ;If so goto SubLow goto SubHigh ;Else goto SubHigh SubHigh bsf CCP1CON,5 decfsz CCPR1L,f goto bsf bcf bcf goto SubLow bcf goto CCP1CON,5 CheckAgain ;Clear bit 1, i.e. subtract 2 from 10;bit value CheckAgain STATEVAR,6 CCP1CON,5 CCP1CON,4 CheckAgain ;Set bit 1 of 10 bit value ;Subtract 1 from bit 0 of upper 8 bits ;i.e. Subtract 4 and add 2 ;if clear, skip and set duty = 0 ;Set low duty flag ;Clear duty cycle control registers

;****************************************************************************** ;****** Clear PWM DutyCycle *************************************************** ;* Called from voltage test if full voltage is reached. Simply clears the ***** ;* duty cycle control register to produce a duty cycle of 0%. ***************** ;****************************************************************************** PWMOFF ;;;Set MOVLF bcf bcf return Duty Cycle;;; B'00000000',CCPR1L CCP1CON,5 CCP1CON,4

;****************************************************************************** ;****** Current Test Subroutine *********************************************** ;

39

CurrentTestAgain bcf STATEVAR,3 bsf STATEVAR,0 CurrentTest bcf bsf MOVLF movlw movwf STATEVAR,1 STATEVAR,2 rdAN0 d'15' ADCHARGETIME

;set statevariable to test voltage next ;Set state variable to test temperature next ;load control for AN1

CheckCurrentLevel Wait4CurrSettle decfsz ADCHARGETIME,F ;delay 30 usec for settling goto Wait4CurrSettle bsf ADWaitCurr btfsc goto ; bcf startAD ;start conversion

ADCON0,GO_DONE ;check GO_DONE bit and continue if clear ADWaitCurr offAD

BANK1 movf ADRESL,w BANK0 movwf AARGB1 MOVFF AARGB1,VOLTLOW MOVFF ADRESH,AARGB0 MOVFF AARGB0,VOLTHIGH nop MOVLF 0,BARGB0 MOVLF 100,BARGB1 call FXM1616U nop MOVLF 3,BARGB0 MOVLF 192,BARGB1 CALL FXD3216U nop ;;;;;;;;;;Binary to ASCII Conversion;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;Peatman 154 ;After you have completed the calculation ; of the temperature, you need to take the two-byte binary result ;and break it into a three-ASCII-character result. ;If you divide the two-byte binary value by ten, the remainder ;will be the units digit value. Add H'30' to this to convert ;it to the ASCII code for the digit. ;Dividing the quotient by ten will yield the tens digit in the ;new remainder and the hundreds digit in the new quotient. ; ;Divide; Output: 32 bit unsigned fixed point quotient in ; AARGB0,AARGB1,AARGB2,AARGB3 ; 16 bit unsigned fixed point remainder in REMB0,REMB1 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; MOVLF MOVLF CALL movlw addwf MOVFF CALL movlw addwf MOVFF CALL 10,BARGB1 ;divide result by 10 0,BARGB0 ;put 0 into BARGB0 FXD3216U ;remainder in BARG is units H'30' REMB1,F ;add H'30' to remainder in REMB1 REMB1,CURRSTG+5 ;put in tenths of a degree slot ;divide again by 10 FXD3216U ;remainder in REMB1 =degrees H'30' REMB1,F ;add H'30' to remainder in REMB1 REMB1,CURRSTG+3 ;put in degrees slot FXD3216U ;divide result by 10 again ;

40

movlw addwf MOVFF CALL movlw addwf movf sublw btfsc goto goto Blank2 MOVLF Digit2 MOVFF MOVLF MOVLF MOVLF MOVLF

H'30' REMB1,F ;add H'30' to remainder in REMB1 REMB1,CURRSTG+2 ;put in 10s degrees slot FXD3216U H'30' REMB1,F REMB1,W H'30' STATUS,Z Blank2 Digit2 H'20',REMB1 REMB1,CURRSTG+1 H'02',REMB1 A' ',CURRSTG+6 A'A',CURRSTG+7 A' ',CURRSTG+8 ;put in 100s degrees slot ;divide result by 10 again ; ;add H'30' to remainder in REMB1

;move space into 100's if = 0

;****************************************************************************** ;****** 16 bit literal subtract *********************************************** ;* Used to check if current is too low. *************************************** ;****************************************************************************** movf VOLTLOW,W sublw movf btfss addlw sublw btfsc goto H'DC' VOLTHIGH,w ;subtract lit-w, result in w ;put high byte into w

STATUS,C ;if C=0->Carry from low byte 1 ;if C set add 1 back to high byte ;if carry bit=0 (not set) reduce high byte by 1 ;if carry bit=0 (not set) reduce high byte by 1 to 10 H'01' ;H'11'-w(+1), result in w STATUS,C IncreaseDutyCycle

;****************************************************************************** ;****** 16 bit literal subtract *********************************************** ;* Used to check if current is too high *************************************** ;****************************************************************************** movf VOLTLOW,W sublw movf btfss addlw sublw btfss goto return H'17' VOLTHIGH,w ;subtract lit-w, result in w ;put high byte into w

STATUS,C ;if C=0->Carry from low byte 1 ;if C set add 1 back to high byte ;if carry bit=0 (not set) reduce high byte by 1 ;if carry bit=0 (not set) reduce high byte by 1 to 10 H'02' ;H'11'-w(+1), result in w _C DecreaseDutyCycle

41

;****************************************************************************** ;****** Turn off charging ***************************************************** ;* Display end of charging message and clear duty cycle *********************** ;****************************************************************************** TurnChargingOff2 call PWMOFF movlw call movlw call goto _Done1 - CDS DisplayC _Done2 - CDS DisplayC StopAll

;****************************************************************************** ;****** Display Subroutine **************************************************** ;* Clears the LCD and displays the new values ********************************* ;****************************************************************************** DisplayAll movlw call movlw call movlw call MOVLW call MOVLW call MOVLW call return ;****************************************************************************** ;****** WaitSeconds Subroutine ************************************************ ;* Utilizes LoopTime to pause for about 2 secs. between duty cycle calcs. ***** ;****************************************************************************** WaitSeconds MOVLF 150,LCD_TEMP ;Wait 2 seconds Waiting call LoopTime ;Call LoopTime 25 times decfsz LCD_TEMP,F goto Waiting return ;****************************************************************************** ;****** StopAll Subroutine **************************************************** ;* Stops all further action of the charger and must be reset. ***************** ;****************************************************************************** StopAll nop goto StopAll end _ClrRow1End - CDS DisplayC _ClrRow2End - CDS DisplayC _ClrRow2Begin - CDS DisplayC VOLTSTG DisplayV CURRSTG DisplayV TEMPSTG DisplayV

42

Das könnte Ihnen auch gefallen