. ‘ .5 . i . €53.61 ran“?! hi... 4.241...." zihwsu 5.4.9.3“. \ army». . v . I: Rn; Qr‘ulcvfizz .. 5.3 a...» ‘ ‘;;d%%$h .ibnafi .. . .u. h. ,m:. {iii-h. a. in 9% null... .. I." i 1...: ..x.u1:dr.r.hua t , .l t. . :n. D A?x$awaaukorfli :9 o ....t....ia}..zx.&1.. d. rt... .. v . ‘ .2 h..." mmrns Ii 5.3.3.3:(9933? 1... 2.1.35! . u . 7 s lit): 5t§\..\1|...01 . x. la} fact-6...... .31. . u, . .. .135?sz y ‘P . . lit? (3....- . . a? {.6 1 ‘ . ‘ . ,‘;%z . V_ . . a . . . M . .x z , . . ‘; , 13:8” Lil‘BRAthate W”? lel nligversity This is to certify that the thesis entitled DESIGN AND CONTROL OF A MULTILEVEL INVERTER FOR ELECTRIC VEHICLES presented by Arthur W. Matteson has been accepted towards fulfillment of the requirements for the MS. degree in Electrical Engineering WW Major Professor’s Signature /'74/p/7/ 36/ 2008 Date MSU is an affirmative-action, equal-opportunity employer . -.-.-.-o-- PLACE IN RETURN BOX to remove this checkout from your record. TO AVOID FINES return on or before date due. MAY BE RECALLED with earlier due date if requested. DATE DUE DATE DUE DATE DUE 5/08 K'lProj/Aoc8PrelelRC/DateDue.indd DESIGN AND CONTROL OF A MULTILEVEL INVERTER FOR ELECTRIC VEHICLES By Arthur W. Matteson A THESIS Submitted to Michigan State University in partial fulfillment of the requirements for the degree of MASTER OF SCIENCE ELECTRICAL ENGINEERING 2008 ABSTRACT DESIGN AND CONTROL OF A MULTILEVEL INVERTER FOR ELECTRIC VEHICLES Bv 1/ Arthur W. Matteson Typical AC induction motor drive systems in electric vehicles use full-amplitude pulse width modulation inverters. These have many drawbacks including the requirement of a cooling system, lack of integrated battery management, and often non-integrated and/or low-power charging. There is also an issue of safety with a permanently connected series battery pack, and motor winding insulation is degraded in this type of controller. Moreover, the size and mass of these inverters reduce available vehicle storage space. An ideal solution is to provide a small inverter for each battery, so that its charge can be maintained individually and a low-distortion sine wave can be applied to the motor. A battery charger is then intrinsic in this layout. This work presents the development and implementation of a such a multilevel inverter. Two separate designs are constructed, each including a master module and six slave modules. Results from driving a GM EV1 using the second design are presented. Hardware and software designs are attached, including PC interface code in ‘C++’ and algorithm simulation code in Python. ACKNOWLEDGMENTS I would first like to thank my advisor, Dr. Michael Shanblatt, and committee members, Dr. Norbert Mueller and Dr. Shantanu Chakrabartty. Dr. Shanblatt has been a great influence, especially during my undergraduate years here at Michigan State, by providing me the rare opportunity to conduct an individualized senior design project with Texas Instruments. The knowledge and experience I gained from this project are invaluable. I have also enjoyed working with Dr. Mueller on the Solar Car Team, of which he is the adviser, and on unrelated projects in his laboratory. I would also like to thank Joel Anderson, Daniel West, and the rest of the Solar Car Team for their support. Joel originally told me of multilevel inverters; without him this project probably would not have been done. Daniel’s assistance in construction of the inverter and his loyalty have been extremely valuable, and enough appreciation cannot be shown to him. Furthermore, I would like to thank my friend Paul Kucher, who has been of immense support during the writing of this thesis. He provided me with IATEX, Python, and matplotlib assistance, and his help made my authoring much more efficient. He also provided general moral support and advice on this work, from inception to completion. My parents, Robert and Anne Matteson, my brother, Andrew Matteson, and my grandfather, Donald Matteson, have all been of help during this project. Andrew reviewed this document very thoroughly and provided great input to revise it to its final state. Donald gave me the opportunity to work on his own electric vehicle, from which I gained vast experience. iii TABLE OF CONTENTS iv LIST OF TABLES .............................. vi LIST OF FIGURES ............................. vii LIST OF ABBREVIATIONS ....................... ix 1 Introduction ................................ 1 1.1 Shortcomings of Electric Vehicle Power Electronics .......... 1 1.2 The Multilevel Inverter as a Solution .................. 3 1.3 Requirements and Difficulties of Implementation ............ 5 1.4 Approach to Overcome Challenges .................... 6 Algorithm Development ......................... 8 2.1 Backbone Implementation Overview ................... 8 2.2 Communication Design .......................... 11 2.3 Battery Selection Algorithm Design ................... 15 2.3.1 Theory ............................... 15 2.3.2 Single—Cycle Discharge Distribution ............... 17 2.3.3 Algorithm Evaluation ....................... 21 2.4 Battery Charging ............................. 27 2.5 Motor Control ............................... 30 Slave Module Development ....................... 31 3.1 Hardware Design One ........................... 31 3.1.1 Power Section ........................... 31 3.1.2 Microcontroller .......................... 34 3.1.3 DC/ DC Converter ........................ 36 3.1.4 Printed Circuit Board ...................... 37 3.2 Redesign Considerations ......................... 39 3.3 Hardware Design Two .......................... 40 3.3.1 Power Section ........................... 40 3.3.2 Microcontroller .......................... 41 3.3.3 DC/DC Converter ........................ 41 3.3.4 Printed Circuit Board ...................... 43 3.4 Software .................................. 45 3.4.1 Main Loop ............................. 45 3.4.2 Serial Interrupt .......................... 45 3.4.3 Bootloader ............................. 48 4 Master Module Development ...................... 49 4.1 Hardware Design One ........................... 49 4.2 Hardware Design Two .......................... 52 4.3 Software Design One ........................... 54 4.3.1 Main Loop ............................. 54 4.3.2 USB Interrupt ........................... 56 4.3.3 Bootloader ............................. 57 4.4 Software Design Two ........................... 58 4.4.1 PIC18F2553 Code ........................ 58 4.4.2 DSC Main Loop .......................... 59 4.4.3 DSC Bootloader .......................... 59 4.4.4 Serial Port Interrupts ....................... 59 5 Results ................................... 60 6 Conclusions ................................ 67 6.1 Design One ................................ 67 6.2 Design Two ................................ 67 6.3 System and Topology ........................... 68 6.4 Future Work ................................ 68 APPENDICES ................................ 71 A PC Interface Software .......................... 71 Al Overview of Software ........................... 71 A2 User Interface Layout: monitor.ui .................... 73 A3 Header File: monitor.h .......................... 78 A.4 Main Code: monitor.cpp ......................... 80 B Slave Printed Circuit Board Layouts ................. 121 C Battery Discharge Distribution Python Script Example ...... 128 D Slave Assembly Code, Design Two .................. 132 E Master Code Listings .......................... 152 E1 DSPIC30F4011 USB Assembly Code, Design One ........... 152 E2 PIC18F2553 Full Assembly Code, Design Two ............. 173 E3 DSPIC30F4011 Full Assembly Code, Design Two ........... 182 BIBLIOGRAPHY .............................. 230 LIST OF TABLES 2.1 Battery Selection Algorithm Simulation Results ............. 24 3.1 1 Megabaud Command Enumeration ................... 47 3.2 125 Kilobaud Bootloader Command Enumeration ............ 48 vi 1.1 2.1 2.2 2.3 2.4 2.5 2.6 2.7 2.8 2.9 2.10 2.11 2.12 2.13 2.14 2.15 2.16 2.17 3.1 3.2 3.3 3.4 4.1 4.2 5.1 5.2 5.3 5.4 5.5 5.6 5.7 5.8 5.9 A.1 LIST OF FIGURES Basic Overview of the Multilevel Inverter. ............... 4 Positive Third Harmonic Injection Amplitude Increase. ........ 10 Negative Third Harmonic Injection Amplitude Decrease. ....... 10 Design One’s Elementary Serial Transmission Buffering ......... 12 Worst-case Communication Bus Loading ................. 12 Battery Charge Regulation, Equal Charge. ............... 16 Battery Charge Regulation, Inequal Charge. .............. 16 Line-to-neutral Generated Waves. .................... 18 Line-to—line Generated Waves. ...................... 18 Discharge Distribution, No Third Harmonic, 020.112 .......... 19 Discharge Distribution, Positive Third Harmonic, 0:0.126 ....... 19 Discharge Distribution, Negative Third Harmonic, 0:0.053. ..... 20 Discharge Dist., 8 Batteries, PF=0.85, Non-hybrid Pack. ....... 22 Time-Spent-On Dist., 8 Batteries, PF=0.85, Non-hybrid Pack. . . . . 22 Discharge Dist., 16 Batteries, PF=0.85, Hybrid Pack. ......... 23 Time-Spent-On Dist., 16 Batteries, PF=0.85, Hybrid Pack ....... 23 Discharge Dist., Battery One Initially 30 Charge Units Depleted. . . . 25 Time-Spent-On Dist., Battery One Initially 30 Charge Units Depleted. 25 Slave Module Design One Schematic. .................. 38 Slave Module Design Two Schematic ................... 44 Communication Bus Byte Format ..................... 46 Slave Module Automatic Frequency Calibration ............. 46 Master Module Design One Schematic. ................. 51 Master Module Design Two Schematic. ................. 53 Slave Module Output Current Waveform, 130A Peak. ......... 61 Slave Module MOSFET Avalanche Waveform, 23V Peak ........ 61 Slave Module PWM Illustrating an 800ns Dead Time .......... 62 Line-to-line Inverter Output Voltage, Six Total Slave Modules. . . . . 62 Design-One Master Multitasking During a USB Transfer ........ 63 EV1 Captured Motor Variables, I;=8A, Slight Loading. ....... 65 EV1 Captured Motor Variables, I 5 =6A, Three Loadings. ....... 65 EV1 Captured Motor Variables, I;=40A, Driving ............ 66 EV1 Captured Motor Variables, 15:40A, Driving ............ 66 Monitor v0.5 Screenshot with Six Battery Modules .......... 72 vii B.1 Slave Design One PCB Top Layer .................... 122 B2 Slave Design One PCB Bottom Layer .................. 123 B3 Slave Design Two PCB Top Layer .................... 124 BA Slave Design Two PCB Power Layer .................. 125 B5 Slave Design Two PCB Ground Layer .................. 126 B6 Slave Design Two PCB Bottom Layer .................. 127 Images in this thesis are presented in color. viii A / D AGM AWG CRC DIP DPST DSC EEPROM FIFO FOC ICE IGBT de LED MBd MIPS MLCC MOSFET PCB PFC PWM SOC SPST THD uC LIST OF ABBREVIATIONS Analog to Digital Absorbed Glass Mat American Wire Gauge Cyclic Redundancy Check Dual In-line Package Double-Pole, Single-Throw Digital Signal Controller Electrically Erasable Programmable Read-Only Memory First In, First Out Field-Oriented Control Internal Combustion Engine Insulated-Gate Bipolar Transistor Kilobaud (thousand bits per second) Light-Emitting Diode Megabaud (million bits per second) Millions of Instructions per Second Multilayer Ceramic Capacitor Metal-Oxide-Semiconductor Field-Effect Transistor Printed Circuit Board Power Factor Correction Pulse Width Modulation State of Charge Single-Pole, Single-Throw Total Harmonic Distortion Microcontroller ix CHAPTER 1 Introduction 1.1 Shortcomings of Electric Vehicle Power Electronics Factory-built electric vehicles (EVs) such as the GM EVl or Toyota RAV4-EV used a high-voltage battery pack consisting of many series cells, nominally 300V or above. An inverter employing full-amplitude pulse-width modulation (PWM) drove the main motor in both cases. PWM was used to generate a set of time-averaged, near- sinusoidal voltages for the motor. This configuration has many drawbacks. A battery pack of this potential is extremely hazardous when permanently connected in series. Many active and passive safety mechanisms must be in place to prevent electric shock to a service person. The electromagnetic interference caused by the PWM is substantial, and the signals are not healthy for the motor because of wire insulation breakdown (high dV/dt) and magnetic core loss. The efficiency of these insulated- gate bipolar transistor (IGBT) inverters is typically around 95% [1]. Unfortunately, IGBTs are not optimized to switch at low loads — those occurring most often in an electric vehicle — because of their relatively constant voltage drop. Some users find a low PWM frequency acoustically offensive, though it is more efficient. There are also issues with reliability because of the fast switching speeds and high voltages. The charging system of these vehicles also had limitations. Though it is possible to use the motor as a boost inductor to do power factor correction (PFC) [2], the input voltage is limited to about 265VAC (only one-phase), and neither the EV1 nor RAV4—EV employed this technique. Both factory-built vehicles needed a large, expensive, external converter unit for quick charging, and this was only available in a few selected areas where it had been chosen to be installed. EV1 owners were obliged to purchase a charging unit for their garage for over $2,000 after installation costs [3]. Management and balancing for a series-cell pack of about 300V requires at least some repeated circuitry for each battery, or many wires routed to a main control unit (EV1). Finally, the main power inverter and its liquid cooling system took up a considerable amount of space, which was already at a premium in the two—seat EV1 (about 32x14x5in. under the hood was needed by the inverter alone). AC Propulsion, a small company based in California, provides inverter/ motor combinations for EVs. Their second-generation AC—150 is a 150kW IGBT inverter paired with an equally powerful custom-design induction motor, both air-cooled [2]. It contains a charger capable of up to 20kW, and uses the motor as a boost inductor to correct its power factor seen at an outlet. It also includes a powerful DC/ DC converter for 12V accessories. It is capable of discharging the vehicle’s batteries onto the power line, features traction control, and operates at a peak efficiency of 91%. The second-generation AC-150’s mass is 80kg, impressive for a system of that power considering a typical 20HP industrial motor is more massive [4], except that the large-dimension inverter constitutes 30kg of that. With EVs already stuffed full of batteries it is a challenge to find a large enough space for such a device. This 150kW power level provides performance comparable to a 300HP internal combustion engine (ICE). AC Propulsion’s Lithium-Ion T-Zero demonstration vehicle, which uses the AC-150, accelerates from 0—60MPH in only 3.6 seconds [5]. This is not necessary for most vehicles, especially those with non-advanced batteries, but this system provides no means of downscaling without a redesign. The datasheet makes no claims of individual battery management capability, and the system does not support hybrid- chemistry battery packs — which could make EVS more affordable, flexible, and upgradable — or at least not hybrid packs with differing capacity modules. Still, the AC-150 sets a bar for performance to be matched or exceeded. PFC is basically a necessity for quick recharging. Regenerative braking is also extremely desirable for EVs; most DC-motor EVS do not have it. Smooth torque control is important in a replacement design. The 85-90% efficiency should be met or exceeded, especially under typical driving conditions. An alternative inverter should be smaller and manage the batteries as well as or better than a typical AC EV setup. There should also be at least the availability of a 150kW power output, but also the ability to meet demands of all sizes at a reasonable cost. 1.2 The Multilevel Inverter as a Solution A multilevel power inverter is a system that can produce a varying output voltage from a given number of interconnected sources without the need for full-amplitude PWM [6]. The multilevel topology being considered here, intended exclusively for a drive motor application in an electric vehicle, consists of many batteries in series through full-bridge inverters that can act as three-state sources: positive twelve volts, zero volts, or negative twelve volts (roughly). In this design, the number of voltage output. levels for each line-to—neutral voltage is twice the number of batteries per phase plus one. Typical drive motors are three-phase induction machines; this type will be the focus in this project. For this motor, the pack’s batteries will be divided into three groups, all connected together at one end and connected to the three motor phases at the other end. A basic schematic can be seen in Figure 1.1. The aim of this thesis is to develop a control algorithm along with a complete implementation for this setup. The major goal is to achieve the highest possible output frequency While managing the batteries to have the highest range capacity and the most equal Charge, while keeping hardware costs to a minimum. Three-phase Motor Master Module Phase C Control Phase A Phase 8 F T I I I Slave Slave __ Slave Slave L Slave Slave Module Module ‘ Module Module 7 Module Module "[ I I I I l I I l I I I 1 Battery Battery Battery Battery Battery Battery Neutral Figure 1.1. Basic Overview of the Multilevel Inverter. The multilevel topology suggested here solves many of the problems associated with traditional PWM drives. The inverter modules (or battery modules) measure only 3.7x2.7x0.5in. each after thermal epoxy potting and conveniently sit atop each sealed lead-acid battery. Only a small amount of extra space is needed for other components, including the master controller. Since the heat loss is distributed, no active cooling mechanism is needed. Instead of IGBTs, metal oxide semiconductor field-effect transistors (MOSFETS) are used for this low-voltage application. MOSFETs are preferred because of their low losses, both conductive and switching, and will give the inverter an efficiency of 98% or above at typical power levels. The switching frequency is typically in the range of 300Hz, compared with 5--20kHz for a PWM inverter. The dV/dt going to the motor will be about 50V/us as compared with 1,000V/IJS, prolonging its reliability and decreasing its heat loss. A high-power charging system is inherent in the design of this multilevel inverter with the addition of a small inductor; contactors can switch the three module groups in series to support up to 480VAC directly with PFC (no external hardware needed). Battery balancing — even charging a weak battery -— can be done during motoring by the control algorithm. A weak and / or damaged battery can be totally bypassed, a feat impossible in the EV1 or RAV4—EV. The highest voltage in a parked vehicle will be the highest battery voltage (about 12V). Finally, a high-power DC / DC converter will be distributed across the battery pack, already built into the battery modules. As few as 15 or as many as 120 of these modules will be supported. 1.3 Requirements and Difficulties of Implementation In general, this topology increases the burden on software, and presenting a design challenge for the master control processor. Many tasks need to be implemented in the master. First of all, module tokens need to be generated to send on the serial bus, in the end form of three sinusoids. A module token usually consists of a seven-bit module address, a two-bit module command, and optional nine-bit arguments. The module command tells the addressed battery module to either turn on positive, turn on negative, turn off, or watch the bus for more extended data (arguments). In the first master design, these tokens are fed through a software FIFO (first in, first out) buffer so that they still can be sent out during an interrupt, though they typically will be sent out in the main loop. The control algorithm must take into account the charge present on each battery. More highly-charged batteries are selected more often to come out of an off state, so that the charge will be equalized among all. The same algorithm also attempts to keep the battery current as level as possible, though ripple is inherent in this hardware design. There needs to be some means of controlling the motor past a simple V/ Hz algorithm to help its dynamic response and efficiency. This involves many calculations and measurements, mainly measuring and processing the three motor phase currents. The microprocessor must devote time to read back accurate battery voltages from the modules, and at some point while the vehicle is parked, calibrate the modules against an accurate master battery voltage measurement by turning on only one at a time. The processor must also control accessories such as contactors and read the status of the ignition, while monitoring temperatures. Finally, a communication interface to a modern computer and an accompanying multi-platform software package is a necessity for setup and troubleshooting. 1.4 Approach to Overcome Challenges The processor choice for the multilevel inverter master control unit is the dsPIC30F 4011 from Microchip Technology, Inc [7]. It is termed a digital signal controller (DSC) and is intended for use in motor control applications. It has a 10— bit, 12-input analog to digital (A / D) converter operating at up to 500kSPS (thousand samples per second). The DSC accepts up to 5V for main power and comes in a 40-pin dual in-line package (DIP), ideal for prototyping. It features a high—speed serial port ~ up to 1.875 megabaud (MBd) without overclocking —— necessary for sending data to the individual inverter modules. It nominally runs at 30 million instructions per second (MIPS), comparatively fast for Microchip products. It has 83 base instructions each a minimum of 24 bits wide (mostly single-cycle), 48kB of Flash program memory, and 2kB of RAM. It also features predecrement, preincrement, postdecrement, and postincrement by one, two, or four for byte, word, or long addressing respectively, as Well as other addressing modes —— mainly useful for signal filtering. Assembly language is used exclusively to program this DSC and the accompanying Atmel microcontrollers (,qu) inside the battery modules. Typical ‘C’ coding is relatively inefficient at memory and cycle usage. Memory and time resources will be at a premium due to the complex, real-time tasks this master is asked to perform. One of the only benefits of using ‘C’ would be portability, but that is not a concern at this time; developing and demonstrating the algorithm is the main goal. A debugging and/ or data collection interface for any complex system such as this one is an important inclusion. For the multilevel inverter, a USB connection is ChOsen. Nearly every computer has at least one USB port and availability is especially important in a mass-produced design. USB includes error checking and flow control over only three wires. A fourth wire (+5V) can also signal to the DSC when the plug is inserted. Version numbers and strings can be built into the USB descriptors, so the user knows immediately which system they have after connection. Finally, master and slave firmware upgrades are possible through USB. Unfortunately, the DSC does not include a USB peripheral. Due to the hardware and cost minimization efforts, it initially was decided to manually implement 1.5MBd USB in software. The Microchip PIC18F67J50 does include a full-speed (12MBd) USB peripheral and fast serial ports, but is only an eight-bit processor, comes only in surface—mount packages, and runs at 3.6V maximum [8]. The dsPIC30F4011 is a 16—bit processor, and its instruction set is far more powerful, even supporting a 17x17- bit multiply. There are (16) 16-bit working registers, while the 18F67J50 has only one eight-bit working register, comparatively. There are also 21 types of conditional branches in the DSC, and it has two 40-bit saturating accumulators for higher math. Finally, it runs at 30MIPS vs. the 18F67J50’s 12MIPS. In the second master design, a PIC18F2553 is added to the circuit since there Were reliability issues with the DSC’s bit-banging implementation. The 18F2553 is Capable of full-speed USB, and allows a computer to record 64 bytes of inverter Status data every millisecond. Included in the 64 bytes is the current status of every module ~— off, on positive, on negative, or performing PWM. The 30F4011 and 18F2553 communicate over a 2MBd serial bus. Though the first master design Used a module token buffer, this is not necessary in the second version because the Serial communications are received in hardware, not software. More details can be f011nd in Chapter 4. CH .llo CHAPTER 2 Algorithm Development 2.1 Backbone Implementation Overview Individual battery modules in the pack must be turned on and off at specific time intervals to produce synthesized sine waves. This requires the modules to be connected by some sort of a communication bus. The DSC must calculate the time intervals for each module to be on and off, accounting for individual battery state of charge (SOC) and other factors. A motor control algorithm must work on top of all this, maximizing the efficiency of the drive system at all times. Simultaneous communication to a Computer through USB is also a necessity. The synthesized sine waves put out by the inverter set should support both positive and negative third-harmonic injection. This technique is considered because SOrnetimes it could be used in the positive form to increase the line-to—line voltage at the motor for high-speed operation, allowing the practical use of 48 battery modules for a two—pole, 230VAC motor capable of around 4,500RPM. It also could be used in the non-traditional, negative form to spread out the charge among batteries by reCIucing the modulation index, though this depends on the chosen battery selection algorithm. A depiction of the ‘voltage—extending’ property of positive third-harmonic 1n.Iection can be seen in Figure 2.1. An opposite injection can be seen in Figure 2.2. AC induction motors are typically controlled with an approximately constant V / Hz ratio because of their nearly linear impedance increase with higher frequency operation. Typical motors, like the 15HP EMB314T from Baldor [9], require a three- phase source of 230VrmS line-to—line nominal at 60Hz. At 30Hz this motor would require about 115Vrms for the same torque output. A traditional PWM drive would require 230Vrms*\/2 = 325V of DC bus to operate at 60Hz, using either positive third harmonic injection or space vector PWM. This is equivalent to about 28 lead- acid batteries under load. To operate with full torque at 4,500RPM, this motor’s maximum safe speed, about 36 batteries would be required. The three sections of the multilevel converter are connected in a wye fashion, so each section’s output is a line- to—neutral voltage. These subtract to create line-to—line voltages as seen in Figures 2.1 and 2.2. The greatest attainable line-to-line voltage, shown in Figure 2.1, has a peak twice that of the line-to-neutral voltage. Therefore, for a 230Vrms motor at 60Hz, the necessary line-to-neutral voltage peak is about 163V or 14 batteries — half that of the PWM inverter for each section, for a total of 42. To operate at 4,500RPM, a total of 54 batteries would be needed. The quotient of the RMS line-to—line voltage divided by the peak line-to-neutral voltage is x/2 c: 1.414 for positive third harmonic injection, 2/\/6 '2: 0.816 for negative third harmonic injection, and £72— 2 1.225 for I10 injection, giving a total range of x/3 2 1.732 by shape alteration alone. Positive Third Harmonic Injection Amplitude Increase Line Battery Voltage (Unconnected Neutral) Units 0.0 0.2 0.4 0.6 0.8 1.0 Tune Units Figure 2.1. Positive Third Harmonic Injection Amplitude Increase. NegativeThird" ' Inject-inn ‘ I‘m ‘ Decrease Line Battery Voltage (Unconnected Neutral) Units -2 I . ' 0.0 0.2 0.4 0.6 0.8 1.0 Time Units Figure 2.2. Negative Third Harmonic Injection Amplitude Decrease. 2.2 Communication Design The DSC’S asynchronous serial port features nine-bit transmission and reception. This is handy for addressing up to 128 objects along with a two-bit command. For safety, values 0 and 127 do not address modules, but perform other functions instead: an address of 0 tells all pCs to enter their bootloaders, and an address of 127 tells all qu to recalibrate their clocks to the DSC’s bit widths. A command of ‘11’ (two passive bits) means turn off the addressed module, ‘01’ means turn on positive, ‘10’ means turn on negative, and ‘00’ means data is to follow. Since the dsPIC is a 16- bit processor, these nine-bit values can be stored in a memory word by themselves. Modules carry an address of 1—120, and values 121~126 have other functions, such as “blink all module LEDs.” Because of the need for USB and reception from modules, the design-one multilevel inverter requires two separate types of buffering, one done inside the DSC and the other done inside the “Cs of the battery modules (Atmel ATTiny24s). Timestamps alongside the module tokens allow the DSC’s USB interrupt routine to send out bytes When delaying for a bit transition on the USB data lines D+ and D-, no matter which Way data is flowing on the lines. This buffer is 256 bytes long. By sending out two Sets of nine-bit words, the DSC can also give the ,qu a relative time in the future to turn on or off — a delayed command. This second type of buffering allows a module to send data back to the DSC (battery voltage) while delayed commands are behig executed by the other modules. Both types of buffering are crucial to glitchless 0Deration. An example of the latter type of buffering can be seen in Figure 2.3. The application of design-one DSC’s FIFO buffer is described in Section 4.3. The design-two inverter does not require the use of either buffering type because of the module serial bus’ pseudo-full—duplex operation. In addition, USB is done eKternally to the dsPIC30F4011. The buffering concept is still being illustrated here fOI‘ academic purposes. Results of both designs are discussed in Chapter 5. 11 Design One’s Elementary Serial Transmission Buffering 10 . . r 1 DD... Module transmissions: Turn on positive (937.5de) Turn on negative (937.5de) Turn off (937.5de) Timestamp (937.5de) Request data (937 .5de) Reply data (115.2de) .1 . V O O 1 D Line-to-neutral Battery Voltage Units <3 DD... 0.0 0.2 0.4 0.6 0.8 1.0 Time Units Figure 2.3. Design One’s Elementary Serial Transmission Buffering. Comm. Bus Loading with 120 Modules at 120Hz, Pos. Third Harm. Inj. I T l I 9.0999 h. tn 0\ ‘V cm Fraction of Bandwidth C) i» 0.1 ' O L l l 1 (9000 0.001 0.002 0.003 0.004 0.005 Time (Seconds) Figure 2.4. Worst-case Communication Bus Loading. 12 Figure 2.4 depicts the worst-case scenario of bus loading: a full 120 modules switching to make full—amplitude waves at 120Hz with positive third-harmonic injection. The topmost curve is the sum of the magnitudes of the output voltages’ rates of change. The higher the rates of change, the more messages that need to be transmitted on the bus. Normally, only a one-word module token would be needed to command a slave module to turn its output on or off (slave buffering requires two-word tokens: one command and one timestamp). The multilevel inverter output voltages in this scenario can be approximated in terms of equal battery voltage increments by: sin(7207rt) 6 ] battery voltage units, (2.1) 80 U3pinj = E [Sin (2407ft) + 2n) sin(7207rt) + — V3pmj = % [sin (240m? + 3 6 ] battery voltage units, (2.2) 4 " 2 t and ngmj = 3% [sin (240w: + g) + M] battery voltage units. (2.3) The magnitude of the derivatives can be represented as follows: 1 2 -'. . U3pinj’ = -9———\/%93 abs [cos (2407rt) + Egg—012)] messages/s, (2.4) 19200 2 ' , 72 t V3pmj1= —\/T7r abs [cos (240le + g) + TEN—22.712] messages/s, (2.5) 192 4 ' 2 t and W3pmj/ = —-\/—0§01 abs [cos (240m + g) + Sin—(77912] messages/s. (2.6) The total approximate bus loading is the sum of these derivatives divided by the maximum number of messages per second. With a design-one serial line running at 9375de, and nine-bit data with one start bit and two stop bits, 78,125 messages Can be sent every second. The bus loading with no slave module buffering is therefore Shown as the topmost curve on Figure 2.4. This is given by: U3pinj’ + V3pinj’ + W3pinj’ 78125 BusLoad3m-nj 2 fraction of bandwidth. (2.7) 13 It should be recognized that for 120 modules at 120Hz — which corresponds to either 3,500RPM or 7,000RPM at 600VAC line-to—line, nearly 150kW/200HP in a typical setup -— the bus loading never exceeds 80%. Condensing these messages to the beginning of a bus loading cycle gives nearly 75% bus loading average in design one, leaving about 25% bandwidth open for other data. This extra time can be used to receive data from modules at 115.2de, up to five bytes’ worth theoretically. A method was devised to accomplish this; however, it was never fully coded in design one. In design two, the ATTiny24s can reply at the full 1MBd, so no module token buffering nor condensation is necessary for proper operation. Though the average forward bus loading is reduced to less than 70%, a practical safe maximum output frequency is no higher than 125Hz for 120 modules and 250Hz for 60 modules. 14 2.3 Battery Selection Algorithm Design 2.3.1 Theory Some type of algorithm to decide when each battery module should turn on or off is necessary to support variable-frequency and variable-amplitude waves. Three duplicate algorithms are needed, one for each motor phase. The inputs to the algorithms must be line-to-neutral phasors — either sin(t), sin(t) + sin(3t)/6, or sin(t) — sin(3t) / 2, each shifted by 120 and 240 degrees of fundamental for algorithms two and three respectively. The processing between input and output is somewhat complicated, needing to take into account the charge remaining on each battery. In an ideal environment, each battery contains an equal amount of charge and therefore should be discharged the same amount as the rest. A visualization of one algorithm’s output is shown in Figure 2.5. It resembles a pyramid, where each battery is on for an equal amount of time. An equal charge will be taken out of the batteries in this case only for a constant—current load, which is quite different from the actual load —- but the general technique can still be demonstrated. For a pack of inequally charged batteries, a scheme such as that shown in Figure 2.6 Could be used. Battery 3 has the highest remaining charge and Battery 4 has the lowest. Therefore, the amount of time Battery 3 is switched on is longer than that 0f Battery 4 (19 vs. 13 time units respectively). The disadvantage of such a scheme iS the complication in attaining the goals set forth: 1) battery modules should only SWitch at most two times a half-cycle to reduce bus loading, and 2) there should be Sufiicient time between module transitions to keep inductive ringing to a minimum. The battery on-times have to be carefully planned inside the ‘pyramid’ waveform to remain contiguous. Because of the variety of output shapes that need to be aceOmodated, a lookup table for every amplitude and pack battery count is not praCtical. Even apart from these issues, only a constant current will produce the 15 Line—to-neutral Battery Voltage Units Line-to-neutral Battery Voltage Units Battery Charge Regulation, Equal Charge 10 8 778 5. 66678 - 5555678 444445678 33333345678 2222222345678 111111112345678 _ éééé 0 111111112345678 2222222345678 33333345678 444445678 5555678 -5 6 6 6 7 8 ' 5 o 7 s 7 7 s E E E E 8 0.0 0.2 0.4 0.6 0.8 Time Units Figure 2.5. Battery Charge Regulation, Equal Charge. 1.0 Battery Charge Regulation, Inequal Charge 10 . . . . 7 667 5. 88867 . 4444867 333334867 55555534867 2222222533867 111111112553367 111111111553367 2222222543867 55555543867 444443867 3333867 88867 667 7 ,eeee ease l l 0.0 0.2 0.4 0.6 0.8 Time Units Figure 2.6. Battery Charge Regulation, Inequal Charge. 16 1.0 desired results. In conclusion, analysis needs to be done to derive the true discharge distribution for any given output waveform. 2.3.2 Single-Cycle Discharge Distribution Figure 2.7 depicts the synthesized output voltage for a single-phase 40—battery module string, the largest to be accomodated. Figure 2.8 shows the associated difference — shifted only for clarity —- between two subtracted line-to-neutral waveforms. Though the peak voltage in the former figure varies, the amplitudes of the differences do not. This is accomplished by third-harmonic injection. The switching scheme used here is similar to that in Figure 2.5, where batteries are turned on and off inlthe same order, with the exception of the small negative excursion on the negative third- harmonic injection wave. All of the graphs presented here have been generated in the Python language using matplotlib under Linux. This allows simulations to be carried out in a completely textual environment, much faster for development than a large, complex package such as Mathworks’s MATLAB or Simulink. The same matplotlib can be embedded in the open-source software interface in 'Ifolltech’s Qt4, useful for comparing empirical to theoretical results. A selected script, used for the following section’s simulations, is included in Appendix C. 17 Line-to-neutral Generated Waves I I I I 40 r - Negative Third Harm. 30 r . i a l—F—l———\—\—\ 5° 20 . :5 Positive Third Harm. N no 10 .. I 0 0 100 200 300 400 500 Time Units Figure 2.7. Line-to—neutral Generated Waves. 60 Line-to-line Generated Waves No Third Harm. Positive Third Harm. Battery Voltage Units Negative Third Harm. _w l 1 l 1 0 200 400 600 800 Tmte Units Figure 2.8. Line-to—line Generated Waves. 18 1000 Discharge Distribution with Vth=0.5"Vbat, No Third Harm, PF=0.85 r r l r I 0.30 ' ‘ 0.20 ' 1 Charge Units _O y—I U1 0.10 r . 0.05 ' ' 0'00 5 10 15 20 25 30 35 40 Battery Number Figure 2.9. Discharge Distribution, No Third Harmonic, o=0.112. Discharge Distribution with Vth=0.5‘Vbat, Pos. Third Harm., PF=0.85 l 0.30 — , 0.20 r ‘ Charge Units 5: H u: 0.10 ' 0.05 ' - 0’00 5 10 15 20 25 30 35 40 Battery Number Figure 2.10. Discharge Distribution, Positive Third Harmonic, 0:0.126. 19 Discharge Distribution with Vth=0.5‘Vbat, Neg. Third Harm, PF=0.85 020 L 0.15 — _ 5 o 10 - - 'a' . :3 Q) g 0.05 - - 0.00 ‘ III - —0'w — I | I | A l | I 5 1o 15 20 25 30 35 40 BatteryNumber Figure 2.11. Discharge Distribution, Negative Third Harmonic, o=0.053. Figures 2.9, 2.10, and 2.11 show the distribution of charge taken out of the batteries for a half-cycle. Notice the much lower standard deviation (0) for the negative third—harmonic injection. It would provide a way of spreading out the charge distribution for a simplistic algorithm. See the end of this chapter for a conclusion regarding injection. The unusual effects for the first four batteries in this graph are due to the negative excursion in Figure 2.7. During the second negative excursion, the i11Volved batteries are actually charged instead of discharged (batteries #1 and #2). This can be applied to the weak cells in the pack, if any exist, or to random batteries Otherwise. For algorithm simplicity, no battery will be used twice in a half-cycle. Neglecting non-ideal effects such as battery manufacturing and/or temperature IniSmatch, each battery should be chosen for the #1—40 spot an equal amount of time so that all are discharged equally. This potentially could be done with a sort of Shift register, but the problem would occur when less than 40 batteries were needed. Furthermore, transitioning between two difierent shift register lengths would require 20 various initializations so that the first battery would not be overly discharged. Even with a pack of closely matched batteries, there will be small variances in capacity and SOC for which the selection algorithm needs to adjust. During operation, the master module will read the individual battery voltages from the slave modules and adjust their estimated SOCs. The entire system is limited by the lowest- SOC battery, so the most ideal situation is to have all at the same SOC all the time. This requires a more complex algorithm than a random chooser. For flexibility, various battery capacities of the same or a different chemistry in the same string could be accomodated. Potentially a hybrid pack could be constructed to increase range per weight, containing both lead-acid and lithium- ion cells. Connecting different capacity batteries in series would be unhelpful for increasing range in a series pack, like the one the EV1 used. The ability to use a combination of various chemistries together without any system modification besides an electrically erasable programmable read-only memory (EEPROM) data reprogram represents a major advantage of the multilevel inverter, especially as lithium-ion batteries are still impractically expensive in large quantities. 2.3.3 Algorithm Evaluation An experimental algorithm was developed in and tested with Python and matplotlib. The single-phase routine turns on the highest-SOC batteries first and turns off the lowest-SOC batteries first. Note that SOC is unitless and is found by dividing remaining charge by capacity. Figures 2.12 and 2.13 depict the discharge and time- Spent-on distribution for eight batteries producing a sine wave with a maximum line-to—neutral amplitude of eight battery voltage units. The graphs represent 100 fundamental cycles (about 50Hz), which corresponds to only two seconds. Figures 2.14 and 2.15 Show distributions for a hybrid pack of 12 lead-acid and four lithium-ion batteries, where the lithium-ion have twice the capacity of the lead-acid. 21 Discharge Dist. for 100 Cycles, Vln(max)=8, PF=0.85, Non—hybrid Pack 42.0 - . fl 1 . . . - 41.5 ’ 40.5 ‘ 400 1 2 3 4 5 6 7 8 Battery Number Figure 2.12. Discharge Dist., 8 Batteries, PF=0.85, Non-hybrid Pack. Time Spent On Dist. for 100 Cycles, V1n(max)=8, PF=0.85, Non-hybrid Pack 63-0 ' ' ' l r v v I 62.5 ‘ 62.0 ' Tune Units (3 H in 61.0 ' 60.5 ‘ 60.0 1 2 3 4 5 6 7 8 Battery Number Figure 2.13. Time-Spent—On Dist., 8 Batteries, PF=0.85, Non-hybrid Pack. 22 Discharge Dist. for 100 Cycles, Vln(max)=8, PF=0.85, Hybrid Pack Charge Units )_| U! 10 ' l 5 . l l 0 l | 2 4 6 8 10 12 14 16 Battery Number Figure 2.14. Discharge Dist., 16 Batteries, PF=0.85, Hybrid Pack. Time Spmt On Dist. for 100 Cycles, Vln(max)=8, PF=0.85, Hybrid Pack 40 l l a” ’ D E h 20 ' l 10 ' 0 2 4 6 8 10 12 14 16 Battery Number Figure 2.15. Time-Spent-On Dist., 16 Batteries, PF=0.85, Hybrid Pack. 23 Total PbA LiIon Cycles % Disch. Span % Time Span 8 8 0 100 0.274 1.188 8 6 2 100 38.715 12.906 10 10 0 100 0.551 1.234 10 8 2 100 0.587 19.319 16 16 0 100 1.008 2.054 16 12 4 100 0.886 9.038 8 8 0 3000 0.009 0.039 8 6 2 3000 38.599 12.527 10 10 0 3000 0.018 0.041 10 8 2 3000 0.020 18.023 16 16 0 3000 0.033 0.061 16 12 4 3000 0.027 7.487 16 12 4 10000 0.010 7.367 16 12 4 100000 0.001 7.115 Table 2.1. Battery Selection Algorithm Simulation Results. Table 2.1 gives the simulation results for the discharge and time-spent-on mismatch between batteries. The mismatch is calculated by dividing the maximum Charge / time by the minimum, and subtracting 100%. Clearly this algorithm is totally Satisfactory for any pack type, giving an incredibly low discharge mismatch of 0.001% for the 12—PbA/4-Lilon pack over the span of less than an hour of driving. If the batteries were completely matched, the vehicle’s range would increase by a mere tWo feet over the tiny mismatch. Even if the batteries begin badly imbalanced, the algorithm can correct this; see Figures 2.16 and 2.17. It has a discharge mismatch of only 0.248% even though only 100 cycles passed. Note that the 8/ 6/ 2 lines in the table above represent the fact that if the required amplitude is eight batteries, then t'here must be at least that many batteries of approximately equal capacity for the aIgorithm to function properly. 24 Discharge Dist. for 100 Cycles, Battery One Initially 30 Charge Units Depleted 46.0 ' Y ' V v v I u 45.5~ 1 2 3 4 5 6 7 8 Battery Number Figure 2.16. Discharge Dist., Battery One Initially 30 Charge Units Depleted. Time Spent On Dist. for 100 Cycles, Battery One Initially 30 Charge Units Depleted 70 . v - - - . . - Time Units 8 8 8 B l 2 3 4 5 6 7 8 Battery Number Figure 2.17. Time-Spent-On Dist., Battery One Initially 30 Charge Units Depleted. 25 This algorithm is easy to implement in the DSC. Its most intensive computation is the minimum- and maximum-searching, which will only be across 40 batteries per phase maximum. The implementation is briefly discussed in Section 4.4. As shown, this algorithm works with no third-harmonic injection. Positive and negative injection capability could be added, but only positive injection would provide a benefit since it can increase the line-to-line motor voltage and hence motor speed. Negative injection is not needed to smooth the charge distribution, but it may provide secondary empirical benefits. This is yet to be determined and neither injection is supported in the second version of the DSC’s code, to be left for further investigation. Also, a function could be implemented to bypass low-power, high-energy cells such as lithium-ion during periods of heavy acceleration, but this is also left as an expansion upon this work. At low motor speeds, PWM will need to be used on a per-module basis to control Current. PWM is implemented in a loop in each slave module; the master sends a. module token with an argument of pulse width and polarity to a slave and the Slave carries out eight PWM cycles in 364us (11,650 clock pulses in the design-two master). The algorithm must handle PWM somewhat differently since the energy uSed in a PWM cycle depends on the pulse width, and since a PWM command is automatically turned off after eight periods. PWM also needs to be combined with n()Imal module operation, and while one slave module cannot be used for PWM twice in a row because of communication overlap, only one module in each string will be uSed for this purpose at a time. A 15-battery system — the minimum limit because of 12OVAC recharging capability — will have at most 12 modules on at any given tiIne while performing PWM. The simplest solution for this algorithm accessory is to choose the least discharged available battery —— one that had not been used for the previous PWM. This will not interrupt the algorithm’s normal regulation and the Cli'Scharge distribution will remain as it is in Table 2.1. 26 ‘ “<6. 2.4 Battery Charging The source of most, if not all, power for vehicle recharging will come from 120/240VAC single-phase grid outlets. During charging, the three module strings are connected in series and are placed across the outlet terminals along with a small series inductor to smooth the battery voltage transitions. The goal is to create a seemingly resistive load so that the outlet’s load current waveform will exactly resemble its voltage in shape and phase. The voltage across the inductor, placed across two identical 60Hz AC voltage sources shifted in phase by 26 can be represented as follows: VL = 170V- [cos(377t + 26) — cos(377t)] = —340V - sin(377t + 6) - sin(6). (2.8) The inductor 60Hz current is then: 4 V- ' 6 4 V- ‘..6 IL = —3 O Lsml ) / sin(377t+6)dt = 3 037752“ )cos(377t+6) (2.9) 4 v. ' .9 IL(7‘ms) = 3 0 827“ ) at 60HZ. (2.10) «E . 377L The worst-case inductor ripple voltage can be approximated by a sine wave with peaks +/-10V and a frequency of half the average switching frequency of 15 batteries per quarter—cycle, or 1.8kHz. The ripple current that results is: 1 11,, = —%Y-/sin(11310t)dt = —113\:Lcos(11310t) (2.11) 1V IL7‘(rms) = m at 1.8kHZ (2.12) I M ~ 0001 2 0.028 for 20 = 4°. (2.13) IL(7‘ms) — 3271(9) The power factor at the fundamental current angle 6, disregarding ripple, is: PF = cos(6) 2 0.99756 for 26 = 4°. (2.14) Both results are more than satisfactory for a high total power factor, which includes a. total harmonic distortion (THD) element as well. 27 Battery selection during charging will operate similarly to motoring, except that the minimum and maximum are reversed. Less-charged batteries will be turned on first and left on for the longest. The voltage phase angle will be modified depending on how much current is commanded; the battery voltage will be lagging the outlet voltage by about 4° during normal operation. Absorbed glass mat (AGM) batteries, like all lead-acid batteries, require a careful charging algorithm to minimize hydrogen gassing which reduces their lifetime. Typically, a balance is struck between charging time and gassing, and the charging voltage is simply capped at about 2.4V/ cell. Though this is often adjusted based on the cell temperature, it does not provide an interactive means of reducing gassing and may also unnecessarily increase charging time. Lead-acid cells have a charge acceptance that varies substantially with SOC. Practically, the cell acts as a varying Current source. When the cell is discharged, it can accept a great amount of current, perhaps even surpassing 2.4V/ cell when resistance is taken into account. When the Cell reaches "IO—80% SOC, the current source reduces in magnitude and increasing the Voltage will not increase the current much, but will make the cell gas more. In this inverter, the charging algorithm attempts to detect the current at which the battery impedance increases substantially and sets this as the current command. At 90% SOC, this current may be as low as 500mA for a 22Ah battery, but at 20% SOC, Could be as high as 40A. No user input is needed to determine this charge acceptance Clllrrent, besides the nominal battery capacities in the master’s data EEPROM. The intrinsic battery current pulsation due to the design of the multilevel inverter has implications for battery capacity, lifetime, and charge acceptance. A literature review was done for the effect of superimposed AC currents in lead-acid batteries. The papers’ results varied [10, 11,12]. Some indicated that some frequencies of alternating Cllrrent decreased capacity. Other results, including a test done for this work, showed IIloderate increases in capacity (10% or less). Techniques were developed in Wilkinson 28 and Covic to evaluate the best method of pulsing to increase charge acceptance [10]. Tests showed that an 800ms period with a 25% duty cycle vastly decreased the necessary charging time, allowing an 80% charge in only two hours. This is easily possible with the multilevel inverter and requires no extra hardware. A typical 60- battery system could separate the batteries into groups of 15 and charge each group for 200ms from 120VAC. This requires no extra hardware and the necessary software modifications are minimal, and adjustable at that. In the second slave hardware design, DC outputs are supported. This would theoretically allow charging from a DC source such as a solar panel collection, an anticipated future source of EV recharge energy. The slave modules would still be in series and could be turned on and off depending on how much charge was still depleted in their associated batteries. This method is not covered nor attempted in this work, but the master module permits future code modification to allow this technique if it is ever needed. AC pulse charging, DC charging, and other optimizations are left as future improvements to this thesis. 29 2.5 Motor Control AC motors are not as simple to control as DC motors because there are two variables to regulate instead of one: amplitude and frequency. This work does not take the approach of field-oriented control (FOC), which requires many motor parameters and a shaft speed sensor. There is no standard for shaft speed sensing; the EV1 used a custom 128-line sensor built-in to the transmission assembly. Speed sensors are often costly and difficult to add, and most AC motors have no published parameters. Speed estimation is a difficult task, and V/Hz control is not sufficient by itself because it requires a speed command which is unsuitable for automobiles. For the code used in this thesis, Id and L, are regulated by means of voltage and frequency, respectively. Id is kept constant and the vehicle’s throttle (and / or brake) determines Iq. In the master module, Clarke and Park transforms decompose three phase currents and an output voltage angle into Id and L1 [13]. These currents are filtered and fed to the proportional controllers. The output voltage is then adjusted depending on the result of a comparison of Id to 1;. The latter value is a reference, about 12A for a 230V, 15HP motor. The frequency also may be adjusted depending On the throttle value. If Iq is too high, there is too much slip, so the frequency is I‘ecluced; the converse is also true. 30 CHAPTER 3 Slave Module Development 3.1 Hardware Design One 3 .1.1 Power Section The most critical component selection of the slave module design is the power MOSFET switch. The IRF1324S-7P from International Rectifier is chosen for this design because of its low 24V Vds rating, giving it a low Rd3(0.,,) of down to 0.8mf2. At least four of these are needed per battery module because of the full bridge topology, So both positive, negative, and zero output voltages can be produced. This MOSF ET Comes in a seven-pin D2Pak, with five source pins to help reduce the resistance to Such a low level. These extra pins also help moderately with heat dissipation. It is rated for 429A continuously at a case temperature of 25°C and 1640A maximum, one Of the most current-dense MOSFETs on the market. The conduction loss for the module operating at the maximum RMS phase current of 150A is continuously 45W assuming a 1mQ on-resistance from temperature rise. This can be spread fairly evenly across the four MOSFETs with certain techniques. Though 45W is high for the module’s size, it is encapsulated in thermal epoxy and Such a current will be rare. Note that a 75A current will produce one-quarter this 1083. Neither the motor nor the batteries will be able to stand 150A for long because Of resistive heating. The total module switching loss for one transition cycle can be 31 approximated as: Eloss = Vbatlphaseur + tfl/Z .lOUIeS- (3-1) For each transition, mainly one MOSFET will be dissipating energy because of the inductive load’s constant current, and the average voltage across the switch is half the supply. To reduce dV/dt, a slower transition speed of about las will be used. For a maximum phase current of 150A and a maximum battery voltage of 15V, only about 4.5mJ of energy is dissipated in one cycle. At 250Hz (half the maximum transition frequency of 500Hz) or a 125Hz output frequency, this represents just over 1.1W of loss total, or an average of 281mW per device. The switching loss is therefore almost negligible in this application. More typical values of loss —— at half the motor’s rated power at its rated speed —— are closer to 25mW per module. An avalanche rating is important for this device because there will be no capacitor across the full bridge. Any time battery power is removed from the phase string, the battery voltage at the MOSFETS will spike up because of stray inductance. The MOSFETS must be rated to absorb this spike. The energy stored in a stray inductor Can be calculated with the following standard equation: E = L12/2 joules. (3.2) An inductance of 250nH, which includes wiring from the battery to the module and a fuse, could be considered worst-case. With this value and the maximum phase Clurent of 150A, 2.8mJ is stored. This is far below the 230mJ single-pulse rating of the IRF1324S-7P, and considerably within the power dissipation limits. If the module Were switching off at the maximum frequency of 250Hz, this 2.8mJ every 4ms would I‘Epresent only 700mW of loss per module. Under typical driving conditions, this Value will decrease similarly to the switching loss by a factor of over 40. Clearly, the conduction losses will dominate the module’s temperature and the iIlverter’s efficiency. At full load with 120 modules and 200 feet of 4AWG wire, a 32 worst-case scenario, total losses are (46W * 120) + (150A2 * 0.24859/ 5) = 6.64kW. This is high, but it should be noted that the total power output to the motor is approximately 40 * 11V * \/2 * 150A * \/3 = 161.6kW, giving an inverter/wiring efficiency of nearly 96%. Again, typical efficiencies will be over 99% due to the greatly reduced MOSFET conduction loss at cruising power (about 1W per module). Some type of gate drive IC is needed for the four MOSFETS because of the 12V gate bias required for both the high and low-side switches. The IRS2004, also from International Rectifier, is the ideal theoretical solution [14]. Each IC can drive two MOSFETS in a half-bridge configuration, and the part draws little quiescent current. The maximum output currents for source and sink are 130mA and 270mA respectively (worst-case). These parts each require a bootstrap capacitor and diode to help provide the floating gate drive supply for the high-side MOSFETS, up to about 25V with reference to the battery negative terminal. If the high-side MOSFETS were P-channel, this would not be necessary; however, considerably fewer models of P-channel devices are produced because of the limited hole mobility and thus worse performance. In this gate drive IC’s operation, a low voltage on the high-side source (also the low-side drain) charges the capacitor through the diode. Therefore, the high-side MOSFET Cannot be turned on continuously. Beginning with a charged capacitor, and neglecting the charge taken to turn on the switch since it is relatively small, the decay time of the capacitor’s voltage can be estimated from its leakage current as follows: dV [leak = Cboot—a’f—S‘ amps. (3'3) Therefore: dt = CbootflB—‘S: seconds. (3.4) [leak Knowing the nominal leakage current is BOaA [14], allowing for a 2V V35 drop, and choosing a 2.2/1F capacitor, dt can be found to be 147ms. This is enough time for 33 the purposes here, though a longer time would be desirable at the expense of module cost. A lower capacitance value could also be used; however, 2.2pF allows non- PWM output frequencies down to about 3Hz without any algorithm complication. A multilayer ceramic capacitor (MLCC) was considered and tested for this part; unfortunately, there were many gate drive IC failures due to ceramic’s DC bias effect. The chosen 2.2uF had only 0.4uF at 12.5V, causing the bootstrap supply to drop many volts and create too high of a dVB S /dt when the VS line returned to zero. The capacitor’s recharging inrush current may have also contributed to the failures. Instead, a tantalum capacitor turned out to be the best choice for this 2.2pF part, having a medium internal series resistance of around 3.59 at lOOkHz. These VB S decay calculations assume a negligible Schottky diode leakage. The generic BAS70 has a very small leakage current compared to 30uA, so it can be neglected [15]. 3.1.2 Microcontroller Cost is the most important factor in selecting microcontrollers for the slaves because of the sheer number (typically 45—60) that will be in a vehicle. The Atmel AVR series offers very competitive pricing for the speed and feature set, down to $111 per hundred 20MHz ICs. The ATTiny24 offers an adjustable internal clock, a 10-bit A / D converter, nearly one instruction per cycle, data EEPROM, and self-programmable Flash memory [16]. A serial port peripheral is not important to have, since it easily can be implemented in software. The ATTiny24 can clock itself up to about 16MHz Without an external crystal; 11.25MHz will be used to coincide with the 9375de serial transmissions, giving twelve cycles. Up to 1000 instructions can be stored in the processor’s non-volatile memory and it has enough pins for all the necessary functions. These include two main gate drive outputs (both with LEDs), a gate driver enable line, two DC/ DC converter gate drive outputs, voltage and temperature sense lines, transmit and receive lines, and an input button (actually on the reset pin). 34 The ATTiny24 supports up to 5V operation. This supply voltage will be generated from the battery via an LP2950 regulator in a TO-92 package [17]. 3.3V could also have been used, but this would have been marginal for the 11.25MHz frequency according to Figure 20—2 in [16]. Isolation between slave modules’ battery negative terminals is required in this inverter because of the multilevel topology. Since the IRS2004 gate drivers do not contain any isolation, it will have to be done on the communication bus side. Optocouplers are the simplest solution and are at worst moderately expensive, but still considerably less than the MOSFETs. A bidirectional communication scheme that requires only two wires is possible if both polarities are used. Reducing the number of wires is important if up to 120 modules are to be connected in parallel on the logic side. The master module can drive LEDs contained inside Optocouplers from the two-wire bus, turning on the output transistor of each and signaling bits to the ATTiny24. This will be done at the rate of 9375de because of the master’s clock rate. An optocoupler with an internal photodiode sourcing current to a transistor’s base is required in order to achieve this speed; a phototransistor was found to not switch fast enough. This circuit requires a DC voltage supply to operate, convenient on the battery side but not on the communication bus side since it would require an extra wire. The photodiode optocoupler (6N136S) is more expensive than a slower phototransistor optocoupler (CNY17F-4S), about $0.65 vs. $0.19. Since the slaves need to communicate little data back to the master, the CNY17F—4S can be used for the other data direction. Its output transistor will need to be placed in antiparallel With the 6N 136S’s LED so that it can work against a lower impedance than the master’s driver 1C. In order to allow the slave modules to communicate back, the master pulls the bus to -4V through about 759. The 6N136S’s LEDs ignore the negative bias and allow the individual slave module’s CNY17F-4S to operate against the 7552 load. The master reads these pulses on the bus and translates them to bytes 35 at approximately 1152de, a reliable sending rate for the slower optocouplers. Voltage sensing is very desirable for the slave modules, so each battery can be monitored and its usage adjusted appropriately in comparison to the rest. This can be done with a simple voltage divider consisting of two resistors. Values of 68k§2 and 33kt) give a 0-5V A / D range that corresponds to a 0-15.3V battery voltage, plenty for lead-acid. Tolerances of 1% are used to increase measurement accuracy. The ATTiny24 implements a calibration byte in EEPROM to further improve readings. A temperature reading is also desirable for the design, but is not as important as the voltage. The temperature should mainly depend on weather conditions, measurable from within the master enclosure. However, batteries with shorted cells or other problems may get warm when charged. The ATTiny24 does include an internal temperature sensor, but it does not accurately represent an overheated battery since the battery would get warm mainly on the sides, not the top (where the module is mounted). This necessitates an external thermistor mounted on the side of the battery. The same 1%, 68kQ resistor as above can be used as the top part of a voltage divider across the 5V supply, the bottom of which is a 68kQ thermistor. This setup gives a large range of temperature readings and both it and the internal sensor can be calibrated and compared to find an overheating battery very early, before it gasses excessively or even explodes. 3.1.3 DC/DC Converter Due to the isolation of batteries, generating a 12V vehicle accessory supply (for headlights, etc.) is difficult. It requires each module to have an individual high- frequency transformer and two small MOSFETs driving it, here in a push-pull configuration for ease of gate drive. Each module should be able to supply about 2A of output current at 12V; 45 of these paralleled outputs would equal 90A total, comparable to an alternator in an internal combustion engine vehicle. The GM EV1 36 requires a high-voltage supply, nominally the same as the twenty-six 12V batteries in series, 312V. Twenty-four 12V outputs in series (instead of parallel for conversion electric vehicles) would make 288V, perfectly acceptable for this purpose. The MOSFET chosen for the push-pull power supply is the Toshiba 2SK2231, in a D-Pak package. It is available for $0.35 each in a quantity of 100 or more and has a 0.1252 Rds(0n). Its gate driver is chosen to be the TC1427 from Microchip, only $0.88 each per 100 purchased. It is one of the least expensive drivers available but still features two 1.2A totem-pole outputs, plenty sufficient for the 2SK2231S. The gate driver, MOSFETs, battery, and microcontroller all share the same ground (the negative battery terminal). The Atmel’s OC1A and OClB, with a dead time to prevent cross conduction, can be used to provide signals for the TC1427. The transformer will consist of two center—tapped windings. The primary will go across the 2SK2231 drains with its center tap at battery 12V, and the secondary will go through two rectifiers to output positive with its center tap at output negative. The converter’s rectifiers can be Schottky barrier diodes because of the low required reverse voltage, about 40V. Two 1N5822s will be used; they cost $0.25 each in a quantity of 100. It was planned to use a small toroid for the DC / DC converter, but this was never implemented due to the unreliability of the first hardware design. See Section 3.3.3 for a description of the functioning redesign of the DC / DC converter. 3.1.4 Printed Circuit Board The first hardware design does not call for a four-layer PCB; a two—layer with added copper thickness is acceptable. The extra copper aids in current conduction and heat spreading/ dissipation. The board was designed in Cadence OrCAD Layout and produced by Silver Circuits in Malaysia. The design one slave’s schematic can be seen in Figure 3.1. 37 ISUE CNY17F-4S REC ENE 1984617 1 13081300131818 ($100052 per bnardjz 150 (541-15A.CT-ND]1— $00238, $00415 1500 {541-150ACT—NDD— $0.023E1.$00475 8200 (541-8201401140) - 33002301300714 3311.0 (541-330KCCT—ND) — $00482, $00482 501-10 (541-500KCCT-ND'1- $002755, $00553 2.20F {ECJ-QFF1E225Z} - $0.073. $0.355 108401? (217-1121-NDJ- 530.358.130.730 IRF1324S-7P — $2.50. $10.32 T131 427COA- $06776, $08776 61‘11363 — $0.649. $0.640 CNY17F-4S - $01013. $01913 BAS70 (135003) - $00643. $01286 Red LED - $0.073. $0.073 DU'I'MINUS . ‘ FIVEV'DLTS l BATFLUS ,4" R4 > 03 34310 0511011315 R5 ,2 .5- R7 U1 GD ENABLE 53110 {72 ,3'08140 23.2014 03 |R82004 "k K D ,3 R3 13 f l, 1ch .20 .1 ,2 8200 E 02 511114124 l 2. L”— H'? 5 \ cc 1 - 2 4 4 SD ‘ 5 f .2 u'LC LINU .-. WLTS E ‘ CUM LEI l— : PEU PM '1 CE c- P01 P1911 '—‘1' U3 IN P04 PFC- “—HUF RECFIN 5 P02 m, 0 U4 IN _ 04 0.4.910 FIN - g Fm- T P3 :1 U4_VB [ 1 Ffio‘ PR5 _ 10411152004—8—H D13! 02 2 I‘U'ILZ 1:: 70,3 7" ——C4 RT1 7 fl “5' — \ :4 :4 5 ,. .. 5 2.2uF R111 Optional ,2 e K 4 4 °'3 W 5 ,. 1: 0011 L0 X‘IV l [ RE 3 R8 < .3: R0 BUTTON 33140 * 8200 , <> 8200 f ‘x f U4 H0 1 U4 vs 01: LTL-10223W (Red) 02: LTL-10233W (Green) RT1: NCPIBWDBS3JD3RB U4 L0 U5 101421 BAMNUS D—[- NC r10 4—5 3 ”LA 0014 3 GND 1.430 e . 4 ”LB 001_0 5 T1 1241.431.221.131 05 1N5322 §— _. 2 f 13 _, C 05 1115022 R11 R12 4 1: s IEI _ 150 150 PL g ATTINH’M- $1.20. $1.20 C5 22 F g ,_ . U 1133320041 - $1.02, $2.04 H 01 02 , E 23K2231 2s1<2231 —‘[l_ ‘° 1011 IRF1324 11112 |RF1324 M} |RF1324 M4 |RF1324 1984617 1145822 11 N5022-E311 01-1101 - $02455, $0.491 Green LED - $0.013, $0.013 rue _ _ _ 2s1<2231 1:...TE16RQCT-NDJ— $03488, $00915 ThermistUr- $0.144, $0.144 “u“"we' “Wm" HIM-"““e W MM" “3119“" LP2050-50LPRE3 - $0.45, 5045 T1 Core - $1.20, $1.20 312; [Jocumem Number e11: Date: Friday. September 07, 2007 [Sheet 1 1 Figure 3.1. Slave Module Design One Schematic. 38 .71 r ‘0. 1 r». .“ 3.2 Redesign Considerations Six prototype design—one slave modules were constructed with two IRS2004S and four IRF1324SS with voltage isolation between slave modules’ microprocessors as described in Section 3.1.2. The gate drivers were unreliable and often exploded for seemingly no reason, even after changing to a tantalum bootstrap capacitor. Because of the high currents and quick voltage transitions involved, a decoupling or isolation was needed between the microprocessor and power grounds. This is not easily achievable with the IRS2004S since they share a logic and power ground. The Solar Car Team’s GM EV] moved about five feet under the power of the six slave modules, but without a load, the modules would randomly fail even after just being repaired and the design was eventually abandoned. Still, many of its aspects remained in the second version. The reason for failure was never fully investigated, but the first hardware design also had the undesirable limitation of requiring the master’s DSC to change its serial port baud rate on the fly, since the slaves could not send at 937.5de. This would have been easy without buffering, but buffering was required in the DSC because of the manual implementation of USB. Without a token buffer, USB transactions would interrupt the flow of tokens to the slave modules. The token buffer caused issues with control, and the bit-banging USB implementation was slow and error—prone despite implementing CRC checking. The design-one slave modules were tedious to connect and disconnect, since they used miniature terminal blocks with screws. This problem was alleviated in the second slave hardware version by the use of 0.1in.-spacing headers and connectors paired with ribbon cable. The master redesign followed the slave redesign by about a month. Master designs one and two are covered in Chapter 4, and conclusions are discussed in Chapter 6. 39 3.3 Hardware Design Two 3.3.1 Power Section The second slave module PCB was designed to support eight IRF1324SS, configured in pairs, to reduce conduction loss in high-power installations. For the same phase current, this ideally reduces the conduction loss to only one-fourth of the original value per part or one-half per module. The avalanche losses will be similar for a given phase current, but since there are more MOSFETs, the module can support more avalanche current. Though the manufacturer recommends against sharing avalanche spikes between paralleled MOSFETs, it is acceptable in this design because of the similar temperature, close proximity, and identical date code of the parts (this must be ensured during PCB population). It was decided to move the isolation barrier from between the communication bus and ATTiny24 to between the ATTiny24 and power MOSFETS, in order to eliminate feedback and spikes from the power section that had been causing problems in the first design. This would allow the [LCS to both transmit and receive at 9375de or 1MBd (depending on the master hardware version), and would not require the master’s DSC to change its baud rate except when reprogramming the Atmels. An optoisolator with a built-in gate driver was found for the second hardware version, the HCPL—314J from Avago Technologies. The AT Tiny24 drives four internal LEDs in the HCPL—314Js to signal their output stages to be either high (12V) or low (0V). Each half of these parts requires an isolated voltage supply to drive the associated MOSFET gate, totaling four isolated supplies in all. These can be taken from the DC / DC converter. Small MLCC capacitors can be used to bypass the gate drivers, along with BAS70 diodes — the same used to create a bootstrapped supply in the first design — to half-wave rectify the transformer windings’ alternating voltages. 40 3.3.2 Microcontroller Since the Atmel qu are isolated from the batteries, they can share a 5V supply with the master’s DSC. Separate transmit and receive lines are shared among the slaves, making four signals total, spread across a ten-pin connector for redundancy and hence connection reliability (5V and ground are given three pins, and transmit and receive are given two). In order to allow each ATTiny24 to reply without requiring any to change its own port direction (input vs. output), a small Schottky barrier diode is placed with its anode to the bus and cathode to the pC, and a pullup resistor is placed inside the master module. The HCPL-314Js associated with the IRF1324SS require a total of four signals from the 11C. A dead time needs to be manually coded in software so that the upper and lower IRF1324S don’t both conduct at the same time while transitioning. Red and green LEDS are placed in parallel (through resistors) with the high-side gate drive LEDS in the Optocouplers, so the module output voltage can always be known. Battery voltage sensing is done through a CNY17F-4S optocoupler operated in an analog mode. A 4.7V Zener diode and 3.3kQ resistor form an ideal input circuit, and another 3.3kfl pull-down resistor on the output side is sourced current through the phototransistor, providing an A / D input for the Atmel. The external thermistor configuration remains the same as in the first design, except for the use of 22kQ each instead of 68kt). 3.3.3 DC/DC Converter The same 2SK2231 MOSFETS are used in the second design as in the first, since they are still ideal for this purpose. However, due to them now being isolated from the logic, another HCPL-314J is used to drive their gates, with its supply directly taken from the battery. This gate supply does not need to be isolated since the DC/ DC converter works with fairly small currents. The same two ATTiny24 pins are used to 41 provide gate drive signals, OClA and OClB. The winding of a toroid was found to be quite difficult for producing even a small number of these modules. Instead, an EFD core is chosen because it can use a bobbin. It consists of two halves that somewhat resemble the letter ‘E.’ Surface mount bobbins from Transformer Bobbin Industrial in China were sampled, and EFD-15 (fiat, 15mm wide) cores from Ferroxcube were purchased through Elna Magnetics. These bobbins each support six isolated windings. In order to achieve the 25-30W needed for the DC/DC converter’s output, two transformers are used in the second slave design. Out of the twelve windings, four are used for MOSFET gate drive supplies and the other eight are split between the input and output and are center-tapped by connecting pairs of windings in series. 26AWG wire is used to wind the transformers, sufficient because it carries only 1A for half the time when the converter is outputting 2A to the vehicle’s accessories. This pair of transformers, operating at about 250kHz, draws just below 60mA of current under no load. Reducing that drain may be possible, but it only amounts to less than one watt of loss. The HCPL-314J has a quiescent load of about 3mA. This would create a nearly 10% monthly depletion in each battery’s charge, quite undesirable if the vehicle were to be stored for any length of time since the lead-acid electrolyte freezes much easier when a battery is discharged. Since the optoisolator only draws about 15mA maximum, a small MOSFET —— the SOT-23 NTR4101 from On Semiconductor ($0.26) — can be inserted in the power supply loop, its gate controlled by another Optoisolator, the LTV-816S (only $0.15). With a clean PCB, this reduces the quiescent current down to less than 111A. 42 3.3.4 Printed Circuit Board Due to the number of connections in the second design, a four-layer board is required if it is to fit on top of the UB12220 battery —— an important space-saving criterion. Both the old and new layouts can be seen in Appendix B. Special attention was paid to the high-current traces. As much copper as possible was laid down to help decrease the trace resistance and aid the spreading of heat away from the main MOSFETs. Figure 3.2 is the redesign’s schematic. 43 5 l 4 l 3 I 2 | 1 BUSFLUS A»— ~ BATPLUSCDND ENABLE [ l 2.20110 02 04 R1 - 01 2.2uF 0211340901 ”—2— “91 W“ [IE—F0337— Mgko‘x U2 A ”J _, 411111 1101 1:, 01 0143? ]’ U1 .411111124 001111-43 3" '3'“ 51““ D 1 . . 4 R11 1500 _ 4111:. 0110 —.-.—-1 JD 01214015113; P50 P190 2 B [A I__.\ g .4512 WC: [0 NEGVB ;— 1101 F141 1 R12 1500 2. 052 002 9 cw— RECFIN 5 110.4 F152 0 ” 1102 01102 R2 7500 E_ [3,233 $3 HCFL-314J-DDDE [4]: 0.45 1145 — 00311111103 _[ ,,__ [ R9 5. 10 ,. U5 15404510 I I V “’ 1505 _> 1500 ‘1 7‘" 1 , H 10 POSVCC I \ < R13 1500 " 2 ”51 "-"L'L‘ 15 R5 K [ [ :3: AN] I'V’Ul T— 00110022152 7500 <1 , F15 C141 ENE” 1.13 1.111 2 UUTMINUS .1/ < .1505 U3 02 2.201 f R14 1500 _ I: [ 1___1' 1101 1.1001 [E F'LTBAT " . D4 .j -’\/\/ 4 121112 01:02 _]0 EV“ '— 31.15 2 1111-1 1.101 ‘ *- ~ —. 3 ‘2 1.1132 ———|- 03 4.110 " 0141 01101 ’4 _L x 2 — —“ 1162 0002 9 l _ ' ' HCFL-314J~000E 1114 1140 7 5 1411“ 1..“ n 11 T ‘ 03:LTL-102231111(Red;1 - l 00 .1 0412 1,3,9: 10 R8 04: LTL-10233W (Green) [”5322 9 11132 0002 g ’ 10° 3211310,? W l POSAC 11150141: 1 1 M11 NTR41011=T1 ,_ T, ‘ 12 01 08 1+ :2 [11 _?JJ MEIDRAIN [— , g [:2 “2 _1_[_] 011510 HAS?!) .1 2 — A " .- N __ g, 112 112 D 1111100R4111 2] P4 114 0 5 P4 ”4 2 F3 “3 1 110311130 115011.131: F5 “5 _J 29142231 2s1<2231 ‘— P1 “1 __[ PCA15EFD FCA15EFD ] I 1115011101410 1:4 05 003110140 1115005 11031115 £140 111501111: 1314111111103 Z'ZUF MUF 0031.10.41: 11501113141: Costanalysis ($23.27501$33.5050 001005110}: Thsrmistor(NCP'ISWDBSSJUE-RBI - $0.144, $0.144 09 010 |F£F1 3248-713 - $2.58, $1 0.32 .1 $20.64 NTR4101F‘TlG- $02567, $02567 3,0350 BAS70 .AITII‘J’1‘24-2088U - $1.20, $1.20 6191870 (568-1608-1—ND)- $007652, $03781 0031.10 NEGW HCPL—314J-000E - $1.80, $5.40 0.117201191183841301711- $00767, $00767 CNYl 7F-48- $01913, $01913 1N5822 (...-E3J'51G|—ND]I- $02455, $04910 22%: 2'32uF LIV-8168 - $01485, $01486 Red LED - $0.073, $0.073 F0303 ' [ I ' NEo'v‘S 28K2231- $03488, $06976 Green LED - $0.073, $0.073 100 (541-10ACT—NDJ- $00414, $00414 081TCU221621IED2609-ND1- $0.224, $0.224 7500 (541-750ACT—ND) - $00238, $02142 4-644540-0 {A31074-NDJ- $0.468, $0.468 % 2.20110 1541-2.201<001-1101- 50021051101100 1-040451-0 1141 030-110) - 50.2155, 120.2150 "’ ”lumen, Comm" mnmum by Mk," Manes.“ 681:0 (541-680KCCT—ND) - $00482, $00482 Transformer core - $0.62, $1.24 Size Document Number e11 2.2UF (ECJ-2FF1E22SZ) - $0.073, $0.438 Transformer bobbin - $0.10, $0.20 A 1 [:4 4.7UF (F931 E4751I11E17-‘12I - $0.267, $0.267 Transformer clamp - $0.15, $0.30 Date: Friday, Jam-law 11 20138 [Sheet 1 of 1 Figure 3.2. Slave Module Design Two Schematic. 44 3.4 Software 3.4.1 Main Loop The only task of the qu’ main loop is to perform A/ D conversions. This includes one battery voltage, one external temperature sensor, and one internal temperature sensor. These are interpreted through lookup tables and calibrated with data EEPROM values. The results can be read through the serial interrupt. 3.4.2 Serial Interrupt Both the design-one and design-two serial interrupts are triggered by a low level on the Atmel’s INTO pin. Following a low start bit of approximate length Ins, nine data bits are read in, each the same length as the start bit. For a clock frequency of 12MHz, 12 cycles pass in 1123. This is ample time to process the transmitted value. It is interpreted as shown in Table 3.1. The first column, Command, is split into seven bits and two bits (see Figure 3.3). This table is for the second hardware design, but differs very little from the first hardware design command set. Many of the commands are intuitive, but those such as “execute a roll call” and “calibrate clock frequency” have somewhat more complexity. The purpose of the roll call command is to find which modules are present in an orderly fashion and to also assign a new address to a previously unassigned module (address of 255). After a roll call command of 126 is sent out, values from 1—120 are sent by the master to the bus to find an attached module to which that address belongs. If a module is called, it echoes its address. At the end, a value of 255 is sent out; if a module has this invalid teInporary address (default for an erased EEPROM byte), it assigns itself the first available spot and writes the value to EEPROM location $01. 45 Start/ A0 X711XA2 x A3 x XAS XAS C1/Stop Stop Address Command y : 7': > Figure 3.3. Communication Bus Byte Format. EDGE CH1 .2" NORML HUG Figure 3.4. Slave Module Automatic Frequency Calibration. The calibrate clock frequency command is used to ensure successful data reception by the slave modules. The design-two master contains an 8MHz precision quartz crystal oscillator, but the slaves have only internal clocks whose frequencies vary with temperature and possibly lifetime. It is therefore advisable to occasionally correct the Slave oscillator calibration bytes (EEPROM location $00) by way of the master. The Value 127 is chosen intentionally as the command because only the start bit needs to be received properly. This greatly increases the chance an uncalibrated module will receive the transmission correctly. After sending two 1273, the master sends thirty- tW0 Us at 125de, to which the Atmel adjusts itself by means of a width measurement. Test results show that even from as high as 14.3MHz or as low as 9.7MHz, the Atmel Could recover. Figure 3.4 shows a divide-by—12 output from a calibrated ATTiny24. 46 Command Arguments Function 0 0X 0 1X 1—120 11 1-120 01 1-120 10 1-120 00 1—120 00 1-120 00 1-120 00 1-120 00 1-120 00 1-120 00 1-120 00 1-120 00 1-120 00 1-120 00 1-120 00 1-120 00 1-120 00 1-120 00 1-120 00 1-120 00 1-120 00 121 !00 122 !00 123 !00 124 !00 125 !00 126 00 127 XX Else 0000000 00 1000000 00 0100000 00 1100000 00 0010000 00 1010000 00 0110000 00 1110000 00 0001000 00 1001000 00 0101000 00 1101000 00 0011000 00 1011000 00 0111000 00 1111000 00 DutycPl 00 EEkey 1 Address X 1111111 XX Enter bootloader (125)de start / 0 bit) Do nothing (dummy command) Turn off module at given address Turn on module pos. at given address Turn on module neg. at given address Echo next byte XOR module address Put addr’d module in state “sleeping” Put addr’d module in state “idle” Put addr’d module in state “ready” Put addr’d module in state “running” Blink addr’d module for three seconds Read EEPROM (address follows) Write EEPROM (address, data follow) Transmit DC battery voltage A/ D value Transmit AC battery voltage A/ D value Transmit local temperature A/ D value Transmit remote temperature A / D value Erase addr’d bootloader flash memory Fill addr’d bootloader memory buffer Write addr’d bootloader flash memory Verify addr’d bootloader flash memory Eight PWM cycles with duty cycle, pol. EEPROM writing key, $92 XOR mod. addr. Put all modules in state “sleeping” Put all modules in state “idle” Put all modules in state “ready” Put all modules in state “running” Blink all modules for three seconds Execute a roll call Calibrate clock frequency Bad command; ignore, exit interrupt Table 3.1. 1 Megabaud Command Enumeration. 47 Data / Command Function $AC $53 Addr XX XX $00 Aer AdrH $01 Aer AdrH $03 Aer AdrH $05 XX XX $11 Addr XX $41 Addr Data $44 Addr XX $54 Addr Data $64 Addr XX $81 Aer AdrH $82 Aer AdrH $84 DatL DatH $88 XX XX $FF Enter bootloader, module to address as third byte Hang and let the watchdog reset all modules Fill page buffer at address with data (call after $88) Erase Flash page at address Write page buffer to Flash page at address Clear temporary page buffer for Flash writing Return value of data EEPROM byte at address Erase; write given data to EEPROM byte at address Erase EEPROM byte at address Write given data to erased EEPROM byte at address Change currently addressed module to first byte Indirect jump to address LSB/MSB in bytes one/ two Return value of program Flash byte at address Load internal data buffer LSB/MSB in bytes one/ two Exit bootloader normally (no watchdog reset) Table 3.2. 125 Kilobaud Bootloader Command Enumeration. 3.4.3 Bootloader The ability to update the ,qu’ code remotely is very important, especially during development. Non—critical updates, such as improving temperature or voltage measurement accuracy, and critical updates, such as fixing bugs, can both be applied through a firmware update. Unfortunately, modifying a certain section of the flash while executing out of it is difficult if not impossible. For this reason a bootloader was developed, placed from $0370 to $03FF in the Atmel flash memory. This small, robust code segment has the capability to rewrite the rest of the flash in a matter of four seconds. All bootloader commands are sent at 125de to ensure communication reliability. Verification is also done at this speed. The command set is shown in Table 3.2. Every command consists of three bytes sent as a group. Only the addressed module will respond to a programming command. The currently addressed module Can be changed by the $81 command; a value of zero addresses all modules globally. The design-two master has the built-in capability of reprogramming the slaves, given the correct data; direct computer control was found too unreliable. 48 CHAPTER 4 Master Module Development 4.1 Hardware Design One The dsPIC30F4011 DSC is the heart of the master module [7]. It provides slave communication signals, throttle and current A/ D inputs, contactor signal outputs, USB signals, as well as others. It is available as a 5V-capable, 40-pin DIP or 44-pin TQFP (thin quad flat pack). In this work, the DIP is chosen because it can fit into an inexpensive ZIF (zero insertion force) socket for replacement, instead of needing to be desoldered like the T QFP. Five volts is the only voltage node that supports full-speed operation, or 30MIPS, so the supply bus voltage is an easy decision. The greatest design challenge for the first hardware version is the communication bus. It needs to be passive at 7552, -4V and active at less than 152, +4V; a strong driver IC is required for this. The TC4452 from Microchip (dsPIC manufacturer) is a 12A driver with an allowable supply of up to 18V — double the necessary ratings. A diode’s anode can be placed at its output, with the cathode to the communication bus, and a 759 resistor can be paralleled with the diode to give the desired impedances and Voltages. Because the logic input will be referenced to -4V, a transistor is needed to turn the non-inverting device on. The dsPIC drives the PNP transistor’s base through a resistor. The -4V supply is generated from a simple charge-pump, consisting of a UCC27423 gate driver from Texas Instruments, fed from the 7 .5MHz oscillator. An 49 NPN transistor is placed across the 759 resistor and senses when a slave module tries to ”short out” the bus. Its response is to pull down the DSC’s receive pin. The DSC needs to accept ignition and throttle/regeneration inputs as well as current sense signals. The ignition signals are typically 0V or 12V, so a lOkQ resistor can be placed in series with them since the current will be considerably below the maximum pin clamping current. The dsPIC will use seven analog inputs: two throttles (one for regeneration), three phase currents, one scaled/ isolated charging plug voltage, and one scaled battery voltage. The plug voltage signals the DSC when the user plugs the car in to charge. The battery voltage allows calibration of slave modules, with the help of three DPST (double-pole, single-throw) reed relays that switch the phase voltages’ grounds to the DSC’s ground and average the scaled phase outputs together. When a single slave module is turned on before or after charging, its voltage is read by the dsPIC, and the slave module’s voltage calibration byte is written to correct its measurement error. This at least guarantees relative accuracy between Slave modules, even if the dsPIC’s analog supply voltage is slightly inaccurate. Six additional SPST (single-pole, single-throw) reed relays drive other relays that make connections between the phase strings, such as connecting their negative ends to drive the motor or putting them in series to be charged from an outlet. A 74ABT245 bidirectional bus transceiver from Texas Instruments is added to the design to interface to USB. The dsPIC will not directly interface to USB because the logic signals are not compatible; i.e., the dsPIC needs a 0.8 - Vdd 2 4.0V input to trigger as high. A TI CAN transceiver, the 65HVD251D, is also added to the master. Since the master module deals with nothing over 5A (for the relays), and dissipates little heat over few components, its size can be relatively small. Enclosure model 1554GGY from Hammond Manufacturing was chosen. It can accommodate a circuit board of approximately 3.6x3.1in. Therefore, all of the parts should fit in this space. The schematic of the first master module PCB design is shown in Figure 4.1. BOXED IN +12v INPUT — SPEED PULSE HE722A0510x3 1554GGY | — GLOBAL GND P82501L-2A ANl CLl lOUF PTN78OOOWAH UCC27423D CO” Contacts CA1 Ell/l1 GND vo 10kohm .AN2 CL2 VI ENA ENB PLUGW CA2 EM2 REF504O'D INH ADJ INA OUTA A _._ DNC DNC_ __ GND VDD , 10km A' ,Mohm +_fi __ VIN NC _ 7.5MHz INB OUTB + TEM OUT 22kohm Clock 10kohm Ikohm GND DNC l _ . PLUGB ] COIl Contacts ikohm 10 F K Av l Jli u - —— o— +4VREF I — 1 II I II II A 8_ 5V I TLRSSTVKE —L ,,_ FILYA ,,_ RLYB ,,_ RLYC ,_ RLYD ,,_ RLYE 10““ 8+ REGEN 10uF 10uF 1UF l CUR_U ' ' ' ‘ ‘ Coil Contacts CUR_V ' CUR’W 10kohm DSPIC3OF4OH Contacts Contacts Contacts Contacts Contacts Contacts ,,_ 0 QEPA —\/\/\f “(SSH Aigg — Coil Coil Coil Coll Coil Coil C- QEPB ‘WV 10koh C+ AN1 PWM1L __ 10mm AN2 RE1 CSX750PCB AN3 RE2 - | - - OEA REs 1uF OE VCC QEB RE4 l GND CLK AN6 RE5 10kohm m; £3 SN65HVD251 D USB VCC ‘ VDD C, RX IN RS RELAY +12v 1 Skohm SN74ABT24SBDW vss C, TX J GND CH TC4452VAT 1000uF (03mm) 68ohm DIR vcc 7 CLKI RF4 l VCC CL GND VCC A1 OE RC15 U2TX L OUT REF IN OUT - 3:: B; W A2 B1 RC13 U1 RX 1kohm 68 h _ A3 32 R014 U1Tx y 0 m __ A4 I33 __ RE8 RF6 1kohm 10kohm - __ A5 B4 __ RDI RDO MODULE DATA __ A6 BS _ RD3 RD2 _ A7 86 __ vss VDD 750m". __ A8 B7 __ ‘ USB GND GND B8 6800hm 1kohm 10kohm - 1kohm 10kohm 47OOhm 10kohm 10kohm 2200hm v RUN A A A 10kohm START LED fi/VV ‘_ REVERSE V V V ACCESSORY 220hm STATUS DATA Figure 4.1. Master Module Design One Schematic. 4.2 Hardware Design Two The main reasons for a master module redesign were the unreliability of the manually- implemented USB and the need for higher—precision A/ D inputs. A PIC18F2553 processor is therefore added to the module. It supports full-speed (12MBd) USB, features an internal USB transceiver, and has ten 12-bit A/D inputs [18]. It also features a serial port with a rate up to 2MBd, necessary for communicating with the dsPIC. Because of the 2553’s USB clock and the need for inter-processor communication, the dsPIC needs to be overclocked by 6.67% to 32MHz; this is not a reliability issue since it is operating well above the minimum voltage for 30MHz. Much of the remaining hardware design is kept the same as the first version with a few exceptions. The reed relays are replaced with MOSFETS for higher current capability, since they were found to be unreliable above a 1A switching current. A boost converter (the PTN0405OC from Texas Instruments) is added to allow accurate regulation of the analog 5V supply from about 10V when the USB port provides nlOdule power. An additional PTN78000W buck converter is introduced to prevent negative VDD spikes when turning on the module bus. This is separate from the 01‘ iginal 5V buck converter to allow a current reduction on the 12V master module SuDply when the vehicle is off. More optoisolators are added to sense the motor phase voltages in addition to the AC input (as before) to check for malfunctioning Slave modules or for other unforeseen purposes. Finally, two LEDS are used in the new design: green for power/ ignition and red for USB activity. The design-two PCB Sellematic is given in Figure 4.2. 52 47kohmx2 BOXED IN + 1554GGY +12V INPUT ADlv SPEED PULSE B+ B- “”0th“ | 7 GLOBAL GND HE722A0510x3 | mm A- TLP281-4 ' PTN78000WAH 10kohm TLP281-4 Coil Contacts A+ lOUF ‘ BDlv AN1 CL1 . GND vo AN1 CL1 , CA1 EM1 VI CA1 EM1 lMOhm AN2 CL2 lOOkOthQ _ INH ADJ g AN2 CL2 A CA2 EM2_, + CA2 EM2 A" AN3 CL3 AN3 CL3 + CA3 EM3 IN GND OUTI 22kohm CA3 EMS - 1Mohm AN4 CL4 j AN4 CL4 BATS CA4 EM4__, LP2950(TO-92) _ _ CA4 EM4 PLUG+ p _ LUG . CONT CHRG PLUG 120v 240v +REF B- +5v I I I lRL3714x5 I I I 8+ THR REG I10uF I1ouF 1uF D D D D D CUR_U ' ' ' G S G S G S G S G S Coil Contacts CUR_v CUFLW 10kohm DSPIC30E4011 ‘I ’_ OEPA W — MCLR AVDD 0— c- QEPB fi/VW AND AVSS C+ 10kohm AN1 PWM“- I I ikohm 5 AN2 RE1 X 10kohm INDX —\/W INDX RE2 I 4.7kohm OEA RE3 mkc’hm OEB RE4 I _ I CSX 750FCC ANe RE5 SN65HVD251 P CANHI AN7 VDD IN Rs wF OE VCC AN8 V88 I GND CH PIC18F2458 5:3 _ GND CLK VDD C1Rx I VCC CL _ RE3 RB7 7 vss C1Tx OUT REF _ ANO R86 - l CLKI U2RX x1 BDlv AN1 R85 RC15 U2TX x2 I ,__ VREF- RB4 _.._ __ U1ATx SDI1/SDA _ . VREF+ AN9 _ 470ohm A PTN U1ARx SDO1/SCL MCP120 RST——I +REF RA4 AN8 22UF 78000 VDD BATS USB_, _ WAH RE8 SCK1__ -475D| VSS CDIV AN4 SCL _ GND PTNO4050CAH iNHGND RD1 RDo _ ENA ENB VSS SDA _ Vi , RD3 RD2 INA OUTA CLKI VDD .._ GND vom - VSS VDD _, 1 GND VDD__4 __ RA6 VSS USB __ v1 I INB OUTB_ 1uF _ Rco Rx __ x2 VCC ADJ ADJ V0 UCC27424P _ __ RC1 TX __ x1 _ RC2 D+ 22kohm VUSB D- 823 Bf W 750ohm 4'7k0hm 10kohm 10kohm 470ohm Green 1kohm 10kohm 10kohm LL +5VMOD ACC REv —\/\/\/‘- LED IGN —\/\/\/U —\/\/\/‘— RUN — MODRx — MODTX +5VMOD Figure 4.2. Master Module Design Two Schematic. 4.3 Software Design One 4.3.1 Main Loop The first task in the dsPIC30F4011 code is variable initialization. This includes sampling each of the three phase currents to determine their idle values, in the case of small offsets in the current sensors or the DSC. Sixteen samples are taken of each channel and are averaged and saved. The module token buffer is cleared, USB variables are reset, and all peripherals are set up in this stage. Initialization constitutes almost 250 lines of code. The functions implemented in the main loop are as follows: check the USB port’s status, calculate the minimum and maximum voltage for the current frequency, find the current accumulated angle, buffer module tokens to generate sinusoidal output Voltages, measure, transform, and filter the currents, calculate and average the power factor, make voltage and frequency adjustments based on the system variables, and Doll the timer value until the 364us loop is over. This is done in a matter of just over 500 lines of assembly language code; only six batteries are supported in this version. The design—one main loop code is excluded from the Appendix as it is essentially the Same as the design-two main loop code. Slope and offset constants (such as a and b in y=ax+b) define the minimum and IIlaximum voltages for any given synchronous frequency. These are calculated by the 17x17-bit hardware multiplier. The limits are saved in a 16-bit format, with the 1CW byte corresponding to fractional batteries and the high byte to integral batteries. Fractional batteries, with 32 divisions, are made by PWM; only one module per phase String conducts PWM at once. These minimum and maximum voltages are used later in the main loop. The elapsed “future time,” or buffer time, is calculated and multiplied by the f . . reQUency to find an angle increment. The merement is added to the previous angle 54 and sine values are found for offsets 0, 120, and 240 degrees. A sine lookup table is used with 10-bit input and output precision (0.3520 and 0.002 result precision). These three results are multiplied by the amplitude and then added in the form of module tokens to the DSC’s FIFO buffer. A simple toggling scheme is used in this prototype code to alternate between energizing batteries #1 / 4, #2/ 5, and #3/ 6; each of these pairs is in series, with the #1 / 2 / 3 negative outputs connected to neutral. Tokens will be sent out by the ‘SENDTOKEN’ macro, shown at the beginning of Appendix E.1, during the USB interrupt and the main loop. This eight-cycle macro was designed to fit inside the USB interrupt without interrupting its processing. Working register W13, used as the buffer output pointer, automatically rolls around at the end of the buffer with help from hardware modulo addressing. The macro compares the timestamp word in the buffer to the current time. If the timestamp is zero, it assumes the buffer is empty. If the current time has surpassed the timestamp, it sends out the word in the buffer that follows and increments the output pointer. The tokens have a transmission time precision of about 2ps due to the spacing of ‘SENDTOKEN.’ The three phase currents are next measured and centered using the initially measured idle values. These numbers are not averaged since doing so would create a phase delay with reference to the applied voltage, destroying the Id and Iq calculations that depend on the voltage/ current phase shift. A Clarke transform creates Ia and 13 through simple arithmetic, and a Park transform results in Id and Iq by using the sine lookup table [13]. The Park transform outputs can be heavily filtered because they should be approximately DC currents. The 32-bit accumulators are used to calculate a 4096-weight infinite impulse response filter. This high precision is needed to prevent hysteresis problems, i.e. a current never approaching the steady-state value due to rounding error. The power factor is calculated by dividing L, by Id, and taking the cosine of 55 the arctangent of the quotient. A 1024—entry lookup table provides this complex conversion. The power factor is not used in this version of the code, but gives the user an indication of what region the motor is operating in. There may be efficiency optimizations possible with this value, but that is left as an extension to this thesis. This value would be used while charging to ensure a high power factor at the outlet; however, a charging algorithm is not implemented in this code version. Finally, the applied motor voltage and frequency are adjusted based on Id and the throttle, respectively. A low Id, or one below the reference 1*, will force the control loop to increase the applied voltage. Similarly, a high throttle (1;) will result in an increased synchronous frequency, which will in turn increase Iq as desired. This control loop structure avoids the need for a rotor speed feedback. 4.3.2 USB Interrupt The DSC’S USB interrupt is triggered by a change on either the D+ or D- lines. A sync byte is read in, followed by the packet identification byte and the address/ endpoint [19]. A cyclic redundancy check (CRC) calculation is performed on the packet’s data as it comes in. For the design-one master’s clock frequency of 30MHz, 20 cycles pass for every low-speed USB bit. The actual bit receive routine tests for an edge in a window of about eight cycles. If it sees no edge, it considers the bit a one. If it does see an edge, it considers the bit a zero and resynchronizes its timing. In this way, no timing errors will be accumulated over the course of a large packet. USB specifies a stuffed zero bit for every six one bits; the receive and transmit routines implement this and exclude these stuffed bits from the CRC. This code implements a nearly complete enumeration sequence [19]. Nearly all functions have been included; even string descriptors are supported. The processor has four software endpoints: EPO in, EPO out, EP1 in, and EP1 out. EPO is used for control transfers and EP1 is used for interrupt transfers, the latter representing all 56 software communications such as firmware upgrades and data collection. The software package developed for the inverter, named “Monitor” as described in Appendix A.1, has the ability to send individual commands to the modules through this USB interrupt. Multiple reports can be sent to the computer, including many different eight-byte combinations of motor variables such as currents and voltages. DSC RAM can also be read or written by the computer to alter control functionality, such as changing loop gain constants. The USB interrupt code is included in Appendix E.1. 4.3.3 Bootloader The design-one master has another copy of the USB interrupt code at DSC flash address 0x006000 to act as a bootloader, when the master firmware needs to be upgraded. The second copy of the code is not an interrupt but actually operates “inside of” the original interrupt and polls the D+ and D- lines in a loop instead. From this bootloader, all 36,864 bytes of memory from 0x000000—0x005FFE can be updated in about three minutes. All 1024 bytes of data EEPROM can be updated and verified from the bootloader. The main loop’s USB interrupt includes similar flash and EEPROM modification code, so the bootloader itself can also be updated. This code mostly ranges from lines 611—749 in Appendix E.1. The main loop contains a routine to update the slave modules’ Atmel memory upon request. The PC writes the new Atmel program to DSC memory, and after telling the DSC which slaves to update, the DSC performs a communications check with each slave and reprograms its memory block-by-block (32 bytes at a time). Direct PC control was found to be unreliable and unnecessarily slow for this task. This routine can update each slave’s entire flash memory in less than four seconds. 57 4.4 Software Design Two 4.4.1 PIC18F2553 Code The PIC18F2553 takes the place of the dsPIC30F4011’s USB interrupt with its full- speed USB peripheral. It communicates with the DSC at 2MBd over two serial lines. Each processor maintains a 256—byte area in RAM containing data for both USB transfer directions, since each of the four endpoints is 64 bytes. When an interrupt OUT command is sent over USB, the resultant data is automatically written to the corresponding location in 2553 RAM by the peripheral. This is then forwarded in software to the DSC over one of the 2MBd lines. Similarly, the DSC periodically sends motor variable data over the other serial line which is then stored in the associated buffer in the 2553. The PC may then read it by an interrupt IN command. The 2553 has a red indicator LED to show USB activity. The 2553 also has the duty of converting four analog values at 12-bit precision. Three of these values are optoisolated phase voltages and the other is a resistor-scaled combination of non-isolated phase voltages. Before and after battery charging, slave modules are turned on in order and calibrated against the standard measurement of the 2553 with the help of three reed relays shown in Figure 4.2 (HE722A, top right). This will give the slave modules better relative accuracy so their respective batteries will not get as mismatched during charging and discharging. The results from these conversions are sent to the DSC occasionally over the serial bus. This part does not contain a bootloader in this project and cannot update its own flash memory; however, this was deemed unnecessary due to its fairly general functionality. It does have the hardware capability to update itself and this function could potentially be written if a need for it arises. 58 4.4.2 DSC Main Loop The design-two main loop greatly resembles the design-one main loop with a couple of exceptions. There is no longer a DSC module token buffer. This is unnecessary here because the main loop will be only lightly interrupted. Due to the absence of a USB interrupt, all flash and EEPROM modification code is in the main loop. The routine to reprogram the slave modules remains in the main loop and is not at all changed. Motor variables are written serially to the 2553 after being recalculated. 4.4.3 DSC Bootloader A bootloader for the design—two master is located at 0x007C00 in dsPIC30F 4011 memory. It begins on line 2577 in Appendix E.3. It is nearly identical to the design- one bootloader except it utilizes both EPO out and EP1 out for a total of 96 program data bytes and can update the DSC’s flash memory in only five seconds as opposed to nearly two minutes for design one. As in the first design, the flash and EEPROM modification code appears twice in the listing, so all code can be updated: once before 0x007C00 and once after. 4.4.4 Serial Port Interrupts Both the PIC18F2553 and dsPIC30F4011 use serial port interrupts that trigger when bytes are received. The communication method uses nine bits, where the top bit is zero for an address byte or one for a data byte. A current address is maintained in each processor, and each writes to the other’s memory nearly transparently. The processor with information to give away will first write an address command, then follow it with one or more data commands. The data commands automatically increment the writing address so that address commands do not need to be constantly sent when large areas of shadow memory are being copied. The DSC bootloader also contains an identical serial port interrupt. 59 CHAPTER 5 Results Various results have been recorded in this work. Most are either oscilloscope graphs or matplotlib graphs of USB-acquired data from the master module. The oscilloscope used was a GW-Instek ODS-820C. Figures 5.1, 5.2, and 5.3 are oscilloscope plots of a slave module’s operation. The first shows a current waveform with i130A peaks, demonstrated with an approximately resistive load (halogen light bulbs). The second depicts a slave MOSFET going into avalanche conduction. Since the MOSFETS are rated for 24V, the 23V peak is fairly accurate. The duration of the avalanche pulse is 1.16113. The module in question had only four IRF1324SS populated. The third shows two 800ns dead-times of a module performing PWM. These dead-times are necessary to prevent cross-conduction and energy waste. 60 1v- . EH1: CH2: 121013st MRIH TRIO-z: E—_I—_DGE FICQ _USB su 500m 50an CH1 x SINGLE SHNPLE Figure 5.2. Slave Module MOSFET Avalanche Waveform, 23V Peak. 61 . I:.‘:.-_-.-:r-.-.=.-.-.-.. 1 m .- m n" "n : LA‘Al‘AAAlAAAAl‘AAAJ-AALIAAAJL-LAMJL-AILAA-A‘L ........ 1‘. B. @665 24. ??88kHz CH1:- ICHET..51E1FJT1E. 5.91:1 I'IHINI ITRIG::I EDGE I HOG! USB 5U CH1 "i. NORHFIL SHNPLE Figure 5.3. Slave Module PWM Illustrating an 800ns Dead Time. - a... n ............................. III 1 P 1 I 1 I 1 i- II P J > I Iv 1 D 1 h- -i r 1 I I M.” h r I 1H- 1 ll- 1 f 1-- -112- 4.. CH1 1' NORNFIL SHMPLE Figure 5.4. Line-to—line Inverter Output Voltage, Six Total Slave Modules. 62 LLLA ,J L... ,JUJLILJDCJ . p h 2 - i 1 AAAAAAAAAAAA LwAnAAAA CH1 ‘5. SINGLE HUG Figure 5.5. Design-One Master Multitasking During a USB Transfer. Figure 5.4 is a plot of a line-to—line voltage with six total batteries and slave modules. No PWM is being conducted here. Figure 5.5 shows the design-one master’s availability during a USB transfer. The lower curve’s high time represents time during which the master can send out module tokens. The upper curve shows the USB D- line. 63 Figures 5.6—5.9 are a demonstration of the EV1 motor’s operation under the multilevel inverter. 5.6 and 5.7 were captured with the EV1’s left front wheel off the ground while providing only a slight load torque by hand. Both plots show a synchronous frequency of 22Hz for the tests. Id and L, are quite noisy, even with a relatively constant amplitude (in units of 1 / 32 line—neutral battery voltage). Figure 5.6 has a slight load applied at six seconds, where L, can be seen to increase slightly and stabilize, then drop off as the load is removed at almost nine seconds. This Iq increase is much more prominent in the three torque loadings of Figure 5.7, where the amplitude adjusts slightly to maintain an average Id of 6A. 5.8 and 5.9 were captured while driving 3—4MPH in a parking lot, both peaking at a frequency of about 20Hz at 4MPH. Id cannot be regulated at 40A as desired because of the conservative voltage limits used here. These were enacted to prevent high currents, though even with the limits in place, Iq still reaches over 120A for about a second. A throttle speed command was used for the driving tests. A polished torque— control algorithm and better current limiting would probably reduce the severe motor oscillations, but the project’s concept is very clearly demonstrated in these tests. 64 EV1 Captured Motor Variables, Id=8A, Slight Loading 25 Freq, Hz Amplitude Id, A lq, A 0 2 4 6 8 10 Time (s) Figure 5.6. EV1 Captured Motor Variables, I;=8A, Slight Loading. EV1 Captured Motor Variables, Id=6A, Three Loadings 50 Freq, Hz Amplitude Id, A Iq, A 0 2 4 6 8 10 12 14 Time (s) Figure 5.7. EV1 Captured Motor Variables, I;=6A, Three Loadings. 65 EV1 Captured Motor Variables, Driving 100 Freq, Hz Amplitude 1d, A 30 Iq, A 20 095 100 105 110 115 Time (s) Figure 5.8. EV1 Captured Motor Variables, I;=40A, Driving. EV1 Captured Motor Variables, Driving 120 Freq, Hz Amplitude Id, A Iq, A 100 20 4% 25 30 35 40 45 50 Time (s) Figure 5.9. EV1 Captured Motor Variables, I;=40A, Driving. 66 CHAPTER 6 Conclusions 6.1 Design One The first system design was functional enough to demonstrate the multilevel inverter topology. It moved the EV1 about five feet, limited only by the garage length, with six batteries and battery modules. Torque control was demonstrated on a rewound 3 / 4HP induction motor. The slave modules unfortunately had reliability issues, mainly under no load. The IRS2004 gate drivers never functioned properly and would explode without warning. The master’s token buffering was difficult to implement and only moderately reliable -— some of the problems were never fully debugged. Not being able to turn on the slaves’ outputs continuously was a major disadvantage for debugging. Finally, the master’s USB interface was second-rate. 6.2 Design Two Most of the issues with design one, except the EV1 motor’s oscillations, were resolved in design two. There were no slave module failures during testing, and the master’s USB implementation was much improved, especially in terms of speed. A workable, inexpensive solution to surface-mount transformers was found for the slave modules, and the 10-pin connector greatly eased prototyping and installation. The master redesign greatly improved module communication reliability as well. 67 6.3 System and Topology This work clearly demonstrates the ability of a multilevel inverter to drive a motor. The implemented system is cost-effective though still more expensive than competing PWM inverters, but its features —— including individual battery balancing — somewhat make up for the difference. The slave modules and the master module are quite small and light compared to PWM inverters. The USB interface is extremely useful for debugging and collecting data, and proved an invaluable resource for development. Firmware upgrades are very reliable in the second design, and the system gives the user the ability to mix lead-acid and lithium-ion cells, a feat impractical in series-connected battery packs. The lack of a permanently series- connected pack greatly increases the inherent safety of the system and makes servicing much more convenient. Finally, the scalability of the project without any necessary redesign is crucial for supporting a varied product base, from golf carts to partially or fully electric trucks and buses. 6.4 Future Work A major unresolved issue with the EV1 inverter implementation is motor oscillation. This is evident in Figures 5.8 and 5.9 as unsteady currents Id and Iq. Without a frequency-dependent limit on amplitude, it would also oscillate. Unfortunately, the limit prevents the vehicle from having enough torque to overcome small obstacles such as potholes. This is in contrast with the author’s previous work, a Renault LeCar converted to use an AC motor and a standard PWM inverter [20], which could climb a fairly steep hill in third gear from a dead stop. The multilevel inverter does not make use of the speed sensor in a feedback loop and this may contribute to the oscillations. There should be otherwise no differences between the two types of inverters, and the EV1’s motor may be simply more diflicult to control. It is almost certainly possible 68 to resolve the oscillations with the correct application of control theory. Although the theory for a battery selection algorithm was developed it was never fully coded or tested in situ. Currently only six batteries are supported and the inverter state (motoring/ charging) is determined manually in ‘masters.’ Contactors and relays for reconfiguring the inverter’s connections to allow motoring or charging were accommodated for but never installed in the inverter. There may be beneficial additions to the master module’s software such as positive or negative third harmonic injection as discussed in Section 2.1, efficiency improvements by means of power factor regulation during motoring, or DC charging capability to allow a solar or wind energy source. None of these changes require any hardware modification and all can be done by means of a USB software upgrade. 69 APPENDICES 70 APPENDIX A PC Interface Software A.1 Overview of Software A program named Monitor was developed solely for the purpose of configuring and debugging the multilevel inverter. It is based on 'Itolltech’s Qt4 ‘C++’ software development kit for multi-platform compilation and makes use of libusb to communicate with the master module. The most recent version, Version 0.5, allows the user to test master—slave communications, view the current slave module list with slave temperatures, voltages, and hardware/ firmware versions, send global and local commands to change the state of the slaves, upgrade the master and slave firmwares, and even reprogram slave data EEPROM to manually modify calibration values. For example, the user can choose to blink any slave’s green LED to test its connection to the module bus. Any battery module can be turned on positive or negative continuously to test or debug power wiring. A “Roll Call” command assigns a new module an address, which can be changed with the “Readdress Module” command. All critical functions, such as rewriting flash or EEPROM, perform a communications check before executing. Module layouts (phase string configurations) can be read from or written to files on disk, and written to master EEPROM. Finally, communication errors are fully reported and all activity is logged to a file. 71 . - ~ .- are”: '3“.- n: :"'-.'=.-“ ‘='.‘.' ":1 i . , '__l a. . .. m Module Description I Battery/waer‘lype I Address I Phase I Reversed I Status [Battery thagIflMHWi/ersrofl 1 Battery I UB12220 PbA AGM 1 .1 $01 One No 011‘. 25 69¢ 12.5% 2.00 I 2.00 2 Battery UB1222O PbA AGM 2 / $02 One No Off. 25 degC 12.94V 2.00 / 2.00 L’3" Battery U312220 PbA AGM 3 / 303 One No 011‘. 24 (kgC 12.47V 2.00 l 2.00 El Battery UB12220 PbA AGM 4 / 504 One No Off. 22 defi 13.521] 2.00 / 2.00 Ed Battery UB12220 PbA AGM 5 / 505 One No Off. 23 derfl 12.98V 2.00 / 2.00 a Battery 0812220 PbA AGM 6 / $06 One No Off, 24 degC 13.13V 2.00 I 2.00 Text Output 1 l0 Xl Her-upgrade master firmware version Is 1.00 I}; Upgrading master bootloader firmware... Smssfully verified 810 bootbader firmware bytes Upgrading master main firmware... Successfully verified 13944 main firmware bytes Master processor reset after successful upgrade Post-upgr ade master firmware version is 1.00 "- Operation completed in 9 seconds Commended Iall modules to sleep Battery module freqmncy calibration performed Found module with address 1 ($01) Found module with address 2 (502) Found module with address 3 (303) Found module with address 4 ($04) Fwnd module with address ‘5 ($05) Found module with address 6 ($06) Roll call surureded, found 6 nodules Canmanded all modules to wake and turnoff the Kit! converter and the low-Side MOSFETS Connected to EV Multilevel Inverter 02/26/06 11:49:27 PM M" til" [ Figure A.1. Monitor v0.5 Screenshot with Six Battery Modules Figure A.1 shows a screenshot of real—time updating temperatures and voltages from six actual battery modules after a master firmware upgrade was performed. The major portions of the Monitor v0.5 code can be seen below. Future versions could potentially support motor performance graphing and logging as well, perhaps with an integration of matplotlib, the same software package used to generate many figures in this work. A large number of variables can be obtained from the master module, including the applied motor amplitude, untransformed and transformed currents, and an entire list of which slave modules are turned on. Another possible feature is automatic tuning of the inverter parameters for a given motor through test runs conducted by Monitor. 72 A.2 User Interface Layout: monitor.ui l (ui version-"4.0" > 2 Monitor 3 4 5 Custom Auto Electronics EV Multilevel Inverter Monitor v0.5 6 7 8 (widget class-"QMenuBar" name="menubar" > 9 (property name="geometry" > 10 11 0 12 0 13 800 14 29 15 16 17 (widget c1ass="QMenu" name="menuFile" > 18 19 kamp;File 20 21 (addaction name="actionNewSettings" /> 22 (addaction name="action0penSettings" /> 23 24 25 26 (addaction name="actionWriteSettings" /> 27 28 29 30 (widget class-"QMenu" name-"menuModeControl" > 31 32 (string>&Mode Control 33 34 (addaction name-"actionEmergencyOff" /> 35 36 37 38 39 (widget class-"QMenu" name="menuGloba1" > 40 (property name-"title" > 41 <3tring>aamp;clobal Commands 42 43 44 (addaction name-"actionWakeAll" /> 45 46 (addaction name="actionDCDCOnA11“ /> 47 (addaction name-"actionBlinkAll" /> 48 (addaction name="separator" /> 49 50 (addaction name-"actionR011Ca11" /> 51 52 53 (widget class-"QMenu" name="menuLoca1" > 54 55 (string>&Local Commands 56 57 (addaction name-"actionSIeepM" /> 58 73 59 6O 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 (addaction name-"actionDCDCOffM" /> (addaction name="actionDCDCOnM" /> (addaction name-"actiononPM" /> (addaction name="action0nNM" /> (addaction name-"separator" /> (addaction name-"actionCheckCommM" /> (addaction name="actionReadEEPROM" /> (addaction name-"actionWriteEEPROM" /> (addaction name="actionReaddressM" /> (widget class-"QMenu" name-"menuFirmware" > (preperty name="title" > (string>Firm&ware (addaction name="actionUpgradeBFw" /> (addaction name-"menuModeControl" /> (addaction name-"menuclobal" /> (addaction name="menuLocal" /> (addaction name-"menuFirmware" /> (action name-"actionNewSettings" > (string>&New Module Layout (action name-"actionflpenSettings" > <3tring>tamp;0pen Module Layout (action name-"actionSaveSettings" > (property name-"text" > tamp;Save Module Layout (lprOperty> (action name-"actionReadSettings" > (string>&Read Layout from EV (property name-"text" > (string>&write Layout to EV (action name-“actionExit" > (property name-"text" > <3tr1ng>Etamp;xit (action name-"actionEmergencyOff" > (property name="text" > <3tr1ng>tamp;Emergency 0ff (action name-"actionDiagnosticMode" > 74 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 (property name-"checkable" > true (property name-"checked" > (bool>false(/bool> (property name-"text" > (string>&Diagnostic Mode (/action> (action name="actionMonitorMode" > (property name-"checkable" > (bool>true(/bool> (property name-"checked" > true (string>&Monitor Mode (action name-"actionSIeepAll" > (property name-"text" > (string>Put All to tamp;Sleep(/string> (action name-"actionWakeAll" > tamp;wake All (DC/DC, FETS Off) (action name-"actionDCDCOffAll" > (property name="text" > <3tring>DC/DC Conv. 0n, FETs &0ff A11 (action name-"actionDCDCOnAll" > <3tring>tamp;DC/DC Conv. 0n, FETs 0n All (action name-"actionBlinkAll" > (property name-"text" > tamp;Blink All Modules LEDs (action namet"actionCalibrate“ > <3tring>Calibrate tamp;Frequency (action name-"actionRollCall" > (string>Perform tamp;Roll Call (action name-"actionCheckCommAll" > lamp;Verify Communication8 75 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 (property name="text" > Put Module to tamp;Sleep (action name-"actionWakeM" > (property name-"text" > <3tring>hamp;wake (DC/DC, FETs 0ff) (string>DC/DC Conv. 0n, FETs kamp;0ff(/string> (string>&DC/DC Conv. 0n, PETS 0n (action name-"actionOnPM" > <3tring>Turn Module 0n hamp;Positive (/action> (action name-“actionOnNM" > <3tring>Turn Module On hamp;Negative (laction> (action name-"actionBlinkM“ > <8tring>&Blink Module LED(/string> (string>&Verify Communications (string>Read tamp;EEPROM (laction> (prOperty name-"text" > (string>wr&ite EEPROM (/action> (action name-"actionReaddressM" > Retamp;address Modu1e lamp;Change Phase (action name-"actionUpgradeMFU" > <3tring>Upgrade tamp;Master... 76 245 246 247 248 249 250 251 252 253 254 255 <8tring>Upgrade tamp;Battery Modules (/action> (/widget> (reeources/> (connections/> 77 ... A.3 Header File: monitor.h r-du—b wowooqcscnboowu— mflcfimhMMI—‘OCDWNOSU‘ OOND-‘OCD ~105CJ1 UNI-lo endosouooazowocoooxxosmbwto #ifndef ldefine #include tinclude tinclude #include tinclude Cinclude #include #include #include #include #include #include #include #include #include tinclude #include #include “include tinclude #include #define tdefine #define #define #define #define #define #define #define #define #define #define #define #define #define #define #define #define #define tdefine class Monitor { HONITOR_H HONITOR_H "ui-monitor.h <0Widget> <0Rect> (QTableWidget (QListUidget> (QDockHidget> (QFileDialOg> (sys/io.h> <3ys/types.h> (stdlib.h> (math.h> VENDOR_ID PRODUCT_ID ERASE_PROG_ROW HRITE_PROG_ROU ERASE_DATA_HORD ERASE_DATA_ROW URITE_DATA_HORD WRITE_DATA_ROW VRITE_CONFIG READ-ROM_LONG READ_ROH_HROH HRITE_RAM_UORD READ_RAM_LONG JUHP_0R_RESET CHECK_COMM_MOD REPROGRAM_MODS SEND_MODS_FAST SEND-HODS_SLOW RECEIVE_MODS CLEAR-STATUS Q_OBJECT public: Monitor(QMainHindow tparentsO); ~Monitor-0: > > 0x04D8 OxDCBA ‘DmflOSCfl-waD-h HHHHHHH mmpmwpo 17 31 public QMainWindov 78 59 private slots: 60 void keep_time(); 61 void on_actionNewSettings_activated(); 62 void on_action0penSettings_activated(); 63 void on_actionSaveSettings_activated(); 64 void on_actionReadSettings_activated(); 65 void on_actionHriteSettings_activated(); 66 void on_actionExit_activated()3 67 void on_actionEmergency0ff_activated(); 68 void on_actionDiagnosticMode_activated(); 69 void on_actionMonitorMode_activated(); 70 void on_actionSleepAll_activated(); 71 void on_actionUakeA11_activated(); 72 void on_actionDCDCfoAll_activated(); 73 void on_actionDCDCOnAll_activated(); 74 void on_actionBlinkA11_activated(); 75 void on_actionCa1ibrate_activated(); 76 void on_actionRollCall_activated(); 77 void on_actionCheckCommA11_activated(); 78 void on_actionSleepM_activated(); 79 void on_actionHakeM_activated(); 80 void on_action0nPM_activated(); 81 void on_action0nNM_activated(); 82 void on_actionDCDCOffM_activated(); 83 void on_actionDCDCOnM_activated(); 84 void on_actionBlinkM-activated(); 85 void on_actionCheckCommM_activated(); 86 void on-actionReadEEPROM_activated(); 87 void on_actionwriteEEPROM_activated(); 88 void on_actionReaddressM-activated(); 89 void on_actionChangePhase_activated(); 90 void on_actionUpgradeMFH_activated(); 91 void on_actionUpgradeBFH_activated(); 92 93 private: 94 UizzMonitor ui; 95 }; 96 97 98 #endif 79 A.4 Main Code: monitor.cpp #include "monitor.h" #define DEBUG NO>U1QODN~ logged[400], bytestext[300]; char statustext[300], timetext[50], manufacturer[80], product[80], buf[400], 8 int receivedso, pleaseprint-O, addresses[120], nummods=0, firmware[2][2][120]; 9 unsigned int address-0; 10 long timebegin-O; 11 unsigned long bufx[64]; 12 struct usb_device *usb_dev=0; 13 struct usb_dev_band1e tusb_handle=0; 14 struct usb_bus tusb_bus; 15 struct usb-device tdev; 16 QLabel tstatuslabel; 17 QTimer *timer; 18 OTableHidget #biglist; 19 OTableUidgetItem *modu1e_entries[128][8]; 20 QListHidget toutputList; 21 QDockHidget toutputDock; 22 23 24 unsigned int readhex(char inhex); 25 int roll_call(bool onlynew); 26 void calibrate(void); 27 bool check_comm(int address); 28 bool check_boot(void); 29 bool issue_prog(long paddress, long pdata, char pcommand); 30 bool vait_finish(void); 31 bool vrite_fast(long vall, long val2, long va13, long va14); 32 bool vrite_slov(long vali, long va12, long va13, long va14); 33 long read_vord(void); 34 void vrite_log(const char *logtext); 35 36 37 Monitor::Monitor(QMainHindow #parent) 38 :QMainWindov(parent) 39 { 40 int i; 41 42 ui.setupUi(this); 43 44 outputDocksneu QDockHidget(tr("Text Output"), this); 45 outputDock->setAllovedAreas(0t::TopDockHidgetAreath::BottomDockHidgetArea); 46 outputList=nev QListwidget(outputDock); 47 outputDock->setHidget(outputList); 48 outputDock->setMinimumHidth(200); 49 addDockHidget(0t::BottomDockwidgetArea, outputDock); 50 51 QFont fixedFont("Helvetica", 11); 52 fixedFont.setStretch(110); 53 statuslabel-nev QLabe1(this); 54 statuslabel->setFont(fixedFont); 55 statuslabel->setSizePolicy(QSizePolicy(QSizePolicy::Expanding, QSizePolicy:: Fixed)): 56 80 57 delete ui.centra1widget; 58 biglist-nev QTableHidgetC); 59 setCentralHidget(biglist); 60 biglist->setColumnCount(8); 61 biglist->setHorizontalfleaderLabels(QStringList() 62 ((" Module Description " 63 ((" Battery/Power Type " 64 ((" Address " 65 ((" Phase " 66 ((" Reversed " 67 ((" Status " 68 (("Battery Voltage" 69 ((“FH/HW Version"); 70 biglist->resizeColumnToContents(O); 71 biglist->resizeColumnToContents(1); 72 biglist->resizeColumnToContents(2); 73 biglist->resizeColumnToContents(3); 74 biglist->resizeColumnToContents(4); 75 biglist->resizeColumnToContents(5); 76 biglist->resizeColumnToContents(6); 77 biglist->resizeColumnToContents(7); 78 79 for(i-0;i<120;i++) addresses[i]=0; 80 81 sprint1(statustext, "
 Unconnected 
"); 82 statuslabel->setText(statustext); 83 statuslabel->setTextFormat(Qt::RichText); 84 85 usb_init(); 86 87 timer'nev 0Timer(this); 88 connect(timer, SIGNAL(timeout()), SLOT(keep_time())); 89 timer->start(900); 90 91 statusBar()->addWidget(statuslabel, 1); 92 93 shovMaximized(); 94 } 95 96 97 Monitor::’Monitor() 98 { 99 if(usb_hand1e!-NULL) usb_close(usb_handle); 100 } 101 102 103 void Monitor::keep_time() 104 { 105 int i, j, k; 106 unsigned long volts, loctemp; 107 struct tm *timestruct; 108 time_t timenov; 109 110 timenow-time(NULL); 111 timestruct-localtime((const time_t *) atimenov); 112 if(timestruct->tm,hour--0) { 113 sprintf(timetext, "ZO2d/ZO2d/Xo2d 12:%O2d:%02d AM", timestruct->tm_mon+1. timestruct->tm_mday, timestruct->tm_year%100, timestruct->tm_min, timestruct->tm_sec); 114 } else if((timestruct->tm_hour>O)&&(timestruct->tm_hour(12)) { 115 sprintf(timetext, "ZO2d/ZO2d/Zo2d %2d:%02d:%02d AM", timestruct->tm_mon 81 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 // // // // // // // // +1, timestruct->tm_mday, timestruct—>tm_year%100. timestruct->tm_hour, timestruct->tm_min, timestruct->tm_sec); } else if(timestruct->tm_hour--12) { sprintf(timetext. "302d/ZO2d/202d 122%O2d:%02d PM", timestruct->tm_mon+1, timestruct->tm_mday, timestruct->tm_year%100, timestruct->tm_min, timestruct ->tm_sec); } else { sprintf(timetext, "ZO2d/ZO2d/Z02d&nb3p;%2d:%02d:%02d PM", timestruct->tm_mon +1, timestruct->tm-mday, timestruct->tm_year%100, timestruct->tm_hour-12, timestruct->tm_min, timestruct->tm_sec); if(usb_dev--NULL) { usb_find_busses(); usb_find_devices(); usb_bussee-usb_get_busses(); for(usb-bus=usb_busses;usb_bus;usb,bus=usb_bus->next) { for(dev-usb_bus->devices;dev;dev=dev->next) { if((dev->descriptor.idVendor--VENDOR_ID)&& (dev->descriptor.idProduct-BPRODUCT_ID)) usb,dev=dev; if(usb_dev!=NULL) { usb_handle=usb_open(usb_dev); if(usb_handle!-NULL) { received-usb_c1aim_interface(usb-handle, 0); if(received8-O) { receivedausb_get_string-simple(usb_handle, 1, manufacturer, 80); printt("Manufacturer: "); for(i-O;i0) printf('Interrupt read vorks\n"); } else { printf("Interface could not be claimed: %d\n", received); printf("%s\n", usb_strerror()); } } } else { nummods-O; for(i=0;i<120;i++) { addresses[i]=0; firmware[0][0][iJ-firmvare[0][1][i]-firmware[1][0][i]=firmuare[1][1][i]=0; } } } else { for(i-O;i<10;i++) { received-usb_interrupt_read(usb_handle, 0x81. buf, 64. 0); printf(”%d\n", received); ir(received>0) break; } if(received<-O) { usb_reset(usb_handle); usb_close(usb_hand1e); usb_dev-O; usb_handle-O; } else { for(i=0;irovCount();i++) { // Find the actual module address sscanf(modu1e_entries[i][2]->text().toAscii(), "Zd", kj); 82 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 // // Read the battery voltage urite_fast(0x8180+j, O, 0, 0); k=read_vord(); volts=(unsigned long) kt2; // Read the local temperature byte vrite_fast(0x8000+j, 0x800A, 0, 0); loctemp-(unsigned long) (read-vord()&0xOOFF); if(k---1) { if(strcmp(module_entries[i][5]->text().toAscii(), "(Not present)")) { delete module_entries[i][S]; module_entries[i][SJ-nev QTableHidgetItem("(Not present)"); module_entries[i][5]->setTextA1ignment(Qt::AlignHCenter); biglist->setItem((int) i, 5, module_entries[i][SJ); if(strcmp(module_entries[i][6]->text().toAscii(), "N/A")) { delete module_entries[i][6]; module_entries[i][61-nev QTableHidgetItem("N/A"); module_entries[i][6]->setTextAlignment(0t::AlignHCenter); biglist->setItem((int) i, 6, module_entries[i][6]); if(strcmp(module_entries[i][7]->text().toAscii(), "N/A")) { delete module-entries[i][7]; module_entries[i][71-nev QTableHidgetItem("N/A"); module_entries[i][7]->setTextAlignment(Qt::AlignHCenter); biglist->setItem((int) i, 7, module_entries[i][71); } } else { sprintf(buf, "Off, Zld degC", if(strcmp(modu1e_entries[i][5]->text().toAscii(), buf)) { delete module_entries[i][S]; module_entries[i][SJ-new QTableHidgetItem(buf); module_entries[i][5]->setTextAlignment(Qt::AlignHCenter); biglist->setItem((int) i, 5, module_entries[i][SJ); } sprintICbuf, "25.2fV } loctemp-128); 7.5+((double) (volts))*0.0095); if(strcmp(module_entries[i][6]->text().toAscii(), buf)) { delete module_entries[i][G]; module_entries[i][6J-nev QTableHidgetItemsetTextAlignment(Qt::AlignHCenter); biglist->setItem((int) i, 6, module_entries[i][6]); sprintf(buf, "Zd.%02x / Zd.%O2X", firmware[0][1][addresses[i]-1], firmware[0][0][addresses[i]-1], firmware[1][1][addresses[i]-1], firmware[1][0][addressesEiJ-1J); if(strcmp(module_entries[i][7]->text().toAscii(), buf)) { delete module_entries[i][7]; module_entries[i][7]=new QTableHidgetItem(buf); module_entries[i][7]->setTextAlignment(Qt::AlignHCenter); biglist->setItem((int) i, 7, module_entries[i][71); roll_ca11(TRUE); 83 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 if(received>0) { sprintf(statustext, "
 Connected to Xs%s&nb3p;
", product, timetext); statuslabel->setText(statustext); statuslabel->setTextFormat(0t::RichText); } else { sprint1(statustext, “(table vidth=10022> Unconnected%s ", timetext); statuslabel->setText(statustext); statuslabel—>setTextFormat(Qt::RichText); timer->start(750); int roll_call(bool onlynev) { char buf[80]; int i, j, k, lastfound-O, neufound=0; lastfound-addresses[nummods-l]; if(!onlyneu) nummods-O; if(!vrite_fast(0x807E, 0, 0, 0)) { outputList->addItem("Failed to command modules to roll call, check USB signal" ); outputList->scrollToItem(outputList->item(outputList->count()-1)): sprintf(logged, "roll_ca11: Notified \"Failed to command modules to roll call, check USB signa1\"\n"); vrite_log(logged); return -2; if(!on1ynev) { for(j-1;j<-120;j++) { // Try to see if the addressed module (address of j) is present if(!vrite_fast(018000+j, 0, 0, 0)) { outputList->addItem("Failed to command modules to roll call, check USB signal"); outputList->scrollToItem(outputList->item(outputList->count()-1)); sprintf(logged, "roll_ca11: Notified \"Failed to command modules to roll call, check USB signal\"\n"); vrite_log(logged); return -2; // Get the reply. if one came karead_vord(); it(k---2) { outputList->addItem("Failed to command modules to roll call, check USB signal"); outputList->scrollToItem(outputList->item(outputList->count()-1)); sprintf(logged, "roll_ca11: Notified \"Failed to command modules to roll call, check USB signal\"\n"); vrite_log(logged); return -2; // Interpret the result 84 286 287 288 289 290 291 292 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 335 336 337 338 339 if(k!--1) { if((k&0xFF)'=j) { sprintf(buf, "Found module with address Zd ($ZO2X)", j, j); outputList->addItem(buf); outputList->scrollToItem(outputList->item(outputList->count()-1)); sprintf(logged, "roll_call: Notified \"%s\"\n", buf); vrite_log(logged); addresses[nummods++]-j; addresses[nummods]=0; lastfound-j; } else { sprintf(buf, "Reply error for address Zd ($Z02X), incorrectly Zd ($102K) ", j, j, k&0x00FF, khOxOOFF); outputList->addItem(buf); outputList->scrollToItem(outputList->item(outputList->count()-1)); sprintf(logged, "roll_call: Notified \"%s\"\n", buf); vrite_log(logged); return -1; if((nummods<120)&&(lastfound(120)) { // Try to see if the addressed module (address of lastfound+1) is present if(!write_fast(Ox8001+lastfound, 0, O, 0)) { outputList->addItem("Failed to command modules to roll call, check USB signal"); outputList->scrollToItem(outputList->item(outputList->count()-1)); sprintf(logged, "roll_call: Notified \"Failed to command modules to roll call, check USB signal\"\n"); urite_log(logged); return -2; // Command the non-addressed module to assign itself an address if(!write_fast(0x80FF, 0, O, 0)) { outputList->addItem("Failed to command modules to roll call, check USB signal"); outputList->scrollToItem(outputList->item(outputList->count()-1)); sprinthlogged, "roll_call: Notified \"Failed to command modules to roll call, check USB signa1\"\n"); write_log(logged); return -2; // Get the reply, if one came ksread_vord(); if(k=--2) { outputList—>addItem("Failed to command modules to roll call, check USB signal"); outputList->scrollToItem(outputList->item(outputList->count()-1)); sprintf(logged, "roll_call: Notified \"Failed to command modules to roll call, check USB signa1\"\n"); urite_1og(logged); return -2; // Interpret the result if(k!=-1) { if((k&OxFF)--OXFF) { sprintf(buf, "Assigned module new address Zd ($ZO2X)", 1astfound+1, lastfound+1); outputList->addItem(buf); 85 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 outputList—>scrollToItem(outputList->item(outputList->count()-1)); addresses[nummods++]=1astfound+1; addresses[nummods]-O; nevfound++; sprintf(logged, "roll_call: Notified \"%s\"\n", but); srite_log(logged); } else { sprintf(buf, "Warning: Unusual reply Zd ($102K) while testing for unassigned modules, should be none or $FF", ktOxOOFF, khOxOOFF); outputList->add1tem(buf); outputList->scrollToItem(outputList->item(outputList->count()-1)); sprintf(logged, "roll_call: Notified \"%s\"\n", buf); write_log(logged); if(!srite_fast(0x8000, 0, 0, 0)) { outputList->addItem("Failed to command modules to roll call, check USB signal" ); outputList->scrollToItem(outputList->item(outputList->count()-1)): sprintf(logged, "roll_call: Notified \"Failed to command modules to roll call, check USB signa1\“\n"); write_log(10gged); return -2; if(!onlynew) { if(nummods=-O) { sprintf(buf, "Roll call succeeded, found no modules"); } else if(nummods--1) { sprintf(buf, “Roll call succeeded, found 1 module"); } else { sprintf(buf, "Roll call succeeded, found Zd modules", nummods); } outputList->addItem(buf); outputList->scrollToItem(outputList->item(outputList->count()-1)); if((on1ynew)&&(!nevfound)) return 0; // Command the battery modules to enter their bootloaders if(!vrite_slov(0x81AC, 0x8153, 0x8100, 0)) { outputList->addItem("Failed to command modules to enter bootloader, check USB signal"); outputList->scrollToItem(outputList->item(outputList->count()-1)); sprintf(logged, "roll_call: Notified \"Failed to command modules to enter bootloader, check USB signal\"\n"); vrite_log(logged); return -2; for(i-(onlyneu?(nummods-1):0);iaddItem("Failed to address module in bootloader, check USB signal"); outputList->scrollToItem(outputList->item(outputList->count()-1)); sprintf(logged, "roll_call: Notified \"Failed to address module in bootloader, check USB signal\"\n"); srite_log(logged); return -2; 86 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 // Get the reply, if one came k-read_word(); if(k---2) { outputList->addItem("Failed to address module in bootloader, check USB signal"); outputList->scrollToItemitem(outputList->count()-1)); sprintf(logged, "roll_call: Notified \"Failed to address module in enter bootloader, check USB signal\"\n"); write_log(logged); return -2; // Interpret the result if((k==-1)||((k&0xFF)!-(Ox4B‘addresses[i]))) { sprintf(buf, "Enter bootloader failed on module with address $ZO2X", addresses[i]); outputList->addItem(buf); outputList->scrollToItem(outputList->item(outputList->count()-1)); sprintf(logged, "roll_call: Notified \"%s\"\n", buf); write_log(logged); return -2; firmware[0][0][addresses[i]-1]=0x00; firmware[0][1][addressesEi]-1]=Ox00; firmware[1][0][addresses[i]-1]=Ox00; firmware[1][1][addresses[i]-1]-Ox00; // Read the firmware version low byte if(!write_slow(0x8122, 0x8100, 0x8184, 0)) { outputList->addItem("Failed to read module firmware version from bootloader, check USB signal"); outputList->scrollToItem(outputList->item(outputList->count()-1)); sprintf(logged, "roll_call: Notified \"Failed to read module firmware version from bootloader, check USB signal\"\n"); write_log(logged); return -2; // Get the reply, if one came k-read_word(); if(k---2) { outputList->addItem("Failed to read module firmware version from bootloader, check USB signal"); outputList->scrollToItem(outputList->item(outputList->count()-1)); sprintf(logged, "roll_call: Notified \"Failed to read module firmware version from bootloader, check USB signal\"\n"); write_log(logged); return -2; } if(k!--1) firmware[0][0][addresses[i]-1]=k&0xFF; // Read the firmware version high byte if(!write_slov(0x8123, Ox8100, 0x8184, 0)) { outputList->addItem("Failed to read module firmware version from bootloader, check USB signal"); outputList->scrollToItem(outputList->item(outputList->count()-1)); sprintf(logged, "roll_call: Notified \"Failed to read module firmware version from bootloader, check USB signal\"\n"); write_log(logged); return -2; 87 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 // Get the reply, if one came k-read_word(); if(k---2) { outputList->addItem("Failed to read module firmware version from bootloader, check USB signal"); outputList->scrollToItem(outputList->item(outputList->count()-1)); sprintf(logged, "roll_ca11: Notified \"Failed to read module firmware version from bootloader, check USB signal\"\n"); write_log(10gged); return -2; } if(k!--1) firmware[0][1][addresses[i]-1]=k&0xFF; // Read the bootloader version low byte if(!write_slow(0x81E0, 0x8106, 0x8184, 0)) { outputList->addItem("Fai1ed to read module firmware version from bootloader, check USB signal"); outputList->scrollToItem(outputList->item(outputList->count()-1)); sprintf(logged, "roll_call: Notified \"Failed to read module firmware version from bootloader, check USB signal\"\n"); write-1og(logged); return -2; // Get the reply, if one came k-read_word(); if(k---2) { outputList->addItem("Failed to read module firmware version from bootloader, check USB signal“); outputList->scrollToItem(outputList->item(outputList~>count()-1)); sprintf(logged, "roll_call: Notified \"Failed to read module firmware version from bootloader, check USB signal\"\n"); write_log(logged); return -2; } if(k!--1) firmware[1][0][addresseSEiJ-1]=k&OxFF; // Read the bootloader version high byte if(!write_slow(0x81E1, 0x8106, 0x8184, 0)) { outputList->addItem("Failed to read module firmware version from bootloader, check USB signal"); outputList->scrollToItem(outputList->item(outputList->count()-1)); sprintf(logged, "roll_call: Notified \"Failed to read module firmware version from bootloader, check USB signa1\"\n"); write_log(10gged); return -2; // Get the reply, if one came k-read_word(); if(k---2) { outputList->addItem("Failed to read module firmware version from bootloader, check USB signal"); outputList->scrollToItem(outputList->item(outputList->count()-1)); sprintf(logged, "roll_call: Notified \"Failed to read module firmware version from bootloader, check USB signal\"\n"); write_log(logged); return -2; } if(k!--1) firmware[1][1][addressesEi]-1]=k&OxFF; 88 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 // Command the battery modules to exit their bootloaders softly if(!write_slow(0x81FF, 0x81FF, 0x81FF, 0)) { outputList->addItem("Failed to command modules to exit bootloader, check USB signal"); outputList->scrollToItem(outputList->item(outputList->count()-1)); sprintf(logged, "roll_call: Notified \"Failed to command modules to exit bootloader, check USB signal\"\n"); write_log(logged); return -2; return (onlynew?1:nummods); void calibrate(void) { int i; if(!usb_handle) return; // Send two 0101798 to make the modules sleep if(!write_fast(0x8179, 0x8179, 0, 0)) { outputList->addItem("Fai1ed to command modules to sleep, check USB signal"); outputList->scrollToItem(outputList->item(outputList->count()-1)); sprintf(logged, "calibrate: Notified \"Failed to command modules to sleep, check USB signal\"\n"); write_log(logged); return; } outputList->addItem("Commanded all modules to sleep"); outputList->scrollToItem(outputList->item(outputList->count()-1)); sprintf(logged, "calibrate: Notified \"Commanded all modules to sleep\"\n"); write_log(logged); // Get the modules into the synchronization routine if(!write_fast(0x81FF, 0x81FF, 0, 0)) { outputList->addItem("Failed to command modules to calibrate, check USB signal" ); outputList->scrollToItem(outputList->item(outputList->count()-1)); sprintf(logged, "calibrate: Notified \"Failed to command modules to calibrate, check USB signa1\"\n"); write_log(logged); return; // Send 125de all-zero words to the modules to help them synchronize for(i-0;i<32;i++) { if(!write_slow(0x8000, 0, 0, 0)) { outputList->addItem("Failed to command modules to calibrate, check USB signal"); outputList->scrollToItem(outputList->item(outputList->count()-1)); sprintf(logged, "calibrate: Notified \"Failed to command modules to calibrate, check USB signal\"\n"); write_log(logged); return; // Send a 856 and a $78 to the modules to tell them that this function is valid if(!write_slow(0x8156, 0x8178, 0, 0)) { 89 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 outputList->addItem("Failed to command modules to calibrate, check USB signal" ); outputList->scrollToItem(outputList->item(outputList->count()-1)): sprintf(logged, "calibrate: Notified \"Failed to command modules to calibrate, check USB signa1\"\n"); write_log(logged); return; outputList->addItem("Battery module frequency calibration performed"); outputList->scrollToItem(outputList->item(outputList->count()-1)); sprintf(logged, "calibrate: Notified \"Battery module frequency calibration performed\"\n"); write_log(logged); // Delay to let the modules rewrite their calibration bytes (EEPROM $00) for(i=0;i<20;i++) received=usb_interrupt_read(usb_handle, 0x82, buf, 64, 0); bool check_comm(int address) // Send a command to the DSPIC to check a battery module’s communications if(!issue_prog(0, address, CHECK_COMM_MOD)) { outputList->addItem("Failed to check battery module’s communications, check USB signal"); outputList->scrollToItem(outputList->item(outputList->count()-1)); sprintf(logged, "check_comm: Notified \"Failed to check battery module’s communications, check USB signa1\"\n"); write_log(logged); return FALSE; // Report the result if(bufx[16]-=1) { sprintf(buf, "Check communications succeeded on module with address Zd ($102K) ", address, address); outputList->addItem(buf); outputList->scrollToItem(outputList->item(outputList->count()-1)); sprintf(logged, "check_comm: Notified \"%s\"\n", buf); write_log(logged); return TRUE; } else if(bufx[16]=-2) { Sprintf(buf, "Check communications failed on module with address Zd ($X02X) - timeout", address, address); outputList->addItem(buf); outputList->scrollToItem(outputList->item(outputList->count()—1))3 sprintf(logged, "check_comm: Notified \"%s\"\n", buf); write_log(logged); return FALSE; } else if(bufx[16]--3) { sprintf(buf, "Check communications failed on module with address Zd (SZO2X) - bad byte", address, address); outputList->addItem(buf); outputList->scrollToItem(outputList->item(outputList->count()-1)); sprintf(logged, "check_comm: Notified \"%s\"\n", buf); write_log(logged); return FALSE; } else { sprintf(buf, "Check communications failed on module with address Zd ($ZO2X) - unknown", address, address); outputList->addItem(buf); 90 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 outputList->scrollToItem(outputList->item(outputList->count()-1)); sprintf(logged, "check_comm: Notified \"%s\"\n", buf); write_log(logged); return FALSE; bool check_boot(void) int i=0, k; while(addresses[i]!-0x00) { // Command the battery modules to enter their bootloaders if(!write_slow(0x81AC, 0x8153, 0x8100+addresses[i], 0)) { outputList->addItem("Failed to command modules to enter bootloader, check USB signal"); outputList->scrollToItem(outputList->item(outputList->count()-1)); sprintf(logged, "check_boot: Notified \“Failed to command modules to enter bootloader, check USB signal\"\n"); write_log(logged); return FALSE; // Get the reply, if one came k-read_word(); if(k=--2) { outputList->addItem("Failed to command modules to roll call, check USB signal"); outputList->scrollToItem(outputList->item(outputList->count()-1)); sprintf(logged, "check_boot: Notified \"Failed to command modules to roll call, check USB signa1\“\n"); write_log(10gged); return FALSE; // Command the battery modules to exit their bootloaders softly if(!write_slow(0x81FF, 0x81FF, 0x81FF, 0)) { outputList->addItem("Failed to command modules to exit bootloader, check USB signal"); outputList~>scrollToItem(outputList—>item(outputList->count()-1)); sprintf(logged, "check_boot: Notified \"Failed to command modules to exit bootloader, check USB signal\"\n"); write_log(logged); return FALSE; // Interpret the result if((k=--1)I|((k&0xFF)!=(0x4B‘addresses[i]))) { sprintf(buf, "Enter bootloader failed on module with address $XO2X", addresses[i]); outputList->addItem(buf); outputList->scrollToItem(outputList->item(outputList->count()-1)); sprintf(logged, "check_boot: Notified \"%s\"\n", buf); write_log(logged); return FALSE; } i++; sprintf(buf, "Enter bootloader succeeded on all Zd modules", 1); outputList->addItem(buf); 91 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 outputList->scrollToItem(outputList->item(outputList->count()-1)); sprintf(logged, "check_boot: Notified \"%s\"\n", buf); write_log(logged); return TRUE; bool issue_prog(long paddress, long pdata, char pcommand) int i; // Write out a data, address, and programming command, and wait for completion // Note: For HRITE_PROG_ROH or HRITE_DATA_ROH, buf[32..63] will not be ruined for(i-0;i<32;i++) buf[iJBO; buf[8J-(char) (paddresstOxFF); buf[91-(char) ((paddress>>8)&OxFF); buf[lOJ-(char) ((paddress>>16)&0xFF); buf[12]-(char) (pdatakOxFF); buf[13]'(Char) ((pdata>>8)&0xFF); buf[11]-0x00; buf[15]-pcommand; if(usb_interrupt_write(usb_handle, 0x01, buf, 64, 0)!-64) return FALSE; for(i-0;i<64;i++) sprintf(bytestext+(it3), “ ZO2X", (unsigned char) buf[i]); sprintf(logged, "issue_prog: Written to USB 0x01: %s\n", bytestext); write_log(logged); if(pcommand-=HRITE_RAM-VORD) return TRUE; if(!wait_finish()) return FALSE; return TRUE; bool wait_finish(void) int i; // Test ’progstat’ bit six to see if the operation completed yet do { received=usb_interrupt_read(usb_handle, 0x82, buf, 64, 0); if(received--64) { for(i-0;i<64;i++) bufx[i]=(unsigned long) ((unsigned char) buf[i]); } else { return FALSE; } } while(!(bufx[1]&0x40)); for(i-0;i<64;i++) sprintf(bytestext+(i*3), " Z02X", (unsigned char) buf[i]); sprintf(logged, "wait_finish: Read from USB 0x82: %s\n", bytestext); write_log(logged); // Send a programming command of ’CLEAR_STATUS’ (31) to clear this flag for(i-0;i<64;i++) buf[i]'((i--15)?CLEAR_STATUS:0x00)3 if(usb_interrupt_write(usb_handle, 0x01, buf, 64, 0)!-64) return FALSE; for(i-0;i<64;i++) sprintf(bytestext+(i*3), " XO2X", (unsigned char) buf[i]); sprintf(logged, "wait_finish: Written to USB 0x01: %s\n", bytestext); write_log(logged); // ’bufx[64]’ contains the last USB 0x82 interrupt read data return TRUE; bool write-fast(long va11, long val2, long va13, long va14) int i; 92 725 726 // Write out a set of four or less 1MBd words to the battery modules 727 // Note: write-fast(0x8123, 0x8000, 0, 0) sends 010123 and 0x0000 728 for(i-0;i<64;i++) buf[iJ-O; 729 buf[0]=(char) (va11&0xFF); 730 buf[1]'(char) ((va11>>8)&0xFF); 731 buf[2J-(char) (va12&OxFF); 732 buf[BI-(char) ((val2>>8)&0xFF); 733 buf[41-(char) (val3&0xFF); 734 buf[SJ-(char) ((va13>>8)&0xFF); 735 buf[6]=(char) (val4l0xFF); 736 buf[7]-(char) ((va14>>8)&0xFF); 737 buf[15]=SEND_MODS_FAST; 738 if(usb_interrupt_write(usb_handle, 0x01, buf, 64, 0)!=64) return FALSE; 739 for(i-0;i<64;i++) sprintf(bytestext+(i*3), " ZO2X", (unsigned char) buf[i]); 740 sprintf(logged, "write_fast: Written to USB 0x01: %s\n", bytestext); 741 write_log(logged); 742 return TRUE; 743 744 745 746 bool write_slow(long va11, long val2, long va13, long val4) 747 748 int i; 749 750 // Write out a set of four or less 125de words to the battery modules 751 // Note: write_slow(0x8123, 0x8000, 0, 0) sends 0x0123 and 0x0000 752 for(i=0;i<64;i++) buf[i]=0; 753 buf[OJ-(char) (va11&OxFF); 754 buf[1]=(char) ((va11>>8)&0xFF); 755 buf[2]=(char) (va12&0xFF); 756 buf[SJ-(char) ((val2>>8)&0xFF); 757 buf[4]-(char) (va13t0xFF); 758 buf[Sl-(char) ((va13>>8)&OxFF); 759 buf[6]'(char) (val4&OxFF); 760 buf[TJ-(char) ((va14>>8)&OxFF); 761 buf[lS]-SEND_HODS_SLOH; 762 if(usb_interrupt_write(usb_handle, 0x01, buf, 64, 0)!-64) return FALSE; 763 for(i-0;i<64;i++) sprintf(bytestext+(i*3), " Z02X", (unsigned char) buf[i]); 764 sprintf(logged, "write_slow: Written to USB 0x01: %s\n", bytestext); 765 write_log(logged); 766 return TRUE; 767 } 768 769 770 long read_word(void) 771 { 772 int i; 773 774 // Write out a command to read a word from a battery module 775 for(i-0;i<64;i++) buf[iJ-O; 776 buf[lSJ'RECEIVE_HODS; 777 if(usb_interrupt_write(usb_handle, 0x01, buf, 64, 0)!=64) return -1; 778 for(i-0;i<64;i++) sprintf(bytestext+(i*3), " ZO2X", (unsigned char) buf[i]); 779 sprintf(logged, "read_word: Written to USB 0x01: %s\n", bytestext); 780 write_log(logged); 781 782 // Read the DSPIC’s reply (no delay is needed) 783 received-usb_interrupt_read(usb_handle, 0x82, buf, 64, O); 784 if(received--64) { 785 for(i'0;i<64;i++) bufx[i]-(unsigned long) ((unsigned char) buf[i]); 786 } else { 93 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 return -2; } for(i-0;i<64;i++) sprintf(bytestext+(it3), " ZO2X", (unsigned char) buf[i]); sprintf(logged, "read_word: Read from USB 0x82: %s\n", bytestext); write_log(logged); // Test ’progstat’ bit seven to see if a word was received if(bufx[1]&0x80) { // Send a programming command of ’CLEAR_STATUS’ (31) to clear this flag for(i=0;i<64;i++) buf[i]'((i"15)?CLEAR_STATUS:OXOO); if(usb_interrupt_write(usb_handle, 0x01, buf, 64, 0)!-64) return -1; for(i-0;i<64;i++) sprintf(bytestext+(i*3), " Z02X", (unsigned char) buf[i]); sprintf(logged, "read_word: Written to USB 0x01: %s\n", bytestext); write_log(logged); // Return the word’s value return (bufx[0]|((bufx[1]&0x01)<<8)); } else { return -1; void write_log(const char tlogtext) { } printf("%s", logtext); void Monitor::on_actionNewSettings_activated() { int i, j, retval; if(!usb_handle) return; if(biglist->rowCount()!-0) { retval-QMessageBox::warning(this, "Create New Module Layout", "Creating a new module layout will replace the module table contents with the result of a roll call. The roll call will discover all present modules and optionally assign an address to one new module. Alternatively, the Global Commands >> Roll Call menu item will only add to the table, not subtract from it. Continuing the New Module Layout command will destroy the existing table contents.\n\nTo save the current layout as a file and continue, press Save .\nTo discard the current layout and continue, press Discard.\nTo discontinue New Module Layout, press Cancel.", QMessageBox::Cancel| QMessageBox::SavelQMessageBox::Discard, QMessageBox::Cance1); if(retval==QMessageBox::Cancel) return; if(retval--0MessageBox::Save) on_actionSaveSettings_activated(); } calibrate(); roll_call(FALSE); biglist->setRowCount(nummods); for(i-O;isetTextAlignment(Qt::AlignHCenter); biglist->setItem((int) i, j, module_entries[i][jJ); } biglist->resizeRowToContents(i); void Monitor::on_actionOpenSettings_activated() char x, moddesc[40], battype[40], address[40], phase[40], reversed[40]; int i, j, retval; if(biglist->rowCount()!=0) { retval-QMessageBox::warning(this, "Open Module Layout", "Creating a new module layout will replace the module table contents with the selected file’s contents. Continuing the Open Layout command will destroy the existing table contents.\n\nTo save the current layout as a file and continue, press Save.\nTo discard the current layout and continue, press Discard \ nTo discontinue Open Module Layout, press Cancel.", 0MessageBox::Cance1l QMessageBox::SavelQMessageBox::Discard, QMessageBox::Cancel); if(retval--0MessageBox::Cancel) return; if(retval--0MessageBox::Save) on_actionSaveSettings_activated(); } QString s-QFileDialog::getOpenFileName(this, "Open Module Layout", "layout.cae", "Module Layout Files (*.cae)"); QFile findata(s); if(s.size()<2) return; if(!findata.open(QIODevice::ReadOnlleIODevice::Text)) { sprintf(buf, "Cannot open 23 for read", (const char *) s.toAscii()); outputList->addItem(buf); outputList->scrollToItem(outputList->item(outputList->count()-1)); return; findata.readLine(buf, 10); sscanf(buf, "2d", ti); if((i(1)||(i>120)) { sprintf(buf, "XS is not a valid module layout file", (const char *) s.toAscii ()); outputList->addItem(buf); outputList->scrollToItem(outputList->item(outputList->count()-1)); return; } biglist->setRowCount(i); if(usb_handle!-NULL) { calibrate(); roll_call(FALSE); } for(i-0;irowCount();i++) { 3'0: do { findata.getChar(&x); 95 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 if((x!=0x0D)&&(x!-0x0A)) moddesc[j++]=(x==0x09)?0x00:x; } whi1e(x!-Ox09); 3‘03 do { findata.getChar(&x); battype[j++]-(x=-0x09)?0x00:x; } while(x!-0x09); i=0: do { findata.getChar(&x); address[j++]-(x--Ox09)?0x00:x; } while(x!-0x09); i=0: do { findata.getChar(&x); phase[j++]-(x-=0x09)?0x00:x; } while(x!-0x09); 3'0; do { findata.getChar(&x); reversed[j++]-((x--0xOD)|l(x==0x0A))?Ox00:x; } while((x!-0x0D)&&(x!-0xOA)); module_entries[i][OJ-new module_entries[i][IJ-new module_entries[i][2]-new module_entries[i][BJ-new module_entries[i][4J-new module_entries[i][SJ-new module_entries[i][GJ-new module_entries[i][7J-new for(j=0;j<8;j++) { QTableWidgetItem(moddesc); QTableWidgetItem(battype); QTableWidgetItem(address); QTableWidgetItem(phase); QTableWidgetItem(reversed); QTableWidgetItem("(Not present)"); QTableWidgetItem("N/A"); QTableHidgetItGM("N/A"); module_entries[i][j]->setTextAlignment(0t::AlignHCenter); biglist->setItem((int) i, j, module_entries[i][jJ); } biglist->resizeRowToContents(i); findata.close(); sprintf(buf, outputList->addItem(buf); "Module layout loaded from XS", (const char #) s.toAscii()); outputList->scrollToItem(outputList->item(outputList->count()-1)); void Monitor::on_actionSaveSettings_activated() { int i; if(biglist->rowCount()==0) return; QString s-QFileDialog::getSaveFileName(this, "Module Layout Files (#.cae)"); QFile foutdata(s); if(s.size()<2) return; "Save Module Layout", "layout.cae" if(!foutdata.0pen(QIODevice::WriteOnlyIQIODevice::TruncatelQIODevice::Text)) { sprintf(buf, outputList->addItem(buf); "Cannot open ZS for write", 96 (const char *) s.toAscii()); 955 outputList->scrollToItem(outputList->item(outputList->count()-1)); 956 return; 957 } 958 959 sprintf(buf, "%d\r\n", biglist->rowCount()); 960 foutdata.write(buf, strlen(buf)); 961 962 for(i=0;i(biglist->rowCount();i++) { 963 sprintf(buf, "%s\t%s\t%s\t%s\t%8\r\n". 964 (const char *) module_entries[i][0]->text().toAscii(). 965 (const char t) module_entries[i][1]->text().toAscii(), 966 (const char #) module_entries[i][2]->text().toAscii(), 967 (const char #) module_entries[i][3]->text().toAscii(), 968 (const char t) module_entries[i][4]->text().toAscii()); 969 foutdata.write(buf, strlen(buf)); 970 } 971 972 foutdata.close(); 973 974 sprintf(buf, "Current module layout saved to Z5", (const char *) s.toAscii()); 975 outputList ->addItem (but); 976 outputList->scrollToItem(outputList->item(outputList->count()-1)); 977 } 978 979 980 void Monitor::on_actionReadSettings_activated() 981 { 982 } 983 984 985 void Monitor::on_actionWriteSettings_activated() 986 { 987 int i, j; 988 unsigned long outprom[144]; 989 990 if(!usb_handle) return; 991 992 // Set up the list of active modules (zero meaning not present) 993 for(i=0;i(144;i++) outprom[iJ-O; 994 for(i-0;irowCount();i++) { 995 sscanf(module_entries[i][2]->text().toAscii(), "2d", aj); 996 J“; 997 if(strcmp(module_entries[i][3]->text().toAscii(), "0ne")==0) { 998 outprom[j>>3]l-(1((((j%8)*2)); 999 } else if(strcmp(modu1e_entries[i][3]->text().toAscii(), "Two")==0) { 1000 outprom[j>>3]l-(2<<((j%8)*2)); 1001 } else if(strcmp(module_entries[i][3]->text().toAscii(), "Three")=-0) { 1002 outprom[j>>3]|‘(3<(((j%8)t2)); 1003 } 1004 if(strcmp(module_entries[i][4]->text().toAscii(), "Yes")-=O) 1005 outprom[(j>>4)+15]|=(1<<(j%16)); 1006 outprom[j+24]-22000>>2; 1007 } 1008 1009 for(i=0;i<144;i++) printf("write_set: outprom[Xd]=ZO4lX\n", i, outprom[i]); 1010 1011 for(i-0;i<144;i+=2) { 1012 // Erase two words of data memory 1013 if(!issue_prog(0x007FFC00+(i<<1), 0, ERASE_DATA_WORD)) goto ws_commerror; 1014 if(!issue_prog(0x007FFC02+(i<<1), 0, ERASE_DATA_WORD)) goto ws_commerror; 1015 1016 // Write out EEPROM words in pairs 97 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 if(!issue_prog(0x007FFC00+(i((1), outprom[i], WRITE_DATA_WORD)) goto ws_commerror; if(!issue_prog(0x007FFC02+(i<<1), outprom[i+1], WRITE_DATA_WORD)) goto ws_commerror; // Verify EEPROM words in pairs if(!issue_prog(0x007FFC00+(i<<1), 0, READ_ROM_LONG)) goto ws_commerror; if((bufx[16]!-(outprom[i]&0xFF))I|(bufx[17]!-((outprom[i]>>8)&0xFF))|I (bufx[19]!-(outprom[i+1]&0xFF))||(bufx[20]!-((outprom[i+1]>>8)&0xFF))) goto ws_promerror; // Reset the master processor if(!issue_prog(0, 0, JUMP_0R_RESET)) goto ws_commerror; for(i=0;i<64;i++) buf[i]-((i--15)?JUMP_0R_RESET:OXOO); if(usb-interrupt_write(usb_handle, 0:01, buf, 64, 0)!-64) goto ws_commerror; outputList->addItem("Master processor reset after successful module layout write to EEPROM"); outputList->scrollToItem(outputList~>item(outputList->count()-1)); sprintf(logged, "write_set: Notified \"Master processor reset after successful module layout write to EEPROM\"\n"); write-log(logged); // Delay for at least two seconds for(i-O;i<2000;i++) if(usb_interrupt_read(usb_handle, 0x82, buf, 64, 0)!-64) goto ws_commerror; return; ws_commerror: outputList->addItem("Communication error while writing master EEPROM"); outputList->scrollToItem(outputList->item(outputList->count()-1)); sprintf(logged, "write_set: Notified \"Communication error while writing master EEPROM\"\n“); write_log(logged); return; ws_promerror: outputList->addItem("Verification error while writing master EEPROM"); outputList->scrollToItem(outputList->item(outputList->count()-1)); sprintf(logged, "write_set: Notified \"Verification error while writing master EEPROM\"\n"); write_log(logged); void Monitor::on_actionExit_activated() QApplication::exit(0); void Monitor::on_actionEmergencyOff_activated() void Monitor::on_actionDiagnosticMode_activated() ui.actionDiagnosticMode->setChecked(TRUE); ui.actionMonitorMode->setChecked(FALSE); 98 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130 1131 void Monitor::on_actionMonitorMode_activated() ui.actionDiagnosticMode->setChecked(FALSE); ui.actionMonitorMode->setChecked(TRUE); void Monitor::on_actionSleepAll_activated() { if(!usb_handle) return; // Send one 0x0179 to make the modules enter the state ”sleeping" if(!write_fast(0x8179, 0, 0, 0)) { outputList->addItem("Failed to communicate with master, check USB signal"); outputList->scrollToItem(outputList->item(outputList->count()-1)); sprintf(logged, "monitor: Notified \"Failed to communicate with master, check USB signal\"\n"); write_log(logged); return; } outputList->addItem("Commanded all modules to sleep"); outputList->scrollToItem(outputList->item(outputList->count()-1)); sprintf(logged, "monitor: Notified \"Commanded all modules to sleep\"\n"); write_log(logged); void Monitor::on_actionWakeAll_activated() { if(!usb_handle) return; // Send one 0x017A to make the modules enter the state "idle" if(!write_fast(0x817A, 0, 0, 0)) { outputList->addItem("Failed to communicate with master, check USB signal"); outputList->scrollToItem(outputList~>item(outputList~>count()-1)); sprintf(logged, "monitor: Notified \"Failed to communicate with master, check USB signal\"\n"); write_log(logged); return; } outputList->addItem("Commanded all modules to wake and turn off the DC/DC converter and the low-side MOSFETS"); outputList->scrollToItem(outputList->item(outputList->count()-1)); sprintf(logged, "monitor: Notified \"Commanded all modules to wake and turn off the DC/DC converter and the low-side MOSFETs\"\n"); write_log(logged); void Monitor::on_actionDCDCOffAll_activated() { if(!usb_handle) return; // Send one 0x017B to make the modules enter the state "ready" if(!write_fast(0x817B, O, O, 0)) { outputList->addItem("Failed to communicate with master, check USB signal"); outputList->scrollToItem(outputList->item(outputList->count()-1)); sprintf(logged, "monitor: Notified \"Failed to communicate with master, check USB signal\"\n"); write_log(logged); return; 99 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 1150 1151 1152 1153 1154 1155 1156 1157 1158 1159 1160 1161 1162 1163 1164 1165 1166 1167 1168 1169 1170 1171 1172 1173 1174 1175 1176 1177 1178 1179 1180 1181 1182 1183 1184 1185 } outputList->addItem("Commanded all modules to turn on the DC/DC converter and turn off the low-side MOSFETS"); outputList->scrollToItem(outputList->item(outputList->count()-1)); sprintf(logged, "monitor: Notified \"Commanded all modules to turn on the DC/DC converter and turn off the low-side MOSFETs\“\n"); write_log(logged); void Monitor::on_actionDCDCOnAll_activated() { if(!usb_handle) return; // Send one 0x017C to make the modules enter the state "running" if(!write_fast(0x817C, 0, 0, 0)) { outputList->addItem("Failed to communicate with master, check USB signal"); outputList->scrollToItem(outputList->item(outputList->count()-1)); sprintf(logged, "monitor: Notified \"Failed to communicate with master, check USB signal\"\n"); write_log(logged); return; } outputList->addItem("Commanded all modules to turn on the DC/DC converter and the low-side MOSFETS"); outputList->scrollToItem(outputList->item(outputList->count()-1)); sprintf(logged, "monitor: Notified \"Commanded all modules to turn on the DC/DC converter and the low-side MOSFETS\"\n"); write_log(logged); void Monitor::on_actionBlinkAll_activated() { if(!usb-handle) return; // Send one 0x017D to make the modules blink if(!write_fast(0x817D, 0, 0, 0)) { outputList->addItem("Failed to communicate with master, check USB signal"); outputList->scrollToItem(outputList->item(outputList->count()-1)); sprintf(logged, "monitor: Notified \"Failed to communicate with master, check USB signal\"\n"); write-log(logged); return; } outputList->addItem("Commanded all modules to sleep and blink the green LED for three seconds"); outputList->scrollToItem(outputList->item(outputList->count()-1)); sprintf(logged, "monitor: Notified \"Commanded all modules to sleep and blink the green LED for three seconds\"\n"); write_log(logged); void Monitor::on_actionCalibrate_activated() { if(!usb_handle) return; calibrate(); 100 1186 1187 1188 1189 1190 1191 1192 1193 1194 1195 1196 1197 1198 1199 1200 1201 1202 1203 1204 1205 1206 1207 1208 1209 1210 1211 1212 1213 1214 1215 1216 1217 1218 1219 1220 1221 1222 1223 1224 1225 1226 1227 1228 1229 1230 1231 1232 1233 1234 1235 1236 1237 1238 1239 1240 1241 1242 1243 1244 void Monitor::on_actionRollCall_activated() { } if(!usb_handle) return; roll_call(FALSE); void Monitor::on_actionCheckCommAll_activated() { int i, numer-O, denom=0; if(!usb_handle) return; calibrate(); if(roll_call(FALSE)<=O) return; for(i-0;i<256;i++) { if(addresses[i]) { if(check_comm(addresses[i])) numer++; denom++; } else { break; if((denom)&&(numer--denom)) { sprintf(buf, "Check communications succeeded on all Zd modules", denom); outputList->addltem(buf); outputList->scrollToItem(outputList->item(outputList->count()-1)); sprintf(logged, "verify_comm: Notified \"%s\"\n", buf); write_log(logged); } else if((denom)&&(numer!-denom)) { sprintf(buf, "Check communications succeeded on only %d of Xd modules", numer, denom); outputList->addItem(buf); outputList->scrollToItem(outputList->item(outputList->count()-1)); sprintf(logged, "verify_comm: Notified \"Xs\"\n", buf); write_log(logged); void Monitor::on_actionSleepM_activated() { bool ok; int adr; if(!usb_handle) return; adr-QInputDialog::getInteger(this, "Put Module to Sleep", "Enter module address: ", 1, 1, 120, 1, tok); if(!ok) return; // Send one 0x0001 to make the addressed module enter the state "sleeping" if(1write_fast(0x8000+adr, 0x8001, 0, 0)) { outputList->addItem("Failed to communicate with master, check USB signal"); outputList->scrollToItem(outputList—>item(outputList->count()-1)); sprintf(logged, "monitor: Notified \"Failed to communicate with master, check USB signal\"\n"); write_log(logged); return; 101 1245 1246 1247 1248 1249 1250 1251 1252 1253 1254 1255 1256 1257 1258 1259 1260 1261 1262 1263 1264 1265 1266 1267 1268 1269 1270 1271 1272 1273 1274 1275 1276 1277 1278 1279 1280 1281 1282 1283 1284 1285 1286 1287 1288 1289 1290 1291 1292 1293 1294 1295 1296 1297 1298 1299 1300 } sprintf(buf, "Commanded module with address Xd ($202K) to sleep", adr, adr); outputList->addItem(buf); outputList->scrollToItem(outputList->item(outputList->count()-1)); sprintf(logged, "monitor: Notified \"%s\"\n", buf); write_log(logged); void Monitor::on_actionWakeM_activated() { bool ok; int adr; if(!usb_handle) return; adr=QInputDialogz:getInteger(this, "Turn Off / Wake Module", "Enter module address: ", 1, 1, 120, 1, tok); if(!ok) return; // Send one 0x0002 to make the addressed module enter the state "idle" if(!write_fast(0x8000+adr, 0x8002, 0, 0)) { outputList->addItem("Failed to communicate with master, check USB signal"); outputList->scrollToItem(outputList->item(outputList->count()-1)); sprintf(logged, "monitor: Notified \"Failed to communicate with master, check USB signal\"\n"); write_log(logged); return; } sprintf(buf, "Commanded module with address Zd ($102K) to wake and turn off the DC/DC converter and the low-side MOSFETs", adr, adr); outputList->addItem(buf); outputList->scrollToItem(outputList->item(outputList->count()-1)); sprintf(logged, "monitor: Notified \"%s\"\n", buf); write_log(logged); } void Monitor::on_actionOnPM_activated() { bool ok; int adr; if(!usb_handle) return; adr-DlnputDialog::getInteger(this, "Turn Module 0n Positive", "Enter module address: ", 1, 1, 120, 1, hok); if(!ok) return; // Send one 0x0100 to make the addressed module turn on positive if(!write_fast(0x8100+adr, 0, 0, 0)) { outputList->addItem("Failed to communicate with master, check USB signal"); outputList->scrollToItem(outputList->item(outputList->count()-1)); sprintf(logged, "monitor: Notified \"Failed to communicate with master, check USB signal\"\n"); write_log(logged); return; } sprintf(buf, "Commanded module with address Zd ($Z02X) to turn on positive continuously", adr, adr); outputList->addItem(buf); outputList->scrollToItem(outputList->item(outputList->count()-1)); 102 1301 1302 1303 1304 1305 1306 1307 1308 1309 1310 1311 1312 1313 1314 1315 1316 1317 1318 1319 1320 1321 1322 1323 1324 1325 1326 1327 1328 1329 1330 1331 1332 1333 1334 1335 1336 1337 1338 1339 1340 1341 1342 1343 1344 1345 1346 1347 1348 1349 1350 1351 1352 1353 1354 1355 1356 } sprintf(logged, "monitor: Notified \"%s\"\n", buf); write_log(logged); void Monitor::on_actionOnNM-activated() { bool ok; int adr; if(!usb_handle) return; adr-QInputDialog::getInteger(this, "Turn Module On Negative", "Enter module address: ", 1, 1, 120, 1, &ok); if(!ok) return; // Send one 0x0080 to make the addressed module turn on positive if(!write_fast(0x8080+adr, 0, 0, 0)) { outputList->addItem("Failed to communicate with master, check USB signal"); outputList->scrollToItem(outputList->item(outputList->count()-1)); sprintf(logged, "monitor: Notified \"Failed to communicate with master, check USB signal\"\n"); write_log(logged); return; } sprintf(buf, "Commanded module with address Zd ($ZO2X) to turn on negative continuously", adr, adr); outputList->addItem(buf); outputList->scrollToItem(outputList—>item(outputList->count()-1)); sprintf(logged, "monitor: Notified \"%s\"\n", buf); write_log(logged); void Monitor::on_actionDCDCOffM_activated() { bool ok; int adr; if(!usb_handle) return; adr-QInputDialog::getInteger(this, "DC/DC Converter Off", "Enter module address: ", 1, 1, 120, 1, &ok); if(!ok) return; // Send one 0x0003 to make the addressed module enter the state ”idle" if(!write_fast(0x8000+adr, 0x8003, 0, 0)) { outputList->addItem("Failed to communicate with master, check USB signal"); outputList->scrollToItem(outputList->item(outputList->count()-1)); sprintf(logged, "monitor: Notified \"Failed to communicate with master, check USB signal\"\n"); write_log(logged); return; } sprintf(buf, "Commanded module with address Zd ($ZO2X) to turn on the DC/DC converter and turn off the low-side MOSFETs", adr, adr); outputList->addItem(buf); outputList->scrollToItem(outputList->item(outputList->count()-1)); sprintf(logged, "monitor: Notified \"%s\"\n", buf); write_log(logged); 103 1357 1358 1359 1360 1361 1362 1363 1364 1365 1366 1367 1368 1369 1370 1371 1372 1373 1374 1375 1376 1377 1378 1379 1380 1381 1382 1383 1384 1385 1386 1387 1388 1389 1390 1391 1392 1393 1394 1395 1396 1397 1398 1399 1400 1401 1402 1403 1404 1405 1406 1407 1408 1409 1410 1411 1412 void Monitor::on_actionDCDCOnM_activated() { bool ok; int adr; if(!usb_handle) return; adr-QInputDialog::getInteger(this, "DC/DC Converter On", "Enter module address: ", 1, 1, 120, 1, tok); if(!ok) return; // Send one 0x0004 to make the addressed module enter the state "idle" if(!write_fast(0x8000+adr, 0x8004, 0, 0)) { outputList->addItem("Failed to communicate with master, check USB signal"); outputList->scrollToItem(outputList->item(outputList->count()-1)); sprintf(logged, "monitor: Notified \"Failed to communicate with master, check USB signal\"\n"); write_log(logged); return; } sprintf(buf, "Commanded module with address Zd ($ZO2X) to turn on the DC/DC converter and the low-side MOSFETS", adr, adr); outputList->addItem(buf); outputList->scrollToItem(outputList—>item(outputList->count()-1)); sprintf(logged, "monitor: Notified \"%s\"\n", buf); write_log(logged); void Monitor::on_actionBlinkM_activated() { } bool ok; int adr; if(!usb_handle) return; adr-QInputDialog::getInteger(this, "Blink Module LED", "Enter module address: ", 1, 1, 120, 1, hok); if(!ok) return; // Send one 0x0005 to make the addressed module blink if(!write_fast(0x8000+adr, 0x8005, 0, 0)) { outputList->addItem("Failed to communicate with master, check USB signal"); outputList->scrollToItem(outputList->item(outputList->count()~1)); sprintf(logged, "monitor: Notified \"Failed to communicate with master, check USB signal\"\n"); write_log(logged); return; } sprintf(buf, "Commanded module with address Zd ($Z02X) to sleep and blink the green LED for three seconds", adr, adr); outputList->addItem(buf); outputList->scrollToItem(outputList->item(outputList->count()-1)); sprintf(logged, "monitor: Notified \"%s\"\n", buf); write_log(logged); void Monitor::on_actionCheckCommM_activated() { bool ok; 104 1413 1414 1415 1416 1417 1418 1419 1420 1421 1422 1423 1424 1425 1426 1427 1428 1429 1430 1431 1432 1433 1434 1435 1436 1437 1438 1439 1440 1441 1442 1443 1444 1445 1446 1447 1448 1449 1450 1451 1452 1453 1454 1455 1456 1457 1458 1459 1460 1461 1462 1463 1464 1465 1466 int adr; if(!usb_handle) return; adr=QInputDialogz:getlnteger(this, "Verify Communications", "Enter module address: ", 1, 1, 120, 1, &ok); if(!ok) return; check_comm(adr); void Monitor::on-actionReadEEPROM_activated() bool ok; int adr, eeadl, eedata; if(!usb_handle) return; eeadl-QInputDialog::getInteger(this, "Read Module EEPROM", "Enter EEPROM address :\n$00: Oscillator calibration byte\n$01: Module address\n$02: Voltage offset calibration byte\n$03: Temperature offset calibration byte ", 4, 0, 127, 1, tok); if(!ok) return; adr-QInputDialog::getInteger(this, "Read Module EEPROM", "Enter module address: ", 1, 1, 120, 1, &ok); if(!ok) return; if(!check_comm(adr)) return; // Read from the addressed module’s EEPROM if(!write_fast(0x8000+adr, 0, 0, 0)) { outputList->addItem("Failed to communicate with master, check USB signal"); outputList->scrollToItem(outputList->item(outputList->count()-1)); sprintf(logged, "monitor: Notified \"Failed to communicate with master, check USB signal\"\n"); write_log(logged); return; } if(!write_fast(0x8006, 0, 0, 0)) { outputList->addItem("Failed to communicate with master, check USB signal"); outputList->scrollToItem(outputList->item(outputList->count()-1)); sprintf(logged, "monitor: Notified \"Failed to communicate with master, check USB signal\"\n"); write_log(logged); return; } if(!write_fast(0x8000+eeadl, 0, 0, 0)) { outputList->addItem("Failed to communicate with master, check USB signal"); outputList->scrollToItem(outputList->item(outputList->count()-1)); sprintf(logged, "monitor: Notified \"Failed to communicate with master, check USB signal\"\n"); write_log(logged); return; // Read and interpret the module’s reply eedata-read_word(); if(eedata>-0) { eedatat=0xFF; switch(eeadl) { 105 1467 1468 1469 1470 1471 1472 1473 1474 1475 1476 1477 1478 1479 1480 1481 1482 1483 1484 1485 1486 1487 1488 1489 1490 1491 1492 1493 1494 1495 1496 1497 1498 1499 1500 1501 1502 1503 1504 1505 1506 1507 1508 1509 1510 case 0x00: sprintf(buf, "EEPROM $00 (oscillator calibration byte) of module Zd ($ZO2X ) is %d ($ZO2X)", adr, adr, eedata, eedata); break; case 0x01: sprintf(buf, "EEPROM $01 (module address) of module Zd ($202K) is %d ($102 x)", adr, adr, eedata, eedata); break; case 0x02: sprintf(buf, "EEPROM $02 (voltage calibration byte) of module Zd ($Z02X) is %d ($Z02X)", adr, adr, eedata, eedata); break; case 0x03: sprintf(buf, "EEPROM $03 (temperature calibration byte) of module Zd (3X02 X) is Zd ($ZO2X)", adr, adr, eedata, eedata); break; default: sprintf(buf, "EEPROM $ZO2X of module Zd ($Z02X) is %d ($Z02X)", eeadl, adr , adr, eedata, eedata); } } else if(eedata-s-l) { sprintf(buf, "EEPROM of module Zd ($ZO2X) failed to read", adr, adr); } else { sprintf(buf, "Failed to communicate with master, check USB signal"); } outputList->add1tem(buf); outputList~>scrollToItem(outputList—>item(outputList—>count()-1)); sprintf(logged, "monitor: Notified \"%s\"\n", buf); write_log(logged); void Monitor::on_actionWriteEEPROM_activated() { bool ok; int adr, eeadl, eedr, eedata, retval; if(!usb_handle) return; eeadl-OInputDialog::getInteger(this, "Write Module EEPROM", "Enter EEPROM address:\n$00: Oscillator calibration byte\n$01: Module address\n$02: Voltage offset calibration byte\n$03: Temperature offset calibration byte ", 4, 0, 127, 1, tok); if(!ok) return; if(eeadl--0x00) { retval-OMessageBox::warning(this, "Write Module EEPROM", "EEPROM address $00 is the 12MHz calibration byte. This location can be automatically adjusted via the global Calibrate Frequency command. Modifying it manually may create unpredictable results and is not recommended. Are you sure you want to overwrite it?", OMessageBox::YeleMessageBox::No, OMessageBox::No); if(retvaII-OMessageBox::No) return; } else if(eeadl--0x01) { on_actionReaddressM_activated(); } else if(eeadl-=0x02) { retval-QMessageBox::warning(this, "Write Module EEPROM", "EEPROM address $02 is the voltage offset calibration byte. This location is automatically written during normal inverter operation. Modifying it manually may create unpredictable results and is not recommended. Are you sure you want to overwrite it?", QMessageBox::YeleMessageBox::No, QMessageBox::No) 106 1511 1512 1513 1514 1515 1516 1517 1518 1519 1520 1521 1522 1523 1524 1525 1526 1527 1528 1529 1530 1531 1532 1533 1534 1535 1536 1537 1538 1539 1540 1541 1542 1543 1544 1545 1546 1547 1548 1549 1550 1551 1552 1553 1554 1555 1556 1557 1558 1559 1560 if(retval-80MessageBox::No) return; } else if(eeadl--0x03) { retval-OMessageBox::warning(this, "Write Module EEPROM", "EEPROM address $03 is the temperature offset calibration byte. This location is automatically written during normal inverter operation. Modifying it manually may create unpredictable results and is not recommended. Are you sure you want to overwrite it?", QMessageBox::YeleMessageBox::No, QMessageBox::No); if(retval==OMessageBox::No) return; eedr=QInputDialogz:getInteger(this, "Write Module EEPROM", "Enter EEPROM data: ", 0, 0, 255, 1, hok); if(!ok) return; adr=OInputDialogz:getInteger(this, "Write Module EEPROM", "Enter module address: ", 1, 1, 120, 1, tok); if(!ok) return; if(!check-comm(adr)) return; // Send the EEPROM key ($92‘address) to the module to allow temporary writing if(!write_fast(0x8000+adr, 0, 0, 0)) { outputList->addltem("Failed to communicate with master, check USB signal"); outputList->scrollToItem(outputList->item(outputList->count()-1)); sprintf(logged, "monitor: Notified \"Failed to communicate with master, check USB signal\"\n"); write_log(logged); return; } if(!write_fast(0x8192‘adr, 0, 0, 0)) { outputList->addItem("Failed to communicate with master, check USB signal"); outputList->scrollToItem(outputList->item(outputList->count()-1)); sprintf(logged, "monitor: Notified \"Failed to communicate with master, check USB signal\"\n"); write_log(logged); return; // Write to the addressed module’s EEPROM if(!write_fast(0x8000+adr, 0, 0, 0)) { outputList->addItem("Failed to communicate with master, check USB signal"); outputList->scrollToItem(outputList->item(outputList->count()-1)); sprintf(logged, "monitor: Notified \"Failed to communicate with master, check USB signal\"\n"); write_log(logged); return; } if(!write_fast(0x8007, 0, 0, 0)) { outputList->addItem("Failed to communicate with master, check USB signal"); outputList->scrollToItem(outputList->item(outputList->count()-1)); sprintf(logged, "monitor: Notified \"Failed to communicate with master, check USB signal\"\n"); write-log(logged); return; } if(!write-fast(0x8000+eeadl, 0, 0, 0)) { outputList->addItem("Failed to communicate with master, check USB signal"); outputList~>scrollToItem(outputList->item(outputList->count()-1)); sprintf(logged, "monitor: Notified \"Failed to communicate with master, check USB signal\"\n"); write_log(logged); 107 1561 1562 1563 1564 1565 1566 1567 1568 1569 1570 1571 1572 1573 1574 1575 1576 1577 1578 1579 1580 1581 1582 1583 1584 1585 1586 1587 1588 1589 1590 1591 1592 1593 1594 1595 1596 1597 1598 1599 1600 1601 1602 1603 1604 1605 1606 1607 1608 1609 1610 1611 1612 1613 return; } if(!write_fast(0x8000+eedr, 0, 0, 0)) { outputList->addItem("Failed to communicate with master, check USB signal"); outputList->scrollToItem(outputList->item(outputList->count()-1)); sprintf(logged, "monitor: Notified \"Failed to communicate with master, check USB signal\"\n"); write_log(logged); return; // Delay to let the module rewrite its EEPROM byte for(retval-0;retval<20;retval++) if(usb_interrupt_read(usb_handle, 0x82, buf, 64, 0)!-64) return; // Read and interpret the module’s reply eedata-read_word(); if((eedata>0)&&((eedatakOxFF)==eedr)) { eedatat=0xFF; switch(eeadl) { case 0x00: sprintf(buf, "Wrote EEPROM $00 (oscillator calibration byte) to new value 2d ($ZO2X) in module Zd ($ZO2X)", eedr, eedr, adr, adr); break; case 0x01: sprintf(buf, "Wrote EEPROM $01 (module address) to new value Zd ($ZO2X) in module Zd ($ZO2X)", eedr, eedr, adr, adr); break; case 0x02: sprintf(buf, "Wrote EEPROM $02 (voltage offset calibration byte) to new value Zd ($ZO2X) in module Zd ($ZO2X)", eedr, eedr, adr, adr); break; case 0x03: sprintf(buf, "Wrote EEPROM $03 (temperature offset calibration byte) to new value Zd ($102K) in module Zd ($ZO2X)", eedr, eedr, adr, adr); break; default: sprintf(buf, "Wrote EEPROM $ZO2X to new value Zd ($ZO2X) in module Zd ($ ZO2X)", eeadl, eedr, eedr, adr, adr); } } else if(eedata---1) { sprintf(buf, "EEPROM of module Zd ($XO2X) failed to read", adr, adr); } else if(eedata---2) { sprintf(buf, "Failed to communicate with master, check USB signal"); } else { sprintf(buf, "Value Zd ($ZO2X) in module Zd ($202K) failed to store in EEPROM $Z02X — verified Zd ($Z02X) instead", eedr, eedr, adr, adr, eeadl, eedata, eedata); } outputList->addItem(buf); outputList->scrollToItem(outputList->item(outputList->count()-1)); sprintf(logged, "monitor: Notified \"%s\"\n", buf); write_log(logged); void Monitor::on_actionReaddressM_activated() { bool ok; int adrold, adrnew, eedata, i; if(!usb_handle) return; 108 1614 1615 1616 1617 1618 1619 1620 1621 1622 1623 1624 1625 1626 1627 1628 1629 1630 1631 1632 1633 1634 1635 1636 1637 1638 1639 1640 1641 1642 1643 1644 1645 1646 1647 1649 1650 1651 1652 1653 1654 1655 1656 1657 1658 1659 1660 1661 1662 1663 1664 1665 1666 1667 adrold-QInputDialog::getInteger(this, "Readdress Module", "Enter old module address: ", 1, 1, 120, 1, &ok); if(!ok) return; adrnew-anputDialog::getInteger(this, "Readdress Module", "Enter new module address: ", 1, 1, 255, 1, tok); if(!ok) return; if(!check_comm(adrold)) return; if((adrnew--0)II((adrnew>120)&&(adrnew1-255))) { outputList->addItem("New address must be 1..120 or 255"); outputList->scrollToItem(outputList->item(outputList->count()-1)); sprintf(logged, "monitor: Notified \"New address must be 1..120 or 255\"\ n“); write_log(logged); return; for(i-0;iaddItem("New address must not be held by any existing module"); outputList->scrollToItem(outputList->item(outputList->count()-1)); sprintf(logged, "monitor: Notified \"New address must not be held by any existing module\"\n"); write_log(logged); return; // Send the EEPROM key ($92‘address) to the module to allow temporary writing if(!write_fast(0x8000+adrold, 0, 0, 0)) { outputList->addItem("Failed to communicate with master, check USB signal"); outputList->scrollToItem(outputList->item(outputList->count()-1)); sprintf(logged, "monitor: Notified \"Failed to communicate with master, check USB signal\"\n“); write_log(logged); return; } if(1write_fast(0x8192‘adrold, 0, 0, 0)) { outputList->addItem("Failed to communicate with master, check USB signal"); outputList->scrollToItem(outputList->item(outputList->count()-1)); sprintf(logged, "monitor: Notified \"Failed to communicate with master, check USB signal\"\n"); write-log(logged); return; // Write to the addressed module’s EEPROM if(!write_fast(0x8000+adrold, 0, 0, 0)) { outputList->addItem("Failed to communicate with master, check USB signal"); outputList->scrollToItem(outputList->item(outputList->count()-1)); sprintf(logged, "monitor: Notified \"Failed to communicate with master, check USB signal\"\n"); write_log(logged); return; } if(!write_fast(0x8007, 0, 0, 0)) { outputList->addItem("Failed to communicate with master, check USB signal"); outputList->scrollToItem(outputList->item(outputList->count()-1)); sprintf(logged, "monitor: Notified \"Failed to communicate with master, check USB signal\"\n"); 109 1668 1669 1670 1671 1672 1673 1674 1675 1676 1677 1678 1679 1680 1681 1682 1683 1684 1685 1686 1687 1688 1689 1690 1691 1692 1693 1694 1695 1696 1697 1698 1699 1700 1701 1702 1703 1704 1705 1706 1707 1708 1709 1710 1711 1712 1713 1714 1715 1716 1717 1718 1719 1720 1721 1722 // } write_log(logged); return; } if(!write_fast(0x8001, 0, 0, 0)) { outputList->addItem("Failed to communicate with master, check USB signal"); outputList->scrollToItem(outputList->item(outputList—>count()-1)); sprintf(logged, "monitor: Notified \"Failed to communicate with master, check USB signal\"\n"); write_log(logged); return; } if(!write_fast(0x8000+adrnew, 0, 0, 0)) { outputList->addItem("Failed to communicate with master, check USB signal"); outputList->scrollToItem(outputList->item(outputList->count()-1)); sprintf(logged, "monitor: Notified \"Failed to communicate with master, check USB signal\"\n"); write_log(logged); return; // Delay to let the module rewrite its EEPROM address byte ($01) for(i-0;i<20;i++) if(usb_interrupt_read(usb_handle, 0x82, buf, 64, 0)!-64) return; // Read and interpret the module’s reply eedatasread-word(); if(eedata!--1) { eedatat=0xFF; if(eedata--adrnew) { if(adrnew9-255) { firmware[0][0][adrnew1-firmware[0][0][adrold]; firmware[0][1][adrnew]=firmware[0][1][adrold]; firmware[1][0][adrnew]=firmware[1][0][adrold]; firmware[1][1][adrnew1-firmwareE1][1][adrold]; firmware[0][0][adroldJ-OxOO; firmware[0][1][adrold]=0x00; firmware[1][0][adrold1-0x00; firmware[1][1][adrold]-Ox00; } sprintf(buf, "Wrote EEPROM $01 (module address) to new value Zd ($ZO2X) of previous module Zd ($%O2X)", adrnew, adrnew, adrold, adrold); } else { sprintf(buf, "New address Zd ($Z02X) of previous module Zd ($ZO2X) failed to store in EEPROM $01 - verified Zd ($XO2X) instead", adrnew, adrnew, adrold, adrold, eedata, eedata); } } else { sprintf(buf, "New address Zd ($ZO2X) of previous module Zd ($Z02X) failed to store in EEPROM $01 - no verification reply", adrnew, adrnew, adrold, adrold); } outputList->addItem(buf); outputList->scrollToItem(outputList->item(outputList->count()-1)); sprintf(logged, "monitor: Notified \"%s\"\n", buf); write_log(logged); roll_call(FALSE); void Monitor::on_actionChangePhase_activated() { 110 1723 1724 1725 1726 1727 1728 1729 1730 1731 1732 1733 1734 1735 1736 1737 1738 1739 1740 1741 1742 1743 1744 1745 1746 1747 1748 1749 1750 1751 1752 1753 1754 1755 1756 1757 1758 1759 1760 1761 1762 1763 1764 1765 1766 1767 1768 1769 1770 1771 1772 1773 1774 1775 1776 1777 void Monitor::on_actionUpgradeMFW_activated() { char x; unsigned char bufsaved[96]; unsigned char bigmemused[512]; unsigned int i, j, intel_1en, intel_addr, intel_type, intel_highaddr; unsigned long readin, bigmem[16384], numbytes[2]; time_t timenow; numbytes[0]-numbytes[1]=0; if(!usb-hand1e) return; // Read the old master firmware version at 0x00000100 if(!issue_prog(0x00000100, 0, READ_ROM_LONG)) { outputList->addItem("Failed to read master firmware version, check connection" ); outputList->scrollToItem(outputList->item(outputList->count()-1)); sprintf(logged, "upgrade_MFW: Notified \"Failed to read master firmware version, check connection\"\n"); write-log(logged): return; // Inform the user of the current version number sprintf(buf, "Current master firmware version is Zld.%02ld.", bufx[17], bufx [16]): sprintf(logged, "upgrade_MFW: Notified \"%s\"\n", buf); write_log(logged); OMessageBox::information(this, "Upgrade Master Firmware", buf, QMessageBox::Ok, QMessageBox::Ok); // Open a dialog box for the user to choose a new firmware file QString s-OFileDialog::getOpenFileName(this, "Choose New Master Firmware", "", " Firmware Files (#.hex)"); OFile findata(s); if(s.size()<2) return; sprintf(logged, "upgrade_MFW: User selected \"%s\" for master firmware upgrade\ n", (const char #) s.toAscii()); write_log(logged); // Try to Open the file if(!findata.open(OIODevice::ReadOnlleIODevice::Text)) { sprintf(buf, "Cannot open %s for read", (const char t) s.toAscii()); outputList->addItem(buf); outputList->scrollToItem(outputList->item(outputList->count()-1)); sprintf(logged, "upgrade_MFW: Notified \"%s\"\n", buf); write_log(logged); return; timenow=time(NULL); // Check the file size if(findata.size()<6000) { sprintf(buf, "Zs appears to be a battery module firmware file instead", (const char *) s.toAscii()); outputList->addItem(buf); outputList->scrollToItem(outputList->item(outputList->count()-1)); 111 1778 1779 1780 1781 1782 1783 1784 1785 1786 1787 1788 1789 1790 1791 1792 1793 1794 1795 1796 1797 1798 1799 1800 1801 1802 1803 1804 1805 1806 1807 1808 1809 1810 1811 1812 1813 1814 1815 1816 1817 1818 1819 1820 1821 1822 1823 1824 1825 1826 1827 1828 1829 1830 1831 1832 1833 1834 1835 1836 findata.close(); sprintf(logged, "upgrade_MFW: Notified \"%s\"\n", buf); write_log(logged); goto mfw_noerror; } else if(findata.size()<40000) { sprintf(buf, "Ks does not appear to be a battery module nor master firmware file", (const char *) s.toAscii()); outputList->addltem(buf); outputList->scrollToItem(outputList->item(outputList->count()-1)); findata.close(): sprintf(logged, "upgrade_MFW: Notified \"%s\"\n", buf); write_log(logged); goto mfw_noerror; sprintf(buf, "Pre-upgrade master firmware version is de.%02ld", bufx[17], bufx [16]); outputList->add1tem(buf); outputList->scrollToItem(outputList->item(outputList->count()-1)); // Clear out the memory space and the memory used markers for(j-0;j<16384;j++) bigmem[jJ-O; for(j-0;j<512;j++) bigmemused[j]=0; intel_highaddr=0x0000; do { do { findata.getChar(&x); } while((!findata.atEnd())&&(x!=’:’)); if(findata.atEnd()) break; if(findata.read(buf, 8)<8) break; buf[81-0x00; sscanf(buf, "X2XX4XX2X", lintel_1en, hintel_addr, hintel_type); // We measure in words, not bytes intel_addr>>-1; intel_1en>>-1; // Check for the type of message/line if(intel_type=-0x04) { // Read the address MSBs findata.getChar(&x); intel_highaddr-readhex(x)<<12; findata.getChar(&x); intel_highaddr+-readhex(x)<<8; findata.getChar(&x); intel_highaddr+-readhex(x)<<4; findata.getChar(&x); intel_highaddr+-readhex(x); intel-highaddr>>-1; } else if((intel_type-80x00)&&(intel_highaddr--0x0000)) { // Write out program memory (actually just store for now) for(i-inte1_addr+intel_highaddr;i>1]-readin&0x00FFFFFF; bigmemused[i>>6]l-1; if(intel_addr<0x00007C00) { numbytes[0]+-3; } else { numbytes[1]+=3; } } } while(inte1_type!=0x01); findata.close(); outputList->addItem("Upgrading master bootloader firmware..."); outputList->scrollToItem(outputList->item(outputList->count()-1)); sprintf(logged, "upgrade_MFW: Notified \"Upgrading master bootloader firmware ...\"\n"); write_log(logged); for(i-2;i<512;i++) { if((bigmemused[i])&&((i<4)||(i>-496))) { // Erase a row of program memory sprintf(logged, "upgrade-MFW: Erasing locations OxZO8X-OXXOBX...\n", i<<6, (1((6)+62); write_log(logged); if(!issue_prog(i<<6, O, ERASE_PROG_ROW)) goto mbl_commerror; // Write a row of program memory sprintf(logged, "upgrade_MFW: Writing locations OxZOBX-OxZOSX...\n", i<<6, (i<<6)+62); write_log(logged); for(j-0;j<16;j++) { buf[jt6+32]-(bigmem[(i<<5)+(j<<1)+0]&0x000000FF); buf[j*6+33]-(bigmem[(i<<5)+(j<<1)+0]&0x0000FF00)>>8; buf[j¢6+34]-(bigmem[(i<<5)+(j<<1)+0]&0x00FF0000)>>16; buf[jt6+35]-(bigmem[(i<<$)+(j<<1)+1]&0x000000FF); buf[j*6+36]-(bigmem[(i<<5)+(j<<1)+1]&0x0000FF00)>>8; buf[j*6+37]-(bigmem[(i<<5)+(j<<1)+1]&0x00FF0000)>>16; } for(j-0;j<64;j++) sprintf(bytestext+(j*3), " XO2X", (unsigned char) buf[j+64]); sprintf(logged, "upgrade_MFW: Written to USB 0x02: %s\n", bytestext); write-log(1ogged); if(usb_interrupt-write(usb_handle, 0x02, buf+64, 64, 0)!=64) goto mbl_commerror; for(J-0;j<64;j++) sprintf(bytestext+(j#3), " ZO2X", (unsigned char) buf[jJ); sprintf(logged, "upgrade_MFW: Written to USB 0x01: %s\n", bytestext); write_log(logged); for(j-0;j<96;j++) bufsaved[jJ-(unsigned char) buf[j+32]; if(usb_interrupt_write(usb_handle, 0x01, buf, 64, O)!-64) goto mbl_commerror; if(!issue_prog(i<<6, 0, WRITE_PROG_ROW)) goto mbl_commerror; 113 1896 1897 1898 1899 1900 1901 1902 1903 1904 1905 1906 1907 1908 1909 1910 1911 1912 1913 1914 1915 1916 1917 1918 1919 1920 1921 1922 1923 1924 1925 1926 1927 1928 1929 1930 1931 1932 1933 1934 1935 1936 1937 1938 1939 1940 1941 1942 1943 1944 1945 1946 1947 1948 1949 1950 // Verify the written locations, first half row sprintf(logged, "upgrade_MFW: Verifying locations OxZOSX-OxZOBX...\n", i (<6, (i<<6)+30); write_log(logged); if(!issue_prog(i<<6, 0, READ_ROM_HROW)) goto mbl_commerror; for(j-0;j<48;j++) if(bufsaved[j]!sbufx[j+16]) goto mbl_vererror; // Verify the written locations, second half row sprintf(logged, "upgrade_MFW: Verifying locations OxZOSX-OxZOBX...\n", (i <<6)+32, (i<<6)+62); write_log(logged); if(!issue_prog((i<<6)+32, 0, READ_ROM_HROW)) goto mbl_commerror; for(j=0;j<48;j++) if(bufsaved[j+48]!-bufx[j+16]) goto mbl_vererror; sprintf(buf, "Successfully verified Zld bootloader firmware bytes", numbytes[1]) outputList->addItem(buf); outputList->scrollToItem(outputList->item(outputList->count()-1)); sprintf(logged, "upgrade_MFW: Notified \"%s\"\n", buf); write_log(logged); // Jump to the bootloader so the main code can be updated if(!issue_prog(O, 0, JUMP_OR_RESET)) goto mfw_commerror; if(!(bufx[1]&0x20)) { outputList->addItem("Could not enter master bootloader"); outputList->scrollToItem(outputList->item(outputList->count()-1)); sprintf(logged, "upgrade_MFW: Notified \"Could not enter master bootloader\"\ n"); write_log(logged); goto mfw_noerror; outputList->addItem(“Upgrading master main firmware..."); outputList->scrollToItem(outputList—>item(outputList->count()-1)); sprintf(logged, "upgrade_MFW: Notified \"Upgrading master main firmware...\"\n" ); write_log(logged); for(i-0;i<496;i++) { if((bigmemused[i])&&(i!-2)&&(i!=3)) { // Erase a row of program memory sprintf(logged, "upgrade_MFW: Erasing locations 0x208X-0x208X...\n", i<<6, (i<<6)+62); write_log(logged); if(!issue_prog(i<(6, 0, ERASE_PROG_ROW)) goto mfw_commerror; // Write a row of program memory sprintf(logged, "upgrade_MFW: Writing locations OxZOBX-OxZO8X...\n", i<<6, (i<<6)+62); write_log(logged); for(j-0;j<16;j++) { buf[j*6+32]'(bigmem[(i<<5)+(j<<1)+0]&0x000000FF); buf[j*6+33]'(bigmem[(i<<5)+(j<<1)+0]&0x0000FF00)>>8; buf[j*6+34]'(bigmem[(i<<5)+(j<<1)+0]&0x00FF0000)>>16; buf[jt6+35]'(bigmem[(i<(5)+(j<<1)+1]&0x000000FF); buf[j*6+36]'(bigmem[(i<<5)+(j<<1)+1]&0x0000FF00)>>8; buf[jt6+37]-(bigmem[(i<<5)+(j<<1)+1]&0x00FF0000)>>16; } for(j=0;j<64;j++) 114 1951 1952 1953 1954 1955 1956 1957 1958 1959 1960 1961 1962 1963 1964 1965 1966 1967 1968 1969 1970 1971 1972 1973 1974 1975 1976 1977 1978 1979 1980 1981 1982 1983 1984 1985 1986 1987 1988 1989 1990 1991 1992 1993 1994 1995 1996 1997 1998 1999 2000 2001 2002 2003 2004 2005 ZXB 2007 sprintf(bytestext+(j*3), " ZO2X", (unsigned char) buf[j+64]); sprintf(logged, "upgrade_MFW: Written to USB 0x02: Zs\n", bytestext); write-1og(logged); if(usb_interrupt_write(usb_handle, 0x02, buf+64, 64, O)!=64) goto mfw_commerror; for(j-0;j<64;j++) sprintf(bytestext+(j¢3), " ZO2X", (unsigned char) buf[j]); sprintf(logged, "upgrade_MFW: Written to USB 0x01: %s\n", bytestext); write_log(logged); for(j-0;j<96;j++) bufsaved[jJ-(unsigned char) buf[j+32]; if(usb_interrupt_write(usb_handle, 0x01, buf, 64, 0)!-64) goto mfw_commerror; if(!issue_prog(i<<6, 0, WRITE_PROG_ROW)) goto mfw_commerror; // Verify the written locations, first half row sprintf(logged, "upgrade_MFW: Verifying locations OxZOBX-OxZO8X...\n", i (<6, (i<<6)+30); write_log(logged); if(!issue_prog(i<<6, 0, READ_ROM_HROW)) goto mfw_commerror; for(j-0;j<48;j++) if(bufsaved[j]!abufx[j+16]) goto mfw_vererror; // Verify the written locations, second half row sprintf(logged, "upgrade_MFW: Verifying locations OxXOBX-OxZO8X...\n", (i <<6)+32, (i<<6)+62); write-log(logged); if(!issue_prog((i<<6)+32, 0, READ_ROM-HROW)) goto mfw_commerror; for(j-0;j<48;j++) if(bufsaved[j+48]!-bufx[j+16]) goto mfw_vererror; sprintf(buf, "Successfully verified Zld main firmware bytes", numbytes[OJ); outputList->addItem(buf); outputList->scrollToItem(outputList->item(outputList->count()-1)); sprintf(logged, "upgrade_MFW: Notified \"%s\"\n", buf); write_log(logged); // Reset the master processor for(i-0;i<64;i++) buf[i]-((i--15)?JUMP_OR_RESET:0x00); if(usb_interrupt_write(usb_handle, 0x01, buf, 64, 0)!=64) goto mfw_commerror; outputList->addItem("Master processor reset after successful upgrade"); outputList->scrollToItem(outputList->item(outputList->count()-1)); sprintf(logged, "upgrade_MFW: Notified \"Master processor reset after successful upgrade\"\n"); write_log(logged); // Delay for at least two seconds for(i-0;i<2000;i++) if(usb_interrupt_read(usb_handle, 0x82, buf, 64, 0)!-64) goto mfw_commerror; // Read the new master firmware version at 0x00000100 if(1issue_prog(OxOOOOOlOO, O, READ_ROM_LONG)) { outputList->addItem("Failed to read master firmware version after upgrade"); outputList->scrollToItem(outputList->item(outputList->count()-1)); sprintf(logged, "upgrade_MFW: Notified \"Failed to read master firmware version after upgrade\"\n"); write_log(logged); goto mfw_commerror; } else { sprintf(buf, "Post—upgrade master firmware version is Zld.%021d", bufx[17], bufx[161); outputList->add1tem(buf); outputList->scrollToItem(outputList->item(outputList->count()-1)); 115 2008 sprintf(logged, "upgrade_MFW: Notified \"%s\"\n", bmf); 2009 write_log(logged); 2010 } 2011 2012 goto mfw_noerror; 2013 2014 mfw_commerror: ans outputList~>addItem("Communication error while upgrading master main firmware“); 20N3 outputList->scrollToItem(outputList—>item(outputList->count()-1)); 2017 sprintf(logged, "upgrade_MFW: Notified \"Communication error while upgrading master main firmware\"\n"); 2018 write_log(logged); 2019 goto mfw_noerror; 2020 2021 mfw_vererror: 2022 outputList->addItem("Verification error while upgrading master main firmware"); 2023 outputList->scrollToItem(outputList->item(outputList->count()-1)); 2024 sprintf(logged, "upgrade_MFW: Notified \"Verification error while upgrading master main firmware\"\n"); 2025 write-log(logged); 2026 goto mfw_noerror; 2027 2028 mbl_commerror: 1Kfl9 outputList->addItem("Communication error while upgrading master bootloader firmware"); 2G“) outputList->scrollToItem(outputList->item(outputList->count()-1)); 2031 sprintf(logged, "upgrade_MFW: Notified \"Communication error while upgrading master bootloader firmware\"\n"); 2032 write-log(logged); 2033 goto mfw_noerror; 2034 2035 mbl_vererror: 2GK3 outputList->addItem("Verification error while upgrading master bootloader firmware"); 2037 outputList->scrollToItem(outputList->item(outputList->count()-1)); 2038 sprintf(logged, "upgrade_MFW: Notified \"Verification error while upgrading master bootloader firmware\"\n"); 2039 write_log(logged); 2040 2041 mfw_noerror: 2042 timenow-time(NULL)-timenow; 2043 sprintf(buf, "Operation completed in %1d seconds", (unsigned long) timenow); 2044 outputList->addItem(buf); 2045 outputList->scrollToItem(outputList->item(outputList->count()-1)); 20fi5 sprintf(logged, "upgrade_MFW: Notified \"%s\"\n", buf); 2047 write-1og(logged); 2048 } 2049 2050 2051 void Monitor::on_actionUpgradeBFW_activated() 2052 { 2053 char x, allmodules-O; 2054 int i, j, k, received; 2055 unsigned int flash[64][16], intel_1en, intel_addr, intel_type, proginfo[16]; 2056 unsigned long readin; 2057 time_t timenow; 2058 2059 if(!usb_handle) return; 2060 2061 calibrate(); 2062 2063 if(roll_ca11(FALSE)<=0) return; 116 2064 2065 2066 2067 2068 2069 2070 2071 2072 2073 2074 2075 2076 2077 2078 2079 2080 2081 2082 2083 2084 2085 2086 2087 2088 2089 2090 2092 2093 2094 2095 2096 2097 2098 2099 2MB 2101 2102 2103 2104 2105 2106 2107 2108 2109 2110 2111 2112 2113 2114 2115 2116 2117 2118 2119 2120 2121 for(i-0;i<16;i++) proginfo[i]=0x0000; for(i-0;i<120;i++) { if(addresses1iJ) { if(!check_comm(addresses[i])) return; proginfo[(addresses[i]-1)>>31|=(1<<(((addressesEi]-1)%8)*2)); } else { break; } if(!check_boot()) return; // Open a dialog box for the user to choose a new firmware file QString s-OFileDialog::getOpenFileName(this, "Choose New Module Firmware", "", " Firmware Files (*.hex)"); QFile findata(s); if(s.size()<2) return; sprintf(logged, "upgrade_BFW: User selected \"%s\" for master firmware upgrade\ n", (const char t) s.toAscii()); write_log(logged); // Try to open the file if(!findata.cpen(OIODevice::ReadOnlyIQIODevice::Text)) { sprintf(buf, "Cannot open %s for read", (const char #) s.toAscii()); outputList->addItem(buf); outputList->scrollToItem(outputList->item(outputList->count()-1)); sprintf(logged, “upgrade_BFW: Notified \"%s\"\n", buf); write_log(logged); return; timenow-timeCNULL); // Check the file size if(findata.size()>-40000) { sprintf(buf, "18 appears to be a master firmware file instead", (const char #) s.toAscii()); outputList->addItem(buf); outputList->scrollToItem(outputList->item(outputList->count()-1)); findata.close(); sprintf(logged, "upgrade,BFW: Notified \"Zs\"\n", buf); write_log(logged); goto bfw_noerror; } else if(findata.size()>=6000) { sprintf(buf, "%s does not appear to be a battery module nor master firmware file", (const char l'I) s.toAscii()); outputList->addltem(buf); outputList->scrollToItem(outputList->item(outputList->count()-1)); findata.close(); sprintf(logged, "upgrade_BFW: Notified \"%s\"\n", buf); write_log(logged); goto bfw_noerror; for(i=0;i<64;i++) for(j=0;j<16;j++) flash[i][j]-0x00; i=0; do { do { findata.getChar(&x); 117 2122 } while((!findata.atEnd())&&(x!=’:’)); 2123 if(findata.atEnd()) break; 2124 2125 if(findata.read(buf, 8)<8) break; 2126 buf[81-Ox00; 2127 sscanf(buf, "Z2XZ4XZ2X", tintel_len, kintel_addr, tintel_type); 21m; intel_addr>>-1; intel_1en>>-1; 2129 2K“) if(intel_type--0x00) { 2131 for(j-0;j<(signed int) intel_1en;j++) { 2132 findata.getChar(&x); 2133 readin-readhex(x)<<4; 2134 findata.getChar(&x); 2135 readin+-readhex(x); 2136 findata.getChar(&x); 2137 readin+=readhex(x)<<12; 2138 findata.getChar(&x); 2139 readin+=readhex (x)((8; 2140 i-j+intel_addr; 2141 f1ash[i>>4][itOxOOOF]=readin; 2142 } 2143 } 2144 } whi1e(intel_type!=0x01); 2145 2146 findata.close(); 2147 2148 // Write the list of modules to be programmed to the master’s memory 2149 for(j'0;j(32;j+-2) if(!issue_prog(0x0820+j, proginfo[j>>1], WRITE_RAM_WORD)) 215) goto bfw_commerror; 2151 2152 // Write the modules’ new firmware to the master’s memory 2153 for(i-0;i<56;i++) 2154 for(j-O;j<32; j+-2) 2155 if(!issue_prog(0x0840+(i<<5)+j, flash[i][j>>1], WRITE_RAM_WORD)) 2156 goto bfw_commerror; 2157 2158 // Issue a reprogram battery modules command to the master 21$) if(!issue_prog(O, 0, REPROGRAM_MODS)) goto bfw_commerror; 2160 2161 // Check for individual module completions 2162 for(j‘0;j<120;j++) { 2163 if((((proginfo[j>>3]>>((j%8)<<1)))&0x03)!-0x01) continue; 2164 2165 // Read master RAM location 0x0820+(j>>3)#2 to check the completed module’s status 2166 if(!issue_prog(0x0820+(j>>3)*2, 0, READ_RAM_LONG)) goto bfw_commerror; 2167 2168 // Interpret the results 2Hfi) switch((((bufx[161+(bufx1171<<8))>>((j%8)#2)))&0x03) { 2170 case 0x00: 2171 for(k-0;k>8; 2175 firmware[1][0][j]-flaah[0x37][0x00]&0xFF; 2176 firmware[1][1][j]-flash[0x37][0x00]>>8; 2177 for(i=0;irowCount();i++) { 2TH! sscanf(module_entries[i][2]*>text().toAscii(), "Zd", treceived): 2179 if(received--j+1) { 2l&) sprintf(buf, "Zd.%02x / Zd.%O2X", firmware[0][1][j], firmware [0][0][j], firmware[1][1][j], firmware[1][0][j]); 2181 delete module_entries[i][7]; 118 2182 2183 2184 2185 2186 2187 2188 2189 2190 2191 2192 2193 2194 2195 2196 2197 2198 2199 22m) 2201 2202 2203 2204 2205 2206 2207 2208 2209 2210 2211 2212 2213 2214 2215 2216 2217 2218 2219 2220 2221 2222 2223 2224 2225 2226 2227 2228 2229 2230 2231 2232 2233 2234 2235 2236 2237 module_entries[i][7]=new QTableWidgetItem(buf); module_entries[i][7]->setTextAlignment(Qt::AlignHCenter); biglist->setItem((int) i, 7, module_entries[i][71); } } sprintf(buf, "Firmware upgrade succeeded on module with address Zd ($202K) j+1, j+1); break; case 0x01: sprintf(buf, "Firmware upgrade on module with address Xd ($ZO2X) failed - could not enter bootloader", j+1, j+1); allmodules++; break; case 0x02: sprintf(buf, "Firmware upgrade on module with address Zd ($ZO2X) failed - no reply during verification", j+1, j+1); allmodules++; break; case 0x03: sprintf(buf, "Firmware upgrade on module with address Xd ($Z02X) failed - no reply during verification", 3+1, j+1); allmodules++; break; } outputList->addItem(buf); outputList->scrollToItem(outputList->item(outputList->count()-1)); sprintf(logged, "upgrade_BFW: Notified \"Zs\"\n", buf); write_log(logged); } if(allmodules) { sprintf(buf, "Firmware upgrade failed on %d modules", allmodules); } else { sprintf(buf, "Firmware upgrade succeeded on all 2d modules", nummods); } outputList->addItem(buf); outputList->scrollToItem(outputList->item(outputList->count()-1)); sprintf(logged, "upgrade_BFW: Notified \"%s\"\n", buf); write-log(logged); goto bfw_noerror; bfw_commerror; outputList->addItem("Communication error while upgrading battery module firmware "); outputList->scrollToItem(outputList->item(outputList->count()-1)); sprintf(logged, "upgrade_BFW: Notified \"Communication error while upgrading battery module firmware\"\n"); write_log(logged); bfw_noerror: timenow-time(NULL)-timenow; sprintf(buf, "Operation completed in Zld seconds", (unsigned long) timenow); outputList->addItem(buf); outputList->scrollToItem(outputList->item(outputList->count()-1)); sprintf(logged, "upgrade_BFW: Notified \"%s\"\n", buf); write_log(logged); return; 119 2238 2239 2240 2241 2242 2243 2244 2245 2246 2247 2248 2249 2250 2251 2252 2253 2254 2255 2256 2257 2258 2259 2260 2261 2262 unsigned int readhex(char inhex) { case ’A’: case ’a’: return case ’3’: case ’b’: return case ’C’: case ’c’: return case ’0’: case ’d’: return case ’E’: case ’e’: return case ’F’: case ’f’: return default: return switch(inhex) { 10; 11; 12; 13; 14; inhex-’0’; 120 APPENDIX B Slave Printed Circuit Board Layouts 121 hf burg... ".1 a m. L m m a B B C P m 0 3 m: m as. D 8 ¥ w. S I a B L q L e Figure B.3. Slave Design Two PCB Top Layer 124 Figure B.4. Slave Design Two PCB Power Layer 125 Figure B.5. Slave Design Two PCB Ground Layer 126 APPENDIX C Battery Discharge Distribution Python Script Example r—r—I Hocooqacn-hwton— 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 #!/usr/bin/env python # Script for generating a battery discharge distribution graph, from matplotlib import rc rc(’font’,It{’family’:’serif’,’serif’:[’Palatino’]}) from pylab import * params - {’backend’: ’ps’, ’axes.labelsize’: 14, ’text.fontsize’: 14, ’xtick.labelsize’: 14, ’ytick.labelsize’: 14, ’text.usetex’: True} rcParams.update(params) # This is 100 cycles (’howlong’ calculated by 100*1000+1) howlong-100001 batcharges=range(16) batpercents-range(16) timeon-range(16) baton-range(16) curvolt-O for i in range(16): batcharges[i]-0 batpercents1i]=0 timeon[i]-0 batonEiJ-O for i in arange(0, howlong/1000.0, 0.001): thiscur-sin(2tpi#i-0.555) thisvolt-7.7tsin(2*pi*i) # Accumulate the counters for each battery that’s on for j in range(16): if(baton[j1>0.5): 128 choosebats.py 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 batcharges[j]-batcharges[j]+(0.001*thiscur) timeon[jl-timeon[j]+0.001 if(baton1j1<-0.5): batcharges[j]-batcharges[j]-(0.001#thiscur) timeon[jl-timeon[j]+0.001 # Account for a hybrid battery pack batpercents[OJ-batcharges[0] batpercents[1]-batcharges[1] batpercents121-batcharges[2] batpercents[SJ-batcharges[3] batpercents[418batcharges[4] batpercents[Sl-batcharges[5] batpercents[6]-batcharges[6] batpercents171-batcharges[7] batpercents[8]'batcharges[8] batpercents[9]-batcharges[9] batpercents[10]=batcharges[10] batpercents[111'batcharge8111] batpercents[121-batcharges[121/2.0 batpercents[13]-batcharges[131/2.0 batpercents[14]-batcharges[141/2.0 batpercents[151-batcharges[151/2.0 # This is for 0-180 degrees if(thisvolt>-0): This is for 0-90 degrees if(thisvolt>0.5+curvolt): 8 Find any battery to compare mindisch to for j in range(16): if(baton[j]--0): mindisch=j # Find the least discharged battery to turn on first for j in range(16): if(baton[j]==0): if(batpercents[j]0.5): maxdisch=j # Find the most discharged battery to turn off first for j in range(16): if(baton[j]>0.5): if(batpercents[j]>batpercents[maxdisch]): maxdisch=j # Turn this battery off (made sure above it’s on) baton[maxdisch]=0 curvolt-curvolt-l # This is for 180-360 degrees if(thisvolt<0): 129 102 I This is for 180-270 degrees 103 if(thisvolt0.5+curvolt): 121 I Find any battery to compare maxdisch to 122 for j in range(16): 123 if(batonEj]<-O.5): 124 maxdisch=j 125 126 # Find the most discharged battery to turn off first 127 for j in range(16): 128 if(baton [j](-0.5): 129 if(batpercents[j]>batpercents[maxdisch]): 130 maxdisch=j 131 132 # Turn this battery off (made sure above it’s on) 133 baton[maxdisch]=0 134 curvolt-curvolt+1 135 136 137 8 Plot the discharge distribution 138 subplot(111) 139 bar([0.7, 1.7, 2.7, 3.7, 4.7, 5.7, 6.7, 7.7, 8.7, 9.7, 10.7, 11.7, 12.7, 140 13.7, 14.7, 15.7], batcharges, width=0.6) 141 title(’Discharge Dist. for 100 Cycles, Vln(max)=8, PF=0.85, Hybrid Pack’, 142 sizeB’large’, color-’k’) 143 ylabel(’Charge Units’, size-14, color-’k’) 144 xlabel(’Battery Number’, size-14, color=’k’) 145 axis([0.1, 16.9, 0, 35]) 146 show() 147 148 8 Plot the time-spent-on distribution 149 subplot(111) 150 bar([0.7, 1.7, 2.7, 3.7, 4.7, 5.7, 6.7, 7.7, 8.7, 9.7, 10.7, 11.7, 12.7, 151 13.7, 14.7, 15.7], timeon, width=0.6) 152 title(’Time Spent On Dist. for 100 Cycles, Vln(max)=8, PF=0.85, Hybrid Pack’, 153 size-’large’, color-’k’) 154 ylabel(’Time Units’, size-14, color-’k’) 155 xlabel(’Battery Number’, size=14, colora’k’) 156 axis([0.1, 16.9, 0, 50]) 157 show() 158 159 8 Account for a hybrid battery pack 160 timeon[12]-timeon[12]/2.0 161 timeon[13]-timeon[13]/2.0 162 timeon[14]-timeon[14]/2.0 163 timeonElSJ-timeon[151/2.0 130 164 165 I Initialize the statistical variables 166 mindisch-0 167 maxdisch-0 168 mintime-O 169 maxtime=0 170 171 3 Calculate the statistics 172 for i in range(16): 173 8 Find the most minimally discharged battery 174 if batpercents[i](batpercents[mindisch]: 175 mindisch-i 176 177 8 Find the most maximally discharged battery 178 if batpercents[i]>batpercents[maxdisch]: 179 maxdisch-i 180 181 # Find the most minimally turned-on battery 182 if timeon[i]timeon[maxtime]: 187 maxtime-i 188 189 8 Print the statistical results 190 print "Charges and Time: Min, Max, % Difference" 191 print batpercents[mindisch], batpercents[maxdisch], 192 ((batpercents[maxdischJ/batpercents[mindisch])-1)*100.0 193 print timeon[mintime], timeon[maxtime], 194 ((timeon[maxtimeJ/timeon[mintime])-1)*100.0 131 APPENDIX D Slave Assembly Code, Design Two (DQKIQCfi-BOJNH Abhwwwwwwwwwwwmmwwwwwww----- MHOCOWNJGUVAOOI’QP—‘OCOWNOUAMMHOQmNQU-BWNHO D Multilevel Converter Module Code By Arthur Matteson, 3/23/2008 Port PAO PA1 PA2 PA3 PA4 pin assignments: Voltage sense A/D input Temperature sense A/D input Positive high side MOSFET gate drive output Negative high side MOSFET gate drive output Positive low side MOSFET gate drive output PAS/PAS - DC/DC converter gate drive outputs, complementary PA7 - Negative low side MOSFET gate drive output PBO/PBI - Paralleled transmit line for 1MBd, active low P82 - Receive line for 1MBd, active low P83 - Battery voltage enable line, active low Register usage: R0 R1 R2 R3 R4 RS R6 R7 R8 89 R10 R11 R12 R13 R14 R15 R16 R17 R18 R19 R20 R21 R22 R23 R24 Bootloader programming data low byte Bootloader programming data high byte 12MHz calibration value (EEPROM $00) 8MHz calibration value Voltage A/D reading DC amplitude (average) Voltage A/D reading AC amplitude (ripple) - bits 1:0 are R4’s bits 9:8 Voltage A/D offset calibration value (EEPROM $02) Temperature A/D reading DC amplitude (average) Temperature A/D offset calibration value (EEPROM $03) EEPROM writing key (normally zero, $92 allows writing next time) Diagnostic variable, status register backup in interrupt Temporary variable in interrupt Delay counter and temporary variable in bootloader and interrupt First received byte in interrupt Extra received bits and temporary variable in interrupt Byte argument to write_byte and temporary variable in interrupt Temporary counter in write_byte Roll call temporary variable in interrupt Module address (EEPROM $01) Received byte count in interrupt 132 43 ; R25 - Temporary variable in mainloop 44 ; R26 (XL) - Temporary variable in mainloop 45 ; R27 (XH) - Temporary variable in mainloop 46 ; R28 (YL) - 47 ; R29 (YR) - 48 ; R30 (ZL) - IJMP’s ZL in bootloader 49 ; R31 (ZR) - IJMP’s ZH in bootloader 50 ; 51 52 53 .DEVICE ATTINY24 54 55 .EQU PRR-OxOO 56 .EQU DIDR'OXOI 57 .EQU ADCSRB-0x03 58 .EQU ADCL=0xO4 59 .EQU ADCH-OXOS 60 .EQU ADCSRA-OXOG 61 .EQU ADHUX-0x07 62 .EQU ACSR=0x08 63 .EQU TIFRI'OXOB 64 .EQU TIMSK1=0x0C 65 .EQU USICR=OXOD 66 .EQU USISR-OXOE 67 .EQU USIDR=OXOF 68 .800 USIBR'OXIO 69 .EQU PCMSKO=OX12 70 .800 GPIOR0=Ox13 71 .800 GPIOR1=0x14 72 .EQU GPIOR2=0x15 73 .EQU PINB‘OXIS 74 .500 DDRB-0x17 75 .EQU FORTE-0x18 76 .800 FINA-0119 77 .EQU DDRA-OxlA 78 .EQU PORTA'OXIB 79 .EQU EECR-OxlC 80 .EQU EEDR‘OXID 81 .EQU BEARL-OXIE 82 .EQU EEARH=0X1F 83 .EQU PCMSK1=OX20 84 .EQU WDTCSR'OX21 85 .EQU TCCRIC-0x22 86 .EQU GTCCR-Ox23 87 .EQU ICRlL-Ox24 88 .EQU ICR1H=OX25 89 .EQU CLKPR=0x26 90 .500 DWDR=0x27 91 .EQU 0CR18L-0x28 92 .EQU OCRIBH'OX29 93 .EQU OCRlAL'0x2A 94 .800 0CR1AH-0x2B 95 .EQU TCNT1L=0x2C 96 .800 TCNTIH-OX2D 97 .800 TCCRIB=OI2E 98 .EQU TCCRlA-OX2F 99 .EOU TCCROA-0x30 100 .EQU OSCCAL'OX31 101 .EQU TCNTO-0x32 102 .EQU TCCROB-Ox33 103 .EOU MCUSR-0x34 104 .EQU MCUCR=0X35 133 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 .EQU OCROA-Ox36 .EQU SPMCSR=0X37 .EQU TIFRO-Ox38 .EQU TIMSKO-0x39 .EQU GIFR-OxSA .EQU GIMSK=0x38 .EQU OCROB-OXSC .EQU SPL-0x3D .EQU SPH-0x3E .EOU SREG-OXBF .ORG 0x0000 RJMP reset RJMP ext_int0 RJMP reset RJMP reset RJMP reset RJMP reset RJMP reset RJMP reset RJMP reset RJMP reset RJMP reset RJMP reset RJMP reset RJMP reset RJMP reset RJMP reset RJMP reset .DW 0x0200 ; The main code firmware version (2.00) reset: ; Initialize the stack pointer address LDI R26,0xDE OUT SPL,R26 LDI R26,0x00 OUT SPH,R26 ; Configure the watchdog timer for one second (execute a timed sequence) LDI R26,0x9E OUT WDTCSR,R26 LDI R26,0x8E OUT WDTCSR,R26 ; Set up the output ports and INTO trigger LDI R26,0x40 OUT MCUCR,R26 LDI R26,0x00 OUT PORTA,R26 LDI R26,0xFC OUT DDRA,R26 LDI R26,0xOB OUT PORTB,R26 LDI R26,0xDE OUT DDRB,R26 ; Set up the pin change interrupt and clear the flag for safety LDI R26,0x40 OUT GIMSK,R26 LDI R26,0x40 134 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 OUT GIFR,R26 ; Set up the A/D converter LDI R26,0x00 OUT ADCSRB,R26 LDI R26,0xFF OUT DIDR,R26 LDI R26,0x96 OUT ADCSRA,R26 ; Set up the timer/counter LDI R26,0x00 OUT TCCR1A,R26 LDI R26,0x11 OUT TCCRIB,R26 LDI R26,0x00 OUT TCNT18,R26 OUT TCNTlL,R26 OUT ICR1H,R26 LDI R26,0x15 OUT ICR1L,R26 LDI R26,0x00 OUT 0CR1AH,R26 LDI R26,0x0C OUT OCR1AL,R26 LDI R26,0x00 OUT OCR1BH,R26 LDI R26,0x09 OUT OCRlBL,R26 ; Check the calibration value in EEPROM LDI R26,0x00 OUT EEARL,R26 OUT EEARH,R26 SBI EECR,0 IN R26,EEDR SBR R26,0xC0 MOV R2,R26 ; Check the module address in EEPROM LDI R26,0x01 OUT EEARL,R26 SBI EECR,0 IN R23,EEDR ; Check the voltage calibration byte in EEPROM LDI R26,0x02 OUT EEARL,R26 SBI EECR,0 IN R6,EEDR ; Check the temperature calibration byte in EEPROM LDI R26,0x03 OUT EEARL,R26 SBI EECR,0 IN R8,EEDR ; Change the initial oscillator frequency to 12MHz IN R3,0SCCAL RCALL speed-up CLR R14 ; Clear the EEPROM writing key ($92 allows writing) 135 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 SBI PORTA,3 RCALL diag_blink SBI PORTA,2 RCALL diag_blink LDI R26,0x04 OUT TCCROB,R26 CLR R26 OUT TCNTO,R26 ; Enable interrupts CLR R20 SEI mainloop: WDR ; Read the voltage LDI R26,0x00 OUT ADMUX,R26 SBI ADCSRA,6 SBIS ADCSRA,4 RJMP PC-1 SBI ADCSRA,4 SBI ADCSRA,6 SBIS ADCSRA,4 RJMP PC-l SBI ADCSRA,4 IN R26,ADCL IN R27,ADCH MOV R25,R6 TST R25 BRMI voltad_offmi ADD R26,R25 BRCC voltad_done INC R27 RJMP voltad_done voltad_offmi: ADD R26,R25 BRCS voltad_done DEC R27 voltad_done: MOVW R4,R26 e D into ; Read the temperature ; LDI R26,0x04 LDI R26,01A2 OUT ADMUX,R26 SBI ADCSRA,6 SBIS ADCSRA,4 RJMP PC-l SBI ADCSRA,4 SBI ADCSRA,6 SBIS ADCSRA,4 RJMP PC-l SBI ADCSRA,4 IN R26,ADCL IN R27,ADCH ADD R26,R8 HOV R7,R26 Turn the red LED on Turn the green LED on Set up the 8-bit timer with a divide-by-256 prescaler Reset the watchdog timer periodically R4/R5 (DC/AC) calibrated by R6 Select ADCO (PAO) Start the conversion Test for a completed conversion Clear the interrupt flag by writing a one Start a conversion again after the reference changed Test for a completed conversion Clear the interrupt flag by writing a one Read in the low byte of the voltage A/D result Read in the high byte (two bits) of the A/D result Buffer this value so it doesn’t change in the middle Is the offset positive or negative? Add the offset calibration byte onto the low byte Add the offset calibration byte onto the low byte Simultaneously move R27:R26 to R5:R4 (interrupt care) into R7 calibrated by R8 Select ADC4 (PA4) Select ADC8 (temperature) Start the conversion Test for a completed conversion Clear the interrupt flag by writing a one Start a conversion again after the reference changed Test for a completed conversion Clear the interrupt flag by writing a one Read in the low byte of the voltage A/D result Read in the high byte (two bits) of the A/D result Add the offset calibration byte onto the low byte Save the result in R7 136 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 RJMP mainloop eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee lP’l’Ifi”IIIII’DDIDISPPPDIDIPPIIIO’O’P”809’),I’DIDDDDFDDD'DDDOOPDDPDI’39,,DDPDI diag_blink: ; Blink quickly LDI R19,0x30 ; Delay about one-tenth of a second RCALL diag_short CBI PORTA,2 ; Turn the green LED off 081 PORTA,3 ; Turn the red LED off LDI R19,0x30 ; Delay about one-tenth of a second RCALL diag_short RET diag_dash: ; Delay about one and a half seconds RCALL diag_dot RCALL diag_dot diag_dot: ; Delay about one half second LDI R19,0x84 ; Loop 180 times diag_short: LDI R18,0xFA ; Loop 250 times LDI R17,0x78 ; Delay 120 cycles plus four more RCALL delay_one DEC R18 ; Subtract one from the remaining loop count BRNE PC-3 ; Repeat the loop if the counter is not zero yet LDI R17,0x7A ; Delay 122 cycles plus one more RCALL delay_one LDI R17,0x7A ; Delay 122 cycles plus two more RCALL delay-one WDR ; Reset the watchdog timer periodically DEC R19 ; Subtract one from the remaining loop count BRNE PC-11 ; Repeat the loop if the counter is not zero yet RET ext_int0: ; Handle a low-level trigger on INTO (P82) IN R15,SREG ; Save the status register RJMP PC+1 ; Ten delays is optimal for +/-4% clock RJMP PC+1 ; Thirteen delays is maximal, six minimal at 12MHz RJMP PC+1 RJMP PC+1 WDR NOP Read in seven bits to R18, 1MBd; module address or broadcast command LDI R18,0x40 ; Load seven bits SBIC PINB,2 ; Test the value of P82 SBR R18,0x80 ; Set bit seven if on NOP ; Each bit is twelve clock cycles RJMP PC+1 RJMP PC+1 RJMP PC+1 LSR R18 ; Shift right (place a zero in the top bit) BRCC PC-7 ; Locate the initially placed bit Read in two bits to R19, 1MBd; ’00’-data, ’01’-pos., ’10’-neg., ’11’-off CLR R19 ; Clear the follow-up bits first SBIC PINB,2 ; Test the value of P82 SBR R19,0x04 ; Set bit two if on TST R18 ; Was the address zero? BRNE pi_skipb11 ; If so, this is actually a 125de byte of $AC probably 137 353 354 355 356 357 358 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 TST R19 BRNE pi_skipb12 LDI R16,0x00 OUT TCCR1A,R16 OUT PORTA,R16 LDI R16,0x08 OUT PORTB,R16 LDI R16,0x40 OUT GIFR,R16 RJMP bootloader pi_skipb11: NOP NOP pi_skipbl2: NOP BST R19,2 BLD R19,4 LDI R22,0x01 SER R24 SBIC PINB,2 SBR R19,0x08 BST R19,3 BLD R19,7 CPI R18,0x7F BREQ pi_nextbytepre ; PA2 - Positive high ; PA3 - Negative high ; If this was a dummy $180 command don’t enter bootloader ; Turn the DC/DC off ; Turn everything off ; Disable the battery voltage to the DC/DC converter ; Clear the interrupt flag now ; Jump to the bootloader now ; Each bit is twelve clock cycles ; Copy the first received bit to location four ; Initialize the roll call address early ; Make the input byte counter $FF (-1) ; Test the value of P82 ; Set hit three if on ; Copy the second received bit to location seven ; Is this a synchronize clock speed command byte one? ; If so, make sure the next byte is also 0x7F side MOSFET gate drive output side MOSFET gate drive output ; PA4 - Positive low side MOSFET gate drive output ; PA7 - Negative low side MOSFET gate drive output ; Test for a single-module on/off command, and check to see the address TST R19 8880 pi_nextbytepre CP R18,R23 BRNE pi_testmore LDI 816,0x00 OUT PORTA,R16 LDI R16,0x0C EOR R19,R16 MOV R20,R4 LSR R20 CPI R19,0x90 BRNE PC+2 RJMP pi_sendvolts NOP OUT PORTA,R19 RJMP pi_done ; Test for single-byte pi_testmore: LDI R16,0x87 ADD 816,818 BRCC pi_done CPI R18,0x79 BREQ pi_sleep CPI 818,0x7A BREO pi_wake CPI 818,0x78 BREQ pi_dcdcfetsoff ; Message follows? (See if R19-’00’) ; Receive the next byte in that case ; Is this module being addressed? If not, skip next ; Never will R19-’00’ get here ; Turn off everything and wait a dead time ; Change the polarity of the pos. HS and neg. HS ; Use an 800ns dead time (do setup for pi_sendvolts) ; Disregard the battery voltage low byte bit zero ; Was the command ’11’? If so, echo the battery voltage ; Output this to the port after the dead time ; If it got here, that’s all we need to do broadcast commands or ignore the single-module command ; Test for broadcast commands (values over 120) ; If no overflow, this was a single-module command ; Is it all sleep (all off)? ; Is it all wake (opto. on, DC/DC off, MOSFETs off)? ; Is it all DC/DC converter on, MOSFETs off? 138 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 CPI R18,0x7C ; BREQ pi_dcdcfetson CPI R18,0x7D ; BREO pi_blink RJMP pi_done pi_blink: LDI R16,0x00 ; OUT TCCR1A,R16 OUT PORTA,R16 ; LDI R16,0xOB ; OUT PORTB,R16 LDI R19,0x30 ; RCALL diag_short LDI R17,0x04 ; OUT PORTA,R17 RCALL diag_dash ; RCALL diag_dash RJMP pi_off ; pi_dcdcfetsoff: LDI R16,0xEO ; OUT TCCR1A,RIS RJMP pi_wakex pi_dcdcfetson: LDI R16,0xEO ; OUT TCCR1A,R16 LDI R16,0x90 ; OUT PORTA,R16 LDI R16,0x03 ; OUT PORTB,R16 RJMP pi_done pi_wake: LDI R16,0x00 : OUT TCCR1A,R16 pi_wakex: LDI R16,0x03 ; OUT PORTB,R16 RJMP pi_off ; Wait for another start pi_nextbytepre: RJMP PC+1 ; RJMP PC+1 RJMP PC+1 RJMP PC+1 RJMP pi_nextbyte pi_sleep: LDI R16,0x00 ; OUT TCCR1A,R16 LDI R16,0x08 ; OUT PORTB,R16 pi_off: LDI R16,0x00 ; OUT PORTA,R16 pi_done: ; LDI R16,0x40 ; OUT GIFR,R16 CLR R14 ; OUT SREG,815 ; RETI Is it all DC/DC converter on, MOSFETs on? Is it all blink? Turn the DC/DC off Turn everything off Disable the battery voltage to the DC/DC converter Delay about one-tenth of a second Turn the green LED on and the rest off Wait about three seconds Turn everything off Turn the DC/DC on Turn the DC/DC on Turn the low side MOSFETs on Enable the battery voltage to the DC/DC converter Turn the DC/DC off Enable the battery voltage to the DC/DC converter bit Delay past the end of the last bit Turn the DC/DC off Disable the battery voltage to the DC/DC converter Turn everything off Put here for branch distance Clear the interrupt flag Clear the EEPROM writing key Recall the status register 139 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 pi-nextbyte: SBIC PINB,2 RJMP PC-1 RJMP PC+1 RJMP PC+1 RJMP PC+1 RJMP PC+1 RJMP PC+1 NOP RJMP pi_readmore pi_writeeeprom: LDI R17,0x92 CP R14,R17 BRNE pi_done RCALL speed_down LDI R16,0x04 OUT EECR,R16 SBI EECR,1 SBIC EECR,1 RJMP PC-1 SBI EECR,0 IN R20,EEDR RCALL speed_up RCALL write_byte RJMP pi_off Test the value of P82 (wait for a low) Thirteen delays works well Seventeen cycles maximal, ten minimal at 12MHz Skip the next function Check to see if the EEPROM key is correct If not, exit immediately Change the oscillator frequency down to 8MHz Set the EEPE bit to enable programming Delay until the possible program operation completes Read the value right back in to verify it Change the oscillator frequency back up to 12MHz Echo the written value Read in eight bits to R19, 1MBd; either data or a single-module command pi_readmore: LDI R19,0x80 LSR R19 SBIC PINB,2 SBR R19,0x80 NOP RJMP PC+1 RJMP PC+1 RJMP PC+1 BRCC PC-7 INC R24 IN R16,PINB CPI R18,0x7F BRNE PC+6 MOV R20,Ri9 ANDI R20,0x7F CPI R20,0x7F BRNE PC+2 RJMP pi_sync Handle the module roll CPI R18,0x78 BRNE pi_check_el TST 819 BRNE PC+2 RJMP pi_done WDR CP 819,R23 BRNE PC+5 MOV R20,R23 RCALL write_byte CPI 819,0xFF BREO PC+3 Load eight bits Shift right (place a zero in the top bit) Test the value of P82 Set bit seven if on These make the loop 12 cycles total Locate the initially placed bit Keep track of the number of bytes received Save the value of the last bit temporarily Does the first byte suggest a synchronize command? Test for a synchronize clock speed command byte two Is this a synchronize clock speed command byte two? If not, check for a roll call command call command Is it roll call? Is it over? (Roll call sent a $00) Reset the watchdog timer during the roll call process Is the byte the same address as this module? If not, don’t reply and let another module reply Reply with this module’s address 18 this a signal for new module on the string? If so, set this module’s address in line with the rest 140 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 MOV R22,R19 RJMP pi_nextbyte CPI R22,0x79 BRCS PC+2 RJMP pi_done TST R22 BRNE PC+2 RJMP pi_done MOV R23,R22 LDI 816,0x01 OUT EEARL,R16 OUT EEDR,R23 RJMP pi_writeeeprom pi_check_el: Handle a single-module, SBRS R16,2 RJMP pi,noteepromkey CP R18,R23 BREO PC+2 RJMP pi_done EOR R19,R23 MOV 814,819 LDI R16,0x40 OUT GIFR,R16 OUT SREG,RIS RETI pi_noteepromkey: TST R24 BREO PC+2 RJMP pi_notfirst MOV R17,R19 SBRC 817,6 RJMP pi_pwm TST R17 BRNE PC+2 RJMP pi_nextbyte CPI R17,0x06 BRNE PC+2 RJMP pi_nextbyte CPI Ri7,0x07 BRNE PC+2 RJMP pi_nextbyte CPI R17,0x0C BRNE PC+2 RJMP pi_nextbyte CPI R17,0x0D BRNE PC+2 RJMP pi_nextbyte CPI R17,0xOE BRNE PC+2 RJMP pi_nextbyte CPI R17,0x0F BRNE PC+2 RJMP pi_nextbyte CP 818,R23 BREO PC+2 RJMP pi_done CPI 817,0x01 BRNE PC+2 RJMP pi_sleep Store the previous address in case it needs assignment Is the address above 120? If so, don’t set it Also don’t write a zero address Write the new module address to EEPROM $01 R23 contains the new module address multi-byte command Was the ninth bit set? If so, this is an EEPROM key Is this module being addressed? If not, take no action This will automatically clear the EEPROM writing key XOR this with the module address just to be unique Save this EEPROM key whether it’s valid or not ($92) Clear the interrupt flag Recall the status register Is this the first byte? Save this command byte for later comparison Is it perform eight PWM cycles (22kHz)? Is it echo byte (following)? Is it read EEPROM? Is it write EEPROM? Is it erase flash? Is it fill flash buffer? Is it write flash? Is it verify flash? Is this module being addressed? If not, take no action Is it sleep (all off)? 141 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 CPI R17,0x02 BRNE PC+2 RJMP pi_wake CPI R17,0x03 BRNE PC+2 RJMP pi_dcdcfetsoff CPI R17,0x04 BRNE PC+2 RJMP pi_dcdcfetson CPI R17,0x05 BRNE PC+2 RJMP pi_blink CPI R17,0x08 BRNE pi_notrecallvdc MOV R20,R4 RCALL write_byte RJMP pi_done pi_notrecallvdc: CPI R17,0x09 BRNE pi_notrecallvac MOV R20,RS RCALL write_byte RJMP pi-done pi_notrecallvac: CPI R17,0x0A MOV R20,R7 RCALL write_byte RJMP pi_done pi_notrecallloctemp: CPI R17,0x08 ; MOV R20,R7 ; RCALL write_byte RJMP pi_done pi_notrecallremtemp: RJMP pi_done pi_notfirst: TST R17 BRNE pi_nf_notecho CP R18,R23 BREQ PC+2 RJMP pi_done MOV R20,R19 EOR R20,R23 RCALL write,byte RJMP pi-done pi_nf_notecho: CPI R17,0x06 CP R18,R23 BREO PC+2 RJMP pi_done OUT EEARL,R19 SBI EECR,0 IN R20,EEDR RCALL write_byte RJMP pi_done pi_nf_notreadeeprom: e D Is Is Is Is Is it it it it it wake (Opto. on, DC/DC off, MOSFETs off)? DC/DC converter on, MOSFETs off? DC/DC converter on, MOSFETs on? blink? recall Transmit the Is it recall Transmit the Is it recall BRNE pi_notrecallloctemp Transmit the Is it recall BRNE pi_notrecallremtemp DC battery voltage A/D reading? data at 1MBd AC battery voltage A/D reading? data at 1MBd local temperature A/D reading? data at 1MBd remote temperature A/D reading? Transmit the data at 1MBd Is it echo byte (in R19, XOR with module address)? Is this module being addressed? If not, take no action Echo the received byte to test communications XOR this with the module address just to be unique Is it read EEPROM? BRNE pi_nf_notreadeeprom Is this module being addressed? If not, take no action Read and send out the data byte at the given argument 142 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 CPI R17,0x07 e I Is it write EEPROM? BRNE pi_nf-notwriteeeprom MOV 816,824 CPI 816,0x01 BRNE PC+3 OUT EEARL,R19 RJMP pi_nextbyte CP 818,823 8880 PC+2 RJMP pi-done IN R16,EEARL TST 816 BRNE PC+3 MOV 82,819 RJMP pi_nf_ee_done DEC 816 BRNE PC+9 CPI 819,0x79 BRCS PC+2 RJMP pi_done TST 818 BRNE PC+2 RJMP pi_done MOV 823,819 RJMP pi_nf_ee_done DEC 816 BRNE PC+3 MOV 86,819 RJMP pi_nf_ee_done DEC 816 BRNE pi_nf_ee_done MOV 88,819 pi_nf_ee_done: OUT EEDR,819 RJMP pi_writeeeprom pi_nf_notwriteeeprom: CPI 817,0x00 If this is the first argument byte, save it It can be written to the EEARL register right now Is this module being addressed? If not, take no action Recall the address to write in case it was $00..$03 Was the address $00? If so, save the value in 82 This is the new module calibration byte (It will be applied soon in the speed_up function) Was the address $01? If so, save the value in 823 Is the address above 120? If so, don’t set it Also don’t write a zero address This is the new module address Was the address $02? If so, save the value in 86 This is the new voltage offset calibration byte Was the address $03? If so, save the value in 88 This is the new temperature offset calibration byte 819 contains the EEPROM byte to be written Is it erase flash? BRNE pi_nf_noteraseflash CP 818,823 8880 PC+2 RJMP pi_done LDI 817,0x92 CP 814,817 8880 PC+2 RJMP pi_done PUSH 830 PUSH 831 LDI 831,0x07 MOV 830,819 RCALL speed_down LDI 816,0x03 OUT SPMCSR,816 SPM RCALL speed_up POP 831 POP 830 RJMP pi_done pi_nf_noteraseflash: CPI 817,0x0D BRNE pi_nf_notfillflash Is this module being addressed? If not, take no action Check to see if the EEPROM key is correct If not, exit immediately Access memory only from 0x700 to 0x7FF (0x380 to 0x3FF) Change the oscillator frequency down to 8MHz for SPM Write 803 to SPMCSR and execute SPM (erase flash page) ; Store (or modify) program memory Change the oscillator frequency back up to 12MHz Is it fill flash buffer? 143 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 MOV 816,824 CPI 816,0x01 BRNE PC+3 MOV 80,819 RJMP pi_nextbyte CPI R16,0x02 BRNE PC+3 MOV 81,819 RJMP pi_nextbyte CP 818,823 8880 PC+2 RJMP pi_done LDI 817,0x92 CP 814,817 8880 PC+2 RJMP pi,done PUSH 830 PUSH 831 LDI 831,0x07 MOV 830,819 RCALL speed_down LDI 816,0x01 OUT SPMCSR,816 SPM RCALL speed_up POP 831 POP 830 RJMP pi-done pi_nf_notfillflash: CPI 817,0x0E ; If this is the first argument byte, save it ; It can be written to the 80 register now ; If this is the second argument byte, save it ; It can be written to the 81 register now ; Is this module being addressed? If not, take no action ; Check to see if the EEPROM key is correct ; If not, exit immediately ; Access memory only from 0x700 to 0x7FF (0x380 to 0x3FF) ; Change the oscillator frequency down to 8MHz for SPM ; Write $01 to SPMCSR and execute SPM (fill flash buffer) ; Store (or modify) program memory ; Change the oscillator frequency back up to 12MHz ; Is it write flash? BRNE pi_nf_notwriteflash CP 818,823 8880 PC+2 RJMP pi_done LDI 817,0x92 CP 814,817 8880 PC+2 RJMP pi_done PUSH 830 PUSH 831 LDI 831,0x07 MOV 830,819 RCALL speed_down LDI 816,0x05 OUT SPMCSR,816 SPM RCALL speed_up POP 831 POP R30 RJMP pi_done pi_nf-notwritef1ash: CPI 817,0x0F ; Is this module being addressed? If not, take no action ; Check to see if the EEPROM key is correct ; If not, exit immediately ; Access memory only from 0x700 to 0x7FF (0x380 to 0x3FF) ; Change the oscillator frequency down to 8MHz for SPM ; Write $05 to SPMCSR and execute SPM (write flash page) ; Store (or modify) program memory ; Change the oscillator frequency back up to 12MHz ; Is it verify flash? BRNE pi_nf_notreadflash CP 818,823 8880 PC+2 RJMP pi_done PUSH 830 PUSH 831 LDI 831,0x07 MOV 830,819 ; Is this module being addressed? If not, take no action ; Access memory only from 0x700 to 0x7FF (0x380 to 0x3FF) 144 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 LPM 820,2 POP 831 POP 830 RCALL write_byte RJMP pi_done pi_nf_notreadflash: RJMP pi_done pi_sendvolts: OUT PORTA,819 BST 85,0 BLD 820,7 RCALL write_byte RJMP pi_done ; PA2 - Positive high Recall this program data byte to send off Output this to the port after the dead time Copy the volt. high byte bit zero to 820 location seven Continue what was started above Transmit the data at 1MBd (send nine bits, also 85<1>) If it got here, that’s all we need to do side MOSFET gate drive output ; PA3 - Negative high side MOSFET gate drive output ; PA4 - Positive low side MOSFET gate drive output ; PA7 - Negative low side MOSFET gate drive output ; Handle the single-module eight-PWM command (0x00-off, 0x01~0x1F=duty cycle) pi_pwm: CP 818,823 8880 PC+2 RJMP pi_done LDI 818,0x08 LDI 824,0x00 LDI 820,0x90 LDI 819,0x18 SBRC 817,5 LDI 819,0x84 ANDI 817,0x1F BREO pi_pwmonfull NOP pi_pwmloOp: OUT PORTA,R24 RJMP PC+1 RJMP PC+1 RJMP PC+1 RJMP PC+1 OUT PORTA,819 ANDI 817,0x1F LDI 816,0x1F NOP pi,pwmrepeat: NOP DEC 817 BRNE pi_pwmnothit OUT PORTA,R24 RJMP PC+1 RJMP PC+1 RJMP PC+1 RJMP PC+1 OUT PORTA,R20 DEC 816 BRNE pi_pwmrepeat DEC R17 DEC 818 BRNE pi_pwmloop Is this module being addressed? If not, take no action Loop for eight PWM cycles This is the value for PORTA to occupy during dead times Turn off the high-sides when the time is appropriate Turn on neg. high side, pos. low side if 817<5>-0 Find the polarity of the PWM (O-negative, 1-positive) Turn on pos. high side, neg. low side if 817(5>-1 Mask off the highest three hits to find the duty cycle If the duty cycle is zero, turn on for the whole time Equalize delays with pi_pwmonfull Output the dead time state to the port Use a dead time of 800ns Output the commanded on state to the port Mask off the highest three bits to find the duty cycle L00p 31 times, once for each duty cycle possibility This makes a duty cycle of one, 16 cycles exactly Delay one cycle for a total of 16 Decrement the duty cycle to see if it is zero If not, don’t change the port value Output the dead time state to the port Use a dead time of 800ns Output the commanded off state to the port Loop 31 times for a total of 495 cycles Make the duty cycle counter roll over Repeat the same Operation eight times (4095 cycles total) 145 849 RJMP pi_pwmdone 850 851 pi_pwmnothit: 852 RJMP PC+1 ; Delay nine cycles for a total of 16 853 RJMP PC+1 854 RJMP PC+1 855 RJMP PC+1 856 NOP 857 DEC 816 858 BRNE pi_pwmrepeat ; Loop 31 times for a total of 495 cycles 859 DEC 817 ; Make the duty cycle counter roll over 860 DEC 818 861 BRNE pi_pwmloop ; Repeat the same operation eight times (4095 cycles total) 862 RJMP pi_pwmdone 863 864 pi_pwmdone: 865 ; LDI 817,0x7F ; Delay 256 cycles before exiting to skip communications 866 ; RCALL delay_one 867 ; LDI 817,0x7F 868 ; RCALL delay_one 869 RJMP pi_done 870 871 pi_pwmonfull: 872 OUT PORTA,R24 ; Output the dead time state to the port 873 RJMP PC+1 ; Use a dead time of 800ns 874 RJMP PC+1 875 RJMP PC+1 876 RJMP PC+1 877 OUT PORTA,819 ; Output the commanded on state to the port 878 LDI 816,0xFF ; Delay 4080 cycles 879 RJMP PC+1 ; Delay 16 cycles per loop; 255 loops total 880 RJMP PC+1 881 RJMP PC+1 882 RJMP PC+1 883 RJMP PC+1 884 RJMP PC+1 885 NOP 886 DEC R16 887 BRNE PC-8 888 RJMP PC+1 ; Delay six more cycles for a total of 4086 889 RJMP PC+1 890 RJMP PC+1 891 OUT PORTA,R24 ; Output the dead time state to the port 892 RJMP PC+1 ; Use a dead time of 800ns 893 RJMP PC+1 894 RJMP PC+1 895 RJMP PC+1 896 OUT PORTA,R20 ; Output the commanded off state to the port 897 RJMP PC+1 ; Equalize delays with pi_pwmloop 898 RJMP PC+1 899 NOP 900 RJMP pi_pwmdone 901 902 pi_sync: 903 LDI 816,0x20 ; Time out after 32 bytes so as not to hang 904 CLR 817 ; Clear 817 to signify uncompletion of this calibration 905 pi_sync_restart: 906 WDR 907 LDI 820,0x91 ; Clear the temporary counter to an offset 908 SBIC PINB,2 ; Test the value of P82 (wait for a low) 909 RJMP PC-1 910 INC 820 ; Increment the temporary counter 146 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 SBIS PINB,2 RJMP PC-2 DEC 816 8880 pi_write TST R17 BRNE pi_sync_restart CPI 820,0x80 BRCS pi_sync_slow BRNE pi_sync_fast $88 817 RJMP pi-sync_restart pi_write: RCALL bl_read_byte CPI 818,0x56 BREO PC+2 RJMP pi_done RCALL bl_read_byte CPI 818,0x78 BREO PC+2 RJMP pi_done RCALL speed_down CLR 816 OUT EEARL,RI6 OUT EEARH,816 OUT EEDR,R2 LDI 816,0x04 OUT EECR,R16 SBI EECR,1 RCALL speed,up SBIC EECR,1 RJMP PC-1 RJMP pi_done pi_sync_slow: INC 82 OUT OSCCAL,82 RJMP pi_sync_restart pi_sync_fast: DEC 82 OUT OSCCAL,R2 RJMP pi_sync_restart write_byte: IN 817,PORTB ANDI 817,0x08 LDI 816,0x00 OR 816,R17 OUT PORTB,RIS LDI 821,0x08 RJMP PC+1 RJMP PC+1 NOP 80R R20 LDI 816,0x00 BRCC PC+2 LDI 816,0x03 08 816,817 OUT PORTB,816 RJMP PC+1 NOP DEC R21 BRNE PC-9 Test the value of P82 (wait for a high) Time out if the calibration is right on the edge Also time out after 32 bytes received Compare with the nominal 239 loops (956 cycles) Remember that this value was correct, and skip $008 Wait for another 125de byte to come in Was a $56 sent? If not, this could be noise; exit Prevent spurious EEPROM writes Wait for another 125de byte to come in Was a $78 sent? If not, this could be noise; exit Prevent spurious EEPROM writes Change the oscillator frequency down to 8MHz Write the EEPROM address to zero Write the correct calibration value to EEPROM The control value used to erase and write at once Set the EEPE bit to enable programming Change the oscillator frequency back up to 12MHz Delay until the program operation completes If the timer is too slow, increase the calibration If the timer is too fast, decrease the calibration Place byte to transmit LSB first at 1MBd in 820 Save the enable status (don’t change it) Turn the transmitter on (PBO/PBl, active low) Clock eight bits Shift right Turn the transmitter on, possibly Is the bit clear? If so, skip the next instruction Override 816-0 and turn the transmitter off Write the new value out to the port Each bit is twelve clock cycles Decrement the bit counter Jump back up to the 808 820 if 821 is not zero yet 147 NOP BST 85,1 LDI 816,0x00 BRTC PC+2 LDI 816,0x03 08 816,817 OUT PORTB,816 RJMP PC+1 RJMP PC+1 RJMP PC+1 RJMP PC+1 NOP LDI 816,0x03 OR 816,817 OUT PORTB,816 RJMP PC+1 RJMP PC+1 RJMP PC+1 RJMP PC+1 RET speed_up: IN 816,0SCCAL CP 816,82 BRNE PC+2 RET INC 816 OUT OSCCAL,816 RJMP PC-5 speed_down: IN R16,0SCCAL CP 816,83 BRNE PC+2 RET DEC 816 OUT OSCCAL,816 RJMP PC-5 delay_one: SUBI 817,0x0C A38 817 BRCS PC+1 A88 817 BRCS PC+1 BRCS PC+1 DEC 817 NOP BRNE PC-2 RET .ORG 0x0370 .DW 0x0200 Bootloader commands: Copy the voltage high byte hit one to the T status bit Turn the transmitter on, possibly Is the bit clear? If so, skip the next instruction Override 816:0 and turn the transmitter off Write the new value out to the port Each bit is twelve clock cycles Turn the transmitter off (P80/P81, active low) Increase the calibration value up to 12MHz Recall the current calibration value Is it 12MHz yet? Skip exiting if the 2 bit is not set yet Exit since 12MHz has been reached Increase the speed one more notch Keep looping until 12MHz is reached Decrease the calibration value down to 8MHz Recall the current calibration value Is it 8MHz yet? (83 is approximately $80) Skip exiting if the 2 bit is not set yet Exit since 8MHz has been reached Increase the speed one more notch Keep looping until 8MHz is reached Call with delay in 817 (0x10-0x7F), number of cycles Account for extra cycles in calling and other (twelve) Divide the delay by two Waste one more cycle on a positive initial bit zero Divide the delay by two (four total) Waste two more cycles on a positive initial hit one Subtract one from the remaining delay This delay makes a four-cycle loop Repeat the loop if the counter is not zero yet The bootloader origin (accessory functions follow) The hardware/bootloader version (2.00) $00 - hang (independent of address) and let the watchdog reset the device 148 $01 - fill page buffer with data (after $88), address in bytes one and two $03 - erase flash page address LS8 in byte one, M58 in byte two $05 - write page buffer to flash page address LSB in byte one, MSB in byte two $11 - clear temporary page buffer for flash writing $41 - read EEPROM byte at address of byte one and return $44 - erase and write EEPROM byte of value byte two at address of byte one $54 - erase EEPROM byte at address of byte one $64 - write EEPROM byte of value byte two at address of byte one $81 - change address with new programming module address in byte one $82 - indirect jump to address L88 in byte one, M88 in byte two $84 - read flash data byte; address LS8 in byte one, M88 in byte two $88 - put byte one into LSB of word flash data to write, byte two into MSB $FF - exit the EXT_INTO interrupt normally (independent of address); no reset bootloader: RCALL bl_start_bit CPI 818,0xAC BRNE bl_exit RCALL bl,read_byte CPI 818,0x53 BRNE bl_exit RCALL bl_read_byte bl_chadr: CP 818,823 BRNE PC+4 LDI 820,0x48 808 820,823 RCALL bl_write_byte MOV 819,818 bl_mainloop: RCALL bl_read_byte MOV 830,818 RCALL bl_read_byte MOV 831,818 RCALL bl_read_byte TST 818 8880 PC CPI 818,0xFF BREO bl_exit CPI 818,0x81 BRNE bl_notchadr MOV 818,830 RJMP bl_chadr bl_notchadr: CP 819,823 8880 bl_local TST 819 BRNE bl_mainloop LDI 816,0x08 OUT PORTA,RIG bl_local: CPI 818,0x82 BRNE bl_notijmp IJMP bl_notijmp: This is the function to call from the EXT_INTO Skip the start bit wait loop Was an $AC sent? If not, this could be noise; exit Wait for another byte to come in Was a $53 sent? If not, this could be noise; exit Wait for another byte to come in Is this module being addressed? Skip three instructions if not Reply with a $48 to let the master know presence Exclusive-OR this with the module address Save this address for comparison later Wait for another byte to come in (address/data low) Load the byte into ZL Wait for another byte to come in (address/data high) Load the byte into ZH Wait for another byte to come in (always a command) If the command is $00, exit the bootloader Hang if $00 and therefore let the watchdog take over Is this an exit soft command? Is this a change address command? Modify the address by the first sent byte (ZL) Is this module being addressed? If so, skip the set red LED and clear green LED instrs. Is the address zero (global reprogram)? If not, keep waiting for an exit or an address change Turn the red LED on and green LED off (global mode) Is this an indirect jump (switch bootloaders) command? Jump to the address stored in 831:830 (ZH:ZL) 149 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 1150 1151 1152 1153 1154 1155 1156 1157 1158 CPI 818,0x84 BRNE bl_notread LPM 820,2 RCALL bl_write_byte RJMP bl_mainloop bl_notread: CPI 818,0x88 BRNE bl_notdata MOVW 80,830 RJMP bl_mainloop bl_notdata: SBRS 818,6 RJMP bl_noteeprom RCALL bl_speed_down OUT EEARL,830 OUT EEDR,R31 OUT EECR,RlB IN 820,EEDR SBI EECR,1 RCALL bl_speed_up SBIC EECR,1 RJMP PC-1 TST 819 8880 bl_mainloop RCALL bl_write_byte RJMP bl_mainloop bl_noteeprom: SBRS 818,0 RJMP bl_mainloop RCALL bl_speed_down OUT SPMCSR,818 SPM RCALL bl_speed_up RJMP bl_mainloop bl_exit: LDI 816,0x00 OUT PORTA,RIS OUT $886,815 RETI bl_speed_up: IN 816,0SCCAL CP 816,82 BRNE PC+2 RET INC 816 OUT OSCCAL,RI6 RJMP PC-S bl_speed_down: IN 816,0SCCAL CP 816,83 BRNE PC+2 RET Is this a read/verify data command? Recall this program data byte to send off Is this a move 831:830 into 81:80 (load data) command? Place 831:830 into 81:80 (address to data conversion) Loop again to receive the address of this data Is this a read/write EEPROM instruction? Change the oscillator frequency down to 8MHz Write the first received byte as an EEPROM address Write the second received byte as data (if writing) Write the third received byte as an EEPROM control byte Input the EEPROM data to 820 to transmit Set the EEPE bit to enable programming if needed or not Change the oscillator frequency back up to 12MHz Delay until the possible program operation completes If this is a global command, don’t reply Keep waiting for more bytes to come in Keep waiting for more bytes to come in Test the LS8 of the command If zero, this must be noise data, so wait for a $00 Change the oscillator frequency down to 8MHz for SPM Write $01/$03/$05 to SPMCSR and execute SPM Store (or modify) program memory Change the oscillator frequency back up to 12MHz Keep waiting for more bytes to come in Turn off the LEDs Recall the status register Increase the calibration value up to 12MHz Recall the current calibration value Is it 12MHz yet? Skip exiting if the 2 bit is not set yet Exit since 12MHz has been reached Increase the speed one more notch Keep looping until 12MHz is reached Decrease the calibration value down to 8MHz Recall the current calibration value Is it 8MHz yet? (83 is approximately $80) Skip exiting if the Z bit is not set yet Exit since 8MHz has been reached 150 1159 1160 1161 1162 1163 1164 1165 1166 1167 1168 1169 1170 1171 1172 1173 1174 1175 1176 1177 1178 1179 1180 1181 1182 1183 1184 1185 1186 1187 1188 1189 1190 1191 1192 1193 1194 1195 1196 1197 1198 1199 1200 1201 1202 1203 1204 1205 1206 1207 1208 1209 1210 1211 1212 1213 1214 1215 1216 1217 1218 1219 1220 DEC R16 OUT OSCCAL,816 RJMP PC-5 bl_delay_one: SUBI 817,0x0C A88 817 BRCS PC+1 A38 817 BRCS PC+1 BRCS PC+1 DEC 817 NOP BRNE PC-2 RET bl_write_byte: LDI 816,0x04 OUT PORTA,816 LDI 816,0x08 OUT PORTB,R16 LDI R21,0x08 LDI 817,0x59 RCALL bl_delay_one 808 R20 LDI 816,0x08 BRCC PC+2 LDI 816,0x08 OUT PORTB,816 LDI 817,0x57 RCALL bl_delay_one DEC 821 BRNE PC-8 LDI 816,0x08 OUT PORTB,816 LDI R17,0x5A RCALL bl_delay_one RET bl_read_byte: WDR SBIC PINB,2 RJMP PC-2 LDI 817,0x72 RCALL bl_delay_one bl_start_bit: LDI 817,0x1D RCALL bl_delay_one LDI 818,0x80 CLC 808 R18 SBIC PINB,2 SBR 818,0x80 IN 816,88EG LDI 817,0x58 RCALL bl_delay_one OUT SREG,816 BRCC PC-7 RET Increase the Speed one more notch Keep looping until 8MHz is reached Call with delay in 817 (0x10-0x7F), number of cycles Account for extra cycles in calling and other (twelve) Divide the delay by two Waste one more cycle on a positive initial bit zero Divide the delay by two (four total) Waste two more cycles on a positive initial bit one Subtract one from the remaining delay This delay makes a four-cycle loop Repeat the loop if the counter is not zero yet Place byte to transmit LS8 first at 125de in 820 Turn the red LED off and green LED on (local mode) Turn the transmitter on (P80/PB1, active low) Clock eight bits Delay exactly 89 cycles Shift right Turn the transmitter on, possibly Is the bit clear? If so, skip the next instruction Override 816-0 and turn the transmitter off Write the new value out to the port Delay exactly 87 cycles Decrement the bit counter Jump back up to the 808 820 if 821 is not zero yet Turn the transmitter off (P80/P81, active low) Delay exactly 90 cycles for the stop bit Receive a byte at 125de for bootloader number one Make sure the watchdog timer doesn’t expire Test the value of P82 If the pin is still set, keep waiting for a start bit Delay exactly 114 cycles on a found start bit This can be called after reading a zero 1MBd byte Delay exactly 27 cycles Load eight bits into 818 Prepare carry flag Shift right Test the value of P82 Set bit seven if on Save the carry flag Delay exactly 88 cycles Recall the carry flag Locate the initially placed bit 151 APPENDIX E Master Code Listings E.1 DSPIC30F4011 USB Assembly Code, Design One mmKICSCJ‘ACAMp—I 000000000000000000WNMNMMMMMMMn—IHr—IHHHHHHH cmummhwwv—ocoooqczotmuwv-‘ocoooxioame-oowwo ; The SENDTOKEN macro, .MACRO SENDTOKEN DEC.W [W13].W10 BTSC.W SR,#C MOV.W TM82,W10 CP.W W10.[W13] BRA N,.+4 MOV.W [++w13].[w14] BRA N,.+4 HOV.W [++W13],W10 .ENDM for quickly sending words to the slave modules This macro tests the FIFO buffer for a token to send Test for a zero token timestamp and simultaneously load If there was a borrow (C-O), don’t load the timer value Recall the low word of the 32-bit timer value Perform (TMR2)-(timestamp); if borrow above, result--1 Skip the next instruction if the time is too early Transmit the token; make use of modulo addressing here Skip the next instruction if the time is too early Have the address generator automatically perform modulo ; The USB interrupt routine (activated by a level change on RC13/8C14) __CNInterrupt: PUSH.S PUSH.W W8 PUSH.W W9 MOV.W PORTC,W9 MOV.W #0x6000,W0 AND.W W9,W0,W9 BRA NZ,notse0 CLR.W TM81 BCL8.W T1CON,#TON BSET.W T1CON,#TCKPSl BSET.W T1CON,#TON BRA __ICN_end notseO: BTSS.W T1CON,#TON BRA HOV.W #MINRESET,WO SUB.W TM81,WREG BRA BCLR.W T1CON,#TON BRA syncbyte GTU,resetit __ICN_end l 9 Save the status register, W0, W1, W2, and W3 Save the working register W8 Save the working register W9 Clear the notification condition Mask the D+/D- bits respectively If SEO, don’t branch and check for a reset Clear the timer value Turn the timer off before changing the prescaler Choose a 1:64 prescaler for 140ms maximum Start the timer counting Wait until something happens Was the timer on? If not, this must be a sync byte Test a pulse of 4215*64/30MHz (9ms) Compute WO-TMRI-WO A long single-ended zero means reset the part Disable the timer for now since the bus is idle Wait until the bus is non-idle 152 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 syncbyte: MOV.W #0x4000,W0 ; XOR.W W9,W0,W0 ; BRA NZ,__ICN_end MOV.W #0x00E8,W0 ; INC.8 W0,W0 ; BTSS.W PORTC,#13 ; BRA NZ,.-4 ; BRA Z,resetit ; SENDTOKEN ; REPEAT #13 ; NOP SENDTOKEN ; REPEAT #13 ; NOP SENDTOKEN ; REPEAT #13 ; NOP SENDTOKEN ; INC.8 W0,WO ; BTSS.W PORTC,#14 ; 88A NZ,.-4 ; BRA Z,resetit ; IIDIPDIISIIDIDDDIOODFDID SETM.W W3 3 MOV.W #0xA001,W8 ; MOV.W #0x00FB,W2 ; MOV.W #inbuffer+1,W0 ; MOV.W #inbuffer+2,W9 SENDTOKEN ; REPEAT #18 ; NOP getpid_j: ; SENDTOKEN ; REPEAT #2 ; NOP INC.8 W2,W2 ; BSET.W S8,#C ; BTSC.W PORTC,#14 ; BRA getpid_j_one ; MOV.W #0x00FA,W2 ; LSR.B [W0],[W01 ; BRA NC,getpid_k ; BRA waitbit_k ; getpid_j_one: 880.8 [W0],[W0] ; BRA NC,getpid_j ; BRA waitbit_j ; getpid_k: ; SENDTOKEN ; REPEAT #2 ; NOP INC.B W2,W2 ; BSET.W SR,#C ; BTSC.W PORTC,#13 ; BRA getpid_k_one ; MOV.W #0x00FA,W2 ; LSR.B [W0].[W0] ; Test for a RC14 (0+) high and RC13 (D-) low If the bus was not D+ high and D- low (K), exit Timeout after 24 cycles cumulatively (0x0100-0x0018) Increment the timeout counter Wait for a D+ low and D- high (J) Branch if no hit and no timeout Exit early on a timeout Insert the SENDTOKEN macro (eight cycles) Delay past most of the sync byte for 15 cycles Insert the SENDTOKEN macro (eight cycles) Delay past most of the sync byte for 15 cycles Insert the SENDTOKEN macro (eight cycles) Delay past most of the sync byte for 15 cycles Insert the SENDTOKEN macro (eight cycles) Increment the timeout counter Wait for a D+ high and D- low (K) Branch if no hit and no timeout Exit early on a timeout ;:;;;; RECEIVE FUNCTION :3:::::::;;;;;:;;;;;;;:;;;;:;;; Set W3 to all ones; this is the CRC "shift register" Set W8 to the CRC-16 polynomial Load (0x0100-0x0005) into W2 for the bitstuffing count Set up W0 and W9 to point to the input buffer areas Insert the SENDTOKEN macro (eight cycles) Delay 20 cycles to find the center of the next bit This label is called after a J to K transition is found Insert the SENDTOKEN macro (eight cycles) Delay four cycles for a total of 20 Increment the bitstuffing count; it may be cleared next Set the carry bit early in case a one bit comes If D+ is now low, this is a zero bit If not, this is surely a one bit Load (0x0100-0x0006) into W2 for the bitstuffing count Shift the location right to put a zero at bit seven If the placed bit has not appeared yet, keep looping This branch ends seven cycles after the PORTC test Shift the location right to put a one at bit seven If the placed bit has not appeared yet, keep looping This branch ends seven cycles after the PORTC test This label is called after a K to J transition is found Insert the SENDTOKEN macro (eight cycles) Delay four cycles for a total of 20 Increment the bitstuffing count; it may be cleared next Set the carry bit early in case a one bit comes If D- is now low, this is a zero bit If not, this is surely a one bit Load (0x0100-0x0006) into W2 for the bitstuffing count Shift the location right to put a zero at bit seven 153 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 BRA NC,getpid_j BRA waitbit_j getpid_k_one: RRC.B [W0].IWO] BRA NC,getpid_k BRA waitbit-k waitbit_k_done: LSR.W W3,W3 BTSC.W SR,#C XOR.W W3,W8,W3 LSR.B [W9],[W9] ADDC.W #0,W9 MOV.W #0x00FA,W2 SENDTOKEN waitbit_j: BTSC.W PORTC,#13 88A waitbit_j_done BTSC.W PORTC,#13 BRA waitbit_j_done BTSC.W PORTC,#13 BRA waitbit_j_done BTSC.W PORTC,#13 BRA waitbit_j_done BTSS.W PORTC,#14 BRA bitstream_done LSR.W W3,W3 BTSS.W SR,#C XOR.W W3,W8,W3 BSET.W SR,#C RRC.B [W9],[W9] ADDC.W #0,W9 NOP INC.8 W2,W2 BRA NZ,waitbit_j MOV.W #0x00F6,W0 INC.B W0,W0 BTSC.W PORTC,#14 BRA NZ,.-4 BRA Z,resetit SENDTOKEN NOP MOV.W #0x00FA,W2 BRA waitbit-k waitbit_j_done: LSR.W W3,W3 BTSC.W SR,#C XOR.W W3,W8,W3 LSR.B [W9].[W9] ADDC.W #0,W9 MOV.W #0x00PA,W2 SENDTOKEN waitbit_k: BTSC.W PORTC,#14 BRA waitbit_k_done BTSC.W PORTC,#14 BRA waitbit_k,done BTSC.W PORTC,#14 BRA waitbit_k_done BTSC.W PORTC,#14 If the placed bit has not appeared yet, keep looping This branch ends seven cycles after the PORTC test Shift the location right to put a one at bit seven If the placed bit has not appeared yet, keep looping This branch ends seven cycles after the PORTC test A zero K bit was found; store it and put it in the CRC Shift the checksum right by one bit into carry Is the previous CRC low bit one? XOR the words together if the bits differ Shift the location right to put a zero at bit seven If the placed one has shown up, increment the address Load (0x0100-0x0006) into W2 for the bitstuffing count Insert the SENDTOKEN macro (eight cycles) Wait for a 0+ low and D- high (J) Skip on a falling edge of D- in case of a stop bit Wait for a 0+ low and D- high (J) Skip on a falling edge of D- in case of a stop bit Wait for a D+ low and D— high (J) Skip on a falling edge of D- in case of a stop bit Wait for a 0+ low and D- high (J) Skip on a falling edge of D- in case of a stop bit Test to see if both D+ and D- are low; if so, branch If this is a single-ended zero, the receive is over Shift the checksum right by one bit into carry Is the previous CRC low bit zero? XOR the words together if the bits differ Set the carry bit early in case a one bit comes Shift the location right to put a one at bit seven If the placed one has shown up, increment the address This makes 20 cycles in the loop total, for each bit Increment the bitstuffing count since a one just came If the count didn’t carry over to 0x00, wait for a one Timeout after 10 cycles (0x0100-0x000A) Increment the timeout counter Wait for a D+ low and D- high (J) Resynchronize on a stuffed bit edge as well Exit early on a timeout Insert the SENDTOKEN macro (eight cycles) Ignore the stuffed bit for one more cycle Load (0x0100-0x0006) into W2 for the bitstuffing count A zero J bit was found; store it and put it in the CRC Shift the checksum right by one bit into carry Is the previous CRC low bit one? XOR the words together if the bits differ Shift the location right to put a zero at bit seven If the placed one has shown up, increment the address Load (0x0100-0x0006) into W2 for the bitstuffing count Insert the SENDTOKEN macro (eight cycles) Wait for a 0+ high and D- low (K) Skip on a falling edge of 0+ in case of a stop bit Wait for a D+ high and D- low (K) Skip on a falling edge of D+ in case of a stop bit Wait for a D+ high and D- low (K) Skip on a falling edge of D+ in case of a stop bit Wait for a D+ high and D- low (K) 154 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 BRA waitbit_k_done BTSS.W PORTC,#13 BRA bitstream_done LSR.W W3,W3 BTSS.W SR,#C XOR.W W3,W8,W3 BSET.W SR,#C RRC.B [W9].[W9] ADDC.W #0,W9 NOP INC.B W2,W2 BRA NZ,waitbit_k MOV.W #0x00F6,W0 INC.8 W0,W0 BTSC.W PORTC,#13 BRA NZ,.-4 BRA Z,resetit SENDTOKEN NOP MOV.W #0x00FA,W2 BRA waitbit_j bitstream_done: COM.W W3,W8 MOV.8 [W9-2],W1 SWAP.W W1 MOV.B [W9-1],W1 SWAP.W W1 MOV.W W9,W3 MOV.W #inbuffer,W9 SU8.W W3,W9,W3 SENDTOKEN REPEAT #15 NOP SENDTOKEN Skip on a falling edge of D+ in case of a stop bit Test to see if both D+ and D- are low; if so, branch If this is a single-ended zero, the receive is over Shift the checksum right by one bit into carry Is the previous CRC low bit zero? XOR the words together if the bits differ Set the carry bit early in case a one bit comes Shift the location right to put a one at bit seven If the placed one has shown up, increment the address This makes 20 cycles in the loop total, for each bit Increment the bitstuffing count since a one just came If the count didn’t carry over to 0x00, wait for a one Timeout after 10 cycles (0x0100-0x000A) Increment the timeout counter Wait for a 0+ high and D- low (K) Resynchronize on a stuffed bit edge as well Exit early on a timeout Insert the SENDTOKEN macro (eight cycles) Ignore the stuffed bit for one more cycle Load (0x0100-0x0006) into W2 for the bitstuffing count Complement the optional CRC to complete the calculation Recall the next-to-last byte received, maybe CRC LS8 Push this to the M88 location temporarily Add on the last byte received, maybe CRC MSB Swap the two bytes in W1 for the proper orientation Save the ending address in W3 Load the buffer address into W9 W3 is now the number of bytes received Insert the SENDTOKEN macro (eight cycles) Delay past the single-ended zero end-of—packet Insert the SENDTOKEN macro (eight cycles) eeeeeeeeeeeeeeeeeeeeeeeeeeee I’IDDBIDDIIDIPPPII’IIIII MOV.8 [++W9],WO SU8.8 #Ox2D,W0 BRA NZ,notsetup MOV.8 [++W9],W0 MOV.B WREG,1astaddr BRA __ICN_end ... SETUP TOKEN COMPARISON ;;;;;;;;:;:;;;:;::;;;;;;;;;; Test the first byte of the data buffer (exclude sync) Compare to the SETUP token at address/endpoint zero If not equal, this is not a SETUP token packet Test the second byte of the buffer Save the address and endpoint of this token packet """"""""""""""" ;;;: IN TOKEN COMPARISON ;;;;;;;;;;;;;;;::;;;;;::;;:;:; I’939898I9IPQI’PDIODBDDI notsetup: MOV.B [W9],W0 SUB.B #0x69,W0 BRA NZ,notin MOV.B [++W9],W0 MOV.B WREG,1astaddr SUB.8 address,WREG BRA Z,in_endp0 AND.W #0x007F,W0 BRA NZ,badpacket IOPODIIPPOODIIDPII" CPO.W nextreport BRA NZ,report Test the first byte of the buffer Compare to the IN token at address/endpoint zero If not equal, this is not an IN packet Test the second byte of the buffer Save the address and endpoint of this token packet Is the address correct and one endpoint bit zero? If equal, this is data for endpoint zero Mask off the tOp bit to test for endpoint one If zero, this is data for endpoint one INPUT INTERRUPT PROCESSING FUNCTION ;;;;;;;;;;;;;;;;;;;;;;; Was a report recently requested? 155 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 ; Try to read in words MOV.W #uartin,W2 BTSC.W [W2],#15 BRA uartOfull BTSS.W U18TA,#URXDA BRA uartread_end MOV.W U1RXREG,WO BSET.W WO,#15 MOV.W W0,[W2l uartOfull: BTSC.W [++W2],#15 BRA uartlfull BTSS.W UISTA,#URXDA BRA uartread_end MOV.W U18XREG,W0 BSET.W WO,#15 MOV.W W0,[W2] uartifull: BTSC.W [++W2],#15 BRA uart2full BTSS.W U1STA,#U8XDA BRA uartread_end MOV.W U18XREG,W0 BSET.W WO,#15 MOV.W W0,[W21 uart2full: BTSC.W [++W2],#15 BRA uartread_end BTSS.W U1STA,#URXDA BRA uartread_end MOV.W U18XREG,W0 BSET.W WO,#15 MOV.W W0,[W2] uartread_end: ; An interrupt IN packet just came in; MOV.W #uartin,Wl MOV.W #outbuffer,W11 MOV.W TMR2,W2 MOV.W TMRBHLD,W3 MOV.D W2,[W11++] BTSS.W [W1],#15 BRA in_int_O MOV.W [W1].[W11++1 BTSS.W [++W1],#15 BRA in_int_i MOV.W [W1],[W11++] BRA in_int_done in_int_O: CLR.W [W11++l in_int_1: CLR.W [W11++] in_int_done: MOV.W #outbuffer,W11 MOV.W #8,W3 BRA senddata_wait report: MOV.W nextreport,WO CLR.W W1 0 9 e D from the hardware UART receive FIFO buffer Test to see if the software UART FIFO buffer is full If the high bit is set Is there at least one available word? Recall the first received word Set the top bit to let the PC know this Store the received word + 0x8000 to the is real data output buffer If the high bit is set Is there another available word? Recall the first received word Set the top bit to let the PC know this Store the received word + 0x8000 to the is real data output buffer If the high bit is set Is there another available word? Recall the first received word Set the top bit to let the PC know this Store the received word + 0x8000 to the is real data output buffer If the high bit is set Is there another available word? Recall the first received word Set the top bit to let the PC know this Store the received word + 0x8000 to the is real data output buffer test for bytes needing to be sent Load the software UART FIFO buffer address Load the output buffer address Load the 32-bit timer value and send it to the PC The four bytes of the long will be little Endian Place a long into the USB output buffer Is there at least one available word? If not, clear out the entire USB output buffer Store the received word + 0x8000 to the output buffer Is there another available word? If not, clear out part of the USB output buffer Store the received word + 0x8000 to the output buffer Don’t clear out any of the USB output buffer Clear out the USB output buffer first word Clear out the USB output buffer second word Load the output buffer address Load the byte count into W3 Test to see which report was requested, and answer Clear the answer initially 156 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 CP.W WO,#1 ; Is this a throttle position report request? BRA NZ,report_notthr MOV.W throttle,W1 BRA report_done report_notthr: CP.W W0,#2 ; Is this a regen position report request? BRA NZ,report_notreg MOV.W regen,W1 BRA report_done report_notreg: CP.W WO,#S ; Is this a random number seed? BRA NZ,report_notseed MOV. TMR2,W2 ; Load the 32-bit timer value to seed with W MOV.W TMRBHLD,W3 MOV.W W2,rand32l MOV.W W3,rand32h CLR.W W1 BRA report_done report_notseed: CP.W WO,#4 ; Is this a random number request? BRA NZ,report_notrand RCALL make_random MOV.W rand32l,W0 MOV.W rand32h,W1 BRA report_done report_notrand: CP.W WO,#S ; Is this a currents U and V request? BRA NZ,report_notcuruv MOV.W curu,W0 MOV.W curv,W1 88A report_done report_notcuruv: CP.W WO,#6 ; Is this a currents alpha, beta, D, and 0 request? BRA NZ,report_notcurabdq MOV.W curalpha,W0 MOV.W curbeta,W1 MOV.W curd,W2 MOV.W curq,W3 MOV.W #outbuffer,W11 ; Load the output buffer address MOV.W W0,[W11++] MOV.W W1,[W11++] MOV.W W2,[W11++] MOV.W W3,[W11++] MOV.W #outbuffer,W11 ; Load the output buffer address MOV.W #8,W3 ; Load the byte count into W3 BRA senddata_wait report_notcurabdq: CP.W WO,#7 ; Is this a number seven request? BRA NZ,report_notnum7 MOV.W frequency,W0 MOV.W pfavg32h,W1 MOV.W curd,W2 MOV.W curq,W3 MOV.W #outbuffer,W11 ; Load the output buffer address MOV.W W0,[W11++1 MOV.W W1,[W11++] 157 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 MOV.W W2,[W11++] MOV.W W3,[W11++] MOV.W #outbuffer,W11 ; MOV.W #8,W3 ; BRA senddata_wait report_notnum7: CP.W WO,#B ; BRA NZ,report_notnumB MOV.W frequency,WO MOV.W pfavg32h,W1 MOV.W amp132l,W2 ; MOV.W amp132h,W3 SL.W W3,#5,W3 : LSR.W W2,#11,W2 3 IOR.W W2,W3,W2 MOV.W currms,W3 MOV.W #outbuffer,W11 ; MOV.W W0,[W11++] MOV.W W1,[W11++] MOV.W W2,[W11++] MOV.W W3,[W11++] MOV.W #outbuffer,W11 ; MOV.W #8,W3 ; BRA senddata_wait report_notnumB: CP.W WO,#9 3 BRA NZ,report_notnum9 MOV.W frequency,W0 MOV.W pfavg32h,W1 MOV.W amp132l,W2 ; MOV.W amp132h,W3 SL.W W3,#5,W3 ; LSR.W W2,#11,W2 ; IOR.W W2,W3,W2 MOV.W curq,W3 MOV.W #outbuffer,W11 ; MOV.W W0,[W11++] MOV.W W1,[W11++] MOV.W W2,[W11++] MOV.W W3,[W11++] MOV.W #outbuffer,W11 ; MOV.W #8,W3 ; BRA senddata_wait report_notnum9: CP.W WO,#10 ; BRA NZ,report_notecho MOV.W pc_loop,W1 MOV.W pc_pfkp,W2 MOV.W pc_pfka,W3 MOV.W #outbuffer,W11 ; MOV.W wo,[w11++l MOV.W W1,[W11++] MOV.W W2,[W11++] MOV.W W3,[W11++l MOV.W #outbuffer,W11 ; MOV.W #8,W3 ; BRA senddata_wait report_notecho: Load the output buffer address Load the byte count into W3 Is this a number eight Send off the amplitude Make room for the five Rid of the unnecessary Load the output buffer Load the output buffer request? in PWM units (32 per low word) lower amplitude bits 11 lowest amplitude bits address address Load the byte count into W3 Is this a number nine request? Send off the amplitude Make room for the five Rid of the unnecessary Load the output buffer Load the output buffer in PWM units (32 per low word) lower amplitude bits 11 lowest amplitude bits address address Load the byte count into W3 Is this an echo bytes request? Load the output buffer Load the output buffer address address Load the byte count into W3 158 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 CP.W WO,#11 BRA MOV. CPO. BRA CLR. BRA NZ,report_notcc W readhigh,W1 W readhigh Z,report_done W usbcommand report_done report_notcc: CP.W WO,#24 BRA MOV. MOV. MOV. MOV. MOV. HOV. MOV. MOV. HOV. MOV. BRA NZ,report_notread bootaddrl,W1 bootaddrh,W2 readhigh,W3 #outbuffer,W11 W0,[W11++] W1,[W11++] W2,[W11++] W3,[W11++l #outbuffer,W11 #8,W3 senddata_wait IZISCIICIZCIZCC report_notread: report_done: MOV.W #outbuffer,W11 MOV.W TMR2,W2 MOV.W TMR3HLD,W3 MOV.D W2,[W11++l MOV.W W0,[W11++] MOV.W W1,[W11++] MOV.W #outbuffer,W11 MOV.W #8,W3 BRA senddata_wait in_ende: MOV.B addrbuf,WREG MOV. HOV. MOV. AND. BRA B WREG,address B nextlen,WREG B W0,W3 W #OxOOFF,W3 senddata_wait Is this a check battery module comm. request? Recall the return value Is this an echo read bytes request? Load the output buffer address Load the output buffer address Load the byte count into W3 Load the output buffer address Load the 32-bit timer value and send it to the PC The four bytes of the long will be little Endian Place a long into the USB output buffer Set the USB output buffer third word to the report type Set the USB output buffer fourth word to the data Load the output buffer address Load the byte count into W3 Update the address only in this status stage Load the previously set byte count into W3 " ""'""""""""':;;; OUT TOKEN COMPARISON ;:;;:;;;;;;;;::;;;;;;;;;;;;:; ’I’I’PIIDDIDIIDPIIIIII’I notin: MOV. SUB. BRA MOV. MOV. SUB. BRA AND. BRA BRA 8 [W9],W0 B #0xEl,W0 NZ,notout B [++W9],W0 B WREG,1astaddr 8 address,WREG Z,out_endp0 W #0x007F,W0 NZ,badpacket __ICN_end out_endp0: MOV. MOV. B addrbuf,WREG B WREG,address Test the first byte of the buffer Compare to the OUT token at address/endpoint zero If not equal, this is not an OUT packet Test the second byte of the buffer Save the address and endpoint of this token packet Is the address correct and one endpoint bit zero? If equal, this is data for endpoint zero Mask off the top bit to test for endpoint one If zero, this is data for endpoint one Update the address only in this status stage 159 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 BRA __ICN_end ;:;:;;;;;;;;;;;;;;;;;:::;;:; DATAO TOKEN COMPARISON ;;;::::;:;;;:;:;;::;;;;;;;;; notout: MOV.B [W9],W0 SUB.B #0xC3,W0 BRA NZ,notdataO MOV.W #0x00C3,W0 88A datapacket e 9 a I a D Test the first byte of the buffer Compare to the DATAO token If not equal, this is not a DATAO packet Record that this was a DATAO packet for toggling """"""""""""" ;;;; DATA1 TOKEN COMPARISON :;;;;;;;;;;:;;;;;;;;:;;;;;;; IDDDPISDDIDIDI’IDPDIIPDP notdataO: MOV.8 [W9],W0 SU8.8 #0x48,W0 BRA NZ,notdata MOV.W #0x004B,W0 datapacket: MOV.8 WREG,1astdata e P e P e 9 e ’ Test the first byte of the buffer Compare to the DATA1 token If not equal, this is not a DATA1 packet Record that this was a DATA1 packet for toggling ; The received CRC is in W1, and the calculated CRC is in W8. Unfortunately, ; the calculated CRC is also over the received CRC. In order to validate the ; packet, the received CRC must be recalculated over itself. MOV.W #16,W2 COM.W W1,W3 MOV.W #0xA001,W0 LSR.W W3,W3 RRNC.W W1,W1 BTSS.W SR,#N XOR.W W3,W0,W3 BTSS.W SR,#C XOR.W W3,W0,W3 DEC.W W2,W2 BRA NZ,.-14 COM.W W3,W3 CP.W W3,W8 BRA NZ,badpacket RCALL sendack MOV.B lastaddr,WREG SUB.B address,WREG BRA Z,processdata AND.W #0x007F,W0 BRA NZ,badpacket eeeeeeeeeeeeeeeeeeee o P e P e 9 Loop 16 times, once for each CRC bit Undo the CRC complement applied by the host, into W3 Set W0 to the CRC-16 polynomial Shift the checksum right by one bit into carry Rotate the byte right to send LS8 first as specified Is the data low bit zero? X08 the words together (destroys negative status bit) Is the previous CRC low bit zero? XOR the words together (perhaps undoing the above XOR) Decrement the remaining bit count If the count is not zero, keep looping Complement W3 as specified by the USB standard Did the CRC’s compare equally? If not, exit now Send an acknowledgment if the packet’s CRC was good Recall the token packet’s address and low endpoint bit Is the address correct and one endpoint bit zero? Start processing this endpoint zero DATAx packet Mask off the top bit to test for endpoint one If zero, this is data for endpoint one OUTPUT INTERRUPT PROCESSING FUNCTION ;;;;;;;;;;;;;;;;;;;;;; ; A DATAx packet just came in, with interrupt data processint: INC.W W9,W9 MOV.W [we++],wo BTSC.W WO,#12 BRA pi_report CLR.W nextreport BTSC.W WO,#11 BRA pi_vartrans BTSC.W WO,#10 BRA pi_program MOV.W #0x0001,W1 BTSS.W WO,#13 Process an endpoint one packet Point to the data buffer Load in the first word of the USB input buffer If the fourth-to-top bit is set, this is a report req. If data is requested instead, clear this variable If the fifth-to-top bit is set, this is a variable sent If the sixth-to-top bit is set, this is programming If the third-to—top bit is set, this is a 937.5de byte 160 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 MOV.W #0x000F,W1 MOV.W W1,U2BRG BTSC.W WO,#15 MOV.W W0,U2TXREG BTSS.W WO,#14 BRA pi_notO MOV.W #uartin,W2 MOV.W #uartin+2,W3 MOV.W [W3++l,[W2++] MOV.W [W3++],IW2++] MOV.W [W3].[W2++1 CLR.W [W3] pi_notO: MOV.W [W9++],W0 BTSC.W WO,#15 MOV.W W0,U2TXREG BTSS.W WO,#14 88A pi_not1 MOV. #uartin,W2 MOV. #uartin+2,W3 MOV. [w3++].[w2++l MOV. [W3].[W2++] w w w MOV.W [w3++].[W2++l w CLR.W [W3] 1 pi_not MOV.W [W9++],W0 BTSC.W WO,#15 MOV.W W0,U2TXREG BTSS.W WO,#14 BRA pi_not2 MOV.W #uartin,W2 MOV.W #uartin+2,W3 MOV.W [w3++],[w2++1 MOV.W [W3++],[W2++J MOV.W [W3],[W2++] CLR.W [W3] 2 pi_not MOV.W [W9++],wo BTSC.W WO,#15 MOV.W W0,U2TXREG BTSS.W WO,#14 BRA pi_not3 MOV.W #uartin,W2 MOV.W #uartin+2,W3 MOV.W [W3++].[W2++] MOV.W [W3++],[W2++] MOV.W [wal.[w2++] CLR.W [W3] 3 pi_not BRA __ICN_end pi_report: MOV.W #0x0FFF,W1 AND.W W0,W1,WO MOV.W W0,nextreport CP.W WO,#11 BRA NZ,__ICN_end MOV.W [W9++],W0 If the bit is clear, this is a 115.2de byte (or four) Modify the baud rate on the fly If the top bit is set, this is real data to transmit Transmit the nine-bit word If the next-to-top bit it set, shift the UART buffer Load the software UART FIFO buffer first word address Load the software UART FIFO buffer second word address Move the second word into the first word Move the third word into the second word Move the fourth word into the third word Clear the fourth word Load in the second word of the USB input buffer If the top bit is set, this is real data to transmit Transmit the nine-bit word If the next-to-top bit it set, shift the UART buffer Load the software UART FIFO buffer first word address Load the software UART FIFO buffer second word address Move the second word into the first word Move the third word into the second word Move the fourth word into the third word Clear the fourth word Load in the third word of the USB input buffer If the top bit is set, this is real data to transmit Transmit the nine-bit word If the next-to-top bit it set, shift the UART buffer Load the software UART FIFO buffer first word address Load the software UART FIFO buffer second word address Move the second word into the first word Move the third word into the second word Move the fourth word into the third word Clear the fourth word Load in the fourth word of the USB input buffer If the top bit is set, this is real data to transmit Transmit the nine-bit word If the next-to-top bit it set, shift the UART buffer Load the software UART FIFO buffer first word address Load the software UART FIFO buffer second word address Move the second word into the first word Move the third word into the second word Move the fourth word into the third word Clear the fourth word Was this a check battery module comm. request? If so, save the address of the battery module 161 598 MOV.W W0,usbcommand 599 CLR.W readhigh 600 88A __ICN_end 601 602 pi_vartrans: 603 MOV.W [W9++],WO ; Load in the second word of the USB input buffer 604 MOV.W W0,pc_1oop 605 MOV.W [W9++],W0 ; Load in the third word of the USB input buffer 606 MOV.W W0,pc_pfkp 607 MOV.W [W9++],W0 ; Load in the fourth word of the USB input buffer 608 MOV.W W0,pc_pfka 609 BRA __ICN_end 610 611 pi_program: 612 CP.B WO,#16 ; Is this a load 23-bit address operation request? 613 BRA NZ,pp_not16 614 MOV.W [W9++],W0 ; Load in the second word of the USB input buffer 615 MOV.W W0,bootaddrl 616 MOV.W [W9++],W0 ; Load in the third word of the USB input buffer 617 MOV.W W0,bootaddrh 618 BRA __ICN_end 619 620 pp_not16: 621 CP.B WO,#17 ; Is this an erase program memory row operation request? 622 BRA NZ,pp_not17 623 MOV.W [W9++],W0 ; Load in the second word of the USB input buffer 624 MOV.W W0,NVMADR 625 MOV.W W0,bootaddrl 626 MOV.W [W9++],W0 ; Load in the third word of the USB input buffer 627 MOV.W W0,NVMAD8U 628 MOV.W W0,bootaddrh 629 MOV.W #0x4041,W0 ; Set up NVMCON for erasing a program memory row 630 MOV.W W0,NVMCON 631 BRA __ICN-prog 632 633 pp_not17: 634 CP.B WO,#18 ; Is this a write program memory row operation request? 635 BRA NZ,pp-not18 636 MOV.W [W9++],W0 ; Load in the second word of the USB input buffer 637 MOV.W W0,NVMADR 638 MOV.W [W9++],W0 ; Load in the third word of the USB input buffer 639 MOV.W W0,NVMADRU 640 MOV.W #0x4001,W0 ; Set up NVMCON for writing a program memory row 641 MOV.W W0,NVMCON 642 BRA __ICN_prog 643 644 pp_not18: 645 CP.B WO,#19 ; Is this an erase data memory word operation request? 646 88A NZ,pp_not19 647 MOV.W [W9++],W0 ; Load in the second word of the USB input buffer 648 MOV.W W0,NVMADR 649 MOV.W W0,bootaddrl 650 MOV.W [W9++],W0 ; Load in the third word of the USB input buffer 651 MOV.W W0,NVMAD8U 652 MOV.W W0,bootaddrh 653 MOV.W #0x4044,W0 ; Set up NVMCON for erasing a data memory word 654 MOV.W W0,NVMCON 655 BRA __ICN_prog 656 657 pp_not19: 658 CP.B WO,#20 ; Is this an erase data memory row operation request? 659 BRA NZ,pp_not20 162 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 MOV.W [W9++],WO MOV.W W0,NVMADR MOV.W W0,bootaddrl MOV.W [W9++],WO MOV.W W0,NVMADRU MOV.W W0,bootaddrh MOV.W #0x4045,W0 MOV.W W0,NVMCON BRA __ICN_prog pp_not20: CP.B WO,#21 BRA NZ,pp-not21 MOV.W [W9++],W0 MOV.W [W9++].W1 MOV.W W1,TBLPAG TBLWTL.W [W9++].[W0] MOV.W #0x4004,W0 MOV.W W0,NVMCON BRA __ICN_prog pp_not21: CP.B WO,#22 BRA NZ,pp_not22 MOV.W [W9++],W0 MOV.W W0,NVMADR MOV.W [W9++],W0 MOV.W W0,NVMADRU MOV.W #0x4005,W0 MOV.W W0,NVMCON BRA __ICN-prog pp_not22: CP.B WO,#23 BRA NZ,pp_not23 MOV.W [W9++],W0 MOV.W [W9++],W1 MOV.W W1,TBLPAG TBLWTL.W [W9++],[W01 MOV.W #0x4008,W0 MOV.W W0,NVMCON BRA __ICN_prog pp-not23: CP.B WO,#24 BRA NZ,pp_not24 MOV.W [W9++],W3 MOV.W [W9++],W1 MOV.W W1,TBLPAG TBLRDL.W [W3++],W1 TBLRDL.W [W3],W2 MOV.W W1,bootaddrl MOV.W W2,bootaddrh TBLRDH.B [W3--],W1 SWAP.W W1 TBLRDH.B [--W3],W1 MOV.W W1,readhigh AND.W #0x00FF,W0 MOV.W W0,uextreport BRA __ICN_end pp_not24: Load in the second word of the USB input buffer Load in the third word of the USB input buffer Set up NVMCON for erasing a data memory row Is this a write data memory word operation request? Load in the second word of the USB input buffer Load in the third word of the USB input buffer Load in the fourth word of the USB input buffer Set up NVMCON for writing a data memory word Is this a write data memory row Operation request? Load in the second word of the USB input buffer Load in the third word of the USB input buffer Set up NVMCON for writing a data memory row Is this a write config. memory operation request? Load in the second word of the USB input buffer Load in the third word of the USB input buffer Load in the fourth word of the USB input buffer Set up NVMCON for writing a configuration memory word Is this a read memory long operation request? Load in the second word of the USB input buffer Load in the third word of the USB input buffer Read the low memory at offsets zero and two Save memory by reusing the address variables Read the high memory at offsets two and zero 163 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 CP.B WO,#25 ; BRA NZ,pp_not25 MOV.W #0x8080,W0 ; MOV.W #inbuffer,W9 ; REPEAT #5 ; MOV.W W0,[W9++] GOTO bootloader pp_not25: CP.B WO,#26 3 BRA NZ,pp_not26 RESET pp_not26: ; AND.W #0x000F,W0 ; SL.W W0,#2,W0 ; ADD.W bootaddrl,WREG ; MOV.W bootaddrh,W1 MOV.W W1,TBLPAG MOV.W [W9++],W1 ; TBLWTL.W W1,[W0++] MOV.W [W9++J,W1 ; TBLWTL.W W1,[W0-—] MOV.W [W9++],W1 3 TBLWTH.W W1,[W0++] SWAP.W W1 ; TBLWTH.W W1,[W0] BRA __ICN_end processdata: ; CP.B W3,#4 ; BRA NZ,notzerolength .;;;; ZERO-LENGTH PACKET ;:;;;:;;;;;:;;:;;;;:;;;;;;;;;:: P’D'IDPPDDOODODO'IIPI’,’ ; A DATAx packet just BRA __ICN_end notzerolength: MOV.B [++W9],W0 ; CPO.8 W0 ; BRA Z,notdatadevhost ; MOV.B [++W9],W0 ; SUB.8 #0x06,W0 ; BRA NZ,notdatadesc ; MOV.B [++W9],W3 : MOV.B [++W9],WO ; SUB.8 #0x01,W0 ; BRA NZ,notdatadevd ; IIID’IOIPIOPDPIII!III”B MOV.W #nextlen,W3 ; CLR.W TBLPAG ; Is this a jump to other bootloader operation request? Return the input buffer to its original condition Load the buffer address into W9 Clear out 12 bytes (maximum overall packet length) Is this a reset processor Operation request? If none Of the above, this is a load write latches op. Mask off all bits but the lower four Multiply this address by four (access 32 instr. words) Add this to the commanded low address Load in the second word of the USB input buffer Load in the third word of the USB input buffer Load in the fourth word of the USB input buffer Now access the high byte of the fourth word, to write Start processing this DATAx packet Were there only four bytes (sync/PID/CRC-16)? came in, with zero data Test the second byte of the buffer Test the byte for zero If zero, this is not a “Device to Host" request Test the second byte of the buffer Is this the "Get Descriptor" data? If not equal, this is not a "Get Descriptor" request Save the third byte of the buffer (string desc. number) Test the fourth byte of the buffer Is this the "Get Device Descriptor" data? If not equal, this is not a "Get Device Descriptor" DEVICE DESCRIPTOR REQUEST :;;;;;::;;;;;:;;;;;;;;;;;;;; First, read the packet lengths into nextlen Set up TBLRDL to grab the string descriptor packets MOV.W #tbloffset(dDevice),W9 REPEAT #7 : TBLRDL.B [W9++],[W3++J MOV.W #outbuffer,W11 ; T8LRDL.B [W91,W0 ; AND.W #0x00FF,W0 INC.W W0,WO ASR.W W0,W0 Read eight bytes into nextlen Load the output buffer address Load the data length and adjust for REPEAT 164 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 DEC.W W0,W0 REPEAT W0 ; Copy up to 64 bytes of data into the output buffer TBLRDL.W [W9++],[W11++] ; A DATAx packet just came in asking for the device descriptor MOV.W #outbuffer,W11 ; Load the output buffer address for later BRA __ICN_end notdatadevd: MOV.B [W9],W0 ; Test the fourth byte of the buffer SU8.8 #0x02,W0 ; Is this the "Get Configuration Descriptor” data? BRA NZ,notdatacond ; If not equal, this is not s ”Get Config. Descriptor" :;:;:;;;;;;;;:;;;;;;;; CONFIGURATION DESCRIPTOR REQUEST 3;;;;;:;;;;:::;;;:;;;;;; PUSH.W W9 MOV.W #nextlen,W3 ; First, read the packet lengths into nextlen CLR.W TBLPAG ; Set up TBLRDL to grab the string descriptor packets MOV.W #tbloffset(dConfiguration),W9 REPEAT #7 ; Read eight bytes into nextlen TBLRDL.B [W9++].[W3++] MOV.W #outbuffer,W11 ; Load the output buffer address TBLRDL.W [++W9],W0 ; Load the data length (actually second word) and adjust INC.W W0,W0 ASR.W W0,W0 DEC.W W0,W0 DEC2.W W9,W9 REPEAT W0 ; Copy up to 64 bytes of data into the output buffer TBLRDL.W [W9++],[W11++] POP.W W9 INC2.W W9,W9 ; Skip the fifth and sixth bytes (zero) MOV.8 [++W9],W0 ; Test the seventh byte of the buffer for short/complete SU8.B #0x09,W0 ; Is this the "Get Configuration Descriptor header" data? BRA NZ,condescfull ; If not equal, this is not a "Get Config. Desc. header" ; Shorten the configuration descriptor for now; limit it to nine data bytes MOV.W #8,W0 ; Set the length of data to send in bytes for next time MOV.B WREG,nextlen MOV.W #1,W0 ; The second packet is shorter MOV.B WREG,nextlen+1 CLR.B nextlen+2 ; The third packet has zero data (if necessary) condescfull: ; A DATAx packet just came in asking for the configuration descriptor MOV.W #outbuffer,W11 ; Load the output buffer address for later BRA __ICN_end notdatacond: MOV.B [W9],W0 ; Test the fourth byte of the buffer SUB.B #0x03,W0 ; Is this the "Get String Descriptor" data? BRA NZ,notdatastrd ; If not equal, this is not a 'Get String Descriptor" :........................ STRING DESCRIPTOR REQUEST ;;;;;:;;;;;;;;;;;;;;;:;;:;;; BSET.W PORTF,#6 ; Turn the LED on MOV.W #outbuffer,W11 ; Load the output buffer address CPO.B W3 ; Test the saved string descriptor number BRA NZ,notdatastrdO ; If not equal, this is not a "Get String Descriptor 0" ; This is preparing for after the IN packet comes in 165 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 MOV.W #0x04,W0 ; This is the string descriptor length MOV.B W0,[W11++] MOV.W #0x03,W0 ; This is the string descriptor type MOV.B wo,[w11++] MOV.W #0x0409,W0 ; This is the United States English language code MOV.W W0,[W11++1 MOV.W #4,W0 ; Set the length of data to send in bytes for next time MOV.8 WREG,nextlen BRA dontcopystr notdatastrdO: DEC.B W3,W3 ; Is this the "Get String Descriptor 1" data? BRA NZ,notdatastrd1 ; If not equal, this is not a "Get String Descriptor 1" MOV.W #tbloffset(sManufacturer),W9 BRA donestringd notdatastrd1: DEC.B W3,W3 ; Is this the "Get String Descriptor 2" data? 88A NZ,notdatastrd2 ; If not equal, this is not s ”Get String Descriptor 2" MOV.W #tbloffset(sProduct),W9 BRA donestringd notdatastrd2: BRA dontcopystr ; There was an error in asking for a string descriptor donestringd: MOV.W #nextlen,W3 ; First, read the packet lengths into nextlen CLR.W TBLPAG ; Set up TBLRDL to grab the string descriptor packets REPEAT #7 ; Read eight bytes into nextlen TBLRDL.B [W9++].[W3++] TBLRDL.B [W9],W0 ; Load the data length and adjust for REPEAT AND.W #0x00FF,W0 INC.W W0,W0 ASR.W W0,W0 DEC.W W0,W0 REPEAT W0 ; Copy up to 64 bytes of data into the output buffer TBLRDL.W [W9++].[W11++] dontcopystr: ; A DATAx packet just came in asking for the string descriptor MOV.W #outbuffer,W11 ; Load the output buffer address for later BRA __ICN_end notdatastrd: MOV.B [W9],WO ; Test the fourth byte of the buffer SUB.B #0x22,W0 ; Is this the "Get HID Descriptor" data? BRA NZ,notdatarepd ; If not equal, this is not a "Get Report Descriptor" :;::;;;;;;;;;:;;:;;;;;;:;; REPORT DESCRIPTOR REQUEST ;;;;;;;;;;:;:;;;;;;:;:;;;:; MOV.W #nextlen,W3 ; First, read the packet lengths into nextlen CLR.W TBLPAG ; Set up TBLRDL to grab the string descriptor packets MOV.W #tbloffset(dReport),W9 REPEAT #7 ; Read eight bytes into nextlen T8LRDL.B [W9++].[W3++1 MOV.W #outbuffer,W11 ; Load the output buffer address TBLRDL.B [++W9],W0 ; Load the data length (actually second byte) and adjust AND.W #0x00FF,W0 INC.W W0,W0 ASR.W W0,W0 DEC.W W0,WO 166 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 DEC.W W9,W9 REPEAT H0 TBLRDL.W [W9++],[W11++] ; Copy up to 64 bytes of data into the output buffer ; A DATAx packet just came in asking for the report descriptor MOV.W #outbuffer,W11 BRA ICN_end notdatarepd: BRA ICN_end notdatadesc: BRA ICN_end notdatadevhost: MOV.B [++W9],W0 SUB.B #0x05,W0 BRA NZ,notdatasetadd ; Load the output buffer address for later ; Test the second byte of the buffer ; Is this the "Set Address" data? ; If not equal, this is not a "Set Address" request :;:;;::;;;;:;;;;;:;;;;;;;;;; SET ADDRESS REQUEST ;;;;;;;;:;;;;:;;;;;;:;:::;:;:;: MOV.B [++W9],W0 ; Read the address from the packet MOV.B WREG,addrbuf ; Store the address for this device in the buffer CL8.8 nextlen ; Set the length of data bytes to zero for next time ; A DATAx packet just came in, with the address BRA __ICN_end notdatasetadd: MOV.B [W9],W0 SUB.B #0x09,W0 BRA NZ,notdatasetcon ; Test the second byte of the buffer ; Is this the "Set Configuration" data? ; If not equal, this is not a ”Set Configuration" request ;;;;;:;;;;;;;:;;;;;:;;;:; SET CONFIGURATION REQUEST ;:::;;;;;;::;;;:;;;:::;::;:: MOV.B [++W9],WO CL8.B nextlen ; Read the configuration number from the packet ; Set the length of data bytes to zero for next time ; A DATAx packet just came in, with the configuration 88A __ICN_end notdatasetcon: BRA __ICN_end notdata: MOV.B [W9],W0 SUB.B #0xD2,WO 88A NZ,notack MOV.B nextlen+1,WREG MOV.B WREG,nextlen MOV.8 nextlen+2,WREG MOV.B WREG,nextlen+1 MOV.D nextlen+3,WREG MOV.B WREG,nextlen+2 MOV.B nextlen+4,WREG MOV.B WREG,nextlen+3 MOV.8 nextlen+5,WREG MOV.B WREG,nextlen+4 ACKNOWLEDGEMENT TOKEN COMPARISON ;;;;;;;;;;;;;;;;;;;;;;;; ; Test the first byte of the buffer ; Compare to the ACK token at address/endpoint zero ; If not equal, this is not an ACK packet ; Shift the buffer of data lengths to send upon request 167 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 MOV.B nextlen+6,WREG MOV.8 WREG,nextlen+5 MOV.B nextlen+7,WREG MOV.8 WREG,nextlen+6 CLR.B nextlen+7 BRA __ICN_end notack: BRA __ICN_end badpacket: BRA __ICN_end ;::;;;:;;;:;:;;;;;;;:; ACKNOWLEDGEMENT TRANSHIT FUNCTION ;::;::;:;;;:;;::;;;;;;; sendack: MOV.W #0x2000,W0 ; D+ should be low (non-inverted) since low-speed MOV.W WO,PORTC ; D- should be high (non-inverted) since low-speed CLR.W TRISC ; This will put the bus into a J (idle) state REPEAT #11 ; Delay 13 cycles to make an idle J for 20 cycles MOV.W #0x6000,W0 ; MOV.W #0xD280,W3 ; DO #15,sendack_end ; RRNC.W W3,W3 ; BTSS.W SR,#N ; XOR.W PORTC ; SENDTOKEN ; REPEAT #6 ; NOP sendack-end: NOP NOP ; NOP CLR.W PORTC ; SENDTOKEN ; REPEAT #21 ; NOP SENDTOKEN : BSET.W PORTC,#13 ; REPEAT #8 ; NOP SENDTOKEN ; MOV.W #oxsooo,wo ; nov.w WO,TRISC ; BSET.W PORTC,#15 ; RETURN eeeeeeeeeeeeeeeeeeeeeeeee IPIDDII’POPDIDDDDFOPID” senddata_wait: ; SENDTOKEN ; REPEAT #14 ; NOP SENDTOKEN ; REPEAT #14 ; NOP SENDTOKEN ; REPEAT #14 ; NOP SENDTOKEN ; Load the bit locations to toggle Load the acknowledgement reply Send 16 bits of the sync/PID Rotate the word right to send LS8 first as specified If the wrapped-around LSB was one, don’t toggle PORTC Toggle the pins together for a zero bit Insert the SENDTOKEN macro (eight cycles) Delay eight more cycles for a total of 20 Delay twice more for 20 cycles total This creates a single-ended zero with RC14 and RC13 Insert the SENDTOKEN macro (eight cycles) Delay 23 more cycles (nearly two bit lengths) Insert the SENDTOKEN macro (eight cycles) D- should be high (non-inverted) since low-speed Delay 10 cycles (nearly one bit length) Insert the SENDTOKEN macro (eight cycles) Load the original TRISC value This will release the bus to the host Turn the 74LS244 to input from the bus ;;; DATA TRANSHIT FUNCTION :z;::;;:;;;;:;:;;::;:;;;:;;; This label can be branched to, to delay before sending Insert the SENDTOKEN macro (eight cycles) Delay a short amount of time Insert the SENDTOKEN macro (eight cycles) Delay a short amount of time Insert the SENDTOKEN macro (eight cycles) Delay a short amount of time Insert the SENDTOKEN macro (eight cycles) 168 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 REPEAT #14 NOP SENDTOKEN senddata: MOV.W #0x8088,WO XOR.B lastdata,WREG MOV.B WREG,1astdata SWAP.W W0 MOV.W W0,W9 MOV.W #1,W2 BTSC.W W9,#15 INC.W W2,W2 MOV.W #0x2000,WO MOV.W WO,PORTC CLR.W TRISC MOV.W #0x6000,W0 DO #15,senddata_end0 SENDTOKEN REPEAT #3 NOP RRNC.W W9,W9 BTSS.W SR,#N XOR.W PORTC NOP NOP NOP senddata_endO: NOP SETM.W W9 CPO.W W3 BRA Z,senddata_crcz SL.W W3,#3,W3 DEC.W W3,W3 CLR.W W1 MOV.W #0xA001,W8 DO W3,senddata_end1 LSR.W W9,W9 RRNC.B [W11].[W11] BRA N,sd_notzero1 CLR.W W2 BTSC.W SR,#C XOR.W W9,W8,W9 XOR.W PORTC NOP BRA sd_continue1 sd_notzerol: BTSS.W SR,#C XOR.W W9,W8,W9 INC.W W2,W2 CP.W W2,#6 BRA NZ,sd_continue1 SENDTOKEN REPEAT #7 NOP XOR.W PORTC CLR.W W2 REPEAT #1 NOP Delay a short amount of time Insert the SENDTOKEN macro (eight cycles) Load in the data-toggle constant and the sync byte Toggle the last DATAx bits for this transfer Save this result for next time Swap the bytes of WO in order to transmit properly This word must be moved into W9 Keep track of how many ones in a row for stuffing If we’re sending a OxC380 instead of a 0x4880, the bitstuff count should be two instead of one D+ should be low (non-inverted) since low-speed D- should be high (non-inverted) since low-speed This will put the bus into a J (idle) state Load the bit locations to toggle Send 16 bits of the sync/PID Insert the SENDTOKEN macro (eight cycles) Delay five more cycles for a total of 20 Rotate the word right to send LSB first as specified If the wrapped-around LSB was one, don’t toggle PORTC Toggle the pins together for a zero bit For some reason, a REPEAT here takes one cycle too many Instead of a REPEAT #1, use three NOPs and a final Set W9 to all ones; this is the CRC "shift register" Should only the CRC be sent? Check the bit count This will branch in the case of a zero-data packet Turn the byte count into a hit count Decrement it by one because the DO loop needs this Clear the bit count (actually shifted left by five) Set W8 to the CRC-16 polynomial Send W3+1 bits total Shift the checksum right by one bit into carry Rotate the byte right to send LSB first as specified If the wrapped-around LSB was one, don’t toggle 887/886 Clear the stuffing hit count since a zero was sent Is the previous CRC low bit one? XOR the words together if the bits differ (zero vs one) Toggle the pins together for a zero bit Equalize the zero/one delays Skip all the next instructions Is the previous CRC low bit zero? XOR the words together if the bits differ (zero vs one) This bit of one may create a need for stuffing Test to see if stuffing is needed (six ones in a row) If the comparison was nonzero, continue Insert the SENDTOKEN macro (eight cycles) Still send out a one first (delay nine cycles) Toggle the pins together for a stuffed zero bit Clear the stuffing hit count since a zero was sent Delay three more cycles to equalize with a normal zero 169 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 1150 1151 1152 1153 1154 1155 sd_continue1: SENDTOKEN ADD.B #32,W1 senddata_end1: ADDC.W #0,W11 senddata_crc: DO #15,senddata_end2 RRNC.W W9,W9 BRA NN,sd_notzero2 CLR.W W2 NOP XOR.W PORTC BRA sd_continue2 sd_notzero2: INC.W W2,W2 CP.W W2,#6 BRA NZ,sd_continue2 SENDTOKEN REPEAT #8 NOP XOR.W PORTC CLR.W W2 NOP sd_continue2: NOP NOP SENDTOKEN NOP NOP senddata_end2: NOP REPEAT #2 NOP CLR.W PORTC SENDTOKEN REPEAT #21 NOP SENDTOKEN BSET.W PORTC,#13 REPEAT #8 NOP SENDTOKEN MOV.W #0x6000,WO MOV.W W0,TRISC BSET.W PORTC,#15 BRA __ICN_end senddata_crcz: NOP NOP NOP BRA senddata_crc resetit: BCLR.W PORTF,#6 RCALL reset_usb MOV.W PORTC,WO BCLR.W IFSO,#CNIF POP.W W9 Insert the SENDTOKEN macro (eight cycles) Add ’00100000’ to W1 to test for the next buffer byte This label points to the last instruction in the loop If it carries, point to the next byte in the buffer Note that the CRC is inverted at this point Send 16 bits of the CRC Rotate the byte right to send LS8 first as specified If the wrapped-around LS8 was one, don’t toggle 887/886 Clear the stuffing bit count since a zero was sent Equalize the zero/one delays Toggle the pins together for a zero bit Skip all the next instructions This bit of one may create a need for stuffing Test to see if stuffing is needed (six ones in a row) If the comparison was nonzero, continue Insert the SENDTOKEN macro (eight cycles) Still send out a one first (delay 10 cycles) Toggle the pins together for a stuffed zero bit Clear the stuffing bit count since a zero was sent Delay one more cycle to equalize with a normal zero Insert the SENDTOKEN macro (eight cycles) This label points to the last instruction in the loop Delay four cycles for 20 cycles total This creates a single-ended zero with RC14 and RC13 Insert the SENDTOKEN macro (eight cycles) Delay 23 more cycles (nearly two bit lengths) Insert the SENDTOKEN macro (eight cycles) D- should be high (non-inverted) since low-speed Delay 10 cycles (nearly one bit length) Insert the SENDTOKEN macro (eight cycles) Load the original TRISC value This will release the bus to the host Turn the 74LS244 to input from the bus Delay five cycles before sending the zero CRC Reset occurs here Turn the LED off Re-initialize all the USB variables Clear the notification condition Clear the CN interrupt flag Restore the working register W9 170 1156 1157 1158 1159 1160 1161 1162 1163 1164 1165 1166 1167 1168 1169 1170 1171 1172 1173 1174 1175 1176 1177 1178 1179 1180 1181 1182 1183 1184 1185 1186 1187 1188 1189 1190 1191 1192 1193 1194 1195 1196 1197 1198 1199 1200 1201 1202 1203 1204 1205 1206 1207 1208 1209 1210 1211 1212 1213 1214 1215 1216 1217 POP.W W8 POP.S RETFIE __ICN_prog: DISI #5 MOV.W #0x55,W0 MOV.W W0,NVMKEY MOV.W #0xAA,W1 MOV.W W1,NVMKEY BSET.W NVMCON,#WR NOP NOP -_ICN_end: MOV.W #0x8080.W0 MOV.W #inbuffer,W9 REPEAT #5 MOV.W W0.[W9++] MOV.W PORTC,WO BCLR.W IFSO,#CNIF POP.W W9 POP.W W8 POP.S RETFIE Restore the working register W8 Restore the status register, W0, W1, W2, and W3 Execute a generic programming cycle for many Operations Return the input buffer to its original condition Load the buffer address into W9 Clear out 12 bytes (maximum overall packet length) Clear the notification condition Clear the CN interrupt flag Restore the working register W9 Restore the working register W8 Restore the status register, W0, W1, W2, and W3 ; USB descriptors (0-32 words real data) bl_dDevice: .WORD 0x0808 .WORD 0x0002 .WORD 0x0000 .WORD 0x0000 .WORD 0x0112 .WORD 0x0110 .WORD 0x0000 .WORD 0x0800 .WORD 0x04D8 .WORD OxABCD .WORD 0x0100 .WORD 0x0201 .WORD 0x0100 bl-dConfiguration: .WORD 0x0808 .WORD 0x0808 .WORD 0x0108 .WORD 0x0000 .WORD 0x0209 .WORD 0x0029 .WORD 0x0101 .WORD OxCOOO .WORD 0x0980 .WORD 0x0004 .WORD 0x0200 .WORD 0x0003 .WORD OxOOOO .WORD 0x2109 .WORD 0x0110 .WORD 0x0100 .WORD Ox2D22 The nextlen packet lengths (LS8 first) The device descriptor length in bytes and number The USB specification number (version 1.1) The device class/subclass (specified in interf. desc.) The device protocol and the maximum packet size The vendor ID (from Microchip) The product ID The device release number The manufacturer/product string descriptor addresses The serial number string addr. and configuration count The nextlen packet lengths (LSB first) The config. descriptor length in bytes and number The total length of bytes returned The number of interfaces and the configuration value The config. string address and the config. attributes The maximum current (by 2mA) and interface desc. length The interface descriptor number and which one this is The alternate setting number and endpoint count The interface class (HID) and subclass The interface protocol and string descriptor address The HID descriptor length in bytes and number The HID specification number (version 1.1) The country code and the number of subordinate descs. The report descriptor length in bytes and number 171 1218 1219 1220 1221 1222 1223 1224 1225 1226 1227 1228 1229 1230 1231 1232 1233 1234 1235 1236 1237 1238 1239 1240 1241 1242 1243 1244 1245 1246 1247 1248 1249 1250 1251 1252 1253 1254 1255 1256 1257 1258 1259 1260 1261 1262 1263 1264 1265 1266 1267 1268 1269 1270 1271 1272 1273 1274 1275 1276 1277 .WORD .WORD .WORD .WORD .WORD .WORD .WORD .WORD 0x0700 0x8105 0x0803 OxOAOO 0x0507 0x0301 0x0008 OxOOOA bl_dReport: .WORD .WORD .WORD .WORD .WORD .WORD .WORD .WORD .WORD .WORD .WORD .WORD .WORD .WORD .WORD .WORD .WORD .WORD .WORD .WORD .WORD .WORD .WORD .WORD .WORD .WORD .WORD .WORD .WORD ; USB strings (2-32 words real data, 0x0808 0x0808 0x0808 0x0010 0x2D22 0x0500 0x0984 0xA159 0x0901 0x151A 0x2600 0x00FF 0x0895 0x0875 0x0281 0x1C09 0x0015 0xFF26 0x9500 0x7508 0x9108 0x0902 0x151E 0x2600 0x00FF 0x0895 0x0875 0x0281 0x00CO ; The ; The ; The ; The ; The ; The ; The ; The report desc. length MSB and IN interrupt desc. len. IN int. desc. number and endpoint number/direction transfer type and maximum packet size LSB maximum packet size M88 and polling interval OUT int. desc. length and number endpoint number and direction and transfer type maximum packet size MSB/LSB polling interval in milliseconds ; The nextlen packet lengths (LSB first) ; The report descriptor length in bytes and number including 0x03xx header; 1-31 characters) bl_sManufacturer: .WORD .WORD .WORD .WORD .WORD .WORD .WORD 0x0808 0x0808 0x0808 0x0000 0x0330 DC), DE), bl_sProduct: .WORD .WORD .WORD .WORD .WORD .WORD .WORD 0x0808 0x0808 0x0608 0x0000 0x032E ’El’)V’, DI!,)n)’ in), )1), ; The nextlen packet lengths (LSB first) ; The string descriptor number and the length in bytes ’8’,’t’,’0’,’m’,’ 8,)A1.)u)’,tl’lo)’) ) ’8’,’C’,’t’,’r’,’0’,’n’,’l’,’c’,’8’ ; The nextlen packet lengths (LS8 first) ; The string descriptor number and the length in bytes ’ ’,’1‘1’,’u’,’1’,'t’,’i’,’1’,’6’,’V’,’e',’1’,’ , ’V’,’e’,’r’,’t',’8’,’r’ 172 E.2 PIC18F2553 Full Assembly Code, Design Two coooqczcnaoosOr— MMMI—ir—Ib—ih—iD—‘t—it—‘b—‘I—AH wwocooosioambwwv—o 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 ; USB Interface Code for the EV Multilevel Inverter ; Arthur W. Matteson - 2/8/2008 LIST P=PIC18F2550 ; Set the processor type #INCLUDE p18f2550.inc ; Variables lOOpvalo EQU 0x00 loopvall EOU 0x01 savedw EQU 0x02 newaddr EQU 0x03 savedl EQU 0x04 savedh EQU 0x05 ledonl EOU 0x06 ledonh EQU 0x07 ORG 0x0000 ; Start at the reset vector BRA main ; We don’t need to do anything else ORG 0x0008 ; Start at the high priority interrupt vector BRA isr ; Point to our interrupt service routine ORG 0x0018 ; Start at the low priority interrupt vector BRA isr ; Point to our interrupt service routine main: RCALL delay_50ms ; Delay if we’re programming so pins don’t short RCALL delay_50ms MOVLW B’01111111’ ; PORTA data direction register MOVWF TRISA,A MOVLW 8’11101111’ ; PORTB data direction register MOVWF TRISB,A MOVLW 8’10000111’ ; PORTC data direction register MOVWF TRISC,A MOVLW B’OOOOOOOO’ ; PORTA output data MOVWF PORTA,A MOVLW 8’00000000’ ; PORTB output data MOVWF PORTB,A MOVLW 8’00000000’ ; PORTC output data MOVWF P08TC,A MOVLW B’00000101’ ; Set the serial port up for 2MBd MOVWF SPBRG,A CLRF SPBRGH,A MOVLW 8’00001000’ ; Set up the 16-bit baud rate generator MOVWF BAUDCON,A MOVLW 8’01100100’ ; Enable serial port transmissions MOVWF TXSTA,A MOVLW 8’11010000’ ; Enable serial port receptions MOVWF RCSTA,A BSF PIE1,RCIE,A ; Enable interrupts upon reception BSF INTCON,PEIE,A BSF INTCON,GIE,A 173 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 CLRF CLRF LFSR ledonl,A ledonh,A 0,0x0000 clearout: CLRF INCF BTFSC INCF BTFSS INDFO,A FSROL,F,A STATUS,Z,A FSROH,F,A FSROH,3,A BRA clearout MOVLW MOVWF MOVLW MOVWF MOVLW MOVWF LFSR B’00010100’ UCFG,A 8’10011111’ UEIE,A B’OOOOIOOO’ UCON,A 2,0x0600 mainlOOp: BCP UCON,SUSPND,A DECFSZ ledon1,F,A BRA noledoff DCPSNZ 1edonh,F,A BCF PORTB,4,A noledof BTFSS UI8,URSTIF,A f: BRA notreset CLRF CLRF CLRF CLRF CLRF MOVLB MOVLW MOVWF MOVLW MOVWF MOVLW MOVWF MOVLW MOVWF CLRF MOVLW MOVWF MOVLW MOVWF MOVLW MOVWF MOVLW MOVWF MOVLW MOVWF MOVLW MOVWF UIR,A UIR,A UIR,A UIR,A UIR,A 0x04 0x40 0x01,B 0x00 0x02,8 0x05 0x03,B 8’10000000’ 0x00,B 0x05,B 0x40 0x06,B 0x05 0x07,8 8’00000000’ 0x04,8 8’00010110’ UEPO,A 0x40 0x09,8 0x80 0x0A,B Clear the LED on-time counter Clear out the RAM beginning at 0 Clear out the data location specified by FSRO Increment the address to clear Was there an overflow? Increment the high address as well Test for an address of 0x0800 Branch if not there yet Set up high-speed operation Enable all error reporting Enable USB and don’t suspend Load FSR2 with the data stream block address Always take the USB module out of suspend Decrement the counter Turn the LED Off when the counter expires Test the USB reset interrupt flag Clear the USB interrupt flag(s) Clear the USB interrupt flag(s) Clear the USB interrupt flag(s) Clear the USB interrupt flag(s) Clear the USB interrupt flag(s) Access bank 4 (USB RAM buffer descriptors) Set up BDOCNT for 64 bytes of buffer Set up BDOADRL to point to 0x500 Set up BDOADRH to point to 0x500 Set up BDOSTAT for 64 bytes of buffer This descriptor is for EPO OUT (control) Clear the 8D1CNT buffer length (no sending yet) Set up BDlADRL to point to 0x540 Set up BDlADRH to point to 0x540 Set up BDISTAT for 64 bytes of buffer This descriptor is for EPO IN (control) Set up endpoint zero (with control transfers) Set up BD2CNT for 64 bytes of buffer Set up BD2ADRL to point to 0x680 174 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 MOVLW 0106 MOVWF 0108.8 MOVLW 8’10000000’ MOVWF 0x08.B MOVLW 0140 MOVWF 0x0D,B MOVLW 0100 MOVWF 0x0E,B MOVLW 0106 MOVWF OXOF,B MOVLW B’10000000’ MOVWF 010C,B MOVLW 8’00011110’ MOVWF UEP1,A MOVLW 0x40 MOVWF 0x11,B MOVLW 01C0 MOVWF 0112.B MOVLW 0106 MOVWF 0113.8 MOVLW 8’10000000’ MOVWF 0110.8 MOVLW 0140 MOVWF 0115.8 MOVLW 0140 MOVWF 0x16,B MOVLW 0x06 MOVWF 0117.8 MOVLW 8’10000000’ MOVWF 0114.8 MOVLW B’00011110’ MOVWF UEP2,A CLRF newaddr,A CLRF UADDR,A BRA mainloop notreset: BTFSS UIR.SOFIF,A BRA notsof BCF UIR,SOFIF,A BCF UCON,PKTDIS,A BRA mainloop notsof: BTFSS UIR,TRNIF,A BRA nottran BSF PORTB,4,A CLRF ledonl,A MOVLW 0110 MOVWF ledonh,A MOVLW 0104 MOVWF FSROH.A MOVP USTAT,W.A BCF UIR,T8NIF,A ANDLW 8’01111100’ MOVWF FSROL,A MOVF INDFO.W,A Set up BD2ADRH to point to 01680 Set up BD2STAT for 64 bytes of buffer This descriptor is for EP1 OUT (DSPIC control) Set up BD3CNT for 64 bytes of buffer Set up BD3ADRL to point to 01600 Set up 8D3ADRH to point to 01600 Set up BDBSTAT for 64 bytes of buffer This descriptor is for EP1 IN (data stream) Set up endpoint one (no control transfers) Set up BD4CNT for 64 bytes of buffer Set up BD4ADRL to point to 016C0 Set up BD4ADRH to point to 0x6C0 Set up BD4STAT for 64 bytes of buffer This descriptor is for 8P2 OUT (DSPIC control) Set up BDSCNT for 64 bytes of buffer Set up BDSADRL to point to 01640 Set up BD5ADRH to point to 01640 Set up BDSSTAT for 64 bytes of buffer This descriptor is for 8P2 IN (DSPIC control) Set up endpoint one (no control transfers) Clear the buffered device address Clear the actual device address Test the USB start-of—frame interrupt flag Clear the USB interrupt flag Take the USB module out of packet disable Test the USB transaction interrupt flag Turn the LED on Set the counter to 4096 Select the INDFO to be USB buf. desc. RAM space Clear the interrupt flag after reading USTAT Find the offset address of the endpoint Check the BDnSTAT byte for the token bits 175 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 ANDLW 8’00111100’ ADDLW 8’11111100’ BNZ tran_notout MOVLW 0100 CPFSEO FSROL,A BRA copy_out BRA no_copy_out Was this an OUT token transaction? Is this endpoint 0 OUT? If not. FSROL should be 0x08 or 0110 ; Possibly endpoint 1 or 2 (interrupt OUT) copy-out: MOVF FSROL,W,A MOVWF savedl,A MOVF FSROH.W,A MOVWF savedh,A LFSR 1.010680 INCF FSROL.F.A MOVF PREINCO.W.A MOVWF FSR1L,A BCF TXSTA,TX9D,A MOVWF TXREG,A MOVLW 0140 MOVWF savedw,A BSF TXSTA.TX9D.A out_loop: BTFSS TXSTA,TRMT.A BRA out_loop MOVF POSTINC1,W.A MOVWF TXREG.A DECFSZ savedw,F,A BRA out_loop MOVF saved1.W,A MOVWF FSROL,A MOVF savedh,W.A MOVWF FSROH,A no_copy_out: MOVLB 0104 MOVLW 8’10000000’ MOVWF POSTINCO.A MOVLW 0x40 MOVWF INDFO,A BCF UCON,PKTDIS,A BRA mainloop tran_notout: ADDLW 8’11100000’ BNZ tran-notin Save the FSRO low byte for later Save the FSRO high byte for later Load FSR1 with the new data block address Access BD2ADRL/BD4ADRL after the pro-increment Recall the low address byte and store in FSRIL Now, this newly received data can be sent out A clear ninth bit means this is an address byte Write this start address to the transmit buffer Transfer 64 bytes to the DSPIC buffer Save the working register (byte count) A set ninth bit means this is a data byte Wait until the transmit buffer is empty Recall the USB-sent value in memory (0x0680...) Write this to the transmit buffer Decrement the loop counter Restore the FSRO low byte for BDnSTAT Restore the FSRO high byte for BDnSTAT Access bank 4 (USB RAM buffer descriptors) Set up BDnSTAT for 64 bytes of buffer This descriptor is for EPO/EPI/EP2 OUT Set up BDnCNT for 64 bytes of buffer Take the USB module out of packet disable Was this an IN token transaction? ; Possibly endpoint 1 or 2 (interrupt IN) MOVLW 0104 CPFSEQ FSROL,A BRA tran_in_toggle MOVF newaddr,W,A MOVWF UADDR,A Is this endpoint 0 IN? If not, FSROL should be 010C or 0114 Set the new address in the status stage (IN) 176 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 BCF UCON,PKTDIS,A MOVLB 0104 CLRF 0105.8 MOVLW 8’11000000’ MOVWF 0104.8 BRA mainloop tran_in_toggle: MOVLW 0140 MOVWF PREINCO.A MOVWF 0115.8 DECF FSROL,F,A MOVLW 8’01000000’ ANDWF INOF0,F.A BTG INDFO,6,A BSF INDFO.7,A BCF UCON,PKTDIS,A BRA mainloop tran_notin: MOVF PREINCO,W,A MOVLB 0105 MOVF 0x01,W,B BTFSC 0100.7,8 BRA tran_get ADDLW OxFB BNZ tran_notsetaddress MOVF 0102,W.B MOVWF newaddr,A MOVLB 0104 CLRF 0105.8 MOVLW 8’11000000’ MOVWF 0104.8 BRA tran_setupdone tran_notsetaddress: ADDLW OxFC BNZ tran_notsetconfig MOVLB 0104 CLRF 0105.8 MOVLW 8’11000000’ MOVWF 0104.8 BRA tran_setupdone tran_notsetconfig: BRA tran_setupdone tran_get: ADDLW OxFA BNZ tran_notgetdesc MOVF 0x03,W,B ADDLW OxFF BNZ tran_notgetdd MOVLW 0x10 MOVWF T8LPTRH,A BRA tran_get_copy Take the USB module out of packet disable Access bank 4 (USB RAM buffer descriptors) Write the zero length out to EPO IN’s 8.0. Set up BDOSTAT for sending data (just in case) This descriptor is for 8P0 IN Set up BDSCNT/BDSCNT for 64 bytes of buffer Write this to 0100 or 0115 (BD3CNT/805CNT) Write next to 0100 or 0114 (BD3STAT/805STAT) Clear out all bits but the data toggle bit Toggle the data toggle synchronization bit Make sure the USB peripheral can read the data Take the USB module out of packet disable This must have been a SETUP token transaction Check the BDnCNT byte for the packet size Read the meequestType and bRequest bytes Recall the bRequest byte into W Was it a SET_xxx request? If not, it was a GET_xxx request Is this a SET_ADDRESS request? This is the new device address Don’t set it quite yet Access bank 4 (USB RAM buffer descriptors) Write the zero length out to EPO IN’s 8.0. Set up BDOSTAT for sending data This descriptor is for EPO IN EPO IN should now be set up to send out data Is this a SET_CONFIGURATION request? Access bank 4 (USB 8AM buffer descriptors) Write the zero length out to EPO IN’s 8.0. Set up BDOSTAT for sending data This descriptor is for EPO IN EPO IN should now be set up to send out data Is this a GET_DESCRIPTOR request? Recall the wValue high byte into W (desc. type) Is this a GET_DEVICE_DESC8IPTOR request? Access the device descriptor in Flash 177 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 tran_notgetdd: ADDLW OxFF BNZ tran_notgetcd MOVLW 0111 MOVWF TBLPTRH,A BRA tran_get_copy tran_notgetcd: ADDLW OxFF BNZ tran_notgetsd MOVF 0102,W,B BNZ tran_sd_notO MOVLW 0112 MOVWF TBLPTRH,A BRA tran_get_copy tran_sd_noto: ADDLW OxFF BNZ tran_sd_not1 MOVLW 0113 MOVWF TBLPTRH,A BRA tran_get_copy tran_sd_not1: ADDLW OxFF BNZ tran_sd_not2 MOVLW 0114 MOVWF TBLPTRH,A BRA tran_get_copy tran_sd_not2: tran_notgetsd: tran_notgetdesc: tran_setupdone: MOVLW 0140 MOVWF POSTDECO,A MOVLW 8’10000000’ MOVWF INDFO.A BCF UCON,PKTDIS,A BRA mainloop tran_get_copy: MOVLW 0105 MOVWF FSR1H.A MOVLW 0140 MOVWF FSRlL.A CLRF TBLPTRU,A CLRF TBLPTRL,A TBLRDt+ MOVF TABLAT,W,A TBLRD*+ MOVWF savedw,A MOVF 0106.W.B CPFSGT savedw,A MOVF savedw,W.A MOVLB 0x04 MOVWF 0105.8 Is this a GET_CONFIGURATION_DESCRIPTOR request? Access the configuration descriptor in Flash Is this a GET_STRING_DESCRIPTOR request? Recall the wValue low byte into W (string num.) Is this s "Get String Descriptor 0" request? Access the string descriptor zero in Flash Is this a "Get String Descriptor 1" request? Access the string descriptor one in Flash Is this a ”Get String Descriptor 2" request? Access the string descriptor two in Flash Set up BDnCNT for 64 bytes of buffer Set up BDOSTAT for 64 bytes of buffer This descriptor is for EPO OUT Take the USB module out of packet disable Select INDFI to be EPO’s IN Space for outputting Access the descriptor in Flash Read the descriptor length byte Skip past the length high byte Don’t send more bytes than the request asked Access bank 4 (USB RAM buffer descriptors) Write the packet length out to EPO IN’s 8.0. 178 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 RCALL c0py_data MOVLW 8’11000000’ MOVWF 0104.8 BRA tran_setupdone nottran: noterror: BRA mainloop blink: BSF PORTB,4,A RCALL delay_50ms RCALL delay_50ms RCALL delay_50ms RCALL delay_50ms BCF PORTB,4,A RCALL delay_50ms RCALL delay_50ms RCALL delay_50ms RCALL delay_50ms BRA blink copy_data: MOVWF savedw,A cd_loop: TBLRDt+ MOVF TABLAT,W,A MOVWF POSTINC1,A DECFSZ savedw,F,A BRA cd_loop RETURN delay_100us: MOVLW 0’239’ MOVWF loopva10,A here_100us: NOP NOP DECFSZ lOOpva10,F,A BRA here_100us RETURN delay_5ms: MOVLW 0’50’ MOVWF loopva11,A here_5ms: RCALL delay_100us DECFSZ loopva11,F,A BRA here_5ms RETURN delay_50ms: MOVLW 0’250’ MOVWF loopva11,A here_SOms: RCALL delay_100us RCALL delay_100us Copy the length of bytes in W to INDF1 Set up BDISTAT for sending data This descriptor is for EPO IN EPO IN should now be set up to send out data Turn the LED on Turn the LED Off Copy the length of bytes in W to INDF1 Save the working register (byte count) Read the Flash byte value Recall the read value Write this to the output buffer Decrement the loop counter Our cycles are 0.0833us long (48MHz/4) These make it 5 cycles total 100us#50-5ms 200ust250-50ms 179 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 DECFSZ lOOpval1,F,A BRA here_50ms RETURN isr: BCF PIR1,RCIF.A ; Clear the received byte interrupt flag MOVF RCREG.W.A ; Recall the received byte BTFSS RCSTA,RX90,A ; Was the received byte a data byte? BRA setaddress ; If not, it was an FSR2L address MOVWF POSTINC2.A ; Store the byte at the indirect pointer address RETFIE 1 ; Restore the contents of W and STATUS setaddress: LFSR 2.0x0600 ; Load FSR2 with the data stream block address MOVWF FSR2L,A ; Store it in the indirect address low pointer isrdone: RETFIE 1 ; Restore the contents of W and STATUS 086 01001000 0W 0x0012 ; The device descriptor is 18 bytes long 0W 0x0112 ; The device descriptor length in bytes and number DW 0x0110 ; The USB specification number (version 1.1) 0W 0x0000 ; The device class/subclass (specified in interf. desc.) 0W 014000 ; The device protocol and the maximum packet size DW 010408 ; The vendor ID (from Microchip) 0W 0xDCBA ; The product I0 DW 010100 ; The device release number 0W 0x0201 ; The manufacturer/product string descriptor addresses DW 0x0100 ; The serial number string addr. and configuration count ORG 01001100 DW 010028 ; The configuration descriptor is 46 bytes as returned DW 0x0209 ; The config. descriptor length in bytes and number 0W 01002E ; The total length of bytes returned DW 010101 ; The number of interfaces and the configuration value 0W OxCOOO ; The config. string address and the config. attributes DW 010980 ; The maximum current (by 2mA) and interface desc. length DW 010004 ; The interface descriptor number and which one this is DW 010400 ; The alternate setting number and endpoint count DW OxOOFF ; The interface class (vendor-specific) and subclass DW 010000 ; The interface protocol and string descriptor address 0W 010507 ; The IN interrupt #1 descriptor length and number 0W 0x0381 ; The endpoint number/dir. and transfer type 0W 010040 ; The maximum packet size MSB/LSB DW 010701 ; The polling interval and OUT interrupt #1 desc. length DW 010105 ; The OUT int. #1 desc. number and endpoint number/dir. DW 014003 ; The transfer type and maximum packet size LSB 0W 010100 ; The maximum packet size MSB and polling interval (ms) 0W 0x0507 ; The IN interrupt #2 descriptor length and number DW 0x0382 ; The endpoint number/dir. and transfer type DW 0x0040 ; The maximum packet size MSB/LSB DW 010701 ; The polling interval and OUT interrupt #2 desc. length 0W 010205 ; The OUT int. #2 desc. number and endpoint number/dir. 0W 014003 ; The transfer type and maximum packet size LSB DW 010100 ; The maximum packet size M88 and polling interval (ms) 080 01001200 0W 010004 ; The string descriptor zero is four bytes long DW 010304 ; The string descriptor length in bytes and number DW 010409 ; The United States English language code 180 493 ORG 01001300 494 DU 010030 ; The string descriptor zero is 48 bytes long 495 ON 010330 ; The string descriptor number and the length in bytes 496 ON ’C’,’\1’,’s’,’t’,’<>’,’m’,’ ’,’A’,’11’,’t’,’o’,’ ’ 497 DH ’E’,’l’,’e’,’c:’,’t’,’r’,’o’,’n’,’i’,’c’,’s’ 498 499 ORG 01001400 500 DH 01002E ; The string descriptor zero is 46 bytes long 501 DH 01032E ; The string descriptor number and the length in bytes 502 DH ’E’ ,’V’ ,’ ’ ,’M’ ,’u’ ,’1’ ,’t’ ,’i’ ,’1’ ,’e’ ,’v’ ,’e’ ,’1’ ,’ ’ 503 DH ’I’,’n’,’v’,’e’,’r’,’t’,’e’,’r’ 504 505 ORG 01300000 ; Point to the configuration word 506 DU 010621 ; 8MHz oscillator, use high-speed USB and 48MHz CPU 507 ON 010038 508 ON 010300 509 ON 010081 510 511 END 181 E.3 DSPIC3OF4011 Full Assembly Code, Design Two tar—- Hocoooqoawawtop Awwwwwwwwwwwwwwwwwwwwwwp—w—usp-u-u-A OCDG:«163014:-WMHOQWNOSCHAOJMHOCDCDNOSUAOOM 41 42 43 44 45 46 47 48 49 5O 51 52 53 54 55 56 57 58 O D e O I I O D e I e I I 9 e I Multilevel Converter Master Code By Arthur Matteson, Pin assignments: Port R80 R81 R82 R83 R84 R85 R86 R87 R88 Port 8: (2): (3): (4): (5): (6): (7): (8): (9): (10): C: 3/23/2008 Analog plug voltage sense input (ANO function) Analog throttle sense input (AN1 function) Analog regen sense input (AN2 function) 08? channel index input, 5-12V logic high (INDX function) QEP channel A OEP channel 8 input, 5-12V logic high (OEA function) input, 5-12V logic high (088 function) Analog phase U current input, 2.5V centered (AN6 function) Analog phase V current input, 2.5V centered (AN7 function) Analog phase U current input, 2.5V centered (AN8 function) RC13 (15): 1MBd module RC14 (16): 1MBd module RC15 (14): Port RDO RD1 RD2 RD3 Port R80 R81 R82 R83 R84 888 Port RFO RFI RF4 RF5 D: (23): (18): (22): (19): E: (38): (37): (36): (35): (34): (17): F: (30): (29): (28): (27): Non-inverted Non-inverted Non-inverted Non-inverted data stream output (UiATX function) data stream input (UIARX function) Module bus pover PTN780008 inhibit line (inhibit low) "Run" signal input, 5-12V logic high "Accessory" signal input, 5-12V logic high "Ignition" signal input, 5-12V logic high l"Reverse" signal input, 5-12V logic high 4000 pulses/mile vehicle speed sensor output, to transistor Non-inverted Non-inverted Non-inverted Non-inverted Non-inverted series connection relay output, to reed relay plug connection relay output, to reed relay 240VAC connection relay output, to reed relay 120VAC connection relay output, to reed relay LED output CAN receive input CAN transmit output 2MBd intra-circuit data stream input (U2RX function) 2MBd intra-circuit data stream output (U2TX function) ; Register definitions RCOUNT,010036 CORCON,010044 INTCON1,010080 INTCON2,010082 IFSi,010086 IEC1,010088 TMR1,010100 PR1,010102 T1C0N,010104 THR2,010106 THR38LD,010108 TMR3,01010A PR2,01010C PR3,010108 T2C0N,010110 T300N,010112 QEICON,010122 DFLTCON,010124 .EQU .EQU .EQU .EQU .EQU .EQU .EQU .EQU .EQU .EQU .EQU .EQU .EQU .EQU .EQU .EQU .EQU .EQU 182 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 .800 .800 .800 .800 .EQU .EQU .EQU .EQU .EQU .EQU .800 .800 .800 .EQU .EQU .EOU .EQU .EQU .EQU .800 .800 .800 .EQU .EQU .EQU .EQU .EQU .EQU .EQU .EQU .EQU .EQU .EQU .800 .800 .800 .800 .EQU .EQU .800 .800 .EQU .EQU .EQU .EQU .EQU .EQU .800 .800 .800 .EQU .800 .800 POSCNT,010126 HAXCNT,010128 0C1RS,010180 OC1CON,010184 0C2RS,010186 0C2CON,01018A 0C3RS,01018C 0C3CON,010190 0C4RS,010192 0C4CON,010196 PTCON,0101C0 PTHR,0101C2 PTPER,010104 PWHCON1,0101C8 PWHCON2,0101CA PDC1,0101D6 PDC2,0101D8 PDC3,0101DA UIHODE,01020C UISTA,010208 U1TXREG,010210 U1RXREG,010212 UIBRG,010214 U2MODE,010216 U2STA,010218 U2TXREG,01021A U2RXREG,01021C U2BRG,0102IE ADCBUF0,010280 ADCBUF1,010282 ADCBUF2,010284 ADCBUF3,010286 ADCON1,0102A0 ADCON2,0102A2 ADCON3,0102A4 ADCHS,0102A6 ADPCFG,0102A8 TRISB,010206 PORTB,0102C8 TRISC,0102CC PORTC,0102CE TRISD,0102D2 PORTD,0102D4 TRISE,0102D8 PORTE,0102DA TRISF,0102DE PORTF,0102EO RCON,010740 OSCCON,010742 NVMCON,010760 NVMADR,010762 NVMADRU,010764 NVMKEY,010766 Bit definitions .800 .800 .EQU .800 .800 .EQU .EQU 0A,01000F 08.010008 SA,01000D SB,01000C 0A8,010008 SAB,01000A DA,010009 183 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 .EQU .EQU .EQU .EQU .EQU .EQU .EQU .EQU .EQU .EQU .EQU .EQU .EQU .800 .800 .EQU .EQU .EQU .EQU .EQU .EQU .EOU .EQU .EQU DC,010008 IPL2,010007 IPL1,010006 IPL0,010005 RA,010004 N,010003 0V,010002 2.010001 C,010000 TON,010008 SHDTEN,010005 UARTEN,01000F UTXEN,01000A UTRHT,010008 URXDA,010000 TCKPS1,010005 TCKPS0,010004 T32,010003 U2RXIE,010008 U2RXIF,010008 SAHP,010001 DONE,010000 IF,010000 UR,01000F Variable definitions .800 .800 .EQU .EQU .EQU .800 .800 .EQU .EQU .EQU .EQU .EQU .EQU .EQU .EQU .EQU .EQU .800 .800 .800 .EQU .EQU .EQU .EQU .EQU .EQU .EQU .EQU .800 .800 .800 .800 .EQU .EQU .EQU .EQU proginfo,010820 proglast,010838 progdone,01083F oldsoc,010820 ; batvolts,010910 ; batamph,010A00 ; batsoc,OxOAFO ; charges,010CDO ; endtables,010880 pc_loop,010880 pc_idkp,010882 pc_idka,010884 timerold1,010886 timeroldh,010888 nextadjust,01088A ang1e321,010E8C angle32h,010888 curcentu,0108C0 curcentv,0108C2 curcentv,010804 curalpha,0108C6 curbeta,OxOEC8 ampl321,0108CA amp132h,0108CC curd32l,010808 curd32h,0108D0 curq321,0xOED2 curq32h,0108D4 pfavg321,0108D6 pfavg32h,OxOED8 angleold,0108DA vmin32l,0108DC vmin32h,0108DE vma1321,010880 vma132h,010882 curu,010884 Each Each Each Each Each module module module module module has has has has has 184 its its its its its own OVD 0911 ovn own word; word; word; long; long; previous SOC (0-100Z) voltage (unsigned mV) capacity (unsigned mAh/4) percent SOC (0-1001) charge (unsng. mAht16384) 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 .800 .EQU .EQU .EQU .EQU .EQU .EQU .EQU .EQU .EQU .EQU .EQU .EQU .EQU .EQU .EQU .EQU .EQU .EQU .EQU .EQU .EQU .EQU .EQU .EQU .EQU .EQU .EQU .EDU .EQU .EQU .EQU .EQU .EQU .EQU .EQU .EQU .EQU .EQU .EQU .EQU .EQU .EQU .EQU .EQU .EQU .EQU 010F00-010F3F, OxOF40-OxOF7F, 010F80-010FBF, OxOFCO-OxOFFF, curv,010886 curw,010888 curd,01088A curq,01088C frequency,OxOEEE pwrfact,OxOEFO temp,0108F2 currms,OxOEF4 linevolts,0108F6 throttle,0108F8 regen,0108FA linecen,0108FC batupdate,OxOEFE atmeldata,010840 atmeldatalastbyte,010FSF carstat,010F00 thrbyte,010801 regbyte,010F02 freqbyte,OxOF03 amplitude,OxOFO4 amplmin,010F05 amplmax,OxOF06 curubyte,010F07 curvbyte,010F08 curwbyte,OxOF09 curdbyte,010FOA curqbyte,010?08 rmsbyte,010FOC pfbyte,010FOD angbyte,010F08 linebyte,OxOFOF timebyte,010F10 lineneg,OxOF11 onlist,010F22 atmelrx,OxOF40 progstat,010F41 dataout,010850 atme1t11,010F80 atmeltx2,010F82 atmelt13,010F84 atmeltx4,010F86 addressl,010F88 addressh,01088A progdata,010F8C whosecar,OxOF88 progcmd,010F8F flashbuf,OxOFAO Line voltage A temporary byte used while charging Encoding: 00: Off, 01: Negative, 10: Positive, 11: This is the reply from an Atmel if OxOF41>-0180 This byte indicates whether or not programming is done A returned RAH, EEPROM, or flash data set (<-48 bytes) When >018000, this is the first 9-bit word to send out When >018000, this is the second 9-bit word to send out When >018000, this is the third 9-bit word to send out When >018000, this is the fourth 9-bit word to send out The DSPIC programming address (RAH, EEPROM, or flash) PWM The DSPIC programming data word (RAM or EEPROM) >-0180, USB control of vehicle; contactors in this byte The DSPIC memory programming command (written last) This 96-byte buffer is to program DSPIC flash endpoint 0181, endpoint 0182, endpoint 0101, endpoint 0102, ; Constant definitions .EQU .EQU .EQU .EQU .EQU .EQU .EQU .EQU MINRESET,4215 MINTHR,010080 ; MAXTHR,010340 ; HINFREQ,24 3 HAXFREQ,200 ; FREQSCALE,4000 ; HINHINV,6 ; MINMAXV,24 ; is output data stream (motor values) is output control (verify DSPIC flash) is input control (program DSPIC flash) is input control (program DSPIC flash) Throttle values <- this are considered zero Throttle values > this are considered W.O.T. The minimum value of The maximum value of HAXFREQ/(HAXTHR-MINTHR)#65536 The lowest lower limit of line-neutral amplitude The lowest upper limit of line-neutral amplitude (Hzt2.097152) (Hz‘2.097152) ’frequency’ ’frequency’ 185 245 .EQU MAXMINV,36 ; The highest lower limit of line-neutral amplitude 246 .EOU MAXMAXV,62 ; The highest upper limit of line-neutral amplitude 247 .EQU MINVPERHZ,240 ; The line-neutral peak batteries per ’frequency’ (12 3V) 248 .800 MAXVPERHZ,2000 ; The line-neutral peak batteries per ’frequency’ (12.3V) 249 .EQU IDOPTIMAL,120 ; The optimal D current in ampst4 (30A) 250 .EOU LOOP,50 ; How many 2.048ms increments to close the loop after 251 .EQU IDKP,100 ; The preportional constant for ID regulation 252 .EQU IDKA,4 ; The integral constant for ID regulation 253 .EQU IQKP,21846 ; The proportional constant for ID regulation 254 255 S:33333;;333333;;iHHHHHHHHHSS33H”;3333333333333HBHHHS#3333333333; 256 3BHEHBEFHHHHS;3:33:333:333:3333333333333;HUSHSSHSHSZHSHHSHSSHFH 257 258 .SECTION .text 259 .WORD 010100 ; Master firmware version 1.00 260 .GLOBAL __reset 261 __reset: 262 BCLR.W RCON,#SWDT8N ; Disable the watchdog timer 263 264 ; Initialize the transmit pin early 265 MOV.W #010028,W0 266 MOV.W WO,PORTF 267 268 ; Set up the data direction registers 269 MOV.W #OxOIFF,W0 270 MOV.W W0,TRISB 271 MOV.W #014000,W0 272 MOV.W W0,TRISC 273 MOV.W #01000F,W0 274 MOV.W W0,TRISD 275 MOV.W #010000,W0 276 MOV.W W0,TRISE 277 MOV.W #010011,W0 278 MOV.W W0,TRISF 279 280 ; Initialize the port values 281 MOV.W #010000,W0 282 MOV.W WO,PORTB 283 MOV.W #012000,W0 284 MOV.W WO,PORTC 285 MOV.W #010000,W0 286 MOV.W WO,PORTD 287 MOV.W #010000,W0 288 MOV.W WO,PORTE 289 MOV.W #010020,W0 290 MOV.W WO,PORTF 291 292 ; Set up serial port number two at 2MBd 293 MOV.W #010007,W0 294 MOV.W W0,U2MODE 295 MOV.W #010000,W0 296 MOV.W W0,U28RG 297 BSET.W U2MODE,#UARTEN 298 BSET.W U2STA,#UTXEN 299 300 ; Disable the two QEP-shared A/D inputs on PORTB, so they can be read digital 301 MOV.W #010038,W0 302 MOV.W W0,ADPCFG 303 304 ; Set up the quadrature encoder interface 305 MOV.W #010600,W0 306 MOV.W W0,QEICON 186 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 CLR.W POSCNT ; Set up the rest of the A/D converter for ANO-AN2/AN6-AN8 (TAD-183.3ns) HOV.“ #011FOA,W0 MOV.W U0,ADCON3 MOV.W #0180E0,W0 MOV.W HO,ADCON1 ; Set up Timer2 and Timer3 as a 32-bit timer SETM.W PR2 SETM.W PR3 BSET.W T2CON,#T32 ; Set up the timers together as a 32-bit pair BSET.W T2CON,#TON ; Start the timers counting with no period limitation ; Initialize the A/D center values (three phase currents and a line voltage) RCALL init_centers MOV.W #batsoc,WO ; Clear out the percent SOC array REPEAT #239 CLR.W [W0++] ; Each module has its own long; percent SOC (0-100X) ; Delay and turn on the battery modules’ power BSET.W PORTD,#2 DO #999,endstart1 REPEAT #16383 ; Delay about a half second NOP endstartl: NOP BCLR.W PORTD,#2 BSET.W TRISC,#15 ; Turn on the module power BSET.W PORTD,#2 DO #1999,endstart2 REPEAT #16383 ; Delay about a second NOP endstart2: NOP BCLR.W PORTD,#2 ; Set up serial port number one at 1MBd MOV.W #010407,W0 MOV.W W0,U1MODE MOV.W #010001,W0 MOV.W W0,U18RG BSET.W UlMODE,#UARTEN BSET.W UISTA,#UTXEN ; Clear out the hardware UART input receive buffers MOV.W U1RXREG,W0 MOV.W U1RXREG,W0 MOV.W U1RXREG,W0 MOV.W UIRXREG,WO MOV.W U2RXREG,W0 MOV.W U2RXREG,W0 MOV.W U2RXREG,H0 MOV.W U2RXREG,W0 ; Load the battery capacities from EEPROM into RAM MOV.W #01007F,W4 ; Load TBLPAG with 010078, the EEPROM location MOV.W W4,TBLPAG MOV.W #01FC30,W4 ; Load the beginning EEPROM address into W1 MOV.W #batvolts,W5 ; Load the voltage table address into W5 MOV.W #batamph,W6 ; Load the capacity table address into W6 187 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 CLR.W W1 ; Start verifying at address one (see INC.W below) DO #119,endloadcap ; Copy 120 words of capacity data from EEPROM to RAM INC.W W1,W1 ; Increment the module address CLR.W [W5++] ; Clear the voltage table simultaneously TBLRDL.W [W4++],W0 CPO.W W0 ; Is the capacity zero? If so, don’t verify comm. BRA Z,endloadcap RCALL check_comm ; Perform a check communications test on this module CP.W W2,#1 ; Was it successful? BRA Z,endloadcap CLR.W W0 ; If the module wasn’t found, don’t use it endloadcap: MOV.W W0,[W6++] ; Store the capacity in RAM (batamph, mAh/4) MOV.W #010178,W1 ; Send a wake (and turn off) command to all modules MOV.W W1,UlTXREG Clear variables (the rest are done below under active:) CLR.W throttle CLR.W regen MOV.W #LOOP,W0 MOV.W W0,pc_loop MOV.W #IDKP,W0 MOV.W W0,pc_idkp MOV.W #IDKA,WO MOV.W W0,pc_idka CLR.8 carstat CLR.B whosecar CLR.W TMR2 ; Clear the timer initially CLR.W TMR3 CLR.W timeroldl CLR.W timeroldh MOV.W pc_loop,WO MOV.W W0,nextadjust ; Also calculate the next time to close the loop CLR.W temp CLR.W batupdate CLR.W angle32l CLR.W angle32h CLR.B lineneg Clear the entire 128-byte output buffer and output this to the PIC1882553 MOV.W #carstat,W2 ; Send the entire section data to the PI018F2553 AND.W #0100FF,W2 MOV.W W2,U2TXREG REPEAT #190 NOP MOV.W #010100,W1 MOV.W #carstat,W2 DO #127,.+14 ; Send 128 data byte commands to the PIC18F2553 CLR.B [W2++) MOV.W W1,U2TXREG REPEAT #190 NOP NOP Perform other initialization and enable the UART2 receive interrupt MOV.W #carstat,W14 ; This is the current writing address for U2RXREG bytes CLR.B progcmd ; Clear the command so nothing happens accidentally CLR.B progstat ; Clear the programming read status byte BCLR.W IFS1,#U2RXIF ; Clear the U2RX interrupt flag in IFSI BSET.W IEC1,#U2RXIF ; Enable the U2RX interrupt in IEC1 188 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 Read the battery voltages of those which are present, CLR.W W12 readvoltsloop: RCALL voltcheck INC.W W12,W12 BRA NZ,readvoltsloop BSET.W PORTE,#8 mainlOOp: MOV.W POSCNT,W0 MOV.W #54613,W1 MUL.UU W0,W1,W2 BTSC.W W3,#9 BRA .+6 BCLR.W PORTE,#0 BRA .+4 BSET.W PORTE,#0 RCALL handle_USB MOV.W #010000,W1 MOV.W W1,ADCHS BSET.W ADCON1,#SAMP BTSS.W ADCON1,#DON8 BRA .-2 BCLR.W ADCON1,#DONE CLR.W W0 MOV.W linevolts,WO MUL.SU W0,#3,W2 MOV.W ADCBUFO,W0 SUBR.W linecen,WREG ADD.W W0,W2,W0 ADD.W #2,W0 ASR.W W0,#2,W0 MOV.W W0,linevolts ASR.W W0,#2,W0 MOV.B WREG,11nebyte MOV.W linevolts,W0 ADD.W #40,W0 BRA NN,.+4 BSET.B lineneg,#0 MOV.W linevolts,W0 SUB.W #40,WO BRA N,.+4 BCLR.B lineneg,#0 MOV.B carstat,WREG AND.B #010F,W0 BTSC.W PORTD,#O BSET.W WO,#4 BTSC.W PORTD,#l BSET.W WO,#S BTSC.W PORTD,#2 BSET.W WO,#G BTSC.W PORTD,#S BSET.W WO,#7 MOV.B WREG,carstat and update the SOC X Turn the LED on eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee I’DIBIIDBDIDDIDODBIPDPDDI’IDOPPOBPIDII’DIPDIDPDPPDDDDI'! Recall This is a scale factor value for the vehicle speed sensor the OEI counter W3:W2=W0*W1 Copy the scaled output Handle Select bit to the vehicle speed sensor USB programming commands channel ANO for A/D conversion (line voltage) Start an A/D conversion Keep lOOping until it completes Recall Weight Weight Center the the the the running average previous value by three current value by one result with the zero volts result Add one-half before dividing by four Store the result (integer format) Get rid of the lower two bits of the new result Store the result (integer format) Is the If so, Is the If so, Recall Is the Is the Is the Is the line voltage reasonably negative? set the line negative flag temporarily line voltage reasonably positive? clear the line negative flag temporarily the status byte low nibble "Run" set W0<4> input high? If so, I'Accessory" input high? If so, set W0<5> "Ignition" input high? If so, set W0<6> "Reverse" input high? If so, set W0<7> 189 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 MOV.W #010001,W1 MOV.W W1,ADCHS BSET.W ADCON1,#SAMP ; BTSS.W ADCON1,#DONE BRA .-2 BCLR.W ADCON1,#DONE CLR.W W0 MOV.W throttle,WO MUL.UU W0.#7,W2 MOV.W ADCBUFO,W0 ADD.W W0,W2,W0 ADD.W #4,W0 LSR.W W0,#3,W0 MOV.W W0,throttle LSR.W W0,#2,W0 MOV.B WREG,thrbyte MOV.W #carstat,W2 AND.W #0100FF,W2 MOV.W W2,U2TXREG REPEAT #190 NOP MOV.B carstat,WREG BSET.W WO,#8 MOV.W W0,U2TXREG REPEAT #190 NOP MOV.B thrbyte,WREG BSET.W WO,#8 MOV.W W0,U2TXREG REPEAT #190 NOP Select channel AN1 for A/D conversion (throttle) Start an A/D conversion Keep looping until it completes Recall the running average Weight the previous value by seven Weight the current value by one Add one-half before dividing by eight Store the result (integer format) Get rid of the lower two bits of the new result Store the result (integer format) Send the carstat address command to the PIC18F2553 Send the carstat data byte command to the PIC18F2553 Send the throttle data byte command to the PIC18F2553 ; Send off the line voltage and current time to the PIC1882553, even for car off MOV.W #1inebyte,W2 AND.W #0100FF,W2 MOV.W W2,U2TXREG REPEAT #190 NOP MOV.B linebyte,WREG BSET.W WO,#8 MOV.W WO,U2TXREG REPEAT #190 NOP MOV.W TMR3,W0 MOV.B WREG,timebyt BSET.W WO,#8 MOV.W W0.U2TXREG REPEAT #190 NOP Send the line voltage address command to the PI018F2553 Send the line voltage data byte to the PIC18F2553 Recall the current timer high word Send the time data byte to the PIC18F2553 eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee DD’IDDDI’ISIBDIIDDDIPPPI’ ; BRA chargemain ; Test the ignition ; CPO.B disable ; BRA NZ,.+6 BTST.W PORTD,#O BRA NZ,active for a IDOODDPPPPOPO’DIDIDDD’DD’I0’99’I39DDP’PII’i!”l’!’!l”’ "Run" signal Is USB commanding the system be in diagnostic mode? If so, don’t active the inverter Is the "Run" signal asserted? 19C) 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 ; Periodically query the battery voltages in sequence when the car is off ; RCALL voltcheck ; Since the ”Run“ signal was off, execute an off sequence BTST.8 carstat,#0 ; Was the inverter recently on? BRA Z,mainloop ; If it’s been off, don’t bother to sleep the modules MOV.W #010001,W0 ; Set the baud rate to 1MBd MOV.W W0,U1BRG MOV.W #010178,W1 ; Send a wake (and turn off) command to all modules MOV.W W1,U1TXREG BCLR.B carstat,#0 ; Set carstat<0> to zero BCLR.W PORTE,#8 ; Turn the LED off ; Clear almost the entire output buffer and output this to the PIC18F2553 MOV.W #freqbyte,W1 REPEAT #12 CLR.B [W1++] MOV.W #onlist,W1 REPEAT #14 CLR.W [W1++] MOV.W #carstat,W2 ; Send the entire section data to the PIC18F2553 AND.W #OxOOFF,W2 MOV.W W2,U2TXREG REPEAT #190 NOP MOV.W #carstat,W2 DO #63,.+14 ; Send 64 data byte commands to the PIC18F2553 MOV.B [W2++],W1 BSET.W W1,#8 MOV.W W1,U2TXREG REPEAT #190 NOP NOP BRA mainloop ; Keep sampling the throttle to see if it comes back on 9’D’DIBDII’D!!!)I'8’PIP’Dl’llPP)!'IDPPDIDDDODDDPPDODODPP’I’DIPDOIDDDDfill’lDDOD'i active: ; Since the "Run" signal was on, execute an on sequence BTST.B carstat,#0 ; Was the inverter recently on? BRA NZ,act_wason ; If it’s been on, don’t bother to wake the modules BTST.W PORTD,#2 ; Is the “Ignition" signal asserted? BRA Z,mainloop ; If not, keep waiting MOV.W #010001,W0 ; Set the baud rate to 1MBd MOV.W W0,U1BRG MOV.W #01017C,W1 ; Send a wake (and turn on) command to all modules MOV.W W1,UITXREG REPEAT #383 ; Wait for this command to execute NOP MOV.W #MINFREQ,W0 ; Initialize many variables MOV.W W0,frequency CLR.W angle32l CLR.W angle32h 191 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 CLR.W curu CLR.W curv CLR.W curw CLR.W curalpha CLR.W curbeta CLR.W curd CLR.W curq MOV.W #MINMINV,WO SL.W W0,#11,W0 MOV.W W0,ampl32l CLR.W amp132h CLR.W curd321 CLR.W curd32h CLR.W curq32l CLR.W curq32h CLR.W pwrfact CLR.W pfavg321 MOV.W #700,W0 MOV.W W0,pfavg32h CLR.W currms MOV.W TMR2,W2 MOV.W TMR3HLD,W3 MOV.W W2,timeroldl MOV.W W3,timeroldh MOV.W pc_loop,W2 ADD.W W1,W3,W1 MOV.W W1,ne1tadjust BSET.B carstat,#0 BSET.W PORTE,#8 MOV.W #carstat,W2 AND.W #0100FF,W2 MOV.W W2,U2TXREG REPEAT #190 NOP MOV.W #carstat,W2 DO #63,.+14 MOV.B [W2++].W1 BSET.W W1,#8 MOV.W W1,U2TXREG REPEAT #190 NOP NOP act_wason: Set carstat<0> to one Turn the LED on Send the entire section data to the PIC18F2553 Send 64 data byte commands to the PIC18F2553 eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee B’I””l"l|l))llI’I!!lllilil!!’D9’1'!8”)!I’IDIB’PDDDIPDIIIIPIII’I’DIPIID’I!I)! Interpret the sampled throttle and generate a frequency command CLR.W W0 MOV.W throttle,WO SUB.W #MINTHR,W0 BRA NN,.+4 CLR.W W0 MOV.W #FREQSCALE,W2 MUL.UU W0,W2,W2 MOV.W W3,W1 MOV.W W1,frequency MOV.W TMR2,W13 MOV.W #10923,W1 MOV.W #18500,W1 ADD.W W1,W13,W13 Recall the throttle command (0-1023) Subtract off the minimum throttle value Skip the next instruction on a non-zero throttle Saturate to zero if less than zero Load in the constant to multiply by, W3:W2-W0*W2 (W3 is the frequency) Move this result to W1 to conserve register space Save the frequency (Hzt2.097152) to find frequency Remember the beginning timer low word 192 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 RCALL find_minmax RCALL do_timer RCALL output-PWM RCALL convert_curs RCALL clarke RCALL park ; RCALL find_rms RCALL find_pf RCALL motor_loop RCALL send-vars MOV.W W13,W0 CP.W TMR2 BRA N,.-2 BRA mainloop Find the minimum and maximum voltages for motoring Find the accumulated time Generate module commands with PWM for motoring Convert the current A/D channels Perform a Clarke transform of the currents Perform a Park transform of the currents Find the RMS current Find the instantaneous power factor and average it Close the loop for motoring Transfer data to the PIC18F2553 Compare the low word of the 32-bit timer with W0 If TMR2 hasn’t passed W0 yet, keep waiting eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee chargemain: MOV.W #126,W0 MOV.W W0,frequency MOV.W #40960,W0 MOV.W W0,amp1321 MOV.W #6,W0 MOV.W W0,amp132h CPO.B lineneg BRA Z,charge_notneg CPO.W linevolts BRA NZ,aharge_notneg CLR.B lineneg CLR.W angle32l MOV.W #145,W0 MOV.W W0,angle32h charge_notneg: RCALL do_timer RCALL output_charge RCALL send_vars BRA mainlOOp DID!POI,Iti’DODIDIDI’P)D'DI’DDBDID’DDIIDD’DDI’Dl’DDIDPD Set the frequency to as close to 6082 as possible Set the fractional battery amplitude to five-eighths Set the integral battery amplitude to six Is the negative line input voltage flag set? Is the line voltage crossing zero just now? If so, clear the flag and reset the angles 145 is good (160 gives no current flow) Find the accumulated time Generate module commands with no PWM for charging Transfer data to the PIC18F2553 eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee init_centers: MOV.W #010006,W1 MOV.W W1,ADCHS CLR.W W2 DO #15,avg_curu BSET.W ADCON1,#SAMP NOP NOP NOP NOP BTSS.W ADCON1,#DONE BRA .-2 BCLR.W ADCON1,#DONE MOV.W ADCBUFO,W1 SL.W W1,#2,W1 INC2.W W1,W1 avg_curu: PlD’I!D,IIIDO’9.P'l'ii’9’DI9D,),’l’I’D'l’i'ii’IDBPDDPPP Initialize A/D center values (currents and voltage) Select channel ANG for A/D conversion Clear the total at the beginning Sample the U current sensor 16 times and average Start an A/D conversion Keep lOOping until it completes Multiply the ten-bit integer result by four Add one-half of four because of the shift 193 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 ADD.W W1,W2,W2 LSR.W W2,#4,W2 MOV.W W2,curcentu MOV.W #010007,W1 MOV.W W1,ADCHS CLR.W W2 DO #15,avg_curv BSET.W ADCON1,#SAMP NOP NOP NOP NOP BTSS.W ADCON1,#DONE BRA .-2 BCLR.W ADCON1,#DONE MOV.W ADCBUFO,W1 SL.W W1,#2,W1 INC2.W W1,W1 avg_curv: ADD.W W1,W2,W2 LSR.W W2,#4,W2 MOV.W W2,curcentv MOV.W #010008,W1 MOV.W W1,ADCHS CLR.W W2 D0 #15,avg_curv BSET.W ADCON1,#SAMP NOP NOP NOP NOP BTSS.W ADCON1,#DONE BRA .-2 BCLR.W ADCON1,#DONE MOV.W ADCBUFO,W1 SL.W W1,#2,W1 INC2.W W1,W1 avg_curw: ADD.W W1,W2,W2 LSR.W W2,#4,W2 MOV.W W2,curcentv MOV.W #010000,W1 MOV.W W1,ADCHS CLR.W W2 DO #15,avg_line BSET.W ADCON1,#SAMP NOP NOP NOP NOP BTSS.W ADCON1,#DONE BRA .-2 BCLR.W ADCON1,#DONE MOV.W ADCBUFO,W1 avg_line: ADD.W W1,W2,W2 LSR.W W2,#4,W2 MOV.W W2,linecen Add the result to the total and maybe sample again Divide the total by 16 Store the result in countstfour (integer format) Select channel AN7 for A/D conversion Clear the total at the beginning Sample the V current sensor 16 times and average Start an A/D conversion Keep looping until it completes Multiply the ten-bit integer result by four Add one-half of four because of the shift Add the result to the total and maybe sample again Divide the total by 16 Store the result in countstfour (integer format) Select channel AN8 for A/D conversion Clear the total at the beginning Sample the W current sensor 16 times and average Start an A/D conversion Keep looping until it completes Multiply the ten-bit integer result by four Add one-half of four because of the shift Add the result to the total and maybe sample again Divide the total by 16 Store the result in countstfour (integer format) Select channel ANO for A/D conversion (line voltage) Clear the total at the beginning Sample the line voltage sensor 16 times and average Start an A/D conversion Keep looping until it completes Add the result to the total and maybe sample again Divide the total by 16 Store the result in counts (integer format) 194 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 RETURN eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee lI’IIDDI’I’D.IllDD),D)DI’9’)!I!DDII,9’DPIIIDIPDIIIDII’IDIDIDDDPDPIDPDOI'I'II’D’l check_comm: MOV.W U1RXREG,W2 MOV.W U1RXREG,W2 MOV.W U1RXREG,W2 MOV.W U1RXREG,W2 CLR.W W3 cc_loop: MOV.W W1,U1TXREG REPEAT #400 NOP MOV.W #010000,W2 MOV.W W2,U1TXREG REPEAT #400 NOP MOV.W W3,U1TXREG BTSC.W UISTA,#URXDA BRA .+10 INC.W W2,W2 BTSS.W W2,#12 BRA .-8 RETLW.W #2,W2 MOV.W U1RXREG,W2 BCLR.W W2,#8 XOR.W W1,W2,W2 CP.B W2,W3 BRA NZ,.+10 INC.W W3,W3 BTSS.W W3,#8 BRA cc_loop RETLW.W #1,W2 RETLW.W #3,W2 Module address in W1 (uncorrupted), return value in W2 Clear out the receive buffer first, to prevent errors Reset the loop counter (value to be echoed) Address the module in W1 Next, send out a zero (echo next byte command) (The timeout counter is simultaneously cleared above) Finally, send out the loop counter value Is there at least one available word? If not, keep waiting; increment the timeout counter If the timer overflowed to 4096, exit with W2 as one If the timer has not overflown, keep waiting Return with an unsuccessful (two) value in W2 Grab the received byte Ignore the ninth bit Undo the applied XOR Is the value the same as the loop counter? Increment the loop counter Has the counter hit 010100? If not, keep looping with the new counter value Return with a successful (one) value in W2 Return with an unsuccessful (three) value in W2 eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee ’I’P’II’IIIBIIDDPPPPP” find_minmax: MOV.W frequency,W1 MOV.W #MINVPERHZ,W2 MUL.UU W1,W2,W2 MOV.W #MINMINV,W6 ASR.W W6,#5,W7 SL.W W6,#11,W6 SUB.W W2,W6,W8 SUBB.W W3,W7,W9 BRA NN,.+4 MOV.D W6,W2 MOV.W #MAXMINV,W6 ASR.W W6,#5,W7 SL.W W6,#11,W6 SUB.W W6,W2,W8 SUBB.W W7,W3,W9 BRA NN,.+4 MOV.D W6,W2 MOV.W W2,vmin32l MOV.W W3,vmin32h MOV.W #MAXVPERHZ,W2 MUL.UU W1,W2,W2 MOV.W #MINMAXV,W6 ASR.W W6,#5,W7 PI9"!I,I)!DDI'D)!lflfll’liiii’DDDIBIDIDDDD’DIIODPIII’D,’ Find the minimum and maximum voltages for motoring Load in the frequency calculated by the PID controller Load in the constant to multiply by, to find amplitude W3:W2-W1*W2 (W3:W2 is the minimum amplitude by slope) Compare the lower limit to the lowest legal value Divide by 32 (create a high word limit) Multiply by 2048 (create a low word limit) Subtract the minimum low word from the new low word Subtract the minimum high word from the new high word If the calculated minimum is legal, don’t adjust it Replace the calculated minimum with the lowest minimum Compare the lower limit to the highest legal value Divide by 32 (create a high word limit) Multiply by 2048 (create a low word limit) Subtract the new low word from the maximum low word Subtract the new high word from the maximum high word If the calculated minimum is legal, don’t adjust it Replace the calculated minimum with the highest minimum Save the minimum ampl. low word (fractional batteries) Save the minimum ampl. high word (integral batteries) Load in the constant to multiply by, to find amplitude W3:W2-W1*W2 (W3:W2 is the maximum amplitude by slope) Compare the upper limit to the lowest legal value Divide by 32 (create a high word limit) 195 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 SL.W W6,#11,W6 SUB.W W2,W6,W8 SUBB.W W3,W7,W9 BRA NN,.+4 MOV.D W6,W2 MOV.W #MAXMAXV,W6 ASR.W W6,#5,W7 SL.W W6,#11,W6 SUB.W W6,W2,W8 SUBB.W W7,W3,W9 BRA NN,.+4 MOV.D W6,W2 MOV.W W2,vma132l MOV.W W3,vmax32h RETURN Multiply by 2048 (create a low word limit) Subtract the minimum low word from the new low word Subtract the minimum high word from the new high word If the calculated maximum is legal, don’t adjust it Replace the calculated maximum with the lowest maximum Compare the upper limit to the highest legal value Divide by 32 (create a high word limit) Multiply by 2048 (create a low word limit) Subtract the new low word from the maximum low word Subtract the new high word from the maximum high word If the calculated maximum is legal, don’t adjust it Replace the calculated maximum with the highest maximum Save the maximum ampl. low word (fractional batteries) Save the maximum ampl. high word (integral batteries) DDIDOPPDIPPPPPBDPIDDPPIO charge_on: MOV.W #batsoc,WO MOV.W #onlist,W1 CLR.W W2 CLR.W W3 SETM.W W4 SETM.W W5 MOV.W #010003,W8 DO #119,c_on_loop MOV ADD. BRA BRA AND. BRA SUB. .D W C. 2. W [W0++] , W6 W6,W7,W9 .+4 c_on_inc W8,[W1],W9 NZ,c-on_inc W W4,W6,W10 SUBB.W W5,W7,W11 LTU,c_on_inc MOV.D W6,W4 INC.W W2,W3 c_on_inc: INC.W W2,W2 RLNC.W W8,W8 BRA NN,.+4 INC2.W W1,W1 c_on_loop: RLNC.W W8,W8 BRA 3”)I!I.'P’DDIIDPODODDPIDD’O’DOO’DI'D’OIDIDPD’PPDPDIDI, Find the least-charged battery of the whole group Load the location of the battery module SOC percents Load the location of the battery module ”on-list” W2 is the current module (clear at the beginning) W3 is the minimum module (clear at the beginning) W5:W4 is the highest possible state of charge (100%) Initialize the battery module status mask Search through the entire battery module list Recall the SOC percent of the tested module Is the tested long zero? If so, exit now If W6+W7 is coincidentally zero, don’t exit Only exit if both W6 and W7 are zero Mask off the status of the current battery module If it isn’t currently off, exit now Subtract the tested low word from the minimum SOC Subtract the tested high word from the minimum SOC An equal or lesser tested module will replace the min. Since this module had less charge, save its parameters Increment the current module address Rotate the battery module status mask left w/o carry If the ’11’ was at the top, increment the list pointer W3 is the least-charged battery (zero if none found) eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee RETURN charge_off: MOV.W #batsoc,WO MOV.W #onlist,W1 CLR.W W2 CLR.W W3 CLR.W W4 CLR.W W5 MOV.W #010003,W8 DO #119,c_off_loop MOV.D [W0++],W6 ADD.W W6,W7,W9 BRA C. .+4 BRA Z,c_off_inc I’ll}!I,'P’P’Dll.’I’ll!!!lIPPDPOIDII’IIDI’IIDPD'Ot'D’I' Find the most-charged battery of the whole group Load the location of the battery module SOC percents Load the location of the battery module “on-list" W2 is the current module (clear at the beginning) W3 is the maximum module (clear at the beginning) W5:W4 is the lowest possible state of charge (0%) Initialize the battery module status mask Search through the entire battery module list Recall the SOC percent of the tested module Is the tested long zero? If so, exit now If W6+W7 is coincidentally zero, don’t exit Only exit if both W6 and W7 are zero 196 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 AND.W W8,[W1],W9 BRA Z,c_off_inc SUBR.W W4,W6,W10 SUBBR.W W5,W7,W11 BRA LTU,c_off_inc MOV.D W6,W4 INC.W W2,W3 c_off_inc: INC.W W2,W2 RLNC.W W8,W8 BRA NN,.+4 INC2.W W1,W1 c_off_loop: RLNC.W W8,W8 RETURN do_timer: MOV.W frequency,W1 MOV.W timerold1,W3 MOV.W timeroldh,W4 MOV.W TMR2,W5 MOV.W TMR3HLD,W6 SUB.W W5,W3,W3 SUBB.W W6,W4,W4 BRA NN,.+10 NEG.W W3,W3 BRA NZ,.+4 DEC.W W4,W4 COM.W W4,W4 MOV.W W5,timeroldl MOV.W W6,timeroldh RETURN output_PWM: MUL.UU W1,W3,W6 MUL.UU W1,W4,W4 ADD.W W4,W7,W3 BTST.W PORTD,#3 BRA NZ,goreverse MOV.W angle32l,W1 ADD.W W1,W6,W6 MOV.W angle32h,W1 MOV.W W1,angleold ADDC.W W1,W3,W3 BRA goforward goreverse: MOV.W angle32l,W1 SUB.W W1,W6,W6 MOV.W angle32h,W1 MOV.W W1,angleold SUBB.W W1,W3,W3 goforward: MOV.W W6,angle32l MOV.W W3,angle32h RCALL sine_table MOV.W W6,W4 ADD.W #341,W3 RCALL sine_tab1e Mask off the status of the current battery module If it isn’t currently on, exit now Subtract the maximum low word from the tested SOC Subtract the maximum high word from the tested SOC An equal or greater tested module will replace the max. Since this module had less charge, save its parameters Increment the current module address Rotate the battery module status mask left w/o carry If the ’11’ was at the top, increment the list pointer W3 is the most-charged battery (zero if none found) eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee D’OD',DI’IDP’OD'9’I’DI!!'P’0’DDOPDDD’DIDODPDDDOPDPPDIII Find the accumulated time Load in the frequency calculated by the PID controller Load the old timer value low word Load the old timer value high word Load the new timer value low word Load the new timer value high word Subtract the old low word from the new low word Subtract the old high word from the new high word If the result is negative, take the absolute value Complement and increment the low word If there was an overflow, decrement the high word This decrement will change into an increment next Negate the high word by complementing Save the timer value low word for next time Save the timer value high word for next time IPIPPIPDDPPIDPPI’IBDIDD I'llDDPOII!Ill)!!!D,P’IDDDDBIIDI’DDDDIIPOI!IPPDIIODDD’I Generate module commands with PWM for motoring W7:W6-W1#W3 (W7 is part of the low product, an angle) W5:W4-W1*W4 (W4 is part of the high product, an angle) W3:W6 is the accumulated angle from frequency Is the "Reverse'I signal active? Recall the old accumulated angle low word Add the old angle low word to the new angle low word Recall the old accumulated angle high word Save this angle for calculations below Add the old angle high word to the new angle high word Recall the old accumulated angle low word Subtract new angle low from old angle low Recall the old accumulated angle high word Save this angle for calculations below Subtract new angle high from old angle high Save the accumulated angle low word for next time Save the accumulated angle high word for next time Return the sin(W3&0103FF) in W6, 010001 to 0103FF Save U’s amplitude in W4 Add about 120 degrees Return the sin(W3&0103FF) in W6, 010001 to 0103FF 197 MOV.W W6,W5 ADD.W #342,W3 RCALL sine_table CPO.W frequency BRA NZ,notzerof MOV.W #010200,W4 MOV.W #010200,W5 MOV.W #010200,W6 MOV.W #MINMINV,WO SL.W W0,#11,W0 MOV.W W0,ampl321 CLR.W amp132h notzerof: Save V’s amplitude in W5 Add about 120 degrees Return the sin(W3&0103FF) in W6, 010001 to 0103FF Is the frequency zero? If so, effectively short out the motor Also reduce the initial applied voltage Generate module commands for all three phases MOV.W #010060,W1 SUB.W #010200,W4 BRA NN,.+6 MOV.W #010040,W1 NEG.W W4,W4 MOV.W amp132l,W2 MOV.W amp132h,W3 MUL.UU W2,W4,W8 MUL.UU W3,W4,W2 ADD.W W2,W9,W9 ASR.W W9,#9,W8 BRA Z,no_ufull MOV.W #010004,W9 MOV.W temp,WO SUB.W W9,W0,W9 MOV.W W9,U1TXREG REPEAT #420 NOP MOV.W W1,U1TXREG REPEAT #420 NOP no_ufull: ASR.W W9.#4,W9 AND.W #01001F,W9 BRA Z,npwm_done ADD.W W1,W9,W9 MOV.W #010001,W1 MOV.W temp,W0 ADD.W W1,W0,W1 MOV.W W1,U1TXREG REPEAT #420 NOP MOV.W W9,U1TXREG REPEAT #420 NOP upwm_done: MOV.W #010060,W1 SUB.W #010200,W5 BRA NN,.+6 MOV.W #010040,W1 NEG.W W5,W5 MOV.W amp132l,W2 MOV.W amp132h,W3 MUL.UU W2,W5,W8 Attempt to turn module #1/#4 on positive If U’s amplitude is below zero, invert it Turn module #1/#4 on negative instead Negate the duty cycle Recall the fractional battery amplitude Recall the integral battery amplitude W9:W8-W2*W4 (W9 is U’s amplitude in part battery units) W3:W2=W3#W4 (W2 is U’s amplitude in full battery units) Add the two results together to get the real amplitude Transfer the integral battery amplitude to W8 If no full batteries are on, just do normal PWM Address module #4/#1 with extra data Send the zero PWM duty cycle (full) out to module #4/#1 Since W9 was up to 511, make it up to 31 Consider only fractional batteries Add on the duty cycle to the PWM command with polarity Address module #1/#4 with extra data Send the PWM duty cycle out to module #1/#4 Attempt to turn module #2/#5 on positive If V’s amplitude is below zero, invert it Turn module #2/#5 on negative instead Negate the duty cycle Recall the fractional battery amplitude Recall the integral battery amplitude W9:W8-W2tW5 (W9 is V’s amplitude in part battery units) 198 MUL.UU W3,W5,W2 ADD.W W2,W9,W9 ASR.W W9,#9,W8 BRA Z,no_vfull MOV.W #010005,W9 MOV.W temp,W0 SUB.W W9,W0,W9 MOV.W W9,U1TXREG REPEAT #420 NOP MOV.W W1,U1TXREG REPEAT #420 NOP no_vfull: ASR.W W9,#4,W9 AND.W #01001F,W9 BRA Z,vam_done ADD. W1,W9,W9 MOV. #010002,W1 MOV. temp,W0 ADD. W1,W0,W1 MOV.W W1,U1TXREG REPEAT #420 NOP MOV.W W9,U1TXREG REPEAT #420 NOP (1:121: vam_done: MOV.W #010060,W1 SUB.W #010200,W6 BRA NN,.+6 MOV.W #010040,W1 NEG.W W6,W6 MOV.W amp132l,W2 MOV.W amp132h,W3 MUL.UU W2,W6,W8 MUL.UU W3,W6,W2 ADD.W W2,W9,W9 ASR.W W9,#9,W8 BRA Z,no_wfull MOV.W #010006,W9 MOV.W temp,W0 SUB.W W9,W0,W9 MOV.W W9,U1TXREG REPEAT #420 NOP MOV.W W1,U1TXREG REPEAT #420 NOP no_wfull: ASR.W W9,#4,W9 AND.W #01001F,W9 BRA Z,wpwm_done ADD. W1,W9,W9 MOV. #010003,W1 MOV. temp,W0 ADD. W1,W0,W1 MOV.W W1,U1TXREG REPEAT #420 NOP MOV.W W9,U1TXREG 1:121:13 W3:W2-W3tW5 (W2 is V’s amplitude in full battery units) Add the two results together to get the real amplitude Transfer the integral battery amplitude to W8 If no full batteries are on, just do normal PWM Address module #5/#2 with extra data Send the zero PWM duty cycle (full) out to module #5/#2 Since W9 was up to 511, make it up to 31 Consider only fractional batteries Add on the duty cycle to the PWM command with polarity Address module #2/#5 with extra data Send the PWM duty cycle out to module #2/#5 Attempt to turn module #3/#6 on positive If W’s amplitude is below zero, invert it Turn module #3/#6 on negative instead Negate the duty cycle Recall the fractional battery amplitude Recall the integral battery amplitude W9:W8-W2*W6 (W9 is W’s amplitude in part battery units) W3:W2-W3tW6 (W2 is W’s amplitude in full battery units) Add the two results together to get the real amplitude Transfer the integral battery amplitude to W8 If no full batteries are on, just do normal PWM Address module #6/#3 with extra data Send the zero PWM duty cycle (full) out to module #6/#3 Since W9 was up to 511, make it up to 31 Consider only fractional batteries Add on the duty cycle to the PWM command with polarity Address module #3/#6 with extra data Send the PWM duty cycle out to module #3/#6 199 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 1150 1151 1152 1153 1154 1155 1156 1157 1158 1159 1160 1161 1162 1163 1164 1165 1166 1167 1168 1169 1170 1171 1172 1173 1174 REPEAT #420 NOP wpwm_done: BTG.W BTG.W RETURN temp,#0 temp,#1 eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee output_charge: MUL.UU W1,W3,W6 MUL.UU ADD. MOV. ADD. MOV. MOV. CCC‘IS W1,W4,W4 W4,W7,W3 angleB2l,W1 W1,W6,W6 angle32h,W1 W1,angleold ADDC.W W1,W3,W3 MOV.W MOV.W W6,angle32l W3,angle32h RCALL sine_table MOV. MOV. MOV. SUB. BRA ; The NEG. NEG. CPO. BRA MUL. MUL. ADD. SUB. BRA CLR. ASR. W amp132l,W2 W amp132h,W3 W temp,W4 W #010200,W6 NN,oc_pos PDOIIII’PDIBDI’D’DPDDOID'IIIIPPPIDI'IDIIPDDI’IPI.I’lI’DI Generate module commands with no PWM for charging W7:W6-W1¢W3 (W7 is part of the low product, an angle) W5:W4-W1tW4 (W4 is part of the high product, an angle) W3:W6 is the accumulated angle from frequency Recall the old accumulated angle low word Add the old angle low word to the new angle low word Recall the old accumulated angle high word Save this angle for calculations below Add the old angle high word to the new angle high word Save the accumulated angle low word for next time Save the accumulated angle high word for next time Return the sin(W3&0103FF) in W6, 010001 to 0103FF Recall the fractional battery amplitude Recall the integral battery amplitude Recall the current output amplitude If the sine amplitude is below zero, invert it charging output reference value is negative W W6,W6 W W4,W4 W W4 N,oc_pos_off UU W2,W6,W0 UU W3,W6,W2 W W1,W2,W2 W #256,W2 NN,.+4 W W2 W W2,#9,W2 CP.W W2,W4 BRA NZ,.+4 RETURN BRA N,oc_neg_off RCALL charge_on CPO. BRA W W3 NZ,.+4 RETURN DEC. W temp BSET.W W3,#7 BRA oc_done oc_neg_off: RCALL charge_off CPO. BRA W W3 NZ,.+4 RETURN INC. BRA W temp oc-done Negate the sine amplitude if it’s negative Also negate the current output amplitude Is the current output amplitude positive? If so, turn off positives before turning on negatives W1:WO-W2tW6 (W1 is the amplitude in part battery units) W3:W2-W3tW6 (W2 is the amplitude in full battery units) Add the two results together to get the real amplitude Subtract half a battery (the minimum threshold) If the reference value undersaturated, set it to zero Divide the value by the sine wave’s peak value (512) Compute the reference value minus the current value If they’re equal, do nothing and return immediately Find the least-charged battery of the whole group (W3) Were none found? If none were found, return immediately Decrement the current output voltage Set bit seven to signify turning on negative Find the most-charged battery of the whole group (W3) Were none found? If none were found, return immediately Increment the current output voltage ; The charging output reference value is positive oc_pos: CPO. W W4 Is the current output amplitude negative? 200 1175 1176 1177 1178 1179 1180 1181 1182 1183 1184 1185 1186 1187 1188 1189 1190 1191 1192 1193 1194 1195 1196 1197 1198 1199 1200 1201 1202 1203 1204 1205 1206 1207 1208 1209 1210 1211 1212 1213 1214 1215 1216 1217 1218 1219 1220 1221 1222 1223 1224 1225 1226 1227 1228 1229 1230 1231 1232 1233 1234 1235 1236 BRA N,oc_neg_off MUL.UU W2,W6,W0 MUL.UU W3,W6,W2 ADD.W W1,W2,W2 SUB.W #256,W2 BRA NN,.+4 oc_pos_zero: CLR.W W2 ASR.W W2,#9,W2 CP.W W2,W4 BRA NZ,.+4 RETURN BRA N,oc_pos_off RCALL charge_on CPO.W W3 BRA NZ,.+4 RETURN INC.W temp BSET.W W3,#8 BRA oc_done oc_pos_off: RCALL charge_off CPO.W W3 BRA NZ,.+4 RETURN DEC.W temp oc-done: MOV.W #onlist,W2 DEC.W W3,W4 ASR.W W4,#2,W5 AND.W #01001E,W5 ADD.W W2,W5,W2 SL.W W4,#1,W5 INC.W W5,W6 MOV.W #010180,W0 AND.W W0,W3,W0 BRA Z,oc_off MOV.W #01007F,W0 MOV.W W0,T8LPAG MOV.W #01FCOF,W0 ASR.W W4,#3,W1 AND.W #OXOOOE,W1 ADD.W W0,W1,W0 TBLRDL.W [W0],W1 BTST.C W1,W3 BRA NC,.+6 BTG.W W3,#7 BTG.W W3,#8 BTST.Z W3,#7 BTST.C W3,#8 BSW.Z [W2],W5 BSW.C [W2],W6 MOV.W W3,U1TXREG REPEAT #420 NOP RETURN oc_off: BTST.Z W3,#7 BTST.C W3,#8 If so, turn off negatives before turning on positives W1:W0-W2aW6 (W1 is the amplitude in part battery units) W3:W2-W3tW6 (W2 is the amplitude in full battery units) Add the two results together to get the real amplitude Subtract half a battery (the minimum threshold) If the reference value undersaturated, set it to zero Divide the value by the sine wave’s peak value (512) Compute the reference value minus the current value If they’re equal, do nothing and return immediately Find the least-charged battery of the whole group (W3) Were none found? If none were found, return immediately Increment the current output voltage Set bit eight to signify turning on negative Find the most-charged battery of the whole group (W3) Were none found? If none were found, return immediately Decrement the current output voltage W3 has the module command (off or on positive/negative) Load the location of the battery module "on-list" Addresses in memory start at zero Find the offset address of the status bits Mask off the module command and the lowest bit W2 contains the status bits’ address (word boundary) W5 contains the status bits’ offset (crumb boundary) Also create a second offset Test to see if this is an "off" command If so, don’t check for reversal; get the voltage word sent afterward by the module, and save it in the array Load TBLPAG with 01007F, the EEPROM location Load the beginning EEPROM address into W0 Find the offset address of the reversal bit Mask off the module command and the lowest bit Load the reversal-specifying EEPROM word into W1 Test the bit associated with this module Toggle bits seven and eight because of reversal Copy these polarity bits to the I'on-list" Signify that this module is either on pos. or neg. Send a turn on positive or turn on negative command The module will be turned off; get the voltage word Copy these polarity bits to the ”on-list" 201 1237 1238 1239 1240 1241 1242 1243 1244 1245 1246 1247 1248 1249 1250 1251 1252 1253 1254 1255 1256 1257 1258 1259 1260 1261 1262 1263 1264 1265 1266 1267 1268 1269 1270 1271 1272 1273 1274 1275 1276 1277 1278 1279 1280 1281 1282 1283 1284 1285 1286 1287 1288 1289 1290 1291 1292 1293 1294 1295 1296 1297 1298 BSW.Z [W2],W5 BSW.C [W2],W6 ADD.W #010180,W3 MOV.W W3,U1TXREG REPEAT #1000 NOP BTST.W U1STA,#URXDA BRA NZ,.+4 RETURN MOV.W U1RXREG,W1 AND.W #0101FF,W1 SL.W W1,#1,W1 MOV.W W3,W0 AND.W #01007F,W0 BRA vc_entering Signify that this module is now off Send a turn off command to the addressed module Is there at least one available word? If not, return immediately Recall the received word Mask off the top seven bits Multiply this result by two (one bit is not sent now) This is for vc_entering (it needs the module address) Mask off bits seven and eight Send execution to the routine below (slave voltage A/D) BDPDDDPDD’!PPIIDDDIPPDI voltcheck: MOV.W #010001,W0 MOV.W W0,U18RG MOV.W batupdate,W0 INC.W W0,W0 MOV.W W0,U1TXREG REPEAT #420 NOP MOV.W #010008,W1 MOV.W W1,UlTXREG REPEAT #1100 NOP BTST.W U18TA,#URXDA BRA Z,vc_skipread MOV.W U1RXREG,W1 AND.W #0100FF,W1 MOV.W W0,U1TXREG REPEAT #420 NOP MOV.W #010009,W2 MOV.W W2,U1TXREG REPEAT #1100 NOP BTST.W U1STA,#URXDA BRA Z,vc_skipread MOV.W U1RXREG,W2 AND.W #010003,W2 SWAP.W W2 ADD.W W1,W2,W1 vc_entering: SL.W W1,#4,W1 MOV.W #38912,W2 MUL.UU W1,W2,W4 MOV.W #7500,W4 ADD.W W4,W5,W6 SL.W W0,#1,W0 DEC2.W W0,W0 MOV.W #batvolts,W1 ADD.W W0,W1,W1 MOV.W [W1],W4 LSR.W W4,#2,W4 MUL.UU W4,#7,W4 ADD.W #4,W4 l!IBPPIDI'I#999,!)’II'PPDPIIIOOIDDIOIIID!FDDPIPPDIIDBII Incrementally check the battery voltages and store Set the baud rate to 1MBd Recall the current battery address to query Send an extended data command to the addressed module Send a battery voltage low byte request Is there at least one available word? Recall the received word Mask off the top eight bits Send an extended data command to the addressed module Send a battery voltage high byte request Is there at least one available word? Recall the received word Mask off the lower two bits Add the high byte to the low byte for a 10-bit result This may be called by other functions Scale this by 16 to get more constant precision This is a scale factor to reach mV W5:W4-W1tW2 (W5 is the new relative voltage in mV) Find the new absolute voltage in mV (add 7.5V) Multiply the address by two Find the appropriate array address Recall the previous voltage Divide it by four Weight it by seven Add one-half (about four) 202 1299 1300 1301 1302 1303 1304 1305 1306 1307 1308 1309 1310 1311 1312 1313 1314 1315 1316 1317 1318 1319 1320 1321 1322 1323 1324 1325 1326 1327 1328 1329 1330 1331 1332 1333 1334 1335 1336 1337 1338 1339 1340 1341 1342 1343 1344 1345 1346 1347 1348 1349 1350 1351 1352 1353 1354 1355 1356 1357 1358 1359 1360 LSR.W W6,#2,W6 ADD.W W4,W6,W6 LSR.W W6,#1,W6 MOV.W W6,[W1] MOV.W #11800,W1 SUB.W W6,W1,W6 BRA NN,.+4 CLR.W W6 MOV.W #2400,W1 CP.W W6,W1 BRA N,.+4 MOV.W #2400,W6 MOV.W #27,W1 MUL.UU W1,W6,W4 MOV.W #20700,W1 MUL.UU W1,W6,W2 ADD.W W3,W4,W3 INC.W W3,W3 SL.W W0,#1,WO MOV.W #batsoc,W1 ADD.W W0,W1,W1 MOV.D W2,[W1] vc_done: MOV.W batupdate,W0 INC.W W0,W0 MOV.W W0,W1 SUB.W #120,W1 BRA N,.+4 CLR.W W0 MOV.W W0,batupdate RETURN vc_skipread: SL.W WO,#1,WO DEC2.W W0,W0 MOV.W #batvolts,W1 ADD.W W0,W1,W1 CLR.W [W1] SL.W W0,#1,W0 MOV.W #batsoc,W1 ADD.W W0,W1,W1 CLR.W [W1++] CLR.W [W1] BRA vc_done IDII’IPODPIIDIDIDOPDDUP convert_curs: MOV.W #010006,W1 MOV.W W1,ADCHS BSET.W ADCON1,#SAMP BTSS.W ADCON1,#DONE BRA .-2 BCLR.W ADCON1,#DONE MOV.W ADCBUFO,W1 SL.W W1,#2,W1 MOV.W curcentu,W2 SUB.W W1,W2,W1 MOV.W W1,curu Divide the new value by four also Average the new and old battery voltages Divide the new total by two Store the final value in the voltage array Find the percent SOC (0-100%) Subtract off the zero-SOC voltage (11.8V) If the measured voltage was lower, assume 0% SOC Compare the measured voltage with 14.2V Perform W6-W1 Saturate to 14.2V if above (assume 100% SOC) This is a scale factor to reach SOC percent high W5:W4-W1tW6 (W4 is the new SOC percent high) This is a scale factor to reach SOC percent low W3:W2-W1tW6 (W3:W2 is the new SOC percent high/low) Do a 32-bit calculation for more precision Make the maximum SOC high word 65535 Multiply the address by two again (four total) Find the appropriate array address Store the final value in the percent SOC array Recall the current battery address to query Increment the randomness counter and update it Has the address overflowed? If so, set it back to zero This battery module is not present; zero voltage/SOC Multiply the address by two Find the appropriate array address Store the final value in the voltage array Multiply the address by two again (four total) Find the appropriate array address Store the final value in the percent SOC array IDPPPIDDIIDFODD’IIPIPOIPPPIPODPOPDIDPI’P'IPDIIFDPPPDPFD Convert the current A/D channels Select channel AN6 for A/D conversion (current U) Start an A/D conversion Keep looping until it completes Weight the current value by one but make it countstfour Multiply the ten-bit integer result by four Recall the center value (zero amps) Perform W1-W1-W2 Store the result (two’s complement format, countstfour) 203 1361 1362 1363 1364 1365 1366 1367 1368 1369 1370 1371 1372 1373 1374 1375 1376 1377 1378 1379 1380 1381 1382 1383 1384 1385 1386 1387 1388 1389 1390 1391 1392 1393 1394 1395 1396 1397 1398 1399 1400 1401 1402 1403 1404 1405 1406 1407 1408 1409 1410 1411 1412 1413 1414 1415 1416 1417 1418 1419 1420 1421 1422 MOV.W #010007,W1 MOV.W W1,ADCHS BSET.W ADCON1,#SAMP BTSS.W ADCON1,#DONE BRA .-2 BCLR.W ADCON1,#DONE MOV.W ADCBUFO,W1 SL.W W1,#2,W1 MOV.W curcentv,W2 SUB.W W1,W2,W1 MOV.W W1,curv MOV.W #010008,W1 MOV.W W1,ADCHS BSET.W ADCON1,#SAMP BTSS.W ADCON1,#DONE BRA .-2 BCLR.W ADCON1,#DONE MOV.W ADCBUFO,W1 SL.W W1,#2,W1 MOV.W curcentv,W2 SUB.W W1,W2,W1 MOV.W W1,curw RETURN Select channel AN7 for A/D conversion (current V) Start an A/D conversion Keep looping until it completes Weight the current value by one but make it countsafour Multiply the ten-bit integer result by four Recall the center value (zero amps) Perform W1=W1-W2 Store the result (two’s complement format, countstfour) Select channel AN8 for A/D conversion (current W) Start an A/D conversion Keep looping until it completes Weight the current value by one but make it countsafour Multiply the ten-bit integer result by four Recall the center value (zero amps) Perform W1-W1-W2 Store the result (two’s complement format, countstfour) eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee IOQPDB’P’DIDPPPIPPID'9'!’9”!D,’l9”D’PF’ODPDOPIPPD9’DIPDDODPPDP’DPPDIPIOPDDOPPD Clarke: PERFORM A CLARKE TRANSFORM OF THE CURRENTS JUST MEASURED Formulas: curalpha - curu-((curu+curv+curw)/3) curbeta - (curu+2tcurv-(curu+curv+curw))/sqrt(3) - (curv-curw)/sqrt(3) Note: Because curw is not normally used in the calculations, here to center the currents properly. be zero; if not, there is an offset error. it can be utilized The sum of all three currents should Assuming the offset error is equal between the three sensors (which it should be approximately). the formulas above cancel it out without destroying the original readings. MOV.W curu,W1 MOV.W curv,W2 MOV.W curw,W3 ADD.W W1,W2,W4 ADD.W W3,W4,W4 MOV.W #015555,W5 MUL.SU W4,W5,W4 MOV.W #018000,W9 ADD.W W4,W9,W4 ADDC.W #0,W5 SUB.W W1,W5,W1 MOV.W W1,curalpha SUB.W W2,W3,W2 MOV.W #0193CD,W5 MUL.SU W2,W5,W4 ADD.W W4,W9,W4 ADDC.W #0,W5 MOV.W W5,curbeta RETURN Recall the current U reading in countst4 Recall the current V reading in counts¢4 Recall the current W reading in countst4 Sum the three together to find the offset error Load one-third into W5 as a multiply constant W5:W4-W4tW5 (take one-third of the total offset error) Add one-half to get better accuracy (+/-0.5 vs. +/-1.0) Calculate curu-((curu+curv+curw)/3) Store in curalpha Calculate curv-curw Load 1/sqrt(3) into W5 as a multiply constant W5:W4-W2tW5 (divide (curv-curw) by sqrt(3)) Store in curbeta 204 1423 1424 1425 1426 1427 1428 1429 1430 1431 1432 1433 1434 1435 1436 1437 1438 1439 1440 1441 1442 1443 1444 1445 1446 1447 1448 1449 1450 1451 1452 1453 1454 1455 1456 1457 1458 1459 1460 1461 1462 1463 1464 1465 1466 1467 1468 1469 1470 1471 1472 1473 1474 1475 1476 1477 1478 1479 1480 1481 1482 1483 1484 PO'DD’DDIDDI’DD”,DDPPIIIPOIDIDPPI’PPIPIDDII’D’OI'IDPIDPPNPPPOPPI’ID”D’Dll’Dfi’! park: PERFORM A PARK TRANSFORM OF THE CURRENTS JUST CALCULATED The alpha-beta currents aren’t too useful without being oriented to the flux angle. This of the motor axis) is the the torque. Formulas: angle, when moving, with Id and Iq that we can control. magnetizing current, and Iq (current along the Q-axis) controls Id is constant and Iq is proportional to the throttle reading. basically represents the synchronous speed Id (current along the D- curd - -curalphatcos(angle32h)+curbetatsin(angle32h) curq - curalphatsin(angle32h)+curbetatcos(angle32h) MOV.W angleold,W3 RCALL sine_table SUB.W #010200,W6 SL.W W6,#6,W1 ADD.W #32,W1 ADD.W #256,W3 RCALL sine_table SUB.W #010200,W6 SL.W W6,#6,W6 ADD.W #32,W6 MOV.W curalpha,W7 MOV.W curbeta,WB MOV.W #018000,W9 MOV.W #24000,W0 MUL.SS W6,W7,W2 ADD.W W2,W9,W2 ADDC.W #0,W3 MUL.SS W1,W8,W4 ADD.W W4,W9,W4 ADDC.W #0,W5 SUB.W W5,W3,W3 MOV.W curd32h,W5 LAC W5,#0,A LAC W5,#O,B MOV.W curd32l,W4 MOV.W W4,ACCAL MOV.W W4,ACCBL SFTAC B,#6 SUB A LAC W3,#O,B SFTAC B,#7 ADD MOV. MOV. MOV. MOV. ACCAL,W4 W4,curd32l ACCAR,W5 W5,curd32h SFTAC A,#14 MOV.W ACCAL,W3 MUL.SU W3,W0,W4 BTSC.W PORTD,#3 NEG.W W5,W5 MOV.W W5,curd Ctt€> MUL.SS W1,W7,W2 ADD.W W2,W9,W2 ADDC.W #0,W3 MUL.SS W6,W8,W4 Recall Return the the old accumulated angle high word sin(W3&0103FF) in W6, 010001 to 0103FF Change the range of 1 to 1023 to -511 to 511 Change the -511 to 511 value into -32704 to 32704 Add one-half for accuracy Add about 90 degrees to change sine into cosine Return the sin(W3&0103FF) in W6, 010001 to 0103FF Change the range of 1 to 1023 to -511 to 511 Change the -511 to 511 value into -32704 to 32704 Add one-half for accuracy Add one-half to get better accuracy (+/-0.5 vs. +/-1.0) This is a scale factor to reach amps¢4 at the end W3:W2-W6tW7 (curalphatcos(ang1e32h)) W5:W4-W1tW8 (curbetatsin(angle32h)) Calculate curbetatsin(angle32h)-curalphatcos(angle32h) Load the old current D into both accumulators Note that the sine and cosine here are +/-0.5 maximum Divide accumulator B by 64 Subtract this from the original value for a 63/64 ratio Load the new value into accumulator 8 Divide the 65536 multiplication by 128 for times 512 Sum these two weighted parts Save this value for next time (with great accuracy) Divide accumulator A by 64*256 for a 16-bit result W5:W4-W3tW0 (W5 is the new D current in amps¢4) Is the "Reverse" signal active? If so, negate the D current W3:W2-W6tW7 (curalphatsin(angle32h)) W5:W4=W1#W8 (curbetatcos(angle32h)) 205 ADD.W W4,W9,W4 ADDC.W #0,W5 ADD.W W3,W5,W3 ; Calculate curalphatsin(angle32h)+curbetatcos(angle32h) MOV.W curq32h,W5 ; Load the old current 0 into both accumulators LAC W5,#0,A LAC W5,#0,B MOV.W curq32l,W4 ; Note that the sine and cosine here are +/-0.5 maximum MOV.W W4,ACCAL MOV.W W4,ACCBL SFTAC B,#6 ; Divide accumulator B by 64 SUB A ; Subtract this from the original value for a 63/64 ratio LAC W3,#0,B ; Load the new value into accumulator B SFTAC B,#7 ; Divide the 65536 multiplication by 128 for times 512 ADD A ; Sum these two weighted parts MOV.W ACCAL,W4 ; Save this value for next time (with great accuracy) MOV.W W4,curq321 MOV.W ACCAH,W5 MOV.W W5,curq32h SFTAC A,#14 ; Divide accumulator A by 64t256 for a 16-bit result MOV.W ACCAL,W3 MUL.SU W3,W0,W4 ; W5:W4-W3:W0 (W5 is the new 0 current in ampss4) MOV.W W5,curq RETURN find_rms: ; Calculate the 64-point averaged RMS phase current in ampst4 from Id and Iq MOV.W curd,WS ; Recall the averaged current D in ampst4 MOV.W curq,W7 ; Recall the averaged current 0 in ampst4 BSET.W CORCON,#IF ; Enable integer mult. (as opposed to fractional) MPY W5tW5,A ; Square the D current to accumulator A MAC W7*W7,A ; Square the 0 current and add to accumulator A SFTAC A,#-7 ; Pretend to multiply by 256; account for Id/q vs. Iu/v/w ; The result high word can range from 0 to 4095, unsigned. In order to get a better relative precision result without a huge lookup table, the magnitude of the result will be recorded, divided by two, then added back to the result of the square root. The magnitude will be in powers of four, to simplify the readdition of the magnitude. Magnitudes from 4‘5 to 4‘0 are supported. The most significant 10 bits of the value are used by a lookup table to find the square root value (the result has a scaling of 32). The first quarter of the table is used only for 4‘0 for better accuracy. MOV.W ACCAH,W1 ; Extract the high word from accumulator A FF1L W1,W2 ; W2l‘: 5/6l5, 7/8l4, 9/10l3, 11/12l2, 13/14I1, 15/16/0l0 BRA NC,.+4 ; If W2 ends up to be zero, the carry will be set MOV.W #16,W2 ; If W1 was zero, assume 4‘0 scaling SUB.W W2,#7,W3 ; W314‘x: -2/-1|5, 0/1l4, 2/3l3, 4/5I2, 6/711, 8/910 BCLR.W W3,#0 ; W3l4‘x now: -2|5, 0|4, 2|3, 4|2, 6|1, 8|0 NEG.W W3,W3 ; W3I4‘x now: 2|5, 0|4, -2|3, -4|2, -6l1, -8|0 SFTAC A,W3 ; Shift the 10 significant bits to the low part of ACCAH MOV.W ACCAH,W1 ; Again extract the high word from accumulator A RCALL sqrt_tab1e ; Return the sqrt(WlROxO3FF) in W1, 010000 to 0103FF SUB.W W2,#5,W2 ; W2l4‘x now: 0/1l5, 2/3I4, 4/5l3, 6/7l2, 8/9l1, 10/11I0 ASR.W W2,#1,W2 ; W2l4‘x now: 015, 1|4, 2|3, 3|2, 4|1, 5|0 ASR.W W1,W2,W1 ; Divide the square root by up to 32 (the table scaling) MOV.W W1,currms ; The phase current amplitude in amps¢4 (0.00-255.75Arms) RETURN find_pf: ; Find the instantaneous power factor and average it 206 1547 1548 1549 1550 1551 1552 1553 1554 1555 1556 1557 1558 1559 1560 1561 1562 1563 1564 1565 1566 1567 1568 1569 1570 1571 1572 1573 1574 1575 1576 1577 1578 1579 1580 1581 1582 1583 1584 1585 1586 1587 1588 1589 1590 1591 1592 1593 1594 1595 1596 1597 1598 1599 1600 1601 1602 1603 1604 1605 1606 1607 1608 Calculate the power factor, MOV.W curd,W2 ; MOV.W curq,W4 CPO.W W2 ; BRA NN,.+4 CLR.W W2 MOV.W W2,W3 ; AND.W #01FF,W2 ; SWAP.W W2 ; SWAP.W W3 ; AND.W #01FF,W3 ; CPO.W W4 ; BRA LE,.+14 REPEAT #17 ; DIV.UD W2,W4 BRA OV,.+8 ; MOV.W W0,W1 SUB.W #0103FF,W1 ; BRA N,.+4 MOV.W #0103FF,W0 ; RCALL pf-table ; MOV.W W1,pwrfact Take an average of the MOV.W pfavg32h,W5 ; LAC W5,#0,A LAC W5,#0,B MOV.W pfavg32l,W4 MOV.W W4,ACCAL MOV.W W4,ACCBL SFTAC B,#10 ; SUB A ; LAC W1,#0,B ; SFTAC B,#10 ; ADD A ; MOV.W ACCAL,W4 ; MOV.W W4,pfavg321 MOV.W ACCAH,W5 MOV.W W5,pfavg32h ; RETURN eeeeeeeeeeeeeeeeeeeeeeee stored to a scale of 1000 to pwrfact Recall the calculated currents D and O Saturate curd to at least zero curd into W3; thus multiply W2 by 256 into W3:W2 retain the original W2 low byte high and low bytes of W2 high and low bytes of W3 retain the original W2 high byte saturate to 1023 COpy Only Swap Swap Only If curq is zero or under, W3:W2/W4 quotient to W0, W3:W2/W4 remainder to W1 If there was an overflow, saturate to 1023 Was the result over 1023? If so, saturate to 1023 (on three separate conditions) Return the cos(atan(W0&0103FF)) in W1, 0100F2 to 010388 power factor for slower (more stable) corrections Load the old power factor average into the accumulators Divide accumulator B by 1024 Subtract this from the original value for 1023/1024 Load the new power factor value into accumulator B Divide the 65536 multiplication by 1024 for times 64 Sum these two weighted parts Save this value for next time (with great accuracy) This can be compared to pwrfact (it is scaled the same) PBBDDIIID’P'QiPi’PPIDDDDIPOOPIPDDO"0"!3"PPDDDPDBDD’PPDID'DDPII’D.IDIDPPBIDII’ motor _loop: ; Close the loop for motoring ; Close the loop - adjust the applied voltage to set the D current to optimal MOV. W TMR3, W1 ; MOV.W nextadjust,W2 ; CP.W W1,W2 ; BRA NZ,noadjust ; MOV.W pc_loop,W2 ADD.W W1,W2,W1 3 MOV.W W1,nextadjust MOV.W curd,W1 ; MOV.W #IDOPTIMAL,W2 ; W W2,W1,W1 ; W pc_idkp,W4 ; MUL.SU W1,W4,W6 ; MOV.W W6,W4 MOV.W amp132l,W2 ; MOV.W amp132h,W3 SUB. MOV. Recall the current 32- bit timer high word Recall the calculated next time to adjust If they aren’t the same, don’t adjust Only close the loop every LOOPt2.18453ms Calculate and store the next time to close the loop Recall the averaged D current Compare it to the optimal D current Calculate the ID command minus the measured ID Multiply the ID difference by the proportional constant W7:W6-W1tW4 (amplitude adjustment due to proportion) Recall the current amplitude 207' 1609 1610 1611 1612 1613 1614 1615 1616 1617 1618 1619 1620 1621 1622 1623 1624 1625 1626 1627 1628 1629 1630 1631 1632 1633 1634 1635 1636 1637 1638 1639 1640 1641 1642 1643 1645 1646 1647 1648 1649 1650 1651 1652 1653 1654 1655 1656 1657 1658 1659 1660 1661 1662 1663 1664 1665 1666 1667 1668 1669 1670 CPO.W W4 BRA NN,.+10 NEG.W W4,W4 SUB.W W2,W4,W2 SUBB.W #0,W3 BRA .+6 ADD.W W2,W4,W2 ADDC.W #0,W3 MOV.W vmin32l,W6 MOV.W vmin32h,W7 SUB.W W2,W6,W8 SUBB.W W3,W7,W9 BRA NN,.+4 MOV.D W6,W2 MOV.W vma132l,W6 MOV.W vma132h,W7 SUB.W W6,W2,W8 SUBB.W W7,W3,W9 BRA NN,.+4 MOV.D W6,W2 MOV.W W2,amp132l MOV.W W3,amp132h BRA noadjust Also close the loop by MOV.W throttle,W1 SUB.W #MINTHR,W1 ASR.W W1,#3,W1 MOV.W curq,W2 SUB.W W1,W2,W1 MOV.W #IQKP,W2 MUL.SU W1,W2,W2 MOV.W frequency,W1 CPO.W W3 BRA Z,noadjust BRA NN,.+6 DEC.W W1,W1 BRA .+4 INC.W W1,W1 MOV.W #MINFREQ,W2 CP.W W1,W2 BRA GEU,.+4 MOV.W W2,W1 MOV.W #MAXFREQ,W2 CP.W W1,W2 BRA LEU,.+4 MOV.W W2,W1 MOV.W W1,frequency noadjust: RETURN Test the sign of the PF difference in voltage units Negate the difference so it can be subtracted The measured was less than the command (less volts) The measured was more than the command (more volts) Compare the new amplitude to the minimum at this speed Subtract the minimum low word from the new low word Subtract the minimum high word from the new high word If the calculated amplitude is legal, don’t adjust it Replace the calculated amplitude with the minimum Compare the new amplitude to the maximum at this speed Subtract the new low word from the maximum low word Subtract the new high word from the maximum high word If the calculated amplitude is legal, don’t adjust it Replace the calculated amplitude with the maximum adjusting the frequency based on the desired torque Recall the throttle command (0-1023) Subtract off the minimum throttle value Change the 0 to 895 value to 0 to 111 (Iq command) Recall the averaged current 0 Calculate the Iq command minus the measured Iq Multiply the Iq difference by the proportional constant W3:W2-W1tW2 (W3=frequency adjustment due to proportion) Recall the current frequency Test the sign of the Iq difference in frequency units If the difference is zero, don’t adjust the frequency The command was less than the measured (less speed) The command was more than the measured (more speed) Test to see if the frequency is too low If so, limit the frequency based on MINFREQ Test to see if the frequency is too high If so, limit the frequency based on MAXFREQ eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee send_vars: Convert many variables MOV.W frequency,W1 MOV.W #31250,W2 MUL.UU W1,W2,W4 PDIPPDPII’DPPPP5”!!!)’IIPDII’DDIIPI’IPDIDPII’IPDDIIISD to one byte in order to send out to the PIC18F2553 Recall the frequency word This is a scale factor to reach Hz from Hzt2.097152 W5:W4-W1tW2 (W5 is the new frequency in Hz) 208 1671 1672 1673 1674 1675 1676 1677 1678 1679 1680 1681 1682 1683 1684 1685 1686 1687 1688 1689 1690 1691 1692 1693 1694 1695 1696 1697 1698 1699 1700 1701 1702 1703 1704 1705 1706 1707 1708 1709 1710 1711 1712 1713 1714 1715 1716 1717 1718 1719 1720 1721 1722 1723 1724 1725 1726 1727 1728 1729 1730 1731 1732 MOV.W W5,W0 MOV.B WREG,freqbyte MOV.W amp132l,W0 LSR.W W0,#11,W0 BTSC.W amp132h,#0 BSET.W WO,#S MOV.B WREG,amplitude MOV.W vmin32l,W0 LSR.W W0,#11,W0 BTSC.W vmin32h,#0 BSET.W WO,#5 MOV.B WREG,amplmin MOV.W vma132l,W0 LSR.W WO,#11,W0 BTSC.W vma132h,#0 BSET.W WO,#S MOV.B WREG,amplmax MOV.W curu,W1 MOV.W curv,W2 MOV.W curw,W3 MOV.W #3000,W6 MUL.SU W1,W6,W4 MOV.W W5,W0 MOV.B WREG,curubyte MUL.SU W2,W6,W4 MOV.W W5,W0 MOV.B WREG,curvbyte MUL.SU W3,W6,W4 MOV.W W5,W0 MOV.B WREG,curwbyte MOV.W curd,WO ASR.W W0,#3,W0 MOV.B WREG,curdbyte MOV.W curq,WO ASR.W W0.#3.W0 MOV.B WREG,curqbyte MOV.W currms,WO ASR.W W0.#3,W0 MOV.B WREG,rmsbyte MOV.W pfavg32h,W1 MOV.W #16777,W2 MUL.UU W1,W2,W4 MOV.W W5,W0 MOV.B WREG,pfbyte MOV.W angle32h,WO ASR.W W0.#2,W0 MOV.B WREG,angbyte nov.w TMR3,W0 MOV.B WREG,timebyte Take care of linebyte MOV.W #freqbyte,W2 Recall the U (A) current word in countst4 Recall the V (8) current word in countst4 Recall the W (C) current word in countst4 This is a scale factor to reach amps/2 at the end W5:W4-W1tW6 (W5 is the new U current in amps/2) W5:W4-W2tW6 (W5 is the new V W5:W4=W3*W6 (W5 is the new W Recall the Convert it Recall the Convert it Recall the Convert it D current to amps/2 0 current to amps/2 RMS phase to amps/2 word in word in current current in amps/2) current in amps/2) amps‘4 ampst4 word in ampst4 Recall the power factor in value01000 This is a scale factor to reach valuet256 W5:W4-W1tW2 (W5 is the new power factor in valuet256) Recall the angle word in degreess1024/360 Convert it to degreest256/360 Recall the current timer high word Send almost the entire section data to the PIC18F2553 2(K) 1733 1734 1735 1736 1737 1738 1739 1740 1741 1742 1743 1744 1745 1746 1747 1748 1749 1750 1751 1752 1753 1754 1755 1756 1757 1758 1759 1760 1761 1762 1763 1764 1765 1766 1767 1768 1769 1770 1771 1772 1773 1774 1775 1776 1777 1778 1779 1780 1781 1782 1783 1784 1785 1786 1787 1788 1789 1790 1791 1792 1793 1794 AND.W #0100FF,W2 MOV.W W2,U2TXREG REPEAT #190 NOP MOV.W #freqbyte,W2 DO #14,.+14 MOV.B [W2++],W1 BSET.W W1,#8 MOV.W W1,U2TXREG REPEAT #190 NOP NOP RETURN handle_USB: MOV.B progcmd,WREG CPO.B W0 BRA Z,pp_none CP.B W0,#1 BRA NZ,pp_notl MOV.W addressl,WO MOV.W W0,NVMADR MOV.W addressh,W0 MOV.W W0,NVMADRU MOV.W #014041,W0 MOV.W W0,NVMCON BRA pp-prog pp_not1: CP.B WO,#2 BRA NZ,pp_not2 MOV.W addressl,W2 MOV.W W2,NVMADR MOV.W addressh,W0 MOV.W W0,NVMADRU MOV.W W0,TBLPAG MOV.W #flashbuf,W1 DO #31,pp2_end 9 e I TBLWTL.B [W1++].[W2++] TBLWTL.B [W1++].[W2--] TBLWTH.B [W1++].[W2] INC2.W W2,W2 pp2_end: NOP MOV.W #014001,W0 MOV.W W0,NVMCON BRA pp_prog pp_not2: CP.B WO,#3 BRA NZ,pp_not3 MOV.W addressl,WO MOV.W W0,NVMADR MOV.W addressh,W0 MOV.W W0,NVMADRU MOV.W #014044,W0 MOV.W W0,NVMCON BRA pp_prog Send 14 data byte commands to the PIC18F2553 9DPDPID!DDDDFPDPDI’,IPPD’!’DDOBPPDDIPDIDI’I’DBIII'DDDDIPDID’DIDI’I’DDPDPIPDIPPI’ Handle USB programming commands Test the DSPIC programming command Is there anything here? Is this an erase program memory row operation request? Recall the low address word and store in NVMADR Recall the high address word and store in NVMADRU Set up NVMCON for erasing a program memory row Is this a write program memory row operation request? Recall the low address word and store in NVMADR Recall the high address word and store in NVMADRU Also store it in TBLPAG for the write latches Write 96 bytes of flash memory to the write latches Loop 16 times for a total of 32 instruction words Set up NVMCON for writing a program memory row Is this an erase data memory word operation request? Recall the low address word and store in NVMADR Recall the high address word and store in NVMADRU Set up NVMCON for erasing a data memory word 210 1795 1796 1797 1798 1799 1800 1801 1802 1803 1804 1805 1806 1807 1808 1809 1810 1811 1812 1813 1814 1815 1816 1817 1818 1819 1820 1821 1822 1823 1824 1825 1826 1827 1828 1829 1830 1831 1832 1833 1834 1835 1836 1837 1838 1839 1840 1841 1842 1843 1844 1845 1846 1847 1848 1849 1850 1851 1852 1853 1854 1855 1856 pp_not3: CP.B WO,#4 ; BRA NZ,pp_not4 MOV.W addressl,WO ; MOV.W W0,NVMADR MOV.W addressh,WO ; MOV.W W0,NVMADRU MOV.W #014045,W0 ; MOV.W W0,NVMCON BRA pp_prog pp_not4: CP.B WO,#5 ; BRA NZ,pp_notS MOV.W addressl,WO ; MOV.W addressh,W1 ; MOV.W W1,TBLPAG MOV.W #progdata,W2 ; TBLWTL.W [W2].[W0] MOV.W #014004,W0 ; MOV.W W0,NVMCON BRA pp_prog pp_not5: CP.B WO,#6 ; BRA NZ,pp_not6 MOV.W addressl,W2 ; MOV.W W2,NVMADR MOV.W addressh,WO ; MOV.W W0,NVMADRU MOV.W W0,TBLPAG ; MOV.W #flashbuf,W1 ; REPEAT #15 3 TBLWTL.W [W1++],[W2++] MOV.W #01400A,W0 ; MOV.W W0,NVMCON BRA pp_prog pp_not6: CP.B WO,#7 ; BRA NZ,pp_not7 MOV.W addressl,WO ; MOV.W addressh,W1 ; MOV.W W1,TBLPAG MOV.W #progdata,W2 ; TBLWTL.W [W2].[W0] MOV.W #014008,W0 ; MOV.W W0,NVMCON BRA pp_prog pp_not7: CP.B WO,#8 ; BRA NZ,pp_not8 MOV.W addressl,Wi ; MOV.W addressh,W2 ; MOV.W W2,TBLPAG MOV.W #dataout,W2 ; TBLRDL.B [W1++].[W2++] TBLRDL.B [v1—-].[w2++] TBLRDM.B [W1].[W2++] INC2.W W1,W1 Is this an erase data memory row operation request? Recall the low address word and store in NVMADR Recall the high address word and store in NVMADRU Set up NVMCON for erasing a data memory row Is this a write data memory word operation request? Recall the low address word Recall the high address word and store in TBLPAG Write the data word to the temporary buffer Set up NVMCON for writing a data memory word Is this a write data memory row operation request? Recall the low address word and store in NVMADR Recall the high address word and store in NVMADRU Also store it in TBLPAG for the write latches Write 96 bytes of flash memory to the write latches Loop 16 times for a total of 16 data EEPROM words Set up NVMCON for writing a data memory row Is this a write config. memory Operation request? Recall the low address word Recall the high address word and store in TBLPAG Write the data word to the temporary buffer Set up NVMCON for writing a configuration memory word Is this a read memory long Operation request? Recall the low address of the flash/EEPROM to read Recall the high address of the flash/EEPROM to read Write six bytes of flash/EEPROM memory to the buffer 211 1857 1858 1859 1860 1861 1862 1863 1864 1865 1866 1867 1868 1869 1870 1871 1872 1873 1874 1875 1876 1877 1878 1879 1880 1881 1882 1883 1884 1885 1886 1887 1888 1889 1890 1891 1892 1893 1894 1895 1896 1897 1898 1899 1900 1901 1902 1903 1904 1905 1906 1908 1909 1910 1911 1912 1913 1914 1915 1916 1917 1918 TBLRDL.B [W1++],[W2++] TBLRDL.B [W1--].[W2++] TBLRDH.B [W1].[W2++] MOV.W #dataout,W2 ; AND.W #0100FF,W2 : MOV.W W2,U2TXREG REPEAT #190 NOP MOV.W #dataout,W2 ; DO #5,pp8_end ; MOV.B [W2++],W1 BSET.W W1,#8 MOV.W W1,U2TXREG REPEAT #190 NOP pp8_end: NOP MOV.W #progstat,W2 ; BSET.B [W2],#6 ; BCLR.B [W2],#5 : MOV.B [W2],W1 AND.W #0100FF,W2 MOV.W W2,U2TXREG REPEAT #190 NOP BSET.W W1,#8 ; MOV.W W1,U2TXREG REPEAT #190 NOP BRA pp_done pp_not8: CP.B WO,#9 ; BRA NZ,pp_notQ MOV.W addressl,W1 ; MOV.W addressh,W2 ; MOV.W W2,TBLPAG MOV.W #dataout,W2 DO #15,pp9_enda ; TBLRDL.B [W1++].[W2++] TBLRDL.B [W1--].[W2++] TBLRDH.B [W1].[W2++1 INC2.W W1,W1 pp9-enda: NOP MOV.W #dataout,W2 ; AND.W #0100FF,W2 ; MOV.W W2,U2TXREG REPEAT #190 NOP MOV.W #dataout,W2 ; DO #47,pp9_endb ; MOV.B [W2++],W1 BSET.W W1,#8 MOV.W W1,U2TXREG REPEAT #190 NOP pp9_endb: NOP MOV.W #progstat,W2 ; BSET.B [W2],#6 : BCLR.B [W2],#5 ; Recall the address to which the bytes were written Send an address command to the PIC18F2553 Recall the written bytes Send six data byte commands to the PIC18F2553 Send an address command to the PIC18F2553 Set bit 811 of progstat to indicate read completion Clear bit five of progstat to indicate not bootloader Send the progstat data byte command to the PIC18F2553 Is this a read 48 memory bytes operation request? Recall the low address of the flash/EEPROM to read Recall the high address of the flash/EEPROM to read Write 48 bytes of flash/EEPROM memory to the buffer Recall the address to which the bytes were written Send an address command to the PIC18F2553 Recall the written bytes Send 48 data byte commands to the PIC18F2553 Send an address command to the PIC18F2553 Set bit six of progstat to indicate read completion Clear bit five of progstat to indicate not bootloader 212 1919 1920 1921 1922 1923 1924 1925 1926 1927 1928 1929 1930 1931 1932 1933 1934 1935 1936 1937 1938 1939 1940 1941 1942 1943 1944 1945 1946 1947 1948 1949 1950 1951 1952 1953 1954 1955 1956 1957 1958 1959 1960 1961 1962 1963 1964 1965 1966 1967 1968 1969 1970 1971 1972 1973 1974 1975 1976 1977 1978 1979 1980 MOV.B [W2],W1 AND.W #0100FF,W2 MOV.W W2,U2TXREG REPEAT #190 NOP BSET.W W1,#8 MOV.W W1,U2TXREG REPEAT #190 NOP BRA pp_done pp_not9: CP.B WO,#10 BRA NZ,pp_notIO MOV.W addressl,W1 MOV.W progdata,W2 BCLR.W W1,#0 MOV.W W2,[W1] BRA pp_done pp_not10: CP.B WO,#11 BRA NZ,pp_not11 MOV.W addressl,W1 MOV.W #dataout,W2 BCLR.W W1,#0 MOV.W [W1++l.[W2++] MOV.W [W1++].[W2--] AND.W #0100FF,W2 MOV.W W2,U2TXREG REPEAT #190 NOP MOV.W #dataout,W2 DO #3,pp11_end MOV.B [W2++],W1 BSET.W W1,#8 MOV.W W1,U2TXREG REPEAT #190 NOP pp11-end: NOP MOV.W #progstat,W2 BSET.B [W2],#6 BCLR.B [W2],#5 MOV.B [W2],W1 AND.W #0100FF,W2 MOV.W W2,U2TXREG REPEAT #190 NOP BSET.W W1,#8 MOV.W W1,U2TXREG REPEAT #190 NOP BRA pp_done pp_not11: CP.B WO,#12 BRA NZ,pp_not12 REPEAT #12798 NOP BSET.W INTCON2,#15 BRA bootloader Send the progstat data byte command to the PIC18F2553 Is this a write RAM word operation request? Recall the low address word Prevent non-word-boundary writes Transfer a word of data to the RAM location No acknowledgement is needed for this operation Is this a read RAM long operation request? Recall the address of the RAM to read Write this to the output buffer Prevent non-word-boundary reads Transfer a long of data to the output buffer Send an address command to the PIC18F2553 Recall the written bytes Send four data byte commands to the PIC18F2553 Send an address command to the PIC18F2553 Set bit six of progstat to indicate read completion Clear bit five of progstat to indicate not bootloader Send the progstat data byte command to the PIC18F2553 Is this a jump to other bootloader operation request? Delay 400us so no more bytes come in from PIC18F2553 Switch to the alternate interrupt vector table The bootloader updates up to 0100007BF8, then resets 213 1981 1982 1983 1984 1985 1986 1987 1988 1989 1990 1991 1992 1993 1994 1995 1996 1997 1998 1999 2000 2001 2002 2003 2004 2005 2006 2007 2008 2009 2010 2011 2012 2013 2014 2015 2016 2017 2018 2019 2020 2021 2022 2023 2024 2025 2026 2027 2028 2029 2030 2031 2032 2033 2034 2035 2036 2037 2038 2039 2040 2041 2042 pp_not12: CP.B WO,#13 BRA NZ,pp_not13 REPEAT #12798 NOP MOV.W #010001,W0 MOV.W W0,U18RG MOV.W progdata,Wl RCALL check_comm SL.W W1,#8,W1 .W W1,W2,W1 MOV.W W1,dataout W W #0100FF,W2 MOV.W W2,U2TXREG REPEAT #190 NOP MOV.W #dataout,W2 DO #1,pp13_end MOV.B [W2++],W1 BSET.W W1,#8 MOV.W W1,U2TXREG REPEAT #190 NOP pp13_end: NOP MOV.W #progstat,W2 BSET.B [W2],#6 BCLR.B [W2],#5 MOV.B [W2],W1 AND.W #0100FF,W2 MOV.W W2,U2TXREG REPEAT #190 NOP BSET.W W1,#8 MOV.W W1,U2TXREG REPEAT #190 NOP BRA pp_done pp-not13: CP.B WO,#14 BRA NZ,pp_not14 REPEAT #12798 NOP Send SAC $53 $00 - MOV.W #01000F,W3 MOV.W W3,UlBRG MOV.W #0101AC,W3 MOV.W W3,U1TXREG REPEAT #3068 NOP MOV.W #010153,W3 MOV.W W3,U1TXREG REPEAT #3068 NOP MOV.W #010100,W3 MOV.W W3,U1TXREG REPEAT #3998 NOP #dataout,W2 I P Is this a check battery modules’ communication request? Delay 400us so no more bytes come in from PIC18F2553 Set the baud rate to 1MBd Recall the desired module address to test this time Send 256 different bytes to the module, check echoes Form a return value for the computer (address, retval) This value will in turn get transmitted back over USB Recall the address to which the bytes were written Send an address command to the PIC18F2553 Recall the written bytes Send two data byte commands to the PIC18F2553 Send an address command to the PIC18F2553 Set bit six of progstat to indicate read completion Clear bit five of progstat to indicate not bootloader Send the progstat data byte command to the PIC18F2553 Is this a reprogram battery modules Operation request? Delay 400us so no more bytes come in from PIC18F2553 enter the bootloader and address all modules globally Set the baud rate to 125de for the Atmel bootloader Delay about 3070 cycles Delay about 3070 cycles Delay about 4000 cycles 214 2043 2044 2046 2047 2048 2049 2050 2051 2052 2053 2054 2055 2056 2057 2058 2059 2060 2062 2063 2065 2066 2067 2068 2069 2070 2071 2072 2073 2074 2075 2076 2077 2078 2079 2080 2081 2082 2083 2084 2085 2086 2087 2088 2089 2090 2092 2093 2094 2095 2096 2097 2098 2099 2100 2101 2102 2103 2104 CLR. MOV. MOV. MOV. W0 U1RXREG,W1 U1RXREG,W1 MOV. U1RXREG,W1 MOV.W U1RXREG,W1 D0 #14,atmelloop (13“: atmelnext: SL.W W0,#1,W5 INC.W W0,W0 INC.W W5,W6 BTST.C [W4],W5 #proginfo,W4 ; BRA NC,atmelnoprog ; Send $00 $00 $81 MOV.W #010100,W1 MOV.W W1,U1TXREG REPEAT #3068 NOP MOV.W W1,U1TXREG REPEAT #3068 NOP MOV.W #010181,W1 MOV.W W1,UlTXREG REPEAT #3998 NOP Send 800 $00 $88 MOV.W #010100,W1 MOV.W W1,UlTXREG REPEAT #3068 NOP MOV.W W1,U1TXREG REPEAT #3068 NOP MOV.W #010188,W1 MOV.W W1,U1TXREG REPEAT #3998 NOP Send XX XX $81 MOV.W W0,W2 BSET.W W2,#8 MOV.W W2,U1TXREG REPEAT #3068 NOP MOV.W W2,U1TXREG REPEAT #3068 NOP MOV.W #010181,W3 MOV.W W3,U1TXREG REPEAT #9998 NOP Clear the module address counter Load the address of the Atmels-to-program list into W1 Clear out the receive buffer first, to prevent errors Create a doubled module address to find the bits Increment the counter early (addresses start at one) Is the pointed module commanded to be programmed? If not, keep looking for one that is - change the bootloader address to the global address (zero) e P - send a dummy command - change the bootloader address BTSS.W U1STA,#URXDA ; BRA atmelfailcc MOV.W U1RXREG,W3 MOV.W #01004B,W2 XOR.W W0,W2,W2 CP.B W2,W3 BRA REPEAT #998 NZ,atmelfailcc ; Delay about 3070 cycles Delay about 3070 cycles Delay about 4000 cycles to turn the red LEDs on Delay about 3070 cycles Delay about 3070 cycles Delay about 4000 cycles to this module’s address Delay about 3070 cycles Delay about 3070 cycles Delay about 10000 cycles; wait for a reception Is there at least one available word? If not, this is an enter bootloader failure If so, check to see if it’s 848 XOR address If it wasn’t this there was an enter bootloader failure Delay about 1000 cycles 215 2105 2106 2107 2108 2109 2110 2111 2112 2113 2114 2115 2116 2117 2118 2119 2120 2121 2122 2123 2124 2125 2126 2127 2128 2129 2130 2131 2132 2133 2134 2135 2136 2137 2138 2139 2140 2141 2142 2143 2144 2145 2146 2147 2148 2149 2150 2151 2152 2153 2154 2155 2156 2157 2158 2159 2160 2161 2162 2163 2164 2165 2166 NOP MOV.W #atmeldata,W7 ; Load the Atmel program data address into W7 CLR.W W8 ; Clear the Atmel address pointer, W8 atmelprog: Send YY 22 $03 - MOV.W W8,W3 BSET.W W3,#8 MOV.W W3,U1TXREG REPEAT #3068 NOP MOV.W W8,W3 MOV.W W8,W9 SWAP.W W3 BSET.W W3,#8 MOV.W W3,U1TXREG REPEAT #3068 NOP MOV.W #010103,W3 MOV.W W3,U1TXREG CLR.W W3 INC.W W3,W3 BRA NZ,.-2 Send $11 $11 $11 MOV.W #010111,W2 MOV.W W2,U1TXREG REPEAT #3068 NOP MOV.W W2,U1TXREG REPEAT #3068 NOP MOV.W W2,UITXREG MOV.W #-10000,W3 INC.W W3,W3 BRA NZ,.-2 atmelmore: erase flash ; Delay ; Delay ; Delay - clear the ; Delay ; Delay ; Delay page #ZZYY about 3070 cycles about 3070 cycles over 150,000 cycles (five milliseconds) temporary page buffer for flash writing about 3070 cycles about 3070 cycles about 30000 cycles ; Send YY ZZ 688 - load flash data into the Atmel’s temporary flash buffer (#1) MOV.B [W7++],W3 BSET.W W3,#8 MOV.W W3,U1TXREG REPEAT #3068 NOP MOV.B [W7++],W3 BSET.W W3,#8 MOV.W W3,U1TXREG REPEAT #3068 NOP MOV.W #010188,W3 MOV.W W3,UITXREG MOV.W #-10000,W3 INC.W W3,W3 BRA NZ,.-2 Send YY 22 $01 - MOV.W W8,W3 BSET.W W3,#8 MOV.W W3,U1TXREG REPEAT #3068 NOP MOV.W W8,W3 ; Delay ; Delay ; Delay about 3070 cycles about 3070 cycles about 30000 cycles load flash data into the Atmel’s temporary flash buffer (#2) ; Delay about 3070 cycles 216 2167 2168 2169 2170 2171 2172 2173 2174 2175 2176 2177 2178 2179 2180 2181 2182 2183 2184 2185 2186 2187 2188 2189 2190 2191 2192 2193 2194 2195 2196 2197 2198 2199 2200 2201 2202 2203 2204 2205 2206 2207 2208 2209 2210 2211 2212 2213 2214 2215 2216 2217 2218 2219 2220 2221 2222 2223 2224 2225 2226 2227 2228 INC2.W W8,W8 SWAP.W W3 BSET.W W3,#8 MOV.W W3,U1TXREG REPEAT #3068 NOP MOV. MOV. MOV. INC. BRA W #010101,W3 W W3,U1TXREG W #-10000,W3 W W3,W3 NZ,.-2 AND. BRA NZ,atmelmore Send YY ZZ 805 - MOV.W W9,W3 BSET.W W3,#8 MOV.W W3,U1TXREG REPEAT #3068 NOP MOV.W W9,W3 SWAP.W W3 BSET.W W3,#8 MOV.W W3,U1TXREG REPEAT #3068 NOP MOV.W #010105,W3 MOV.W W3,U1TXREG CLR.W W3 INC.W W3,W3 BRA NZ,.-2 MOV.W W9,W8 W W8,#01001F,W3 ; write MOV.W #atmeldata,W7 ; ADD.W W7,W8,W7 atmelverify: Send YY ZZ 884 - MOV.W W8,W3 BSET.W W3,#8 MOV.W W3,U1TXREG REPEAT #3068 NOP MOV.W W8,W3 INC.W W8,W8 SWAP.W W3 BSET.W W3,#8 MOV.W W3,U1TXREG REPEAT #3068 NOP MOV.W #010184,W3 MOV.W W3,U1TXREG REPEAT #9998 NOP Delay about 3070 cycles Delay about 30000 cycles Has the end of this page been reached? If not, keep loading this page buffer flash page SZZYY Delay about 3070 cycles Delay about 3070 cycles Delay over 150,000 cycles (five milliseconds) Rewind in memory and verify the written bytes Load the Atmel program data address into W7 Access the beginning of this flash page in DSPIC RAM read flash location SZZYY e 9 BTSS.W UISTA,#URXDA ; BRA atmelfailnoreply ; MOV.W U1RXREG,W3 CP.B W3,[W7++] BRA NZ,atmelfailbad ; REPEAT #998 NOP Delay about 3070 cycles Delay about 3070 cycles Delay about 10000 cycles; wait for a reception Is there at least one available word? If not, this is a communications error If so, check to see if it’s the same as the written If it wasn’t the same, this is a verification error Delay about 1000 cycles 217 2229 2230 2231 2232 2233 2234 2235 2236 2237 2238 2239 2240 2241 2242 2243 2244 2245 2246 2247 2248 2249 2250 2251 2252 2253 2254 2255 2256 2257 2258 2259 2260 2261 2262 2263 2264 2265 2266 2267 2268 2269 2270 2271 2272 2273 2274 2275 2276 2277 2278 2279 2280 2281 2282 2283 2284 2285 2286 2287 2288 2289 2290 AND.W W8,#01001F,W3 BRA NZ,atmelverify ADD.W #010140,W9 BTSS.W W9,#11 BRA atmelprog BCLR.W SR,#C BSW.C [W4],W5 BSW.C [W4],W6 BRA atmelnoprog atmelfailcc: BSET.W SR,#C BSW.C [W4],W5 BCLR.W SR,#C BSW.C [W4],W6 BRA atmelnoprog atmelfailnoreply: BCLR.W SR,#C BSW.C [W4],W5 BSET.W SR,#C BSW.C [W4],W6 BRA atmelnoprog atmelfailbad: BSET.W SR,#C BSW.C [W4],W5 BSW.C [W4],W6 atmelnoprog: MOV.B WREG,proglast AND.W W0,#010007,W1 BRA NZ,atmelnext INC2.W W4,W4 atmelloop: NOP ; Has the end of this page been reached? ; If not, keep verifying the written bytes ; Has the end of the program been reached (0106C0 done)? ; The above addition would create an overflow to 010800 ; This sequence programs bytes 010000-0106DF ; A return ; A return ; A return ; A return value value value value of of of of ’00’ means success ’01’ means entering bootloader failed ’10’ means a no reply verify error ’11’ means a bad byte verify error ; Note the last processed module address ; Test the lower three hits to know if this word is done ; If the bits are nonzero, there are still more data left ; Send $00 $00 $81 - change the bootloader address to the global address (zero) MOV.W #010100,W1 MOV.W W1,U1TXREG REPEAT #3068 NOP MOV.W W1,U1TXREG REPEAT #3068 NOP MOV.W #010181,W1 MOV.W W1,U1TXREG REPEAT #3998 NOP ; Delay about 3070 ; Delay about 3070 ; Delay about 4000 ; Send $00 $00 $88 - send a dummy command MOV.W #010100,W1 MOV.W W1,U1TXREG REPEAT #3068 NOP MOV.W W1,U1TXREG REPEAT #3068 NOP MOV.W #010188,W1 MOV.W W1,U1TXREG REPEAT #3998 NOP ; Delay about 3070 ; Delay about 3070 ; Delay about 4000 218 cycles cycles cycles to turn the last module’s red LED on cycles cycles cycles 2291 2292 2293 2294 2295 2296 2297 2298 2299 2300 2301 2302 2303 2304 2305 2306 2307 2308 2309 2310 2311 2312 2313 2314 2315 2316 2317 2318 2319 2320 2321 2322 2323 2324 2325 2326 2327 2328 2329 2330 2331 2332 2333 2334 2335 2336 2337 2338 2339 2340 2341 2342 2343 2344 2345 2346 2347 2348 2349 2350 2351 2352 ; Send $00 800 $00 - hang the modules and MOV.W #010100,W1 MOV.W W1,U1TXREG REPEAT #3068 NOP MOV.W W1,U1TXREG REPEAT #3068 NOP MOV.W W1,U1TXREG REPEAT #3068 NOP Delay about 3070 Delay about 3070 Delay about 3070 Inform the computer upon completion Set the baud rate back to 1MBd MOV.W #010001,W1 MOV.W W1,UiBRG MOV.W #progstat,W2 BSET.B [W2],#6 BCLR.B [W2],#5 MOV.B [W2],W1 AND.W #0100FF,W2 MOV.W W2,U2TXREG REPEAT #190 NOP BSET.W W1,#8 MOV.W W1,U2TXREG REPEAT #190 NOP BRA pp_done pp_not14: CP.B WO,#15 BRA NZ,pp-not15 MOV.W #010001,W0 MOV.W W0,UIBRG REPEAT #12798 NOP BTSS.W atmelt11,#15 BRA .+6 MOV.W atmelt11,W0 MOV.W W0,U1TXREG REPEAT #380 NOP BTSS.W atmeltx2,#15 BRA .+6 MOV.W atmeltx2,W0 MOV.W W0,U1TXREG REPEAT #380 NOP BTSS.W atmelt13,#15 BRA .+6 MOV.W atmelt13,W0 MOV.W W0,U1TXREG REPEAT #380 NOP BTSS.W atmeltx4,#15 BRA .+6 MOV.W atmeltx4,W0 MOV.W W0,U1TXREG REPEAT #380 NOP CLR.W atmelt11 CLR.W atmeltx2 let the watchdogs reset them cycles cycles cycles Send an address command to the PIC18F2553 Set bit six of progstat to indicate read completion Clear bit five of progstat to indicate not bootloader Send the progstat data byte command to the PIC18F2553 Is this a send 1MBd data to battery modules request? Set the baud rate Delay 400us so no Is Is Is Is the the the the top bit of top bit of top bit of top bit of 219 to 1MBd more bytes come in from PIC18F2553 atmelt11 set? If so, send out a word atmeltx2 set? If so, send out a word atmelt13 set? If so, send out a word atmeltx4 set? If so, send out a word 2353 2354 2355 2356 2357 2358 2359 2360 2361 2362 2363 2364 2365 2366 2367 2368 2369 2370 2371 2372 2373 2374 2375 2376 2377 2378 2379 2380 2381 2382 2383 2384 2385 2386 2387 2388 2389 2390 2391 2392 2393 2394 2395 2396 2397 2398 2399 2400 2401 2402 2403 2404 2405 2406 2407 2408 2409 2410 2411 2412 2413 2414 CLR.W atmelt13 CLR.W atmeltx4 BRA pp_done pp_not15: CP.B W0,#16 BRA NZ,pp_not16 MOV.W #OxOOOF,W0 MOV.W W0,U18RG REPEAT #12798 NOP BTSS.W atmelt11,#15 BRA .+6 MOV.W atmelt11,W0 MOV.W W0,U1TXREG REPEAT #3068 NOP BTSS.W atmeltx2,#15 BRA .+6 MOV.W atmeltx2,W0 MOV.W W0,U1TXREG REPEAT #3068 NOP BTSS.W atmelt13,#15 BRA .+6 MOV.W atmelt13,WO MOV.W W0,U1TXREG REPEAT #3068 NOP BTSS.W atmeltx4,#15 BRA .+6 MOV.W atmeltx4,W0 MOV.W W0,U1TXREG REPEAT #3068 NOP CLR.W atmelt11 CLR.W atmeltx2 CLR.W atmelt13 CLR.W atmeltx4 BRA pp_done pp_not16: CP.B WO,#17 BRA NZ,pp_not17 BTSS.W UlSTA,#URXDA BRA pp_noreply MOV.W U1RXREG,W1 BSET.W W1,#15 BCLR.W W1,#13 MOV.W W1,atmelrx MOV.W #atmelr1,W2 AND.W #0100FF,W2 MOV.W W2,U2TXREG REPEAT #190 NOP MOV.W #atmelr1,W2 DO #1,pp17_end MOV.B [W2++],W1 BSET.W W1,#8 MOV.W W1,U2TXREG REPEAT #190 NOP Is this a send 125de data to battery modules request? Set the baud rate to 125de Delay 400us so no more bytes come in from PIC18F2553 Is the tap bit of atmelt11 set? If so, send out a word Is the top bit of atmeltx2 set? If so, send out a word Is the top bit of atmelt13 set? If so, send out a word Is the top bit of atmeltx4 set? If so, send out a word Is this a check battery module reply operation request? Is there at least one available word? If not, clear progstat and send it to the PIC18F2553 Recall this available word Set bit seven of progstat to indicate a received word Clear bit five of progstat to indicate not bootloader This value will in turn get transmitted back over USB Recall the address to which the bytes were written Send an address command to the PIC18F2553 Recall the written bytes Send two data byte commands to the PIC18F2553 Note that one of these bytes is progstat 220 2415 2416 2417 2418 2419 2420 2421 2422 2423 2424 2425 2426 2427 2428 2429 2430 2431 2432 2433 2434 2435 2436 2437 2438 2439 2440 2441 2442 2443 2444 2445 2446 2447 2448 2449 2450 2451 2452 2453 2454 2455 2456 2457 2458 2459 2460 2461 2462 2463 2464 2465 2466 2467 2468 2469 2470 2471 2472 2473 2474 2475 2476 pp17-end: NOP BRA pp_done pp_not17: CP.B W0,#31 BRA NZ,pp_done pp_noreply: MOV.W #progstat,W2 CLR.B [W2] AND.W #0100FF,W2 MOV.W W2,U2TXR8G REPEAT #190 NOP MOV.W #010100,W1 MOV.W W1,U2TXREG REPEAT #190 NOP BRA pp_done PP-Prog: REPEAT #12798 NOP DISI #5 MOV.W #0155,W0 MOV.W W0,NVMKEY MOV.W #01AA,W1 MOV.W W1,NVMKEY BSET.W NVMCON,#WR NOP NOP BTSC.W NVMCON,#WR BRA .-2 MOV.W #progstat,W2 BSET.B [W2],#6 BCLR.B [W2],#5 MOV.B [W2],W1 AND.W #0100FF,W2 MOV.W W2,U2TXREG REPEAT #190 NOP BSET.W W1,#8 MOV.W W1,U2TXREG REPEAT #190 NOP pp_done: CLR.B progcmd pp_none: RETURN ; Is this a clear output buffer operation request? ; Send an address command to the PIC18F2553 ; Clear progstat to indicate no completion (UART or read) ; Send the progstat data byte command to the PIC18F2553 ; Delay 400us so no more bytes come in from PIC18F2553 ; Execute a generic programming cycle for many operations ; Wait until the Operation completes (for EEPROM) ; Send an address command to the PIC18F2553 ; Set bit six of progstat to indicate read completion ; Clear bit five of progstat to indicate not bootloader ; Send the progstat data byte command to the PIC18F2553 ; Clear the command so nothing happens accidentally eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee ;D’IDPIDBDDDIPIIDIDDDIPIIIIPPDDDBIPPPPIII’DPDPDDPIID’DPPOIPIDIIQPDPPPPDDIIBPD’,’ Interrupt service routine .global _,U2RXInterrupt __U2RXInterrupt: PUSH.S MOV.W U2RXREG,WO BTSS.W W0,#8 ; Save the status register, W0, W1, W2, and W3 ; Is bit eight low? If so, this is a new writing address 221 2477 BRA .+6 2478 MOV.B W0,[W14++] ; Write the received data to the local buffer at 010F00 2479 BRA .+6 2480 MOV.W #carstat,W14 ; Start the buffer address off at OxOFOO 2481 ADD.W W0,W14,W14 ; Save this byte as the new memory buffer writing address 2482 2483 U2RXInt_end: 2484 BCLR.W IFSl,#U2RXIF ; Clear the U2RX interrupt flag in IFS1 2485 POP.S ; Restore the status register, W0, W1, W2, and W3 2486 RETFIE 2487 2488 1$333333i;;;;;;i;;3333HiiiiififiifiHHFHHSE333;;HUMMBSSHSHHHS33333;; 2489 2490 ; Sine table 2491 2492 sine_table: ; 1024 entries, scaled/offset by 512, O to (360 degrees 2493 AND.W #0103FF,W3 ; Disallow any data access past 1023 (round to ten bits) 2494 INC2.W W3,W3 ; Account for the “D8C2 W3,W3" and "RETURN” instructions 2495 RCALL W3 2496 DEC2.W W3,W3 ; Put W3 back to what it was before sine_table was called 2497 RETURN 2498 RETLW.W #010200,W6 sin(000.0) - +0.0000 (0512) 2499 RETLW.W #010203,W6 sin(000.4) +0.0061 (0515) 2500 RETLW.W #010206,W6 sin(000.7) +0.0123 (0518) 2501 RETLW.W #010209,W6 sin(001.1) +0.0184 (0521) 2502 RETLW.W #010200,W6 sin(001.4) +0.0245 (0524) 2503 RETLW.W #01020F,W6 sin(001.8) +0.0307 (0527) 2504 RETLW.W #010212,W6 sin(002.1) +0.0368 (0530) 2505 RETLW.W #010215,W6 sin(002.5) +0.0429 (0533) 2506 RETLW.W #010219,W6 sin(002.8) +0.0491 (0537) 2507 RETLW.W #01021C,W6 sin(003.2) +0.0552 (0540) 2508 RETLW.W #01021F,W6 sin(003.5) +0.0613 (0543) 2509 RETLW.W #010222,W6 sin(003.9) +0.0674 (0546) 2510 RETLW.W #010225,W6 sin(004.2) +0.0736 (0549) 2511 RETLW.W #010228,W6 sin(004.6) +0.0797 (0552) 2512 RETLW.W #01022B,W6 sin(004.9) +0.0858 (0555) 2513 RETLW.W #01022E,W6 sin(005.3) +0.0919 (0558) 2514 2515 (Only 16 of 1024 entries shown) 2516 2517 3$33§3Si§3$§3333§§3§333"3133313333153333333:3331333313333313;3333HFHHHHSH 2518 2519 ; Power factor table 2520 2521 pf,table: ; 1024 entries, 1000¢cos(atan(ratio)), 0 to 1023/256 2522 AND.W #0103FF,WO ; Disallow any data access past 1023 (round to ten bits) 2523 INC2.W W0,W0 ; Account for the "DEC2 W0,W0" and “RETURN" instructions 2524 RCALL W0 2525 DEC2.W W0,W0 ; Put W0 back to what it was before pf_table was called 2526 RETURN 2527 RETLW.W #010388,W1 100.cos(atan(0.000)) - 100.00 (1000) 2528 RETLW.W #010387,W1 100tc0s(atan(0.004)) - 099.99 (0999) 2529 RETLW.W #010387,W1 100.cos(atan(0.008)) - 099.99 (0999) 2530 RETLW.W #010387,W1 100*cos(atan(0.012)) - 099.99 (0999) 2531 RETLW.W #010387,W1 100tcos(atan(0.016)) - 099.98 (0999) 2532 RETLW.W #010387,W1 100*cos(atan(0.020)) - 099.98 (0999) 2533 RETLW.W #010387,W1 100*cos(atan(0.023)) - 099.97 (0999) 2534 RETLW.W #010387,W1 100tcos(atan(0.027)) - 099.96 (0999) 2535 RETLW.W #010387,W1 100tcos(atan(0.031)) - 099.95 (0999) 2536 RETLW.W #010387,W1 100tcos(atan(0.035)) - 099.93 (0999) 2537 RETLW.W #010387,W1 100tcos(atan(0.039)) - 099.92 (0999) 2538 RETLW.W #010387,W1 100tcos(atan(0.043)) - 099.90 (0999) 222 2539 2540 2541 2542 2543 2544 2545 2546 2547 2548 2549 2550 2551 2552 2553 2554 2555 2556 2557 2558 2559 2560 2561 2562 2563 2564 2565 2566 2567 2568 2569 2570 2571 2572 2573 2574 2575 2576 2577 2578 2579 2580 2581 2582 2583 2584 2585 2586 2587 2588 2589 2590 2591 2592 2593 2594 2595 2596 2597 2598 2599 2600 RETLW. RETLW. RETLW. RETLW. W W W W #010386,W1 #010386,W1 #0103E6,W1 #0103EG,W1 (Only 16 of 1024 entries 100-cos(atan(0.047)) 100*cos(atan(0.051)) 100tcos(atan(0.055)) 100*cos(atan(0.059)) shown) 099.89 099.87 099.85 099.82 (0998) (0998) (0998) (0998) eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee D’DIIIPPD’IPPI’IPIDDIDI e ’ Square root table sqrt_table: AND.W #0103FF,W1 INC2.W W1,W1 RCALL W1 RETURN RETLW. RETLW. RETLW. RETLW. RETLW. RETLW. RETLW. RETLW. RETLW. RETLW. RETLW. RETLW. RETLW. RETLW. RETLW. RETLW. CCCCCCCCCCCCCCCC #010000,W1 #010020,W1 #01002D,W1 #010037,W1 #010040,W1 #010047,W1 #01004E,W1 #010054,W1 #01005A,W1 #010060,W1 #010065,W1 #01006A,W1 #01006E,W1 #01007B,W1 #010077,W1 #01007B,W1 (Only 16 of 1024 entries '9'!!!)l’i’l'9'9’9'9’ 1024 entries, D!IOODPIOI’DDDPIIOPPI’IDPPD’IODD sqrt(value), 0 to 1023 Disallow any data access past 1023 (round to ten bits) Account for the 32*sqrt(0000) 32*sqrt(0001) 32*sqrt(0002) 32*sqrt(0003) 32*sqrt(0004) 32*sqrt(0005) 32*sqrt(0006) 32*sqrt(0007) 32tsqrt(0008) 32*sqrt(0009) 32*sqrt(0010) 32tsqrt(0011) 32*sqrt(0012) 32*sqrt(0013) 32tsqrt(0014) 32tsqrt(0015) shown) 'DEC2 W1,W1" 0000 0032 0055 0064 0071 0078 0084 0096 0106 0110 0115 0119 0123 .00 .00 0045. .43 .00 .55 .38 .66 0090. 25 51 .00 0101. 19 .13 .85 .38 .73 .94 and "RETURN" instructions (0000) (0032) (0045) (0055) (0064) (0071) (0078) (0084) (0090) (0096) (0101) (0106) (0110) (0115) (0119) (0123) eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee The bootloader location (a custom section .SECTION boot,code,address(01007C00) Bootloader’s interrupt service routine .globa PUSH.S 1 __AltU2RXInterrupt __AltU2RXInterrupt: MOV.W U2RXREG,W0 BTSS.W W0,#8 BRA .+6 MOV.B W0,[W14++] BRA .+6 MOV.W #carstat,W14 ADD.W W0,W14,W14 BCLR.W IFSI,#U2RXIF POP.S RETFIE Save the status register, Is bit eight low? I’l’lIDPBDDDIDDIBPDIDSIIPPIID!!!)l’i’P’lll’Pl If so, l!)I9,I’DIPPPPP”,I!!!)P’D’OIP’P at 01007C00) W0, W1, W2, and W3 this is a new writing address Write the received data to the local buffer at 010F00 Start the buffer address off at OxOFOO Save this byte as the new memory buffer writing address Clear the U2RX interrupt flag in IFS1 Restore the status register, W0, W1, W2, and W3 eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee bootloader: CLR.B progcmd MOV.W #progstat,W2 BSET.B [W2],#6 The bootloader for updating 0100000000-0100007BFE flash Clear the command so nothing happens accidentally Send an address command to the PIC18F2553 Set bit six of progstat to indicate read completion 1223 2601 2602 2603 2604 2605 2606 2607 2608 2609 2610 2611 2612 2613 2614 2615 2616 2617 2618 2619 2620 2621 2622 2623 2624 2625 2626 2627 2628 2629 2630 2631 2632 2633 2634 2635 2636 2637 2638 2639 2640 2641 2642 2643 2644 2645 2646 2647 2648 2649 2650 2651 2652 2653 2654 2655 2656 2657 2658 2659 2660 2661 2662 BSET.B [W2],#5 3 MOV.B [W2].W1 AND.W #0100FF,W2 MOV.W W2,U2TXREG REPEAT #190 NOP BSET.W W1,#8 ; MOV.W W1,U2TXREG REPEAT #190 NOP bp_loop: MOV.B progcmd,WREG ; CPO.B W0 ; BRA Z,bp_loop CP.B WO,#1 : BRA NZ,bp_not1 MOV.W addressl,WO ; MOV.W W0,NVMADR MOV.W addressh,WO : MOV.W W0,NVMADRU MOV.W #014041,W0 ; MOV.W W0,NVMCON BRA bp_prog bp_noti: CP.B WO,#2 ; BRA NZ,bp_not2 MOV.W addressl,W2 ; MOV.W W2,NVMADR MOV.W addressh,WO ; MOV.W W0,NVMADRU MOV.W W0,TBLPAG ; MOV.W #flashbuf,W1 ; DO #31,bp2_end ; TBLWTL.B [v1++].[w2++] TBLWTL.B [W1++].[W2--] TBLWTH.B [w1++].[v2] INC2.W w2,w2 bp2_end: NOP MOV.W #014001,W0 : MOV.W W0,NVMCON BRA bp_prog bp_not2: CP.B W0,#3 ; BRA NZ,bp_not3 MOV.W addressl,WO ; MOV.W W0,NVMADR MOV.W addressh,WO ; MOV.W W0,NVMADRU MOV.W #014044,W0 ; MOV.W W0,NVMCON BRA bp_prog bp-not3: CP.B W0,#4 ; BRA NZ,bp_not4 MOV.W addressl,WO ; MOV.W W0,NVMADR Set bit five of progstat to indicate bootloader Send the progstat data byte command to the PIC18F2553 Test the DSPIC programming command Is there anything here? Is this an erase program memory row operation request? Recall the low address word and store in NVMADR Recall the high address word and store in NVMADRU Set up NVMCON for erasing a program memory row Is this a write program memory row Operation request? Recall the low address word and store in NVMADR Recall the high address word and store in NVMADRU Also store it in TBLPAG for the write latches Write 96 bytes of flash memory to the write latches Loop 16 times for a total of 32 instruction words Set up NVMCON for writing a program memory row Is this an erase data memory word operation request? Recall the low address word and store in NVMADR Recall the high address word and store in NVMADRU Set up NVMCON for erasing a data memory word Is this an erase data memory row operation request? Recall the low address word and store in NVMADR 224 2663 2664 2665 2666 2667 2668 2669 2670 2671 2672 2673 2674 2675 2676 2677 2678 2679 2680 2681 2682 2683 2684 2685 2686 2687 2688 2689 2690 2691 2692 2693 2694 2695 2696 2697 2698 2699 2700 2701 2702 2703 2704 2705 2706 2707 2708 2709 2710 2711 2712 2713 2714 2715 2716 2717 2718 2719 2720 2721 2722 2723 2724 MOV.W addressh,WO MOV.W W0,NVMADRU MOV.W #014045,W0 MOV.W W0,NVMCON BRA bp-prog bp_not4: CP.B W0,#5 BRA NZ,bp_not5 MOV.W addressl,WO MOV.W addressh,W1 MOV.W W1,TBLPAG MOV.W #progdata,W2 TBLWTL.W [W2].[W0] MOV.W #014004,W0 MOV.W W0,NVMCON BRA bp_prog bp_not5: CP.B W0,#6 BRA NZ,bp_not6 MOV.W addressl,W2 MOV.W W2,NVMADR MOV.W addressh,WO MOV.W W0,NVMADRU MOV.W W0,TBLPAG MOV.W #flashbuf,W1 REPEAT #15 TBLWTL.W [W1++].[W2++] MOV.W #01400A,W0 MOV.W W0,NVMCON BRA bp_prog bp_not6: CP.B W0,#7 BRA NZ,bp_not7 MOV.W addressl,WO MOV.W addressh,W1 MOV.W W1,TBLPAG MOV.W #progdata,W2 TBLWTL.W [W2].[W0] MOV.W #014008,W0 MOV.W W0,NVMCON BRA bp_prog bp_not7: CP.B W0,#8 BRA NZ,bp_not8 MOV.W addressl,W1 MOV.W addressh,W2 MOV.W W2,TBLPAG MOV.W #dataout,W2 TBLRDL.B [W1++].[W2++] TBLRDL.B [W1‘-].[W2++] TBLRDH.B [W1].[W2++] INC2.W W1,W1 TBLRDL.B [W1++].[W2++] TBLRDL.B [W1--].[W2++] TBLRDH.B [W1].[W2++] MOV.W #dataout,W2 AND.W #0100FF,W2 MOV.W W2,U2TXREG Recall the high address word and store in NVMADRU Set up NVMCON for erasing a data memory row Is this a write data memory word operation request? Recall the low address word Recall the high address word and store in TBLPAG Write the data word to the temporary buffer Set up NVMCON for writing a data memory word Is this a write data memory row operation request? Recall the low address word and store in NVMADR Recall the high address word and store in NVMADRU Also store it in TBLPAG for the write latches Write 96 bytes of flash memory to the write latches Loop 16 times for a total of 16 data EEPROM words Set up NVMCON for writing a data memory row Is this a write config. memory operation request? Recall the low address word Recall the high address word and store in TBLPAG Write the data word to the temporary buffer Set up NVMCON for writing a configuration memory word Is this a read memory long operation request? Recall the low address of the flash/EEPROM to read Recall the high address of the flash/EEPROM to read Write six bytes of flash/EEPROM memory to the buffer Recall the address to which the bytes were written Send an address command to the PIC18F2553 225 2725 2726 2727 2728 2729 2730 2731 2732 2733 2734 2735 2736 2737 2738 2739 2740 2741 2742 2743 2744 2745 2746 2747 2748 2749 2750 2751 2752 2753 2754 2755 2756 2757 2758 2759 2760 2761 2762 2763 2764 2765 2766 2767 2768 2769 2770 2771 2772 2773 2774 2775 2776 2777 2778 2779 2780 2781 2782 2783 2784 2785 2786 REPEAT #190 NOP MOV.W #dataout,W2 ; DO #5,bp8-end 3 MOV.B [W2++],W1 BSET.W W1,#8 MOV.W W1,U2TXR8G REPEAT #190 NOP bp8_end: NOP MOV.W #progstat,W2 ; BSET.B [W2],#6 : BSET.B [W2],#5 ; MOV.B [W2],W1 AND.W #0100FF,W2 MOV.W W2,U2TXREG REPEAT #190 NOP BSET.W W1,#8 ; MOV.W W1,U2TXREG REPEAT #190 NOP BRA bp_done bp_not8: CP.B W0,#9 ; BRA NZ,bp_not9 MOV.W addressl,W1 ; MOV.W addressh,W2 ; MOV.W W2,TBLPAG MOV.W #dataout,W2 DO #15,bp9_enda ; TBLRDL.B [W1++].[W2++] TBLRDL.B [W1--].[W2++] TBLRDH.B [w1].[w2++] INC2.W W1,W1 bp9_enda: NOP MOV.W #dataout,W2 ; AND.W #0100FF,W2 ; MOV.W W2,U2TXREG REPEAT #190 NOP MOV.W #dataout,W2 ; DO #47,bp9_endb ; MOV.B [W2++],W1 BSET.W W1,#8 MOV.W W1,U2TXREG REPEAT #190 NOP bp9_endb: NOP MOV.W #progstat,W2 ; BSET.B [W2],#6 : BSET.B [W2],#5 ; MOV.B [W2],W1 AND.W #0100FF,W2 MOV.W W2,U2TXREG REPEAT #190 NOP BSET.W W1,#8 ; Recall the written bytes Send six data byte commands to the PIC18F2553 Send an address command to the PIC18F2553 Set bit six of progstat to indicate read completion Set bit five of progstat to indicate bootloader Send the progstat data byte command to the PIC18F2553 Is this a read 48 memory bytes operation request? Recall the low address of the flash/EEPROM to read Recall the high address of the flash/EEPROM to read Write 48 bytes of flash/EEPROM memory to the buffer Recall the address to which the bytes were written Send an address command to the PIC18F2553 Recall the written bytes Send 48 data byte commands to the PIC18F2553 Send an address command to the PIC18F2553 Set bit six of progstat to indicate read completion Set bit five of progstat to indicate bootloader Send the progstat data byte command to the PIC18F2553 226 2787 2788 2789 2790 2791 2792 2793 2794 2795 2796 2797 2798 2799 2800 2801 2802 2803 2804 2805 2806 2807 2808 2809 2810 2811 2812 2813 2814 2815 2816 2817 2818 2819 2820 2821 2822 2823 2824 2825 2826 2827 2828 2829 2830 2831 2832 2833 2834 2835 2836 2837 2838 2839 2840 2842 2843 2844 2845 2846 2847 2848 MOV.W W1,U2TXREG REPEAT #190 NOP BRA bp_done bp_not9: CP.B WO,#10 BRA NZ,bp_not10 MOV.W addressl,W1 MOV.W progdata,W2 BCLR.W W1,#0 MOV.W W2,[W1] BRA bp_done bp_not10: CP.B W0,#11 BRA NZ,bp_not11 MOV.W addressl,W1 MOV.W #dataout,W2 BCLR.W W1,#0 MOV.W [v1++],[w2++] MOV.W [W1++l,[W2--] AND.W #0100FF,W2 MOV.W W2,U2TXREG REPEAT #190 NOP MOV.W #dataout,W2 D0 #3,bp11-end MOV.B [W2++],W1 BSET.W W1,#8 MOV.W W1,U2TXREG REPEAT #190 NOP bp11_end: NOP MOV.W #progstat,W2 BSET.B [W2],#6 BSET.B [W2],#5 MOV.B [W2],W1 AND.W #0100FF,W2 MOV.W W2,U2TXREG REPEAT #190 NOP BSET.W W1,#8 MOV.W W1,U2TXREG REPEAT #190 NOP BRA bp_done bp-not11: CP.B W0,#12 BRA NZ,bp_not12 REPEAT #12798 NOP RESET bp_not12: CP.B W0,#31 BRA NZ,bp_done MOV.W #progstat,W2 CLR.B [W2] BSET.B [W2],#5 Is this a write RAM word Operation request? Recall the low address word Prevent non-word-boundary writes Transfer a word of data to the RAM location No acknowledgement is needed for this Operation Is this a read RAM long operation request? Recall the address of the RAM to read Write this to the output buffer Prevent non-word-boundary reads Transfer a long of data to the output buffer Send an address command to the PIC18F2553 Recall the written bytes Send four data byte commands to the PIC18F2553 Send an address command to the PIC18F2553 Set bit six of progstat to indicate read completion Set bit five of progstat to indicate bootloader Send the progstat data byte command to the PIC18F2553 Is this a reset processor operation request? Delay 400us so no more bytes come in from PIC18F2553 Is this a clear output buffer Operation request? Send an address command to the PIC18F2553 Clear progstat to indicate no completion (UART or read) Set bit five of progstat to indicate bootloader 227 2849 2850 2851 2852 2853 2854 2855 2856 2857 2858 2859 2860 2861 2862 2863 2864 2865 2866 2867 2868 2869 2870 2871 2872 2873 2874 2875 2876 2877 2878 2879 2880 2881 2882 2883 2884 2885 2886 2887 2888 2889 2890 2891 2892 2893 2894 2895 2896 2897 2898 2899 2900 2901 2902 2903 AND.W #0100FF,W2 MOV.W W2,U2TXR8G REPEAT #190 NOP MOV.W #010120,W1 MOV.W W1,U2TXREG REPEAT #190 NOP BRA bp_done bp_prog: REPEAT #12798 NOP DISI #5 MOV.W #0155,W0 MOV.W W0,NVMKEY MOV.W #01AA,W1 MOV.W W1,NVMKEY BSET.W NVMCON,#WR NOP NOP BTSC.W NVMCON,#WR BRA .-2 MOV.W #progstat,W2 BSET.B [W2],#6 BSET.B [W2],#5 MOV.B [W2],W1 AND.W #0100FF,W2 MOV.W W2,U2TXREG REPEAT #190 NOP BSET.W W1,#8 MOV.W W1,U2TXREG REPEAT #190 NOP bp_done: CLR.B progcmd BRA bp_loop Send the progstat data byte command to the PIC18F2553 Delay 400us so no more bytes come in from PIC18F2553 Execute a generic programming cycle for many operations Wait until the operation completes (for EEPROM) Send an address command to the PIC18F2553 Set bit six of progstat to indicate read completion Set bit five of progstat to indicate bootloader Send the progstat data byte command to the PIC18F2553 Clear the command so nothing happens accidentally eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee ;!P”!!’I!l!)!l'l’ll’3’!!!)9’0099”!9”,ID’DI9’I.))9IOPDPIPPDI'DI’DDDDDPSIIIII” ; Configuration fuses .SECTION __FOSC.sec,psv .WORD 01C30F .SECTION __FWDT.sec,psv .WORD 01003? .SECTION __FBORPOR.sec,psv .WORD 010783 .SECTION __FGS.sec,psv .WORD 010007 .END 228 BIBLIOGRAPHY 229 BIBLIOGRAPHY [1] Metric Mind Engineering, “EV power inverters for street vehicle or boat conversion,” Online, Revised 18 August 2007. [2] AC Propulsion, Inc., “AC-150 Gen-2 EV Power System: Integrated Drive and Charging for Electric Vehicles,” Online, Accessed March 2008. [3] Kodama, Dave and Jean Kodama, “EV1 Chronicles: EV1 Chargers,” Online, Accessed March 2008. [4] Baldor Electric Company, “Product Overview: EJMM4106T,” Online, Accessed March 2008. [5] AC Propulsion, Inc., “tZerO Electric Sports Car Presentation,” Online, Accessed March 2008. [6] Corzine, Keith A., “Operation and Design of Multilevel Inverters,” University of Missouri—Rolla, June 2005. [7] Microchip Technology, Inc., “dsPIC30F401 1 / 4012 Data Sheet: High—Performance, 16—Bit Digital Signal Controllers,” DS70135E, Revised January 2007. [8] Microchip Technology, Inc., “PIC18F87J50 Family Data Sheet: 64/80-Pin High- Performance, 1-Mbit Flash USB Microcontrollers with nanoWatt Technology,” DS39775B, Revised May 2007. [9] Baldor Electric Company, “Product Overview: EM3314T,” Online, Accessed March 2008. [10] J. J. A. Wilkinson and G. A. Covic, “A New Pulse Charging Methodology for Lead Acid Batteries,” IPENZ Transactions, Vol. 25, No. 1, 5 March 1998. [11] Okazaki S., S. Higuchi, O. Nakamura, and S. Takahashi, “Influence of Superimposed Alternating Current on Capacity and Cycle Life for Lead-acid Batteries,” Journal of Applied Electrochemistry, V01. 16, NO. 6, November 1986, pp. 894-898. [12] Harris L. B. and J. P. D. Martin, “The Influence of Pulsed Discharge on the Capacity Of Lead/ Acid Batteries,” Journal of Power Sources, Vol. 12, NO. 1, 1984, pp. 71-80. [13] Ross, Dave, John Theys, and Steve Bowling, “Using the dsPIC30F for Vector Control of an ACIM,” Microchip, Inc., AN908, 2004. 230 [14] International Rectifier Corp., “IRS2004(S)PbF: Half-Bridge Driver,” PD60270, 27 November 2006. [15] Infineon Technologies AG, “BAS70.../BAS17OW: Silicon Schottky Diode,” 19 September 2007. [16] Atmel Corporation, “8—bit AVR Microcontroller with 2/4/8K Bytes In—System Programmable Flash: ATtiny24/44/ 84 Preliminary,” DOC8006G, Revised January 2008. [17] Texas Instruments Inc., “LP2950, LP2951: Adjustable Micropower Voltage Regulators with Shutdown,” SLVS582D, Revised March 2007. [18] Microchip Technology, Inc., “PIClSF2458/ 2553/ 4458 / 4553 Data Sheet: 28/ 40 / 44—Pin High-Performance, Enhanced Flash, USB Microcontrollers with 12- Bit A/ D and nanoWatt Technology,” DS39887B, Revised June 2007. [19] Axelson, Jan, “USB Complete: Third Edition,” Lakeview Research LLC, 2005. [20] Matteson, Arthur. “Arthur Matteson / Custom Auto Electronics Homepage,” http: / /home.comcast .net/ ~awmatt. 231