. ‘ .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, "");
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, "", product, timetext);
statuslabel->setText(statustext);
statuslabel->setTextFormat(0t::RichText);
} else {
sprint1(statustext, “(table vidth=10022> Unconnected
td> | %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