command translator for medical device...
Post on 10-Mar-2020
13 Views
Preview:
TRANSCRIPT
Command Translator for
Medical Device Testing
Senior Design Dec05-05
Final Report
Client
Guidant Corporation
Faculty Advisors
Professor John Lamont
Professor Ralph Patterson III
Team Members Adam Guy, CprE
Justin Koob, CprE
Marc McKee, EE
Adam Mishler, CprE
REPORT DISCLAIMER NOTICE
DISCLAIMER: This document was developed as a part of the requirements of an electrical and computer engineering course at Iowa State University, Ames, Iowa. This document does not constitute a professional engineering design or a professional land surveying document. Although the information is intended to be accurate, the associated students, faculty, and Iowa State University make no claims, promises, or guarantees about the accuracy, completeness, quality, or adequacy of the information. The user of this document shall ensure that any such use does not violate any laws with regard to professional licensing and certification requirements. This use includes any work resulting from this student-prepared document that is required to be under the responsible charge of a licensed engineer or surveyor. This document is copyrighted by the students who produced this document and the associated faculty advisors. No part may be reproduced without the written permission of the senior design course coordinator.
December 14, 2005
i
Table of Contents List of Figures ................................................................................................................ iii List of Tables ................................................................................................................. iv List of Definitions ........................................................................................................... v 1 Section 1: Introductory Material................................................................................. 1 1.1 Executive Summary .................................................................................... 1 1.2 Acknowledgement ...................................................................................... 2 1.3 Problem Statement ...................................................................................... 2
1.3.1 General Problem Statement ........................................................................ 2 1.3.2 General Solution Approach......................................................................... 3
1.3.2.1 GPIB Bus Overview .................................................................... 3 1.4 Operating Environment............................................................................... 4 1.5 Intended Users and Intended Uses.............................................................. 4
1.5.1 Intended Users ............................................................................................ 5 1.5.2 Intended Uses.............................................................................................. 5
1.6 Assumptions and Limitations ..................................................................... 5 1.6.1 Updated Assumptions List.......................................................................... 5 1.6.2 Updated Limitations List ............................................................................ 6
1.7 Expected End Product and Deliverables..................................................... 6 2 Section 2: Approach and Product Design Results ...................................................... 7 2.1 End Product Functional Requirements ....................................................... 7 2.2 Resultant Design Constraints...................................................................... 8 2.3 Approach considered and one selected ....................................................... 8
2.3.1 FPGA Approach.......................................................................................... 8 2.3.2 Virtual Instruments Software Abstraction Approach ............................... 11 2.3.3 National Instruments PXI approach.......................................................... 13 2.3.4 LabVIEW Component Approach ............................................................. 14 2.3.5 Selected Approach: NI-Device (C++) Based Solution ............................. 15
2.4 Detailed design.......................................................................................... 17 2.4.1 System Integration .................................................................................... 17 2.4.2 NI-Device Component .............................................................................. 19 2.4.3 Custom C++ Code .................................................................................... 20
2.4.3.1 Parser Function .......................................................................... 21 2.4.3.2 Command Process Functions..................................................... 22
2.4.4 Agilent I/O Libraries................................................................................. 23 2.4.5 Hardware Components.............................................................................. 25
2.5 Implementation process description ......................................................... 26 2.6 End-Product Testing Description.............................................................. 27 2.7 Project End Results ................................................................................... 27 3 Section 3: Resource and Schedule Requirement ...................................................... 28 3.1 Personnel Effort Requirements ................................................................. 28 3.2 Other Resource Requirements .................................................................. 30 3.3 Financial Requirements ............................................................................ 31 3.4 Schedule.................................................................................................... 32
ii
4 Section 4: Closure Material ...................................................................................... 35 4.1 Project Evaluation..................................................................................... 35 4.2 Commercialization.................................................................................... 37 4.3 Recommendations for Additional Work................................................... 38 4.4 Lessons Learned........................................................................................ 38 4.5 Risk and Risk Management ...................................................................... 40 4.6 Project Team Information ......................................................................... 41
4.6.1 Client Information..................................................................................... 41 4.6.2 Faculty Advisor Information..................................................................... 42 4.6.3 Student Team information ........................................................................ 42
4.7 Closing Summary...................................................................................... 43 4.8 References................................................................................................. 43 4.9 Appendices................................................................................................ 45
Appendix A........................................................................................................... 45 Appendix B ........................................................................................................... 50
iii
List of Figures Figure 1: General Block Diagram....................................................................................... 3 Figure 2: FPGA Approach, Flow Refinement .................................................................. 10 Figure 3: FPGA Approach, Command Translator Block Diagram .................................. 11 Figure 4: Software Abstraction Approach ........................................................................ 12 Figure 5: Basic Architecture of National Instruments PXI approach ............................... 13 Figure 6: Flow chart for LabVIEW component approach ................................................ 14 Figure 7: Basic System Integration................................................................................... 18 Figure 8: System Integration............................................................................................. 18 Figure 9: NI-Device Integration........................................................................................ 20 Figure 10: Custom Code Integration................................................................................. 20 Figure 12: Parser, Example Code ..................................................................................... 22 Figure 13: Command Process Function, Example Code .................................................. 23 Figure 14: Agilent I/O Libraries Integration..................................................................... 24 Figure 15: Agilent I/O Libraries, Example Code.............................................................. 25 Figure 16: Basic Hardware Layout ................................................................................... 26 Figure 17: Original Schedule ............................................................................................ 32 Figure 18: Revised Schedule ............................................................................................ 33 Figure 19: Actual Schedule............................................................................................... 34 Figure 20: Project Deliverables Schedule......................................................................... 34
iv
List of Tables Table 1: Original Personal Effort Estimates (hours)......................................................... 28 Table 2: Revised Personnel Effort Estimates (hours) ....................................................... 29 Table 3: Actual Personal Effort (hours)............................................................................ 29 Table 4: Other Resource Requirements (Original) ........................................................... 30 Table 5: Other Resource Requirements (Revised)............................................................ 30 Table 6: Other Resource Requirements (Actual).............................................................. 30 Table 7: Financial Requirements (Original) ..................................................................... 31 Table 8: Financial Requirements (Revised)...................................................................... 31 Table 9: Financial Requirements (Actual)........................................................................ 31
v
List of Definitions SMU (source measurement unit) – Fully programmable instrument capable of
measuring and sourcing both voltage and current simultaneously. Acts as four
instruments in one: voltage source, current source, voltage meter, and current
meter.
GPIB (general purpose instrumentation bus) – A bus developed to connect and
control programmable instruments. Provides a standard interface as defined by
the IEEE 488 standard for communication between instruments from different
sources.
ESD (electrostatic discharge) – A potentially hazardous surge of electrical charge
due to a voltage difference between objects.
FPGA (field programmable gate array) – An FPGA is a device that allows for
customization through a hardware design language of the low level hardware
elements.
IDE (integrated development environment) – A development environment in
which software can be developed, usually includes advanced features including a
debugger.
SCPI (standard commands for programmable instrumentation) – A standard
encompassing common commands used in testing and measurement
instrumentation.
VHDL – A very high speed integrated circuit hardware design language
1
1 Section 1: Introductory Material
The following material will provide an introductory overview of the project as a
whole.
1.1 Executive Summary A pacemaker helps to control one of the most critical organs of the human body,
the heart. Due to the level of importance of the pacemaker’s correct and
accurate functionality, pacemakers are extensively tested using computer driven
instrumentation. Furthermore, the software used to test pacemakers is subjected
to rigorous testing and is granted certification through government agencies. The
command translator project was proposed to minimize recertification upon
obtaining a new SMU for Guidant’s laboratory test system.
A translator device was created to intercept commands destined for the Keithley
237 SMU to be replaced on Guidant’s test platform. After intercepting these
commands, the device translates them from 237 proprietary commands into
SCPI compliant commands that can be used with newer, enhanced
instrumentation. The device also returns responses to the translator. This
process will allow Guidant to use existing, certified software programs with newer
equipment while not having to totally re-certify all applications. This device will
save time, resources, and facilitate the use of existing, reliable, certified test
software.
The team came up with several design approaches and ultimately chose to use a
software based approach on a separate PC for the translation. The translation
PC was designed to operate like a black box imitating the Keithley 237 SMU.
This included the use of NI-Device, a component that allowed the PC to listen on
the bus. C++ code was integrated into NI-Device to perform the actual
translation.
2
Despite several setbacks during the project’s timeline, a “proof of concept” for the
device was completed. The pursuit of other design approaches enabled the
team to have a better understanding of the project’s purpose. Had recertification
not been an issue, a more direct approach could have been taken. The best one
identified by the team would be to compute the translation on the PC issuing
commands, or the “Virtual Instruments Software Abstraction Approach.”
1.2 Acknowledgement The team would like to thank the faculty advisors, Dr. John Lamont and Prof.
Ralph Patterson for their assistance and direction throughout the project. The
team is also grateful for the immense financial assistance and technical guidance
from Andrew Garnett, and Greg Stamp and Max Cortner of Guidant. Andy
Marshall of National Instruments also contributed to our project by providing us
with required software and additional technical experience and expertise.
1.3 Problem Statement The following two sections outline the general solution approach.
1.3.1 General Problem Statement Guidant currently uses a Keithley 237 SMU (source measurement unit) that
receives proprietary commands via a GPIB (general purpose instrumentation
bus) bus. In the near future, Guidant may upgrade these source measurement
units to a different, more capable model requiring newly formatted commands. In
order to update the source measurement units, the testing software will need to
be changed, essentially requiring extensive re-certification. A device will be
designed that will, instead of requiring a change in the testing software, be fitted
in-line with the software and instrumentation to translate the old commands into
commands recognizable by newer instrumentation. This approach should
minimize the client’s re-certification obligations by only requiring certification of
the device itself, and not the original testing software.
3
1.3.2 General Solution Approach
Figure 1: General Block Diagram
The following two sections will provide overview information regarding the
operations of the GPIB bus and bus timing issues.
1.3.2.1 GPIB Bus Overview The GPIB is a complex bus used normally to transmit testing commands and
data between a test controller (only one controller can be present on a bus) and
testing instrumentation (up to 15 devices may be daisy-chained together).
During the operation of the GPIB, the controller (usually a computer)
administrates all bus operations. Only one device is allowed to talk at one time
and is addressed by the controller. Listeners are also addressed by the
controller.
One very important part of the GPIB is the handshake sequence as well as the
handshake control lines. There are three handshake control lines as defined in
the following:
1) DAV (data available) – This line is controlled by the talker and indicates to
the listeners that valid data is present on the bus.
2) NRFD (not ready for data) – This line is controlled by the listeners. The
signal on this line indicates whether all of the listeners are ready for data.
The talker should not send data until this line is false. This signal does not
change unless all listeners maintain the same signal.
4
3) NDAC (not data accepted) – This line is controlled by the listeners. The
signal on this line indicates whether all of the listeners have accepted the
data. This signal does not change unless all listeners maintain the same
signal.
A handshake sequence is initiated and carried out through the use of three
handshake control lines. The handshake facilitates accurate and credible
transmissions across the bus. The basic handshake takes place as follows:
1) All listeners indicate that they are ready to listen by setting NRFD to false,
and NDAC to false.
2) The talker places data on the data lines and indicates to the talkers that
data is valid and available by setting DAV to true.
3) The listener receives the indication that the data is valid. Initially the
listeners will see the data and set NDAC to true, indicating that they see
the data, but have not yet copied it. When the listeners are finished
accepting the data, they indicate their status by setting NDAC to false.
4) The listeners indicate they are prepared to accept another byte of data,
and the sequence continues again by setting NRFD to false.
1.4 Operating Environment Since the system will be used for testing pacemakers, the primary operating
environment will be in an indoor lab setting. The system could be sensitive to
ESD and should be kept in a controlled environment. The system should also be
relatively durable, as it could be moved around.
1.5 Intended Users and Intended Uses The following two sections will detail the intended users and intended uses of the
Command Translator for Medical Device Testing.
5
1.5.1 Intended Users The translator will be designed to meet specific requirements set forth by
Guidant. The intended users of the device are Guidant test engineers,
technicians, and anyone else that Guidant designates should be working with the
testing equipment. The device is not proprietary to Guidant, however, and it is
therefore conceivable that the device could be used by someone or some other
company facing a similar situation.
1.5.2 Intended Uses
The team anticipates that Guidant will use this tool to assist in the testing of their
pacemakers. The translation device will be designed to translate commands
specifically destined for the Keithley 237 source measurement unit (SMU). The
device will be designed so that it is possible to easily reprogram it to translate
any arbitrary command to be recognized by any sort of instrument utilizing a
GPIB bus.
1.6 Assumptions and Limitations The following two sections detail the initial assumptions and limitations that the
project will be designed around. These lists will be updated frequently to reflect
design decisions.
1.6.1 Updated Assumptions List The updated assumptions are shown in the following list:
• While other equipment could be supported, the translator will be
configured to make translations originally destined for the Keithley SMU to
another newer, more capable device
• Translator will be used exclusively by Guidant Test Engineers
• Translator will be easily connected
6
• Any language (C, C++, assembly) can be used to initially program the
device software
• The translator will run on a standard computer with two GPIB cards
• NI-Device will be used to emulate a device on the GPIB bus
• Guidant will provide all required technical specifications
• Guidant will provide all required equipment
• Replacement SMU will be SCPI compliant
1.6.2 Updated Limitations List The updated limitations are shown in the following list:
• The bus interface will be GPIB (IEEE 488)
• Commands to be translated originated from a Keithley 237 SMU
• Replacement SMU model is unknown
• Software must compute and translate new command within time intervals
that do not interfere with other instrumentation
1.7 Expected End Product and Deliverables
The primary deliverable will be software that will serve as a proof of concept for
the command translation. This software will be able to translate a subset of the
commands previously utilized by the Keithley 237 source measurement unit, into
the correct SCPI compliant command to be used by the replacement SMU.
Although this software will not be capable of being immediately integrated into
Guidant’s testing platform, the translation concept may be proven as a plausible
solution upon the Agilent GPIB components within the senior design lab at Iowa
State University.
In addition, as this project has been transformed into a “proof of concept”
problem, the testing performed on the software will be minimal. Thus, the only
testing deliverables will be a short document illustrating the correct translation of
the small subset of Keithley 237 commands. Finally, the main end product
7
deliverables will be delivered to Guidant before the end of the project, December
of 2005.
2 Section 2: Approach and Product Design Results
In order to maintain success throughout the project, it is necessary to define an
approach from which to operate. The following section details the functional
requirements and design considerations for the command translator project.
2.1 End Product Functional Requirements
The working design will have several requirements in order to function properly.
This functionality is explained below:
• Translate SMU data – A subset of viable data received by the translator
device must be accurately converted to that which is required by the SMU.
• GPIB compatible – I/O data must adhere to IEEE STD-488 (GPIB).
• Multiple addresses – The device software must be able to correctly
distinguish and communicate with multiple testing devices at once.
• Two-way communication – The device software must be able to send and
receive data to/from the SMU. While the primary function of the interface
is to translate and submit commands, it may also be required to receive
information such as device status or a data dump.
• Selective processing – Some of the data being transmitted across the
GPIB bus to the translator device may not be intended for the SMU. The
translator device must choose to ignore and pass undesired information.
8
2.2 Resultant Design Constraints
Obtaining successful results in design completion and implementation will
depend upon the constraints listed below. Initially based on assumptions and
limitations, the list has been expanded to include additional constraints.
• Available data - It is likely that some of the data on the bus available to the
device will not be intended for the SMU. Translator operation must take
this into account and pass data through when necessary.
• Bus type – The data interface must be compatible with the IEEE 488 GPIB
bus. This instrumentation bus is used in Guidant’s testing system and
with the Keithley 237 SMU. The new SMU will also be utilizing this bus
type.
• Compatibility – Newer instrument models use standard commands for
programmable instrumentation (SCPI). This standard allows for common
programming functions among similar devices. As the new SMU has not
yet been identified, the SCPI commands will be used in the translation
design. This will allow for different options in the choice of SMU,
minimizing the effect of proprietary commands.
2.3 Approach considered and one selected
Throughout the development process, a large number of different approaches
were considered. In the following section, information pertaining to the FPGA
approach, Virtual Instruments software abstraction approach, National
Instruments PXI approach, and the LabVIEW component approach.
2.3.1 FPGA Approach One of the initial and more prominent approaches was the FPGA approach.
Initially, the product was to be designed as a stand-alone device consisting of
9
some sort of processing element. As the name indicates, the FPGA approach
took advantage of the flexibility of an FPGA and the sheer speed of hardware in
order to solve the translation problem.
The design employed for the FPGA approach was derived originally from states,
as described in the flowchart in Figure 2. The flow chart consisted of five
different states; idle, setup I/O, process data, send data, and termination. After
the development of the states, it was perceived that there was a need for some
refinement in the model used.
In going through a refinement process, the five original states were refined into
three functional units. These functional units consisted of setup I/O, process
data, and data transmission.
• Setup I/O: This functional unit was refined from the original idle and setup
IO states. The refinement was made in order to simplify the design. The
idle state of the original flow chart was simple and could easily be handled
with the setup IO functional unit.
• Process data: The process data functional unit is quite a complex unit.
Due to the complexity of the unit, the process data functional unit will not
be refined further.
• Data transmission: The data transmission functional unit was comprised of
the original send data and termination states. The original states were
very similar in the fact that they both dealt with the transmission of the
data. For this reason, it seemed logical to refine the original states into
one data transmission module.
10
Figure 2: FPGA Approach, Flow Refinement
Figure 3 below details the block diagram of the command translator device as
seen while focusing on the FPGA approach. The input and output pins can be
seen surrounding the functional block (center). As discussed prior, the command
translator will be placed in-line on a bidirectional bus. For this reason, the pins
that the translator needs to append are configured along the left and right sides
of the functional block. The left side defines bidirectional pins connected to the
controller, while the right side indicates bidirectional pins connected to the
listeners.
11
Pins located at the bottom of Figure 3 define pins that the command translator
will need to monitor, however, will not need to append. For this reason, the pins
have been defined as inputs. The bus will only be tapped for these pins.
Figure 3: FPGA Approach, Command Translator Block Diagram
The FPGA approach was a viable approach that would have met functional
requirements of the system, however, would have taken a lot of time to
successfully implement. With issues regarding the amount of time remaining for
implementation, the team reluctantly decided to step away from this approach
and determine a more suiting approach considering the amount of time that
remained for development.
2.3.2 Virtual Instruments Software Abstraction Approach
An additional alternative approach was the Virtual Instruments software
abstraction approach. The idea seemed quite simple to implement, and also
quite efficient, however, there were a few issues with FDA certification.
12
The software abstraction approach looked directly at the Guidant controller pc,
and considered wrapping the current drivers with another layer of software
abstraction. Basically, this approach would intercept commands sent between
Guidant’s test drivers and the actual GPIB interface card on the Guidant
controller PC. This way, if one wanted to translate commands destined for the
Keithley 237 SMU, one could intercept the commands before they were ever
interjected onto the bus and change them immediately on the Guidant controller
PC. Figure 4 illustrates further the method in which the software abstraction
approach works.
Guidant Drivers
Software Abstraction Layer
GuidantController
Figure 4: Software Abstraction Approach
The approach is quite clean, as well as basic. The approach also wouldn’t
require any additional hardware to meet functional requirements. The main issue
that was discussed with regards to this solution was that there could be
unforeseen issues with future FDA certification. In order to maintain certification,
Guidant cannot change any of the test drivers without recertifying the testing
platform. Since this solution put a wrapper around the drivers, it is arguable as to
whether or not the drivers are the same. In the end, this solution could cause
legal issues with regards to certification, and with that a decision was made to
move to a different approach.
13
2.3.3 National Instruments PXI approach
The National Instruments PXI approach was an idea that used a PXI-based
system. The solution would use a PXI chassis and LabVIEW software to develop
a framework that would be run on a controller or PC. The PXI instrument would
monitor the GPIB bus and responds to commands sent to a specific address on
the bus. It would then take those commands and translate them using LabVIEW
VI’s acting like the existing instrument utilizing an XML configuration file to
maintain the correct translations. The VI’s also make sure the new translations
take in the appropriate return value, whether that is a measurement or just an
acknowledgement of completion. A diagram of the basic architecture is shown in
Figure 5.
1
Figure 5: Basic Architecture of National Instruments PXI approach
The advantage of using the PXI approach is the system has already been built by
National Instruments and the team would just have to implement the command
mappings by editing the XML file. This would also have been of great help to
Guidant in future upgrades. They again would only have to edit the XML file.
One of the biggest disadvantages of the PXI approach was that the total cost
would have been around $10,000. The added simplicity might be worth it for
Guidant to pursue this approach in the future, but as of now there are simpler
solutions available. Another disadvantage for this approach was time. When the
1 from “Instrument Obsolescence Replacement Solution” by Dany Cheij
14
team was pursuing this approach it was the end of the first semester and the
team was leaving for summer break. As a result when the team came back the
focus had changed and time to implement a solution was shortening.
2.3.4 LabVIEW Component Approach The LabVIEW component approach would first accept a command using NI-
Device. NI-Device would then call written LabVIEW code. The LabVIEW code
takes that command and does a database query which looks up the translation of
the old command and returns the new command. After this translation, the new
command would then be sent to the LabVIEW instrument I/O assistant which
would then send the command to the replacement SMU. Once this command
has been sent to the SMU the appropriate response would then be returned back
from the SMU to the LabVIEW instrument I/O assistant, then to NI-Device, and
finally back to the controller. The flow chart is given in Figure 6.
Figure 6: Flow chart for LabVIEW component approach
The advantage of this approach is that writing LabVIEW code is simple. It
basically involves dragging and dropping of components. It is also characterized
15
by a relatively simple flow. In addition, the system is easily adaptable to any new
device on the bus. There would only have to be changes to the database and
minor changes to some of the remaining applications to accommodate for further
translation.
On the other hand, for this approach there are a few disadvantages. One of the
disadvantages is adding another software layer. The complexity is increased
when going from the NI-Device, which is written in C++ code, to LabVIEW. The
communication between these two components proved to be difficult. While
using this approach Guidant would also have to have access to multiple toolkits.
The team found that in acquiring communication from LabVIEW to the database
there is the need for National Instruments Database Connectivity Toolkit which
alone costs around $1,000.
2.3.5 Selected Approach: NI-Device (C++) Based Solution After carefully considering and/or experimenting with each of the approaches
discussed above, the approach chosen was a C++ based solution. Although this
approach is extremely similar to the LabVIEW Component, this final approach
completely eliminated the use of LabVIEW within the project. As discussed, this
approach requires an additional PC to be added to the GPIB bus in place of the
old Keithley 237 source measurement unit (SMU); the PC will be addressed as if
it were the old SMU. Generally speaking, this PC will receive the old commands
that are distributed by the controller, supposedly destined for the old SMU.
These commands will then be translated into the appropriate sequence of
commands for the new replacement SMU. The generated code sequence would
be sent back down the bus to the new SMU.
Unfortunately, the command translator PC cannot act as a listener on the GPIB
bus without containing software that provides that type of functionality. As a
donation from National Instruments, the design team was able to acquire such
16
software. This application, known as “NI-Device”, serves as the foundation for
the rest of the translation software developed upon the PC.
After the translator PC initially receives an old Keithley 237 command via the NI-
Device software, the command is passed on to the C++ parsing code written by
the design team. As can be observed in Figure 12, this code parses the
incoming command string into two variables, a command letter which is
associated with a specific function, followed by a corresponding sequence of
parameters. Then, this data is sent to the command processing function, which
formulates the new SCPI command to be passed onto the new SMU. However,
before the new command actually reaches the replacement SMU, the data must
be linked to the appropriate libraries, based on the GPIB card used on the PC,
allowing for the command to be correctly interpreted by the new SMU.
This final approach is very advantageous when compared to the other designs
considered, due to the uniformity of the software, as well as the ability to
implement it in a quick and efficient manner. Since all of the code is written in
C++, this approach eliminates the complexity of adding new software layers such
as LabVIEW to the design. In addition, with the assistance of NI-Device, this
approach only required some basic C++ coding, without the need to incorporate
several different components. Thus, this approach allowed for a quick
implementation that could be successfully completed within the restricted time
limit resulting from the early project complications.
Furthermore, as this approach was clearly the optimal choice out of all the
designs considered, the disadvantages were minimal. The only major
disadvantage to this approach is lack of updateability, due to the command
translations being hard-coded within the C++ code. This characteristic requires
the ability to code in C++, in order to make any modifications/updates to the
current command translations, while other approaches would only require the
ability to add an entry to a simple database or file.
17
2.4 Detailed design The NI-Device (C++) based approach consists of a number of different
programming libraries that make up the system as a whole. In the following
section, you will find specific information regarding the component system
integration, the NI-Device component, and the Agilent I/O Libraries component.
2.4.1 System Integration
As previously stated, the design employed to meet requirements is an integration
of a number of C++ programming libraries interfacing with hardware. Every
integrated component of the system plays a crucial role in the functionality of the
design. The primary components consist of NI-Device, custom C++ code,
Agilent GPIB I/O libraries, and two hardware GPIB interface cards.
Figure 7 details briefly how the components fit together in order to make one
system. Basically, the NI-Device libraries manage the operation of the GPIB
interface card that communicates with the Guidant controller. The custom C++
code in the center of the diagram is used to process the command passed to NI-
Device from the Guidant controller. At the completion of execution of the custom
C++ code, a new SCPI command is formulated. From there, the command is
passed to the Agilent I/O libraries, which manage the interfacing with the second
GPIB card, connected to the replacement SMU. The Agilent I/O libraries are
responsible for sending and receiving information to the new SMU.
18
Figure 7: Basic System Integration
A more specific system integration can be seen in Figure 8 on the following page.
In order to better understand the integration, the following will describe the main
success scenario for the command translator as data flows through the
components of the system.
Figure 8: System Integration
19
• A command is sent from the Guidant test controller to the Command
Translator. Through the use of NI-Device, the command translator looks
just like any other instrument on the GPIB bus.
• The command is propagated through the NI-Device library, to the parser.
The parser separates the command letter (see the appendix for command
letters and mappings) and the parameters.
• Each command has a unique command processing function. This
function is then called to formulate the new SCPI command. SCPI is a
new standard used by most instrument manufacturers.
• The new SCPI command is then sent to the replacement SMU through the
use of the Agilent I/O libraries.
• If the command was a query, the replacement SMU will respond, and the
message will be retrieved again through the use of the Agilent I/O
libraries.
• The response is then sent to NI-Device, which in turn indicates to the
Guidant controller that a message is ready to be sent.
2.4.2 NI-Device Component NI-Device is a software library sold by National Instruments that allows for the
emulation of a GPIB device. This component of the system is critical in making
the command translator appear to the Guidant controller as if it were the old
Keithley 237 SMU. The NI-Device libraries are bound to the GPIB interface card,
managing all of the communications with the Guidant controller. The software
provides an input and output buffer to the GPIB card. By reviewing the input
buffer, and placing data on the output buffer, information can be passed between
the Guidant controller and the command translator.
20
Figure 9: NI-Device Integration
By using the NI-Device software libraries, a number of bus specific factors no
longer need to be considered. The Ni-Device software deals with all of the bus
specific commands, including handshaking. The use of the libraries has helped
to create a more controlled and modular software development environment.
2.4.3 Custom C++ Code In order to make all of the components work together, it was necessary to write
custom code. This custom code consists of calls to both the NI-Device libraries,
as well as the Agilent I/O libraries.
Figure 10: Custom Code Integration
As you can see in Figure 10 above, the custom code is really the code that holds
the whole system together, as well as does all of the processing and actual
translation in receiving the old command and outputting the new command.
21
The custom code is broken down into a few
different sub-processes; the parser and the
command function code (see Figure 11). The
parser is the software written to break down the old
command string into its specific components, and
then calls the specific command process function
code in order to parse out the remaining
parameters and formulate the new command.
In the following two sections, more specific details
including C++ code excerpts will be shared with
regards to the custom code.
2.4.3.1 Parser Function
The parser custom code (see Figure 12) is activated when an event is triggered
by input coming in on the NI-Device message buffer. Each device dependent
command for the Keithley 237 is made up of one letter, followed by the
subsequent parameters specific to that command. With that said, the parser
needs to go through the message buffer and determine the first letter of the
command string. Once the first letter is identified, the parser can then call the
command processing function for the specific command in order to further
process the command and formulate the new command.
Custom Code (C++)
Parser (C++ Code)
Command Processing Function
Figure 11: Custom Code Components
22
Figure 12: Parser, Example Code
As can be seen above in Figure 12, the first character of the command string is
parsed from the message buffer (pMsgBuf) into the commandBuffer character.
From there, the switch statement guides the software to the correct command
process function. The remainder of the C++ code can be found in the
appendices.
2.4.3.2 Command Process Functions For each command supported by the translator, there is a command process
function. In this function, the parameters are further parsed from the message
buffer. The parameters are then saved as local variables and sent back out to
the new SMU in the newly formatted SCPI command. An example of a
command process function for the Keithley 237 “D” (Display) command can be
seen in Figure 13.
void CSimpleDeviceFramework::_ParseMessage (C4882MsgBuffer * pMsgBuf) { char* commandBuffer = (char*)pMsgBuf->GetBuffer(); char command = *commandBuffer; switch(command) { //the command was Display (D) case 'D': processCommandD(commandBuffer); break; case 'F': processCommandF(commandBuffer); break; }
23
Figure 13: Command Process Function, Example Code
As is demonstrated above in Figure 13, the command is stripped of its
parameters. In this specific example, the crucial parameter is the message to
display (displayMessage). Once this parameter is parsed, it is then sent using
Agilent I/O libraries to the replacement SMU.
2.4.4 Agilent I/O Libraries
The Agilent I/O libraries are C++ libraries used to communicate directly to GPIB
compliant devices connected to Agilent GPIB cards. In the case of this project
and the resources available, an Agilent GPIB card was used. Dependent upon
void CSimpleDeviceFramework::processCommandD(char* command) { eStatusMessage _Status = NIDEVICE_STATUS_UNKNOWN; int i = 0; int commandOptionNumber = *(command+1) - 48; char parser = *(command+3); char displayMessage[20]; for(i = 1; parser != 'X'; i++) { displayMessage[i-1] = parser; parser = *(command+3+i); } displayMessage[i] = '\0'; ViSession defaultRM, vi; char buf [256] = {0}; /* Open session to GPIB device at address 5 */ viOpenDefaultRM (&defaultRM); viOpen (defaultRM, "GPIB0::5::INSTR", VI_NULL,VI_NULL, &vi); /* Initialize device */ viPrintf (vi, "*RST\n"); /* Send the displayMessage to the device */ viPrintf (vi, "DISP:TEXT \"%s\"\n", displayMessage); /* Close session */ viClose (vi); viClose (defaultRM); }
24
the type of GPIB interface card being used, a different set of libraries (such as
NI-488.2) may need to be used.
As NI-Device fit with the first GPIB card (left) and allowed for the communication
with the Guidant controller, the Agilent I/O libraries are bound to the second
GPIB interface card. The Agilent I/O libraries manage all communications
between the second (right) GPIB interface card and the replacement SMU.
Figure 14: Agilent I/O Libraries Integration
The Agilent I/O libraries allow the software to communicate directly to the
replacement SMU through C++ code. This solution helps to reduce overhead by
allowing for direct communication with the GPIB instrument, as opposed to
calling some third party or external software to accommodate the task. In Figure
15, some example code utilizing the Agilent I/O libraries can be seen. This
particular code will connect to the device on address GPIB0::5::INSTR and query
the instrument to identify (command “*IDN?”) itself.
25
Figure 15: Agilent I/O Libraries, Example Code
As can be seen in Figure 15, once the connection has been established to the
GPIB device, the sending and receiving of data is quite basic, using very similar
syntax as the standard C/C++ printf() and scanf() functions.
2.4.5 Hardware Components The approach chosen uses minimal hardware. The hardware components of the
system incorporate two GPIB PCI cards in a single computer, known as the
translator. Each GPIB card will both be connected to independent busses. The
first bus connected to interface 1 will communicate directly with the Guidant
controller. The second bus, connected to interface 2 will communicate directly
with the replacement SMU. The translator will act as an administrator, verifying
that commands and data passed through it are compatible with the subsequent
destination bus. For example, if a device dependent command destined for the
Keithley 237 is moving from the Guidant controller (interface 1) to the
replacement SMU (interface 2), the translator will need to make sure that the
device dependent command has been translated to SCPI before being allowed to
/* Open session to GPIB device at address 5 */ viOpenDefaultRM (&defaultRM); viOpen (defaultRM, "GPIB0::5::INSTR", VI_NULL,VI_NULL, &vi); /* Initialize device */ viPrintf (vi, "*RST\n"); /* Send an *IDN? string to the device */ viPrintf (vi, "*IDN?\n"); /* Read results */ viScanf (vi, "%t", &buf); /* Print results */ printf ("Instrument identification string: %s\n", buf); /* Close session */ viClose (vi); viClose (defaultRM);
26
be passed through the translator. In Figure 16, the basic layout of the hardware
can be seen.
Guidant Test Controller
CommandTranslator
Other Instruments
GPIB Communication Bus
Replacement SMU
GPIBInterface 1
GPIBInterface 2
Figure 16: Basic Hardware Layout
2.5 Implementation process description The implementation process went relatively smooth, considering all of the
adversity previously presented to the design team. The only major problem
encountered within the short time period allotted for implementation, was the
combination of the LabVIEW component with the C++ code. Rather than
attempting to accomplish this joining of two different types of software, the design
team found it to be much more simple and efficient to implement the LabVIEW
component in C++, effectively eliminating LabVIEW from the project entirely.
Thus, the resulting project consisted only of an additional PC to be added to the
GPIB bus, NI-Device software, and the additional C++ code written by the design
team. With respect to improvements, the only major suggestion would be to
allow for flexibility with regard to updates/modifications to the command
27
translations; rather than hard-coding the translations in C++, a database could be
developed to store the translations.
2.6 End-Product Testing Description As stated in section 1.7, this project will serve as a “proof of concept” for
command translation. The goal is to prove that the design is sound and that the
possibility to put such a device into production is feasible. Due to time
constraints, it should be known that testing of the actual implementation was
minimal.
The team does not have an actual Keithley 237 SMU or any instrument that can
perform all of the operations of the 237. However, it has been agreed that an
Agilent power supply in the senior design lab can be used to test a subset of the
237 commands.
Testing will be performed by sending device dependent Keithley 237 commands
from a controller PC (meant to act like the Guidant controller) to our development
command translator. The command translator should reformat the command to
the SCPI standard and relay the new command to the replacement SMU (which
in the testing phase will be the Agilent power supply). If the command translation
functions as expected, the test will pass for that particular command. All
commands implemented will be tested in this fashion.
2.7 Project End Results For this project the team has completed a proof of concept. The team has
successfully implemented a translation of a subset of commands. The end
product could also have all needed command mappings implemented. After this
implementation the completed product could then be integrated into the Guidant
testing system.
28
3 Section 3: Resource and Schedule Requirement This section reports on the estimated resources and schedule. The estimated
resources are broken down into personnel effort requirements, other resource
requirements, and financial requirements.
3.1 Personnel Effort Requirements The personal effort requirements is a breakdown of the amount of time spent
working on the different tasks involved for the project. An updated amount of
time each member works on the project has been sent out with weekly emails.
The original, revised, and final personal effort estimates are shown below in
Tables 1, 2, and 3 respectively. The actual amount of personal effort put in to the
project was a result of the dynamic timetable and project schedule. As the
project progressed, the team was forced to reassess current efforts and adjust as
necessary. As more time was spent on project definition and technology
selection than originally planned, the team was limited to the amount of time
spent on training. This in turn affected efforts spent on design and
implementation.
Table 1: Original Personal Effort Estimates (hours)
Proj
ect
Def
initi
on
Trai
ning
Tech
nolo
gy
Res
ourc
es
Sele
ctio
n
Prod
uct D
esig
n
Prod
uct
Impl
emen
tatio
n
Prod
uct T
estin
g
Prot
otyp
e D
ocum
ents
Prod
uct
Dem
onst
ratio
n
Tota
l
Adam Guy
25 11 22 38 22 28 21 8 175
Justin Koob
24 13 20 39 25 26 23 9 179
Marc McKee
26 12 21 40 24 27 22 10 182
Adam Mishler
29 11 26 40 23 30 21 8 188
Total 104 47 89 157 94 111 87 35 724
29
Table 2: Revised Personnel Effort Estimates (hours)
Proj
ect
Def
initi
on
Trai
ning
Tech
nolo
gy
Res
ourc
es
Sele
ctio
n
Prod
uct D
esig
n
Prod
uct
Impl
emen
tatio
n
Prod
uct T
estin
g
Prot
otyp
e D
ocum
ents
Prod
uct
Dem
onst
ratio
n
Tota
l
Adam Guy
27 17 14 38 25 28 21 8 178
Justin Koob
26 15 12 39 24 26 23 9 174
Marc McKee
28 16 19 40 25 27 22 10 187
Adam Mishler
31 15 23 40 26 30 21 8 194
Total 112 63 68 157 100 111 87 35 733
Table 3: Actual Personal Effort (hours)
Proj
ect
Def
initi
on
Trai
ning
Tech
nolo
gy
Res
ourc
es
Sele
ctio
n
Prod
uct D
esig
n
Prod
uct
Impl
emen
tatio
n
Prod
uct T
estin
g
Prot
otyp
e D
ocum
ents
Prod
uct
Dem
onst
ratio
n
Tota
l
Adam Guy
29.5 13 22 36 30 13 8 11 162.5
Justin Koob
29.5 11 20 37 35.5 12 8 11 164
Marc McKee
30.5 13 20 37.5 43 15 6 11 176
Adam Mishler
31.5 16 25 40 46 15 7 11 191.5
Total 121 53 87 150.5 154.5 55 29 44 694
30
3.2 Other Resource Requirements The original, updated, and actual resource requirements shown in Tables 4, 5,
and 6 respectively are a breakdown of equipment and associated cost required
to complete the project. The actual resource requirements reflect the choice of
technology. As the design approach shifted throughout the course of the project,
the project costs shifted as well. Much of the required resources were available
to the team through the senior design lab on campus. This included one of the
GPIB cards and the connectors needed. Guidant provided the additional GPIB
card, and National Instruments provided the NI-Device software.
Table 4: Other Resource Requirements (Original)
Item: Cost: FPGA $12.00 GPIB interface card $300.00 Various wires and connectors $100.00 Microcontroller $280.00 Total $692.00
Table 5: Other Resource Requirements (Revised)
Item: Cost: FPGA $200.00 GPIB interface card $300.00 Various wires and connectors $100.00 Total $600.00
Table 6: Other Resource Requirements (Actual)
Item: Cost: NI-Device $150.00 (Donated by NI) GPIB interface card (Quantity = 2) $600.00 Various wires and connectors $100.00 Total $850.00
31
3.3 Financial Requirements The financial requirements are a breakdown of the cost of the project. This will
represent the total cost associated with other resources, project deliverables, and
labor. The final financial requirements are shown below in Table 9, reflecting the
changes mentioned above in “other resources,” and “personal efforts.”
Table 7: Financial Requirements (Original)
Item: Cost: Poster Board $8.00 Poster Adhesive $5.00 Poster Lamination $15.00 Labor @ $10.50/hr $7,476.00 Other Resources: $692.00 Total $8,206.00
Table 8: Financial Requirements (Revised) Item: Cost: Poster Board $8.00 Poster Adhesive $5.00 Poster Lamination $15.00 Labor @ $10.50/hr $7,696.50 Other Resources: $600.00 Total $8,324.50
Table 9: Financial Requirements (Actual)
Item: Cost: Poster Board $8.00 Poster Adhesive $5.00 Poster Lamination $15.00 Labor @ $10.50/hr $7287.00 Other Resources: $600.00 Total $7915.00
32
3.4 Schedule The schedule is a breakdown of when the project is going to start and how long it
will take. A comparison of the original, updated, and actual schedules (shown in
Figures 17, 18, and 19 respectively) show that the group has had to deviate
considerably under certain phases of the project. A complete overhaul of the
project definition has caused the rest of the schedule to be pushed back by a
significant amount of time. Also, an extension of the technology selection phase
left little time for implementation and testing. The project deliverables schedule
shown in Figure 20 represents the firm submission dates for project milestones.
Figure 17: Original Schedule
33
Figure 18: Revised Schedule
34
Figure 19: Actual Schedule
Figure 20: Project Deliverables Schedule
35
4 Section 4: Closure Material
This section contains information about the project team as well as the
conclusion.
4.1 Project Evaluation With regards to project evaluation, it’s difficult to evaluate the project effectively
without taking into account the struggles the team has faced throughout the
project period. At the time of creation of the project plan, there was a very
different view of what would be accomplished. The following milestones were
established in the project plan, and are given weights and a percentage of
success. The overall success of the project was calculated to be 82.75%.
Milestone 1: Project definition
Details: The team struggled to define the problem. The project changed during
the first semester, which complicated project definition. The team spent a lot of
time defining the project. Project definition was completed early second
semester. The project was in the end defined, however, it took a very long time
to come up with a solid definition.
Importance Level: High – 15%
Success Level: 80%
Milestone 2: Technology research and selection
Details: The team did a pretty good job at making sure all options were
considered in terms of technology considerations. As can be seen in section
three, there were a number of different considerations. Given the time frame to
work with, the team feels that the correct technology was selected.
Importance Level: Medium – 10%
Success Level: 75%
36
Milestone 3: Product design Details: The team had difficulty determining what the end product would be.
After a lot of thought and consideration given to the amount of time available to
the team, the decision was made to reduce the project to a “proof of concept.”
Importance Level: High – 20%
Success Level: 100%
Milestone 4: Implementation of design
Details: The end product is a reduced device from what the team had originally
envisioned. Due to the extenuating circumstances, the team was forced to
reduce the end product to a more manageable project due to time constraints.
Importance Level: High – 20%
Success Level: 85%
Milestone 5: Prototype testing
Details: Like the end product prototype implementation, testing was also
reduced. The team did some minimal testing on the “proof of concept” model.
The team had envisioned an extensive testing phase, however, didn’t have the
necessary time to complete such testing.
Importance Level: High – 15%
Success Level: 75%
Milestone 6: Document development
Details: The team was planning on providing the client with a brief instruction set
in order to allow them to run the translator code, as well as edit and add to the
design. The time constraints and limited implementation did not allow for this to
occur.
Importance Level: Low – 5%
Success Level: 0%
37
Milestone 7: Prototype demonstration
Details: Despite the issues the team has faced, a brief demonstration was given
to the members of the industrial review panel. The demonstration showed
operation of a subset of the command translator.
Importance Level: High – 15%
Success Level: 100%
Project Reporting: The team has done well producing project reporting deliverables. All of the
necessary deliverables have been completed on the agreed date between the
team and senior design coordinators.
4.2 Commercialization This device is currently being designed solely for Guidant to translate device
dependent commands specifically destined for the Keithley 237 SMU. Due to the
nature of this translator, the device wouldn’t work in any environment other than
Guidant’s specific test platform.
If the command translator was further refined and designed to be a stand-alone
unit rather than run on a PC there could be some potential for commercialization.
The unit would need to be small, and also should be more of a generic translator
that could accommodate any sort of translations, rather than translations specific
to one instrument. The device would also need some user interface allowing for
the mapping of commands.
It’s difficult to say how much such a unit would cost. If the current
implementation (designed to run on a PC) was commercialized, the product
would cost roughly $950.00 to build.
The market for such a device definitely exists. Any organization under
government regulation could use such a translator. The device would serve to a
38
niche market with relatively low volumes comparatively, and, therefore, could
probably be sold for a few hundred times more than it would cost to produce.
4.3 Recommendations for Additional Work There are a number of potential adjustments and additions that could be valuable
to the future development of the command translator.
• Standalone operation: The initial vision for the command translator was
that the device would be a standalone unit. Due to time constraints, this
wasn’t feasible in the current development period. The translator would
be much more portable and cleaner if the device could be implemented in
a standalone unit.
• User interface: The command translator in its current state has command
translation mappings hard-coded into the application. A more expandable
and flexible solution would be to implement some sort of user interface to
allow the device to be reprogrammed with new command mappings. This
addition could open a number of opportunities to use the command
translator in a wide variety of different translation applications.
• Full SCPI compliance: The command translator could be programmed
with the entire SCPI standard, and therefore have the ability to “talk” to
any device that supports SCPI. This addition would make command
mappings easier by allowing the user to choose what SCPI command
they’d like to map to.
4.4 Lessons Learned The academic experience in senior design was a good one. In the following, the
project team will discuss what went well, what did not go well, technical
knowledge gained, non-technical knowledge gained, and things that would have
been done differently.
39
There were a number of items that went well throughout our project. During the
project period, there were a number of times that things didn’t go exactly as
planned. Through those time periods, the team continued to pursue the
challenge and worked well together. Even though there were some major
setbacks in terms of the schedule, the design team still was able to produce
valuable and applicable deliverables.
There were some major problems throughout the project development. Most of
the issues were contained to the first semester and revolved around project
definition. The team was initially assigned a different project, and after some
discussions with the client in late February, the project was changed. This
project change resulted in a severe scheduling setback which has had rippling
effects throughout the entire project period.
The project presented the team with a unique opportunity to see how a company
like Guidant proceeds in the testing of their products. The team learned a vast
amount of information pertaining specifically to the GPIB bus, as well as learned
LabVIEW (even though it wasn’t incorporated into the final design) and some
information pertaining to automated testing platforms. NI-Device also gave the
team a chance to further understand GPIB.
The team feels that the majority of the non-technical skills learned revolved
around the hardships that were experienced throughout the development of the
project. Though the issues that were confronted were difficult at the time, they
presented the team with a great opportunity to really see how projects can work
in industry. The team learned how to be flexible, which is a great skill to have
due to an ever-changing world. In the end, the difficulties that were faced were
indeed the elements that were able to teach some of the most important non-
technical skills.
40
There are a number of things that the team would have done differently. It would
have been nice to have had more time to implement some advanced features
into the translator. The team felt short for time and was trying to catch up
throughout the entire project. The team would have liked to have visited
Guidant’s facility to see their testing platform and really visualize what the
product would be doing. The team also would have like to have had a Keithley
237 unit, and would have made the request to Guidant first semester. Actually
having a Keithley 237 would have allowed the team to better understand the
workings of the device and its command structure.
4.5 Risk and Risk Management Since the project was never truly defined from the start, there were really no risks
that could be anticipated other than the loss of a teammate. Fortunately, this
situation did not occur. However, if the team were to have lost a member, the
remaining team members would simply have to attempt to divide the work
previously associated with the lost member, among those remaining. There is no
other practical way to solve this problem, other than to recruit a new member that
possessed knowledge of the topic at hand.
All of the risks encountered during the development of this project were
unanticipated. First, the initial project assigned to the design team was no longer
needed by Guidant and the project had to be scrapped. In order to overcome
this situation, Guidant communicated with several other engineers within the
company to attempt and find another plausible project that could be completed
within two semesters. Then, after proposing the idea of the command translation
project, several approaches were considered to implement a solution. Several
approaches were considered and then discarded before the design team finally
decided on an approach that could be partially implemented within the remaining
time. This serves as a prelude to the final and most significant unanticipated risk,
the fact that the team was only allotted approximately four weeks to complete the
implementation. In order to successfully overcome this complication, the design
41
team was forced to expend extra hours in the senior design lab at Iowa State
University, and settle on only a partial implementation of the actual design idea;
the project turned into a “proof-of-concept” assignment and only incorporated a
subset of the commands used within the old Keithley 237 source measurement
unit. As a result, the team was able to successfully implement the revised
project, and provide proof that a command translator is feasible to be added to
the Guidant test platform.
4.6 Project Team Information This section contains information about the client, faculty advisors, as well as the
team.
4.6.1 Client Information Greg Stamp
Guidant Corporation
4100 Hamline Avenue North
St. Paul, MN 55112-5798
Gregory.stamp@guidant.com
Andrew Garnett
Guidant Corporation
4100 Hamline Avenue North
St. Paul, MN 55112-5798
651-582-5679 office
Andrew.garnett@guidant.com
42
Max Cortner
Guident Corporation
4100 Hamline Avenue North
St. Paul, MN 55112-5798
651-260-2781
Max.cortner@guidant.com
4.6.2 Faculty Advisor Information Professor John Lamont
324 Town Engineering, Iowa State University
Ames, Iowa 50011
515.294.3600 office
515.294.6760 fax
jwlamont@iastate.edu
Professor Ralph Patterson III
326 Town Engineering, Iowa State University
Ames, Iowa 50011
515.294.2428 office
515.294.6760 fax
repiii@iastate.edu
4.6.3 Student Team information Adam Guy
207 Stanton Avenue #3
Ames, IA 50014
563-299-5443
adamguy@iastate.edu
43
Justin Koob
507 Welch Avenue #2
Ames, IA 50014
515-290-7118
jkoob@iastate.edu
Marc McKee
106 National Drive #207
Huxley, IA 50124
515-991-1630
marcmm1@iastate.edu
Adam Mishler
3910 Tripp Street #132
Ames, IA 50014
612-986-4366
amishler@iastate.edu
4.7 Closing Summary
The thorough testing of all medical devices is an important process. Government
agencies require the certification of all software and hardware used for medical
application. The translator device will allow the team’s client, Guidant, to use
existing, certified software with upgraded testing instrumentation. The
application of this device will allow for minimal re-certification while utilizing
government certified software.
4.8 References
[1] Keithley Instruments, Inc., Model 236/237/238 Source Measurement Unit’s
Operators Manual, Rev. E, March 2001, Viewed April 10, 2005,
http://www.keithley.com
44
[2] Keithley Instruments, Inc., Model 2410 Source Measurement Unit’s User’s
Manual, Rev. G, May 2002, Viewed March 5, 2005,
http://www.keithley.com
[3] Al-Dhaher, A. H. G., Development of Microcontroller/FPGA-based
systems, International Journal of Engineering Education, 2004, Vol. 20,
No. 1, pp. 52-60, Viewed April 1, 2005, http://www.ijee.dit.ie/latestissues/
45
4.9 Appendices The following appendices contain the C++ code used for the translator.
Appendix A contains the header file CSimpleDeviceFramework.h, while appendix
B contains CSimpleDeviceFramework.cpp.
Appendix A /////////////////////////////////////////////////////////////////////////////// // // File: // // CSimpleDeviceFramework.h // // Purpose: // // This file defines the framework for a simple device based on NI-Device. // // This class defines a simple device controlled by IEEE 488.2 commands. // The defined behavior for this class is to: // // 1) Respond to Device Clear messages // 2) Respond to Device Trigger messages // 3) Respond to Status Byte Requests // 4) Respond to the IEEE 488.2 Identification Query ("*IDN?") with an // IEEE 488.2 compatible response // 5) Use the IEEE 488.2 MAV bit for service requests. Accepts "*SRE 16" // to turn on the MAV bit in the SRE register, "*SRE 0" to disable // the MAV bit in the SRE register, and "*SRE?" to retrieve the // current state of the SRE Register. // // This class is derived publicly from CGpibDevice, which is derived from // C4882Device. Therefore, this simple device has the resources necessary to // communicate across GPIB using the IEEE-488.2 protocol. // // Notice that certain virtual methods *must* be overridden as documented // in the C4882Device class (C4882Device.h). // /////////////////////////////////////////////////////////////////////////////// #include <string> using std::string; // Make sure this header file is included only once per compilation unit #if !defined (___csimpledeviceframework_h___) #define ___csimpledeviceframework_h___ ////////////////////////////////////////////////////////////////////////////// // // Include files // ////////////////////////////////////////////////////////////////////////////// #if !defined (___cgpibdevice_h___) #include "CGpibDevice.h" #endif ////////////////////////////////////////////////////////////////////////////// // // Forward Declarations // //////////////////////////////////////////////////////////////////////////////
46
class C4882MsgBuffer; ////////////////////////////////////////////////////////////////////////////// // // Class definition // ////////////////////////////////////////////////////////////////////////////// class CSimpleDeviceFramework : private CGpibDevice { public: /////////////////////////// // // Construction/Destruction // /////////////////////////// // Constructor for the simple device framework. CSimpleDeviceFramework (); // Destructor for the simple device framework. This method frees any // resources that were allocated for an object of this class. virtual ~CSimpleDeviceFramework (); /////////////////// // // Interface Status // /////////////////// // This method returns true if the framework was constructed // successfully. Otherwise, false is returned. bool IsConstructedSuccessfully () const; ///////////////////////// // // Interface Management // ///////////////////////// // This method enables the interface to communicate over GPIB // using the passed in Bus specific parameters. The passed in // pDeviceDescription is used to store a description of the // interface to be printed during each event handler invokation. // This method returns true if the I/O could be enabled, and false // otherwise. bool EnableGpibIo( const char * pDeviceDescription, unsigned char PrimaryAddress ); private: ////////////////////////////// // // Private enumeration // ///////////////////////////// enum { // This is the bit in the IEEE 488.2 status byte that represents // indicates that a message is available. kStatusByteMsgAvailableBit = 0x10 };
47
////////////////////////////// // // Overridden Event Handlers // ////////////////////////////// // This Event Handler is invoked when the device receives a // message, such as "*IDN?". The 'Event' parameter describes the // conditions under which the given message was received. The 'pMsgBuf' // parameter contains the received message; inherited from C4882Device. virtual void InputQueueEventHandler ( eInputQueueEvent Event, C4882MsgBuffer * pMsgBuf ); // This event handler is invoked when a Response Message is removed // from the Output Queue by the Controller. Notice that a Controller // can remove a Response Message by reading the message from the Queue // or by sending a Device Clear message. The 'Event' parameter // describes how the Response Message was removed from the Output // Queue. The 'pMsgBuf' parameter contains the message that has been // removed from the Output Queue; inherited from C4882Device. virtual void OutputQueueEventHandler ( eOutputQueueEvent Event, C4882MsgBuffer * pMsgBuf ); // This event handler is invoked when the device receives a Device // Clear message; inherited from C4882Device. virtual void DeviceClearEventHandler (); // This event handler is invoked when the device receives a Device // Trigger message; inherited from C4882Device. virtual void DeviceTriggerEventHandler (); // This event handler is invoked when NI-Device detects an unrecoverable // software or hardware error; inherited from C4882Device. virtual void ExceptionEventHandler ( eExceptionEvent Event ); // This event handler is invoked when NI-Device detects a change // in the remote/local state of the device; inherited from C4882Device. virtual void RemoteLocalEventHandler ( eRemoteLocalState State ); ////////////////// // // Value Semantics // ////////////////// // Copy constructor. This object is not to be copied, so the // copy constructor is made private. CSimpleDeviceFramework ( const CSimpleDeviceFramework & SrcObj ); // Assignment operator. This object is not to be assigned, so // the assignment operator is made private. CSimpleDeviceFramework & operator = ( const CSimpleDeviceFramework & SrcObj ); ////////////////////////// // // Private Helper Methods //
48
////////////////////////// // This method stores the device description void _StoreDeviceDescription ( const char * pDeviceDescription, eStatusMessage & rStatus ); // This method allocates the required message buffers void _CreateMessageBuffers (eStatusMessage & rStatus); // This method deallocates the allocated message buffers void _DestroyMessageBuffers (); // This method enables the appropriate event handlers void _EnableEventHandlers (eStatusMessage & rStatus); // This method displays information about the device void _DisplayDeviceInformation (); // This method displays the specified information void _DisplayInformation ( char * pInformation ); // This method displays information about a method void _DisplayMethodInformation ( char * pMethodDescription ); // This method displays information about an event void _DisplayEventInformation ( char * pEventDescription ); // This method displays information about an action void _DisplayActionInformation ( char * pActionDescription ); // This method displays information about an error void _DisplayErrorInformation ( char * pErrorDescription, eStatusMessage Status ); // This method parses a given input message received by the device // and responds if appropriate. This parser is not fully IEEE 488.2 // compliant. // // It recognizes and responds to single commands or queries, and ignores // subsequent commands in a compound command. It correctly parses *IDN?, // *SRE?, and *SRE n, where 'n' is a decimal integer. It recognizes // invalid uses of these commands, such as *IDN? XYZ, or *SRE, and // reports them as invalid. This parser is not meant to provide a full // 488.2 compliant implementation, but rather to demonstrate the // capabilities of NI-Device. void _ParseMessage ( C4882MsgBuffer * pMsgBuf ); // This method processes a *IDN? qury and returns the appropriate // IEEE 488.2 identification response. void _ProcessIdnQuery (); // This method processes a *SRE? query and returns the current value // of the SRE register. void _ProcessSreQuery (); // This method processes a *SRE n command and sets the current SRE // register to the value n.
49
void _ProcessSreCommand ( const void * pArgument, unsigned long ArgumentSize ); void processCommandD( char* command ); void processCommandF( char* command ); void processCommandL( char* command ); void processCommandB( char* command ); void processCommandH( char* command ); void processCommandN( char* command ); void processCommandG( char* command ); void sendData( const char* data ); ////////////////// // // Private Members // ////////////////// // This member stores this information to be returned from *IDN?, By // definition, the *IDN? response contains four comma separated fields: // Field 1 returns the manufacturer // Field 2 returns the instrument model number // Field 3 returns the serial number // Field 4 returns the firmware level const char * m_ID; // Description for this device char * m_pDeviceDescription; // Input buffer for the device. C4882MsgBuffer * m_pInputBuffer; // Output buffer for the device. C4882MsgBuffer * m_pOutputBuffer; // Status byte of the device unsigned char m_StatusByte; // Service Request Enable Register unsigned char m_SRERegister; // Stores whether a device was enabled successfully bool m_IoEnabled; // This member variable stores whether the constructor completed // successfully. bool m_ConstructedSuccessfully; }; // end of class CSimpleDeviceFramework
50
#endif // end #if !defined (___csimpledeviceframework_h___)
Appendix B ////////////////////////////////////////////////////////////////////////////// // // File: // // CSimpleDeviceFramework.cpp // // Purpose: // // Implementation of CSimpleDeviceFramework - refer to the header file, // CSimpleDeviceFramework.h, for a detailed description of what each // method is intended to do, and for the inheritance hierarchy of // the methods implemented here. // // Notice that many of the method calls return a status parameter with // which you can check the success or failure of the method invocation. // In this example, we perform error checking only on the most important // calls, such as initialization and enabling the input queue. Error // checking of all calls is highly encouraged for production software. // ////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////// // // Include files // ////////////////////////////////////////////////////////////////////////////// // Ensure compatibility with different standard library implementations. namespace std {} using namespace std; #include <string> #include <windows.h> using std::string; // Include the interface file for this class #if !defined (___csimpledeviceframework_h___) #include "CSimpleDeviceFramework.h" #endif // Includes the definition of the parser used for this simple example #if !defined (___csimpleparser_h___) #include "CSimpleParser.h" #endif // Includes the definition of C4882MsgBuffer #if !defined (___C4882MsgBuffer_h___) #include "C4882MsgBuffer.h" #endif // Includes the standard I/O library for printing diagnostic information // to the console. #include <cstdio> // Includes the definition of strcpy #include <cstring> // Includes the definition of isdigit #include <cctype> // Includes Labview write function #include "writeGpib.h"
51
// Include the HPIB VISA #include <visa.h> ////////////////////////////////////////////////////////////////////////////// // // Implementation // ////////////////////////////////////////////////////////////////////////////// /*used to determine if current or voltage will be sourced source = false -> voltage sourced source = true -> current sourced */ bool source = false; int delay = 0; CSimpleDeviceFramework::CSimpleDeviceFramework () : m_ID ("NATIONAL INSTRUMENTS, " "SIMPLE IEEE 488.2 DEVICE, " "0x1234, " "0\n" ), m_pDeviceDescription (NULL), m_pInputBuffer (NULL), m_pOutputBuffer (NULL), m_StatusByte (0), m_SRERegister (0), m_IoEnabled (false), m_ConstructedSuccessfully (false) { eStatusMessage _Status = NIDEVICE_STATUS_UNKNOWN; _CreateMessageBuffers (_Status); if (NIDEVICE_STATUS_SUCCESS == _Status) { m_ConstructedSuccessfully = true; } } // CSimpleDeviceFramework CSimpleDeviceFramework::~CSimpleDeviceFramework () { eStatusMessage _Status = NIDEVICE_STATUS_UNKNOWN; // Disable the interface if I/O has been previously enabled. if (m_IoEnabled) { m_IoEnabled = false; _Status = C4882Device::DisableInterface (); if (NIDEVICE_STATUS_SUCCESS != _Status) { _DisplayErrorInformation( "Unable to disable the interface", _Status ); } } // Release allocated resources _DestroyMessageBuffers (); // Delete the stored Device Description. if (NULL != m_pDeviceDescription) { delete m_pDeviceDescription; }
52
} // ~CSimpleDeviceFramework bool CSimpleDeviceFramework::IsConstructedSuccessfully () const { return m_ConstructedSuccessfully; } // IsConstructedSuccessfully bool CSimpleDeviceFramework::EnableGpibIo ( const char * pDeviceDescription, unsigned char Pad ) { eStatusMessage _Status = NIDEVICE_STATUS_UNKNOWN; if (false == IsConstructedSuccessfully()) { _DisplayErrorInformation ( "The framework was not constructed successfully", NIDEVICE_STATUS_DEVICE_ERROR ); return false; } // Disable the interface if I/O has been previously enabled. if (m_IoEnabled) { _Status = C4882Device::DisableInterface (); if (NIDEVICE_STATUS_SUCCESS != _Status) { // Failed disabling the device _DisplayErrorInformation( "Unable to disable the current enabled interface.", _Status ); return false; } // Clear the state of various member variables when we are disabled. m_IoEnabled = false; m_StatusByte = 0; m_SRERegister = 0; } // Store the Device Description _StoreDeviceDescription ( pDeviceDescription, _Status ); if (NIDEVICE_STATUS_SUCCESS != _Status) { _DisplayErrorInformation ( "Unable to Store the Device Description.", _Status ); return false; } // Display the device information after the device description string // has been copied to the member variable. _DisplayDeviceInformation (); _DisplayMethodInformation ( "CSimpleDeviceFramework::EnableGpibIo" );
53
// Enable Event Handlers _EnableEventHandlers (_Status); if (NIDEVICE_STATUS_SUCCESS != _Status) { // Error message will be printed by the internal method. return false; } // Enable Interface _Status = CGpibDevice::EnableInterface ( Pad ); if (NIDEVICE_STATUS_SUCCESS != _Status) { _DisplayErrorInformation( "Unable to enable the interface.", _Status ); return false; } // Interface was successfully enabled. m_IoEnabled = true; // Enable the Input Queue to begin receiving messages. _Status = C4882Device::EnableInputQueue ( m_pInputBuffer ); if (NIDEVICE_STATUS_SUCCESS != _Status) { _DisplayErrorInformation( "Unable to enable the input queue", _Status ); return false; } // We were able to successfully enable the GPIB I/O. _DisplayInformation ("Device successfully created\n\n"); return true; } // EnableGpibIo //////////////////// // // Event Handlers // //////////////////// void CSimpleDeviceFramework::InputQueueEventHandler ( eInputQueueEvent Event, C4882MsgBuffer * pMsgBuf ) { eStatusMessage _Status = NIDEVICE_STATUS_UNKNOWN; // Display information to the console _DisplayDeviceInformation (); _DisplayMethodInformation ( "CSimpleDeviceFramework::InputQueueEventHandler." ); switch (Event) { // If we received a complete message
54
case END_MSG_RECEIVED: _DisplayEventInformation ("END_MSG_RECEIVED"); // Parse the input message and respond appropriately. // // Normally the parser runs in a separate thread context. In this // case, pass the message buffer to the parser and return. _ParseMessage ( pMsgBuf ); // END message acknowledgement must be performed after any query // has been detected. Since query detection is completed in the // _ParseMessage call above, acknowledgement of the end message // is done here. // // Normally an END message is passed to the parser after the // input message. When the parser parses the END message, it should // invoke the following call. The parser used in this example does // not allow the END message to be passed in, so the END message // is acknowledged here. Refer to the Multi-Threaded example for a // more realistic example of the interactions between the I/O control // and the parser. _Status= C4882Device::AcknowledgeEndOfMessage(); // Treat the status NIDEVICE_STATUS_DEVICE_CLEAR_ACTIVE as a warning // since it is a non-fatal error. To treat it as a warning, change // the status back to success. if (NIDEVICE_STATUS_DEVICE_CLEAR_ACTIVE == _Status) { _Status = NIDEVICE_STATUS_SUCCESS; } else if (NIDEVICE_STATUS_SUCCESS != _Status) { _DisplayErrorInformation( "Could not acknowledge end of message.", _Status ); return; } break; case BUFFER_IS_FULL: _DisplayEventInformation ("BUFFER_IS_FULL"); // Parse the input message and respond appropriately. // // Normally the parser runs in a separate thread context. In this // case, pass the message buffer to the parser and return. _ParseMessage ( pMsgBuf ); break; case MSG_UNTERMINATED: _DisplayEventInformation ("MSG_UNTERMINATED"); // Note: This is an UNTERMINATED condition. The action to // take when an UNTERMINATED condition occurs is // defined in Section 6.3.2.2 of the IEEE 488.2 standard. // If status reporting were implemented, we would set the // Query Error bit in the Standard Event Status Register. // We also have the option of parsing or ignoring the // contents of the incomplete input buffer. We choose to
55
// parse the buffer. // If the input queue was not enabled, pMsgBuf will be NULL. If no // data was received before the UNTERMINATED condition was detected, // the message length will be zero. // // Normally the parser runs in a separate thread context. In this // case, pass the message buffer (if any) and the UNT message to the // parser and return. if ( (NULL != pMsgBuf) && (0 != pMsgBuf->GetMsgLength ()) ) { _ParseMessage ( pMsgBuf ); } // Normally an UNT message is passed to the parser after the // input message (if any). When the parser parses the UNT message, // it should invoke the following call. The parser used in this // example does not allow UNT messages to be passed in, so the UNT // message is acknowledged here. Refer to the Multi-Threaded example // for a more realistic example of the interactions between the I/O // control and the parser. _Status = C4882Device::AcknowledgeMsgUnterminated(); // Treat the status NIDEVICE_STATUS_DEVICE_CLEAR_ACTIVE as a warning // since it is a non-fatal error. To treat it as a warning, change // the status back to success. if (NIDEVICE_STATUS_DEVICE_CLEAR_ACTIVE == _Status) { _Status = NIDEVICE_STATUS_SUCCESS; } else if (NIDEVICE_STATUS_SUCCESS != _Status) { _DisplayErrorInformation ( "Could not acknowledge the message unterminated condition.", _Status ); return; } break; case TRIGGER_RECEIVED: _DisplayEventInformation ("TRIGGER_RECEIVED"); // Note: This situation is a Command Error. The action to take // when the parser detects a Command Error is defined in // section 6.1.6.1.1 of IEEE 488.2. If status reporting // were implemented, we would set the Command Error bit in // the Standard Event Status Register. We also have the // option of parsing or ignoring the contents of the incomplete // input buffer. Since the Command Error is received after // the data, we choose to parse the data first. // If the input queue was not enabled, pMsgBuf will be NULL. If no // data was received before the UNTERMINATED condition was detected, // the message length will be zero. // // Normally the parser runs in a separate thread context. In this // case, pass the message buffer (if any) and the GET message to the // parser and return. Refer to the Multi-Threaded example for a // more realistic example of the interactions between the I/O control // and the parser. if (
56
(NULL != pMsgBuf) && (0 != pMsgBuf->GetMsgLength ()) ) { _ParseMessage ( pMsgBuf ); } break; } // We need to request service if we have a new reason for service. // For this simplified example, we only have to take into account // the MAV bit. Normally this algorithm a slightly more complex due // to other bits. For a better example, review the multithreaded // example. // // A new reason for service exists if: // 1) The MAV bit in the Status Byte transitions from false to // true. In this example, we know that if the MAV bit is set // in the Status Byte, it was just set by the method // '_ParseMessage'. // 2) The MAV bit in the Service Request Enable Register is true. if ( (kStatusByteMsgAvailableBit & m_StatusByte) && (kStatusByteMsgAvailableBit & m_SRERegister) ) { _DisplayActionInformation ( "Requesting Service due to Message Available." ); // Note that we ignore the status return value C4882Device::RequestService (); } // If necessary, re-enable the input queue to receive more messages. Note // that it is possible to receive a NULL message buffer with the // TRIGGER_RECEIVED and MSG_UNTERMINATED events. if (NULL != pMsgBuf) { // Enable the input queue to begin receiving messages. _Status = C4882Device::EnableInputQueue (pMsgBuf); if (NIDEVICE_STATUS_SUCCESS != _Status) { _DisplayErrorInformation ( "Could not re-enable the input queue.", _Status ); } } // Display an extra linefeed for proper formatting _DisplayInformation ("\n"); } // InputQueueEventHandler void CSimpleDeviceFramework::OutputQueueEventHandler ( eOutputQueueEvent Event, C4882MsgBuffer * pMsgBuf ) { eStatusMessage _Status = NIDEVICE_STATUS_UNKNOWN; // Display information to the console _DisplayDeviceInformation (); _DisplayMethodInformation ( "CSimpleDeviceFramework::OutputQueueEventHandler."
57
); switch (Event) { case MESSAGE_SENT: // If a message has been broken apart into multiple message buffers, // the next message buffer to be sent would be queued here. _DisplayEventInformation ("MESSAGE_SENT"); break; case MSG_REQUESTED: // Note: The MSG_REQUESTED event must be enabled by the method // EnableMsgRequestedEvents before they can be used. This // example does not use MSG_REQUESTED events. // // EnableMsgRequestedEvents() enables NI-Device to dispatch // the message requested events. A message requested event // is generated when the Controller requests a response message. _DisplayEventInformation ("MSG_REQUESTED"); break; case MSG_INTERRUPTED: // Note: This is an INTERRUPTED condition. The action to // take when an INTERRUPTED condition occurs is // defined in Section 6.3.2.3 of the IEEE 488.2 standard. // The output queue is cleared automatically, and if // status reporting were implemented, we would set the // Query Error bit in the Standard Event Status Register. _DisplayEventInformation ("MSG_INTERRUPTED"); break; case CLEAR_RECEIVED: _DisplayEventInformation ("CLEAR_RECEIVED"); break; } // We no longer have a message to send, so clear the MAV bit from // the Status Byte. m_StatusByte &= ~kStatusByteMsgAvailableBit; _Status = UpdateStatusByte ( m_StatusByte ); if (NIDEVICE_STATUS_SUCCESS != _Status) { _DisplayErrorInformation ( "Could not update the status byte.", _Status ); } // If we were requesting service, cancel the request. We can do this since // the only reason why we request service is if we have a message available. if (kStatusByteMsgAvailableBit & m_SRERegister) { _DisplayActionInformation ( "Cancelling Request for Service due to Message No Longer Available." ); // Note that we ignore the status return value; production software may // want to check for an error here C4882Device::CancelRequestForService (); } // Display an extra linefeed for proper formatting _DisplayInformation ("\n");
58
} // OutputQueueEventHandler void CSimpleDeviceFramework::DeviceClearEventHandler () { // Display information to the console _DisplayDeviceInformation (); _DisplayMethodInformation ( "CSimpleDeviceFramework::DeviceClearEventHandler" ); // Display an extra linefeed for proper formatting _DisplayInformation ("\n"); } // DeviceClearEventHandler void CSimpleDeviceFramework::DeviceTriggerEventHandler () { eStatusMessage _Status = NIDEVICE_STATUS_UNKNOWN; // Display information to the console _DisplayDeviceInformation (); _DisplayMethodInformation ( "CSimpleDeviceFramework::DeviceTriggerEventHandler" ); // As defined in the IEEE 488.2 specification, a device trigger is treated // as an END message. Hence the END message is acknowledged here. _Status = C4882Device::AcknowledgeEndOfMessage (); if (NIDEVICE_STATUS_SUCCESS != _Status) { _DisplayErrorInformation ( "Could not acknowledge the end of message condition.", _Status ); } // Display an extra linefeed for proper formatting _DisplayInformation ("\n"); } // DeviceTriggerEventHandler void CSimpleDeviceFramework::ExceptionEventHandler (eExceptionEvent Event) { // Display information to the console _DisplayDeviceInformation (); _DisplayMethodInformation ( "CSimpleDeviceFramework::ExceptionEventHandler" ); switch (Event) { case HARDWARE_FAULT: _DisplayEventInformation ("HARDWARE_FAULT"); break; case HARDWARE_REMOVED: _DisplayEventInformation ("HARDWARE_REMOVED"); break; case SOFTWARE_FAULT: _DisplayEventInformation ("SOFTWARE_FAULT"); break; }
59
// Display an extra linefeed for proper formatting _DisplayInformation ("\n"); } // ExceptionEventHandler void CSimpleDeviceFramework::RemoteLocalEventHandler ( eRemoteLocalState State ) { // Display information to the console _DisplayDeviceInformation (); _DisplayMethodInformation ( "CSimpleDeviceFramework::RemoteLocalEventHandler" ); switch (State) { case LOCAL_STATE: _DisplayEventInformation ("LOCAL_STATE"); break; case REMOTE_STATE: _DisplayEventInformation ("REMOTE_STATE"); break; case LOCAL_LOCKOUT_STATE: _DisplayEventInformation ("LOCAL_LOCKOUT_STATE"); break; case REMOTE_LOCKOUT_STATE: _DisplayEventInformation ("REMOTE_LOCKOUT_STATE"); break; } // Display an extra linefeed for proper formatting _DisplayInformation ("\n"); } // RemoteLocalEventHandler /////////////////////////// // // Private Helper Methods // /////////////////////////// void CSimpleDeviceFramework::_StoreDeviceDescription ( const char * pDeviceDescription, eStatusMessage & rStatus ) { rStatus = NIDEVICE_STATUS_SUCCESS; // Set the Device Description if (NULL == pDeviceDescription) { rStatus = NIDEVICE_STATUS_PARAMETER_ERROR; _DisplayErrorInformation ( "NULL pDeviceDescription was passed in.", rStatus ); return; }
60
m_pDeviceDescription = new char [strlen(pDeviceDescription) + 1]; if (NULL == m_pDeviceDescription) { // Failed allocating the Device Description rStatus = NIDEVICE_STATUS_INSUFFICIENT_MEMORY; _DisplayErrorInformation ( "Unable to allocate memory for Device Description", rStatus ); return; } // Store the Device Description strcpy ( m_pDeviceDescription, pDeviceDescription ); } // _StoreDeviceDescription void CSimpleDeviceFramework::_CreateMessageBuffers ( eStatusMessage & rStatus ) { // Initialize status to success to start. rStatus = NIDEVICE_STATUS_SUCCESS; // Allocate input buffer of default size. The default size is defined in // C4882MsgBuffer.h m_pInputBuffer = new C4882MsgBuffer (); if (NULL == m_pInputBuffer) { // Failed allocating input buffer rStatus = NIDEVICE_STATUS_INSUFFICIENT_MEMORY; _DisplayErrorInformation ( "Unable to create the input message buffer.", rStatus ); return; } if (!m_pInputBuffer->IsMsgBufferOk ()) { // Something is wrong with input buffer rStatus = NIDEVICE_STATUS_INSUFFICIENT_MEMORY; _DisplayErrorInformation ( "Input Message Buffer is not okay.", rStatus ); return; } // Allocate output buffer of default size. The default size is defined in // C4882MsgBuffer.h m_pOutputBuffer = new C4882MsgBuffer (); if (NULL == m_pOutputBuffer) { // Failed allocating output buffer rStatus = NIDEVICE_STATUS_INSUFFICIENT_MEMORY; _DisplayErrorInformation ( "Unable to create the output message buffer.", rStatus );
61
return; } if (!m_pOutputBuffer->IsMsgBufferOk ()) { // Something is wrong with output buffer rStatus = NIDEVICE_STATUS_INSUFFICIENT_MEMORY; _DisplayErrorInformation ( "Output Message Buffer is not okay.", rStatus ); return; } } // _CreateMessageBuffers void CSimpleDeviceFramework::_DestroyMessageBuffers () { if (NULL != m_pInputBuffer) { delete m_pInputBuffer; m_pInputBuffer = NULL; } if (NULL != m_pOutputBuffer) { delete m_pOutputBuffer; m_pOutputBuffer = NULL; } } // _DestroyMessageBuffers void CSimpleDeviceFramework::_EnableEventHandlers ( eStatusMessage & rStatus ) { // Enable the device trigger event handler rStatus = EnableDeviceTriggerEventHandler (); if (NIDEVICE_STATUS_SUCCESS != rStatus) { // Failed enabling device trigger event handler. _DisplayErrorInformation ( "Unable to enable the trigger event handler.", rStatus ); return; } // Enable the exception event handler rStatus = EnableExceptionEventHandler (); if (NIDEVICE_STATUS_SUCCESS != rStatus) { // Failed enabling exception event handler. _DisplayErrorInformation ( "Unable to enable the exception event handler.", rStatus ); return; } // Enable the remote/local event handler. We are enabling this in enhanced // mode to see all four events of the remote/local state machine. rStatus = EnableRemoteLocalEventHandler ( REMOTE_LOCAL_ENHANCED_MODE );
62
if (NIDEVICE_STATUS_SUCCESS != rStatus) { // Failed enabling exception event handler. _DisplayErrorInformation ( "Unable to enable the remote/local event handler.", rStatus ); return; } } // _EnableEventHandlers void CSimpleDeviceFramework::_DisplayDeviceInformation () { // Only print if the device description is not NULL. if (NULL != m_pDeviceDescription) { printf ( "Device Description: %s", m_pDeviceDescription ); } } // _DisplayDeviceInformation // Display the information about the device void CSimpleDeviceFramework::_DisplayInformation ( char * pInformation ) { // Only print if the method information is not NULL. if (NULL != pInformation) { printf ( "%s", pInformation ); } } // _DisplayInformation void CSimpleDeviceFramework::_DisplayMethodInformation ( char * pMethodDescription ) { // Only print if the method information is not NULL. if (NULL != pMethodDescription) { printf ( "Method: %s\n", pMethodDescription ); } } // DisplayMethodInformation void CSimpleDeviceFramework::_DisplayEventInformation ( char * pEventDescription ) { // Only print if the method information is not NULL. if (NULL != pEventDescription) { printf ( "Event: %s\n", pEventDescription ); } } // _DisplayEventInformation
63
void CSimpleDeviceFramework::_DisplayActionInformation ( char * pActionDescription ) { // Only print if the method information is not NULL. if (NULL != pActionDescription) { printf ( "Action: %s\n", pActionDescription ); } } // _DisplayActionInformation void CSimpleDeviceFramework::_DisplayErrorInformation ( char * pErrorDescription, eStatusMessage Status ) { // Only print if the error information is not NULL. if (NULL != pErrorDescription) { printf ( "Error: %s\n" "Cause of failure: %s\n", pErrorDescription, C4882Device::StatusDescription (Status) ); } } // _DisplayErrorInformation ////////////////// // // Parser Methods // ////////////////// void CSimpleDeviceFramework::_ParseMessage ( C4882MsgBuffer * pMsgBuf ) { eStatusMessage _Status = NIDEVICE_STATUS_UNKNOWN; int i = 0; printf ( "Message Length: %lu\n" "Message: ", pMsgBuf->GetMsgLength () ); //write the message to the screen fwrite ( pMsgBuf->GetBuffer (), 1, pMsgBuf->GetMsgLength (), stdout ); // Flush the fwrite to the display. fflush (stdout); // Display an extra linefeed for proper formatting _DisplayInformation ("\n"); char* commandBuffer = (char*)pMsgBuf->GetBuffer(); char command = *commandBuffer; switch(command) {
64
case 'D': processCommandD(commandBuffer); break; case 'F': processCommandF(commandBuffer); break; case 'B': processCommandB(commandBuffer); break; case 'H': processCommandH(commandBuffer); break; case 'N': processCommandN(commandBuffer); break; case 'G': { QueryDetected(); processCommandG(commandBuffer); if (NIDEVICE_STATUS_SUCCESS != _Status) { _DisplayErrorInformation ("Error: Unable to Acknowledge a query",_Status); } break; } } } // _ParseMessage void CSimpleDeviceFramework::_ProcessIdnQuery () { eStatusMessage _Status = NIDEVICE_STATUS_UNKNOWN; // Notice that we are making the assumption that the entire // identification string for this device can fit into the // output buffer. // Note that by dereferencing the output buffer, it is automatically cast // to char *, which is the type expected by strcpy. strcpy (*m_pOutputBuffer, m_ID); m_pOutputBuffer->SetMsgLength (strlen (m_ID)); // Queue the response message with NI-Device. This implementation violates // the IEEE 488.2 standard since we are queueing a response message with // SendEnd=true (default parameter) before we have parsed the END message. // Normally the response message should be placed in an internal output // buffer and should only be queued to NI-Device when the END message has // been parsed or the internal buffer is full. // // ...Note that some parameters are left at their default settings. _Status = C4882Device::QueueResponseMsg (m_pOutputBuffer); if (NIDEVICE_STATUS_SUCCESS != _Status) { _DisplayErrorInformation ( "Unable to queue the response string", _Status ); return; } // Set the Message Available (MAV) bit in the Status Byte m_StatusByte |= kStatusByteMsgAvailableBit; _Status = C4882Device::UpdateStatusByte ( m_StatusByte
65
); if (NIDEVICE_STATUS_SUCCESS != _Status) { _DisplayErrorInformation ( "Unable to update the status byte.", _Status ); } _DisplayActionInformation ( "Queued device identification string." ); } // _ProcessIdnQuery void CSimpleDeviceFramework::_ProcessSreQuery () { eStatusMessage _Status = NIDEVICE_STATUS_UNKNOWN; // Note that by dereferencing the output buffer, it is automatically cast // to char *, which is the type expected by sprintf and strlen. sprintf ( *m_pOutputBuffer, "%d\n", m_SRERegister ); m_pOutputBuffer->SetMsgLength (strlen (*m_pOutputBuffer)); // Queue the response message with NI-Device. This implementation violates // the IEEE 488.2 standard since we are queueing a response message with // SendEnd=true (default parameter) before we have parsed the END message. // Normally the response message should be placed in an internal output // buffer and should only be queued to NI-Device when the END message has // been parsed or the internal buffer is full. // // ...Note that some parameters are left at their default settings. _Status = C4882Device::QueueResponseMsg (m_pOutputBuffer); if (NIDEVICE_STATUS_SUCCESS != _Status) { _DisplayErrorInformation ( "Unable to queue contents of Service Request Enable Register.", _Status ); return; } // Set the Message Available (MAV) bit in the Status Byte m_StatusByte |= kStatusByteMsgAvailableBit; _Status = C4882Device::UpdateStatusByte ( m_StatusByte ); if (NIDEVICE_STATUS_SUCCESS != _Status) { _DisplayErrorInformation ( "Unable to update the status byte.", _Status ); return; } _DisplayActionInformation ( "Queued contents of Service Request Enable Register." ); } // _ProcessSreQuery
66
void CSimpleDeviceFramework::_ProcessSreCommand ( const void * pArgument, unsigned long ArgumentSize ) { // *SRE requires an ASCII format decimal number in the range 0 to 255 if ( (0 == ArgumentSize) || (3 < ArgumentSize) ) { // This situation is a Command Error. The action to take when // the parser detects a Command Error is defined in section // 6.1.6.1.1 of IEEE 488.2. _DisplayInformation ( "Invalid parameter: Expected decimal number ranging from\n" " 0 through 255 following *SRE command.\n" ); return; } size_t _Index = 0; const char * _pDecimalNumber = static_cast <const char *> (pArgument); unsigned int _NumericData = 0; for (_Index = 0; _Index < ArgumentSize; _Index++) { if (0 == isdigit (_pDecimalNumber [_Index])) { // This situation is a Command Error. The action to take when // the parser detects a Command Error is defined in section // 6.1.6.1.1 of IEEE 488.2. _DisplayInformation ( "Invalid parameter: Expected decimal number ranging from\n" " 0 through 255 following *SRE command.\n" ); return; } _NumericData = (10 * _NumericData) + (_pDecimalNumber [_Index] - 0x30); } if (_NumericData <= 255) { // Update Service Request Enable Register m_SRERegister = static_cast <unsigned char> (_NumericData); _DisplayActionInformation ( "Updated Service Request Enable Register." ); } else { // This situation is a Command Error. The action to take when // the parser detects a Command Error is defined in section // 6.1.6.1.1 of IEEE 488.2. _DisplayInformation ( "Invalid parameter: Expected decimal number ranging from\n" " 0 through 255 following *SRE command.\n" ); } } // _ProcessSreCommand void CSimpleDeviceFramework::processCommandD(char* command) { eStatusMessage _Status = NIDEVICE_STATUS_UNKNOWN;
67
int i = 0; int commandOptionNumber = *(command+1) - 48; char parser = *(command+3); char displayMessage[20]; for(i = 1; parser != 'X'; i++) { displayMessage[i-1] = parser; parser = *(command+3+i); } displayMessage[i] = '\0'; /*char commandTest[] = "DISP:TEXT \"Test from NI-Device\""; writeGpibDevice(); printf("testing\n"); //invoke labview instrument assistant to send the data down the secondary */ ViSession defaultRM, vi; char buf [256] = {0}; /* Open session to GPIB device at address 5 */ viOpenDefaultRM (&defaultRM); viOpen (defaultRM, "GPIB0::5::INSTR", VI_NULL,VI_NULL, &vi); /* Initialize device */ viPrintf (vi, "*RST\n"); /* Send an *IDN? string to the device */ //viPrintf (vi, "DISP:TEXT \"Test from NI-Device\"\n"); viPrintf (vi, "DISP:TEXT \"%s\"\n", displayMessage); //Attempt at Querying follows............................... /* char responseMessage[] = "Response Delivered Successfully\0"; strcpy (*m_pOutputBuffer, responseMessage); m_pOutputBuffer->SetMsgLength (strlen (responseMessage)); _Status = C4882Device::QueueResponseMsg (m_pOutputBuffer); if (NIDEVICE_STATUS_SUCCESS != _Status) { _DisplayErrorInformation ( "Unable to queue the device identification string.", _Status ); return; } // Set the Message Available (MAV) bit in the Status Byte m_StatusByte |= kStatusByteMsgAvailableBit; _Status = C4882Device::UpdateStatusByte ( m_StatusByte ); if (NIDEVICE_STATUS_SUCCESS != _Status) { _DisplayErrorInformation ( "Unable to update the status byte.", _Status ); } _DisplayActionInformation (
68
"Queued device identification string." ); .............................................*/ /* Read results */ //viScanf (vi, "%t", &buf); /* Print results */ //printf ("Instrument identification string: %s\n", buf); /* Close session */ viClose (vi); viClose (defaultRM); } void CSimpleDeviceFramework::processCommandF(char* command) { int i = 0; int function = -1; //check to make sure that the source is defined if(*(command + 1) != ',') { source = (*(command + 1))-48; //check if function is defined if((*(command + 3) == '0') || (*(command +3) == '1')) { function = *(command + 3); } } else { //check to make sure that the function is defined if((*(command + 2) == '0') || (*(command +2) == '1')) { function = *(command + 2); } } } void CSimpleDeviceFramework::processCommandL(char* command) { int i = 0; char level; int range = -1; //check to make sure that the level is defined if(*(command + 1) != ',') { level = *(command + 1); //check if range is defined if((*(command + 3) >= '0') && (*(command + 3) <= '10')) { range = *(command + 3); } } else { //check to make sure that the range is defined if((*(command + 2) >= '0') && (*(command + 2) <= '10')) { range = *(command + 2); } } //lookup the new command from the database or wherever
69
//invoke labview instrument assistant to send the data down the secondary } void CSimpleDeviceFramework::processCommandB(char* command) { int i = 0; int level = -1; int range = -1; //will only support level of up to 6 volts level = (*(command + 1))-48; //range always 0, not supported range = (*(command + 3))-48; //will only support delays of up to 9 seconds delay = (*(command + 5))-48; ViSession defaultRM, vi; char buf [256] = {0}; /* Open session to GPIB device at address 5 */ viOpenDefaultRM (&defaultRM); viOpen (defaultRM, "GPIB0::5::INSTR", VI_NULL,VI_NULL, &vi); viPrintf (vi, "TRIG:SEQ:DEL %d\n", delay); viPrintf (vi, "TRIG:SEQ:SOUR BUS\n"); if(delay > 0 && source == false) { viPrintf (vi, "SOUR:VOLT:TRIG %d\n", level); } if(delay == 0 && source == false) { viPrintf (vi, "SOUR:VOLT %d\n", level); } if(delay > 0 && source == true) { viPrintf (vi, "SOUR:CURR:TRIG %d\n", level); } if(delay == 0 && source == true) { viPrintf (vi, "SOUR:CURR %d\n", level); } /* Send an *IDN? string to the device */ //viPrintf (vi, "DISP:TEXT \"Test from NI-Device\"\n"); //viPrintf (vi, "DISP:TEXT \"%s\"\n", displayMessage); //Attempt at Querying follows............................... /* char responseMessage[] = "Response Delivered Successfully\0"; strcpy (*m_pOutputBuffer, responseMessage); m_pOutputBuffer->SetMsgLength (strlen (responseMessage)); _Status = C4882Device::QueueResponseMsg (m_pOutputBuffer); if (NIDEVICE_STATUS_SUCCESS != _Status) { _DisplayErrorInformation ( "Unable to queue the device identification string.", _Status ); return; }
70
// Set the Message Available (MAV) bit in the Status Byte m_StatusByte |= kStatusByteMsgAvailableBit; _Status = C4882Device::UpdateStatusByte ( m_StatusByte ); if (NIDEVICE_STATUS_SUCCESS != _Status) { _DisplayErrorInformation ( "Unable to update the status byte.", _Status ); } _DisplayActionInformation ( "Queued device identification string." ); .............................................*/ /* Read results */ //viScanf (vi, "%t", &buf); /* Print results */ //printf ("Instrument identification string: %s\n", buf); /* Close session */ viClose (vi); viClose (defaultRM); } void CSimpleDeviceFramework::processCommandH(char* command) { //Can only support IMM trigger, so tell the power supply to work with Immediate trigger ViSession defaultRM, vi; char buf [256] = {0}; /* Open session to GPIB device at address 5 */ viOpenDefaultRM (&defaultRM); viOpen (defaultRM, "GPIB0::5::INSTR", VI_NULL,VI_NULL, &vi); viPrintf (vi, "TRIG:SEQ:SOUR BUS\n"); viClose (vi); viClose (defaultRM); } void CSimpleDeviceFramework::processCommandN(char* command) { ViSession defaultRM, vi; char buf [256] = {0}; /* Open session to GPIB device at address 5 */ viOpenDefaultRM (&defaultRM); viOpen (defaultRM, "GPIB0::5::INSTR", VI_NULL,VI_NULL, &vi); viPrintf (vi, "OUTPUT:STATE on\n"); viPrintf(vi, "INIT\n"); viPrintf(vi, "*TRG\n"); viClose (vi); viClose (defaultRM); } void CSimpleDeviceFramework::processCommandG(char* command) {
71
int items = 0; int format = 0; int lines = 0; int counter = 0; ViSession defaultRM, vi; char buf [256] = {0}; /* Open session to GPIB device at address 5 */ viOpenDefaultRM (&defaultRM); viOpen (defaultRM, "GPIB0::5::INSTR", VI_NULL,VI_NULL, &vi); string outputString = ""; items = (*(command + 1))-48; format = (*(command + 3))-48; lines = (*(command + 5))-48; switch(items) { case 0: { //not supported break; } case 1: { outputString = "NSDC"; if(source == false) { outputString = outputString + "V"; viPrintf(vi, "sour:volt:lev?\n"); } else { outputString = outputString + "I"; viPrintf(vi, "sour:curr:lev?\n"); } viScanf(vi, "%t", &buf); outputString = outputString + buf + "B0CRLF"; break; } case 2: { outputString = "D"; outputString = outputString + char(delay+48); outputString = outputString + "BOCRLF"; break; } case 4: { if(source == false) { viPrintf(vi, "MEAS:CURR?\n"); } else { viPrintf(vi, "MEAS:VOLT?\n"); } viScanf(vi, "%t", &buf); outputString = "NMDC"; if(source == false) { outputString = outputString + "V"; } else { outputString = outputString + "I";
72
} outputString = outputString + buf; break; } case 8: { //not supported break; } } outputString = outputString + '\0'; printf("Send this to the Guidant Controller: "); while(outputString[counter] != '\0') { printf("%c", outputString[counter]); counter = counter + 1; } sendData(outputString.c_str()); //close the connection to the instrument viClose (vi); viClose (defaultRM); } void CSimpleDeviceFramework::sendData(const char* data) { eStatusMessage _Status = NIDEVICE_STATUS_UNKNOWN; // Notice that we are making the assumption that the entire // identification string for this device can fit into the // output buffer. // Note that by dereferencing the output buffer, it is automatically cast // to char *, which is the type expected by strcpy. strcpy (*m_pOutputBuffer, data); m_pOutputBuffer->SetMsgLength (strlen (data)); // Queue the response message with NI-Device. This implementation violates // the IEEE 488.2 standard since we are queueing a response message with // SendEnd=true (default parameter) before we have parsed the END message. // Normally the response message should be placed in an internal output // buffer and should only be queued to NI-Device when the END message has // been parsed or the internal buffer is full. // // ...Note that some parameters are left at their default settings. _Status = C4882Device::QueueResponseMsg (m_pOutputBuffer); if (NIDEVICE_STATUS_SUCCESS != _Status) { _DisplayErrorInformation ( "Unable to queue the device identification string.", _Status ); return; } // Set the Message Available (MAV) bit in the Status Byte m_StatusByte |= kStatusByteMsgAvailableBit; _Status = C4882Device::UpdateStatusByte ( m_StatusByte ); if (NIDEVICE_STATUS_SUCCESS != _Status) { _DisplayErrorInformation (
73
"Unable to update the status byte.", _Status ); } _DisplayActionInformation ( "Queued response message" ); }
top related