Title: How to Use VBA
1How to Use VBA Excel with the Bit3 to VME
- R. Angstadt
- March, 5 2004
2Why VBA? Why not VB? C?Great Glue Language
- VBAVisual Basic (for) Applications. VBVisual
Basic is a standalone product that must be
purchased separately. (VB(A) indicates
features common to both!) VBA comes with Excel
so there is no extra cost. If you are running
some version of windows with Office installed
then it is on your machine now! VB(A) allows
reuse of the million(s) of lines of C code that
Excel is written in to be reused. It provides an
easy way to re-use code. No building of custom
forms are required because it is so easy to use a
Worksheet as a pre-defined form its usually not
worth the trouble to make a custom one! - a. VBA knows about and can access all of
the functions in Excel either natively or using
the function Application.ExecuteExcel4Macro
(an_Excel_Function_Here!) VBA runs in
Excels context/scope. VBA can access 99.9 of
all the Excel menus and any objects, methods or
functionality they perform. - b. VB(A) can call almost any function of
any DLL on your computer including Kernel calls,
anything you write or someone else writes in
other languages such as C and/or inline
assembler. The DLL it calls into could also be a
driver DLLs that goes to hardware! VBA and
Excel are engineered to be extensible DDE, OLE,
COM DCOM. Microsoft has published book on
extending it. (Excel Developers Kits (various
years) Book and disk(s) or CD.) - c. Spreadsheets analyze stock market quotes
in real time and the original article Marvin read
in EE times involved operating a Nuclear Reactor!
3- 2. VBA Excel Enables Rapid code
prototyping/development - Saves lines of code! Engineered to be simpler
than C. Savings can be as much as 40 lines of
C to one VB line! A simple peek, poke real
windows program all in C takes gt 1k lines
excluding the Bit3 libraries/drivers. A Bit3
list processor can be done in lt 100 lines
excluding the same Bit3 libraries/drivers.
Typical realized average savings in lines of code
has been on the order of a factor of 10. - Saves person power The HV front panel
application that took 6 to 8K lines of Pascal
and 2-3 PERSON YEARS was done in 1,000 lines
in 4 months. (In terms of lines saved for this
project at least a factor of 6 (conservatively)
or more in savings.) - 3. Originally suggested by M. Johnson as an
easier way to move from DOS (Turbo Pascal) to
Windows environment. (We actually tried
something similar cerca 86 with Lotus 123 macros
but it was not modular and hard to maintain and
the screen flashed annoyingly.) VBA and Excel
have overcome these deficiencies.
4FYI only Before GetOpenFileName in C can be
called it takes 44 lines to fill the structure
call!
- void PopFileInitialize (HWND hwnd)
-
- static char szFilter "Data Files
(.DAT)\0.dat\0" \ - "Paint Files (.BMP)\0.bmp\0" \
- "Text
Files (.TXT)\0.txt\0" \ - "All
Files (.)\0.\0\0" - ofn.lStructSize sizeof
(OPENFILENAME) - ofn.hwndOwner hwnd
- ofn.hInstance NULL
- ofn.lpstrFilter szFilter
- ofn.lpstrCustomFilter NULL
- ofn.nMaxCustFilter 0
- ofn.nFilterIndex 0
- ofn.lpstrFile NULL //
Set in Open and Close functions - ofn.nMaxFile _MAX_PATH
- ofn.lpstrFileTitle NULL //
Set in Open and Close functions - ofn.nMaxFileTitle _MAX_FNAME
_MAX_EXT - ofn.lpstrInitialDir NULL
5File Dialoque Boxes in VBA takes a few lines.
- Information can always be read or written to a
file. Furthermore it can be done using the same
file dialogue box Excel uses with just a few
lines of VBA code. Here the whole get a file to
read routine takes 14 lines. 2 lines for
arguments 1 to make the call is 3 lines max. - Sub getFileNameVRB()
- Dim szTitle As String
- Dim szFilter As String
- Dim szFileName As String
- Dim szOKwasPressedNoCancel As String
- ' Select a file to print, use the standard
excel get a file dialogue box - szTitle "S record .hex file to process"
- szFilter "S record .hex Files (.hex), .hex"
- szOKwasPressedNoCancelApplication.GetOpenFilena
me(FileFilterszFilter,TitleszTitle) - If szOKwasPressedNoCancel ltgt "False" Then
- szFileName szOKwasPressedNoCancel
- Cells(12, 4).Value szFileName could
open it here and do something! - End If
- End Sub 'getFileNameVRB
-
- Heres the VBA help notes dialogue box for
obtaining a file to write (save) to taking 1
lines (plus 3 for error handling!). -
- fileSaveName Application.GetSaveAsFilename( _
fileFilter"Text Files (.txt), .txt")
6VBA has been Modernized!
- Line numbers obsolete (though still supported!)
Its now a modern language with block structure
concepts like Pascal and C including Case
statements. Has modules (ala Modulo), arguments
passed by reference by default (like Fortran so
arrays can be passed like many are used to. ) - Has a very nice IDE (Integrated Development
Environment (ala Borland Turbo Pascal) with
editor and real debugger, and context sensitive
help. - Has Short Comings but is Quite Useable. Any
shortcomings can usually be worked around and/or
overcome with some C code in a DLL! No show
stoppers so far! - It has maintained its backward compatibility
very well. (Old code has not been broken. Of
course macro recording on say Version 10.x and
then running on Version 9.x may not work! Have
to record on Version 9.x and run on Version
10.x!) (Even includes code from some early DOS
versions as per Craig Symonds in 96 Group
Programmer Manager for the VB group (VB, VBA, and
scripting for last 5 years from 96. (pg xxx of
VBA Developers Handbook, Sybex 97 by K. Getz
M. Gilbert.) I can verify this as one time I used
an old function IBM DOS function from an IBM
Basic manual that I had laying around but was not
Microsofts help at the time. When I typed it in
it ran! (Integer is still 16 bits even after
they ported it from a 16 bit version to a 32 bit
version!) - Will try to not speak too much about operating
systems as its almost a moot point here but it
will be unavoidable at some point so I would like
to explain the following right now in the
beginning. - Win 9x shall mean Windows 95, (OSR2), Windows
98 (1 and 2) through ME (Millennium Edition)
because from the stand point of most driver
development including my Bit 3 drivers they are
the same. (Same driver for all of them!) - Likewise, NT shall also include Win2k, and XP
in a generic sense as they all use basically the
same driver model which is of course is a
completely different driver model from 1 above.
Win 9x and NT were developed by two completely
different programmer teams with different
design goals and emphasis even thought they are
from the same company so they are really pretty
different operating systems. Though they share
some components, their code base is different.
Go to File Explorer, Help, About in each
and you will see NT is Release 4 (Service pack
6) Win2k is Release 5.0 (Service pack 3) and
XP is (only) Release 5.1. So except for
marketing and hype they are all still basically
NT. The same goes for driver model. The same
Bit3 driver of mine almost works on all of them
except for XP which requires a tweak and has its
own version. (Havent tried the XP tweak on
Win2k and/or XP.) Thus, NT shall mean any and all
of NT, Win2k, or XP unless XP or Win2k are
specifically mentioned.
7Our First VBA program!
- The key combo Alt-F11 brings up the VBA IDE. (If
not then Tools, Macro, Visual Basic Editor)
brings it up. (Many ways to get back to Excel
including Alt-F11. - On the VB IDE click on Insert and then Module
and Module1 comes up in the property box. In
the largest white area type in -
- Sub hello()
- Cells(1, 1).Value "hello world"
- End Sub
-
- Go back to Excel (Alt-F11) and run it. Various
ways but Alt-F8 works! And then run! hello
world appears in upper left hand corner cell
A1. Not so exciting by itself. But it could
be put anyplace on the screen by changing the 1,1
to any row numbers of 1 to 65536 and the column
number from 1 to 255. Also note that cells
ends in an s (plural.) This is intentional and
signals it is an array and/or collection. For
arrays and/or collections within Excel this is
amazingly consistent! The . is an separator for
objects and/or methods. Thus object.sub_object_ch
ild1sub_object_n.final_object_or_method is
typical syntax. More and better concrete
examples to follow.
8CountWrite our 2nd Subroutine!
- Slightly more exciting is
-
- Sub count2Ten()
- Dim lRow As Long
-
- For lRow 1 To 10
- Cells(lRow, 1).Value lRow
- Next lRow
- End Sub
-
- Although it is not required to declare
everything, it is a good idea to do so. Putting
Option Explicit at the top of the module
requires that you declare everything. - Another thing that is good to get in the habit of
doing is after you make an edit in the VBA IDE is
to go to the Debug menu pick and then
Compile. One good reason to do this is that if
you are just making a small change it may not be
seen by VBA so the Compile will be grayed out!
For example if you go to the 10 and type in 5 and
then go run the macro chances are it will do the
loop 10 times. To remedy this one must move the
cursor off the line and/or go to the end of a
line and hit the Enter key (it adds a blank
line, you can delete it if you want!). But the
point of all this is to make some changes in the
code that it will see so that the Compile will
not be grayed out. While some interpretation is
done while you type, Compiling does a lot more
including (argument) type checking of the whole
project. Its just a good idea and may save you
having to come back and add a Dim statement as
well as possibly helping to keep the Worksheet
file size minimal. Also when the button is
pushed there will be lower latency the first time
as it wont have to automatically compile it
first (assuming it was a big enough change that
it saw) it will already have been compiled!
9Our Third Puts Formulas in Sheet!
-
- Sub Circumference()
- 'puts formulas in the cells sheet can update
immedietly when user types in a new radius! - Dim lAry(11) As Long
- Dim lRow As Long
- Dim lIndex As Long
-
- 'put up some headers
- Cells(1, 3).Value "Radius"
- Cells(1, 5).Value "Circum Formulas!"
-
- 'read output of sub count2Ten()
- For lRow 1 To 10
- lAry(lRow) Cells(lRow, 1).Value
- Next lRow
-
- 'make a little radius, circumference table
- lIndex 1
- For lRow 2 To 11
10Circumference2 Just the Values!
-
- Sub Circumference2()
- ' another way all from code the macro must be
re-run if any radi are changed! ignores user
input! - Dim lAry(11) As Long
- Dim lRow As Long
- Dim lIndex As Long
- Dim dPi As Double
-
- 'put up some headers
- Cells(1, 3).Value "Radius"
- Cells(1, 7).Value "Circum Values Only"
-
- 'read output of sub count2Ten()
- For lRow 1 To 10
- lAry(lRow) Cells(lRow, 1).Value
- Next lRow
-
- way cool calling an excel function from
within VBA! - dPi Application.ExecuteExcel4Macro("Pi()")
11Circumference 3 Using Functions
-
- Sub Circumference3()
- ' uses functions for better code reuse
- Dim dAry(11) As Double
- Dim lRow As Long
- Dim lIndex As Long
- Dim dPi As Double
-
- 'put up some headers
- Cells(1, 3).Value "Radius"
- Cells(1, 9).Value "Circum Values Using
Function calls in code" -
- 'read output of sub count2Ten()
- For lRow 1 To 10
- dAry(lRow) Cells(lRow, 1).Value
- Next lRow
-
- 'make a little radius, circumference table
12User Written Functions called by Circumference3!
(can call from Excel!)
-
- Function getPi() As Double
- ' this is a way cool thing any function in
excel you can call from vba! - ' pi() is an excel function returning pi to 15
places its not intrinsic to vba! - getPi Application.ExecuteExcel4Macro("Pi()")
- End Function
- Function doCircum(dRadius As Double, dPi As
Double) As Double - ' you can call this from another vba sub or
function or right from excel! - ' if excel doesn't have a function you need you
can write your own! - ' even making use of the functions built into
excel. - doCircum 2 dRadius dPi
- End Function
13Icing on the (GUI) cake Buttons
- Lets Make a button and assign it to a macro
from Excel go to View, Toolbars and make sure
Forms is clicked. Then click on the fourth
object on the Forms toolbar which is a
Button. Move the mouse to the Worksheet and
press and hold down the left mouse button and
drag it somewhere and release it. - Youve made Button 1 It can be attached
directly to any subroutine (without arguments)
that you write. Right click on it and attach it
to one of the macros that we wrote earlier, say
Circumference, change the text on the button,
Button 1, to Circumference. Unselect the
button by clicking anywhere on the sheet. - Now any time you left click on the button the
Circumference macro will be run. It took 0
code to make the button and assign it to the
macro! Alternatively you could have done it all
from code You can change its name or re-assign
which macro it is run with at any time. Its all
built into Excel. - Macros can be attached to many objects
TextBoxes, Pictures, (.gifs) etc. - Note there are 2 buttons, (2 kinds of
objects). Global and restricted (or local scope).
(historically, old and new). I prefer the
Global objects on the Forms button because I
believe they are more useful one can attach them
to generalized code that will work on the
Active sheet. The other Control Toolbox
buttons/objects are restricted and work only on
the sheet they are attached to. They cant be
copied to another sheet. Further pain is that
they are edited in Design mode. (Sheet cannot
be saved when in Design Mode) They are just one
pain after another and I believe they should be
avoided (like the plague.... Button from hell,
etc. Youve been warned!)
14Subroutines and Functions Which when?
- Though Functions can return something in a cell
when they are placed in a cell they generally can
NOT in any way act on the screen e.g. cant use
Cells(row,col).Value in a function to write to
the sheet. (Must use a Sub) Also - Function try2callExcel4(szS As String)
- 'if szs"zoom(75)" it doesn't zoom the screen
because a function can NOT act on the screen - Application.ExecuteExcel4Macro szS
- End Function
- Sub zoom()
- ' however this changes the screen size of the
active sheet! - Application.ExecuteExcel4Macro "ZOOM(75)"
- End Sub
- If something doesnt work in a function try a
sub! - Functions can NOT be hooked to a button or other
object. (Must use a Sub). (Also if a
subroutine has arguments it cannot be attached to
a button directly. So to test a generalized
module (with arguments) must write a Test module
to call the generalized module. Sometimes
referred to as a shell sub or just shell.) - But functions can be put in a cell and return a
value! (Sub can NOT.)
15Real Power, Importing External Functions in DLLs!
- VB(A) has the ability to Register any external
function in any DLL (Dynamic Link Library) on
your computer. This DLL could ultimately be
connected through a driver to hardware! This can
be as general or as specific as the DLL and
driver it connect to. Dont get thrown by DLL.
It just means that the address of an external
(object, binary or library) function is found,
resolved, loaded, and executed (run) dynamically
when called by a Click of a button somewhere or
by some piece of code doing something! This is
in contrast to the older more typical Static
Library that is linked to at Code Compile and
Link time. Win9x and NT are both almost entirely
based on modules of (typically C) code which
can call into each other back and forth by the
mechanism(s) of dynamic loading and linking
(DLL)! What it allows is just a code module
(DLL) to be recompiled and/or replaced (as long
as no entry points are deleted or no arguments of
functions have been reduced. Their can be more
functions but not less.) It also saves having to
recompile the (whole) kernel. One just has to
recompile that DLL which may or may not be part
of the Kernel. - For our first example of registering and
calling an external DLL well go right for the
maximum gusto! Remember when Basics of yore
included peek and poke (ing) to hardware?
This is now once again made possible on NT if
Dale Roberts clever driver giveio.sys is
installed. (Win 9x needs no driver, just a
similar DLL!) (For more about this see DDJ May
96. Port I/O under Windows and NT Dales
driver also saves a few hundred clocks of NT CPU
time each time a peek or poke is made so that
NT is now as fast as Win9x!) The old (and new)
Peek() and Poke() routines allow one to get out
on both the ISA and PCI buses via the
motherboards chipset(s) to physical address 0 to
65535 in I/O space. On Intel 86 (all my stuff
is only for Wintel platform no other CPU is
supported and only 1 CPU at that!) there is
actually a physical CPU pin labeled I/O! When
this pin (bit) is high then the address on the
bus is to I/O. When not high its a normal
(non-I/O) address. (Basically a 1 bit address
modifier instead of VMEs 6 bit address
modifier.) If NT is like eating at McDonalds
and Win98 is like eating at Burger King then this
driver allows us to eat at McDonalds and have it
(our) way! With this one can be destructive and
corrupt your hard drive or you can be
constructive and get to the Parallel Port
directly or talk directly to CAMAC via an ISA bus
DSP card board set written entirely in VBA?
(Used in the village and else where.). (The NT
Bit3 DLL is as fast as it is partly due to this
driver.) The DLL is on d0server4\users\angstadt\
ntuitl\ntio\debug\ntio.dll Giveio.sys install
instructions are on the web - http//d0server1.fnal.gov/users/angs
tadt/www/b3/b3_61x.htm
16Real Power, Importing External Functions in
DLLs! (2)
- There is a similar DLL and driver for normal
non-I/O memory as well d0server4\users\angstadt\n
tuitl\ntphsad\debug\ntphsad.dll so with these 2
drivers and DLL one can get to the hardware of
their machine on a Virtual Operating System.
Drivers may be written entirely in VBA! - Peeking probably wont hurt your machine too much
but (RANDOM) Poking is STRONGLY discouraged! - So here it finally is the VBA code modNT_IO.
Copy and paste it in to your spreadsheet or use
d0server4\users\angstadt\ntutil\ntio.xls once
giveio.sys is installed. Lets be careful out
there! I/O address 0x200 is nominally safe as
it is nominally the game port. However your
machine may be different and/or not have one.
Reading 0xff back is essentially not there, not
initied, or in VME speak, NO DTACK. I/O
address 0x378 is nominally LPT1 but this depends
on the BIOS and Ports, Resources in the
Device Manager of the Control Panel. Your
mileage will definitely vary. Finally this is
the VB(A) code -
- Declare Function pokeport Lib "ntio.DLL" (iAdd As
Integer, iVal As Integer) As Integer - Declare Function peekport Lib "ntio.DLL" (iAdd As
Integer) As Integer - Function VB_pokeIO(iAdd As Integer, iVal As
Integer) As Integer - Dim i As Integer
- i pokeport(iAdd, iVal)
- VB_pokeIO i
- End Function
- Function VB_peekIO(iAdd As Integer) As Integer
- Dim i As Integer
- i peekport(iAdd)
- VB_peekIO i
- End Function
17The C code the VBC calls
- Part of ntio.c (so simple I didnt make a
ntio.h file. (Shame on me.) Normally the .h
file is what one looks at to register (import)
C DLLs into VB(A) as above.) If I did have a
ntio.h file it would have about 2 lines in it - DLLEXPORT short int WINAPI pokeport(long
plportadd, long plval) - DLLEXPORT short int FAR PASCAL
peekport(long plportadd) - The .h file is supposed to hold the function
declarations that are exported so other modules
can include the .h file so that they may
(static) link to it at compile time. I
(unfortunately must have been in a hurry) and
skipped making a .h file. I evidently only
cared about the VB(A) dynamic part. Below is the
relevant part of ntio.c -
- DLLEXPORT short int WINAPI pokeport(long
plportadd, long plval) - //writes ival to the p.c. port address specified
by plportadd. -
- int ilocalval
- unsigned short wadd
- ilocalval(int) plval
- wadd(unsigned short) plportadd 0xffff
-
- return _outp(wadd,ilocalval)
-
- //-----------------------------------------------
---------------------------- - DLLEXPORT short int FAR PASCAL peekport(long
plportadd) - //reads a value, itemp, from the p.c. port
address specified by plportadd.
18FYI MOST OF THE WHOLE NTIO.C DLL SOURCE clipped
some commentsALSO IS AN EXAMPLE OT WHAT EXCEL
SAVES YOU FROM! (FYI only. No quiz on this.)
- .
- include ltwindows.hgt
- // include ltc\cpp\xlcall.hgt nothing from this
really used - include ltstdio.hgt
- include ltconio.hgt // macro def for port i/o
outp is in conio.h - include ltdos.hgt
- // include ltalloc.hgt // nec. for malloc()
- include ltprocess.hgt // nec. for exit()
- include ltstring.hgt
- // include ltmath.hgt
- //true defined in windows.h and other places
- //define FALSE 0
- //define TRUE 1
-
- HANDLE hDriver
- / gets called at load time...when the function
is "registered". / - / main entry point /
- BOOL WINAPI DllMain(HINSTANCE hDLLInst, DWORD
fdwReason, LPVOID lpvReserved)
19- //printf("Couldn't access giveio device\n")
- bretFALSE
-
-
- else
-
- bretTRUE
-
- //giveio modifies the i/o permission map in
this task's tss - // task segment selector (according to the
author) so - // once this is done, it's done! when the last
dll user goes so - // does the task and it's permission map so we
don't need giveio.sys - // anymore! (very very nice!) no cpu cycles
are used on nt protection! - CloseHandle(hDriver)
-
- break
- case DLL_PROCESS_DETACH
- // The DLL is being unloaded by a
given process. Do any
20- // A thread is exiting cleanly in a
process that has already - // loaded this DLL. Perform any
per-thread clean up here. The - // return value is ignored.
- bretTRUE
- break
-
- return bret
-
- define DLLEXPORT __declspec(dllexport)
- // this is the meat and potatoes
- //-----------------------------------------------
---------------------------- - DLLEXPORT short int WINAPI pokeport(long
plportadd, long plval) - //writes ival to the p.c. port address specified
by iportadd. -
- int ilocalval
- unsigned short wadd
- ilocalval(int) plval
- wadd(unsigned short) plportadd 0xffff
21Call the Kernel from VBA! This is modTime in
some sheets (VB(A)) code!
- Public Declare Function timeGetTime Lib
"winmm.dll" () As Long - Declare Sub Sleep Lib "Kernel32.DLL" (ByVal
dwMillisecconds As Long) - Const lSLEEPTIME As Long 12 'milliseconds
- Sub AbenchRead()
- Dim l As Long
- Dim lTime As Long
- Dim lPlace As Long
- Dim ival As Integer
- Const lMAX As Long 1000
- Const lAdd As Long 33536 '"H8300" -gt a
neg. num! - 'MsgBox (" get ready to set your stop watch")
- lTime getTimerVal
- For l 1 To lMAX
- ival VB_readi(lAdd)
- Next l
- lTime getTimerVal - lTime
- MsgBox (" done reading " Format(lMAX) "
times took " Format(lTime) " milliseconds ") - End Sub
22Registering Bit3 DLL Functions! (some)
registered functions
- 'have managed to unmangle the names for the 617
driver! - '
- Declare Function initio Lib "bntdv617.dll" (i As
Integer, l As Long) As Integer - ' Besides initing the bit3, setting the address
modifier, it initializes all - ' secondary error handling bite.dll flags to the
correct state including - ' the internal static variable "abortflg"0
false (no failures) or 1 a failure - Declare Function islatcherri Lib "bntdv617.dll"
() As Integer - 'returns true1a failure if a bit3 error
detected since the last time - 'initio() or clrlatcherri() was called. call
getlasterrps() to find last error! - Declare Function clrlatcherri Lib "bntdv617.dll"
() As Integer - ' set static variable "abortflg" to 0 false no
failures and should always return 0. - ' the "c" code is
- ' abortflgFALSE
- ' return abortflg
- Declare Function getlasterrps Lib "bntdv617.dll"
(ByVal s As String) As Integer - ' tells what the last bit3 error was in english
(more or less). - ' this is a noteworthy example of how to return
call a "c" function that - ' returns a pointer to a null terminated string
which VBasic has troubles with. - ' the "byval" fixes the null terminated part but
it still has trouble with the pointer so
23Bit3 Some Registered Functions Wrapped
- Function VB_InitIOi(addmod As Integer, Address As
Long, model406flg As Boolean) As Integer - Dim i As Integer
- Dim j As Integer
- Dim i406adapter As Integer
- If model406flg Then
- i406adapter 1
- Else
- i406adapter 0
- End If
- i initio(addmod, Address)
- j setb32flgi(i406adapter) ' constant set
1 or 0 after bit3.dll declarations above - VB_InitIOi i
- End Function
- Function VB_getlasterrs() As String
- Dim s As String 255
- Dim iworked As Integer ' this is more of
J.W's trick - s String(255, 0)
- iworked getlasterrps(ByVal s) ' this is
the final part of J.W.'s trick
24Why Wrap Functions?
- Initially it made things more robust. (Fewer blue
screens of death when a c routine was passed the
wrong type in an argument.) Allows VB(A) to
better trap common programmer type (checking)
errors b4 making the c call. - Can hide any tricks being used to call into the c
code. (Strings are a bit tricky/weird/magic.)
Keeps all the tricks in one place to prevent
argument calling errors and thus aide robustness
as above. - However the best reason here is that it allows
one to change Libraries (DLLs) easily. Due to
differences in price and capability and
historical reasons, various test stand use
different Bit3 models and even busses. By
wrapping whatever DLL is called in its own
module one may isolate and minimize any
differences to the rest of the Worksheet. Thus a
whole worksheet developed on one test stand may
be run on a completely different test stand by
changing out the Bit3 module. A worksheet that
is moved this way can be changed to run on a
different operating system (different ways of
getting to the hardware drivers) and/or use a
different Bit3 model (different busses!) in 5
minutes by cutting and pasting in different VBA
modules. (Note the similarity to a language
called modula which was a successor to
Pascal.) This module feature has been
extremely useful for us. Specific Bit3 DLL and
modules info available at http//d0server1.fnal.
gov/users/angstadt/www/d0notes/2589/convertb3.htm.
In general modules are a good thing (precursor
of objects) and should be used if and where ever
possible. In most cases the cost is at most a
microsecond or 2 at run time depending on the
hardware one is running on. - Modules are also a way of dividing and
conquering. If two people are working on the
same sheet one could make their changes in one
module and the 2nd in another. Periodically the
new modules could be replace or updated and then
checked to make sure they worked as intended
together. If so then it could be released.
Then the next round of changes would be made and
the process repeated until the Workbook was
done. This is good practice. If you take an
existing Worksheet over and start to modify it
then if possible please add new code
modifications (your) own new module(s). That way
if fixes/improvements/patches are made to the old
ones in your sheet are developed by someone else
the old modules can be easily changed and/or
upgraded as necessary. This is easy to do as
long as your changes are not mixed in with
theirs. Then its all yours and you have to fix
everything yourself! Modules can be a very good
thing! -
25Typical Bit3 Calling Sequence
- It is assumed InitVME() is called outside of and
sometime before a VME access (reading or writing)
to the VME bus. Once it is called it should not
have to be called again unless any of the
following occurs - Power is removed from the crate and turned back
on. - VBA execution is halted via the Esc key or
Ctrl-Brk. (Best to do this but may not be
required) - If for some reason a piece of code is run where a
lot of invalid VME addresses occur. After a lot
of no DTACK ing the Bit3 may be need to be
reinitialized before valid addresses start
working again. (After a big bunch something
latches in the Bit3 that the quicker error
clearing mechanisms dont clear it. A total
global restart is the only thing that brings it
back but this nominally takes a second so it is
not done automatically all the time in the
driver. It usually takes seconds of invalid
addresses before the Bit3 really latches like
this. (On a fast machine in tight code this
could theoretically be gt300 to 400K times.)
See next major section for more. - Any time a valid VME address is put on the bus
and a VME modules DTACK LED does not light.
(Occurs rarely but it can happen. Reseating
cards in the busses and/or cables may be the fix.
Check VME crate power. Pull all VME modules but
the arbitrator, Bit3 and target to check if of
them has latched bus grant or some other VME bus
signal. Check that all jumpers are correct on
all boards. If not a VIPA crate and if the VME
Bit3 card is not jumpered for Bus Arbitrator and
not in slot 1 then Bus Grant 3 may not be making
it to the VME Bit3 card. (The 1553 card does not
jumper bus grant 3 across for example. (Crate
configuration and jumpers may need to be
changed.) - After calling InitVME() one should wait at least
a second (and perhaps 2) to be safe. (The latest
2nd version for NT has a half second delay in
it at the end of it (already) for the precision
timing calibration it uses.) All of the Bit3
driver DLLs keep a (latch on a Bit3 detected
error) global flag in them, iabortflg. It is
reset (false) only at power up and only whenever
VB_clrlatcherri() is called. VB_islatcherri()
always returns 0. VB_islatcherri() value is in
re-setting the flag without going through the
whole init Bit3 process and not in what it
returns. This flag, iabortflg gets set True ( ltgt
0) for any detected Bit3 VME failures. It
latches in this state until reset via a call to
InitVME() or VB_clrlatcherri() is called. (Thus
any granularity of error checking is achieved.)
26Typical Bit3 Calling Sequence 2
- VB_getlasterrs() returns a string that indicate
what the Bit3 and driver thought the last VME
error detected was. Possible values are " VME
TIMEOUT, " VME BUS ERROR, " VME PARITY
ERR, " DMA ERROR, "connect giveio.sys
failed , connect mapmem.sys failed , "no
vme access no buff from mapmem.sys . Any and
all combinations of the first 3 may be seen
together on a VME access and mean that for some
reason NO DTACK came back for that address.
The latter errors may occur only in the NT
version of drivers and indicate that something is
not working between the driver DLL and one of the
drivers (.sys binaries) it is using. They may
not be running for some reason and/or installed.
If "no vme access no buff from mapmem.sys
occurs it means something pretty bad happened in
mapmem.sys and/or its connection was broken.
Saving any unsaved work if desired and leave
Excel entirely and come back into it. This seems
to mostly happen when stopping VBA code execution
via Ctrl-Break (during VBA development.) I
apologize for not being able to totally make
this go away entirely. Although it is an
annoying nuisance it has not been a show stopper.
Recently Ive found that not putting
VB_InitIOs() in any of the cells seems to delay
the onset of this considerably. More on this
later in How Excel Recalculates determinism
(or lack of it.) ... Most VME errors are
trapped but there are a few conditions that may
not be. These rare and usually due to a botched
driver install for some reason or other. Best is
to call me if you are having troubles so I can
come and look. - Read routines have no error status back and one
must use the above mechanisms for error checking.
They just return the value they read. (This
allows the function to be placed directly in an
Excel cell!) Write routines return 0 on detected
failure and a 1 on success. There is enough
their so that one could re-write the wrappers to
give back any kind of calling interface that
could be imagined/desired.) - If using getvmedmal() (only available for the 6xx
models) then it returns its own error code in
addition to the first mechanism described above.
On return this must be checked after every DMA
for a 1truedata is valid. The user buffer is
not zeroed in the interest of speed. (The caller
may 0 some or all of their buffer before doing
the DMA if they desire.) Other error codes are
from bntdv617.h - after the dma is complete the data will be
copied to this buffer and - // the routine will return success1 or
failure false0 or -99 if - // it could not create a 64K special dma
buffer. if the return is -99 - // close the program (unload the dll) and then
reload it.
27Example of a Typical Bit3 Calling Sequence after
a call to VB_InitIOs() or VB_InitIOi() (and crate
not power cycled.)
-
- Sub go53mhzOsc()
- Dim lAdd As Long
- Dim iIs1EqualTrueIfSuccessElse0EqualFalse As
Integer - Dim iPlace As Integer
-
- iPlace VB_clrlatcherri ' clear the
latching bit3 status error flg (iplace should
always 0 !) - lAdd lVRBCBASEADD 32782 H800E 'vba
sign extension bug bites... - iIs1EqualTrueIfSuccessElse0EqualFalse
VB_Writew(lAdd, 0) ' select normal operation
for autotest fpga -
- If VB_islatcherri 0 Then
- 'MsgBox "no errors detected processing list
in sub ListProc may not honor empty block if! - Else
- MsgBox "one or more NODTACK during
go53mhzosc" - End If
-
- ' Go to IDLE NRZ in order for the Sequencer to
establish the framing bit - ' Call goIDLEnrz
-
28A Word on Notation
- In the code fragments presented so far you may
have noticed odd things like an l (lower case
L) in front of lAdd (Add is short for address
in this case a VME physical address.) l is
short for long. In iPlace the i is short for
integer. sz indicates a string with a zero
terminating it (basically a C type of string as
opposed to a VB string which probably will not
have a 0 terminating it.) b is for Boolean.
mod is short for module. txt is short for a
text box. It must be stressed that this is a
convention only. What really determines what
type of the variable is the Dim statement
wherever it is declared. Thus Dim iVal as
Long fails to follow the convention and is
misleading i implies a 16 bit integer but it
is in reality a 32 bit Long. - This is a form of Hungarian Notation (plenty
under Google for this string!) originally
described by Charles Simonyi, a Hungarian with
an unpronounceable last name, hence it became
known as Hungarian notation. Originally this was
for c code. G Reddick and Lesznynski expanded
it to be the Reddick VBA or RVBA naming
convention. One of the things Simonyi argued
pretty strongly (about if I remember correctly)
was to put the type up front followed by a
capital letter signifiying when the type ends and
when the variable name starts as opposed to
having it at the end which was previously more
common before Simonyis writings. - Naturally a lot of my C DLL code base is from
code written before I knew of Hungarian or RVBA
notations. It was based on suffix indicators
such as flg for a flag, w, for (unsigned) word,
i for integer, ect. After RVBA I begin using a
mixture! Thus in the c variable iAbortflg the
i is for (short 16 bit) integer (used as) a
flg. I avoided a Boolean type because I wasnt
sure if VB and C would use the same number of
bits. - For VB I think Hungarian notation is useful
because an integer in VB has always been 16 bits
and a Long has always been 32 bits from the 16
bit version 4 (Office XP is 10 now.) 8 years
ago. For C where the code iVal int can change
from 16 bits to 32 bits or vice-versa with a
different platform and/or compiler! (or perhaps
even 64 with a third platform!) So old C code on
ports like this could require tons of editing to
fix completely misleading notations. If its a
large quantity of lines then probably no one is
going to go and change all the names because of
the high probability of introducing too many
mistakes. Any notation is just meant to be an
aide to quick understanding of code. If it does
not succeed at that then it might not be worth
the extra work.
29A Word on Notation (2)
- Why so important? Were doing mixed language
programming so the arguments and return values
must match or a possible blue screen of death
could result. We have to pay attention to our
calling arguments and make them match the
declarations in the Bit3 module. A word or two
about the code may help you decipher it if you
feel the need to have to look at it! - Back to trying to clarify what youre looking at.
Heres the C code for the islatcherri()
function - DLLEXPORT __int16 WINAPI islatcherri(void)
- return (short) iabortflg
- Another example is getlasterrps the p
is short for pointer and s is short for string.
So the whole thing spelled out is Get the last
error returning a pointer to a string. - So for a lot of the Bit3 functions I used use my
old suffix notation on the type of VME operation
they will put on the bus and/or return 8, 16 or
32 bits. It sort of works i for signed 16
bits, b for byte8. Very nice! - For a model 6xx that support 32 bit operations
then the wrapped VB_readl(), VB_writel() and
VB_readul() are provided that wrap the DLL
readvmeul() and putvmeli() actual DLL entry
point. - Note that in all Bit3 functions is a
misleading/confusing name ending writevmeli()
Another reason to just use the wrapped
functions. Here the conventions break down. I
apologize for this. WriteVMEli() exported
function available which the header file tries to
clarify - DLLEXPORT __int16 WINAPI writevmeli(long
pdaddress,long lval) - / this name and the second argument of
writevmeli are misleading. - writevmeli does NOT do a 32 bit word vme bus
cycle. - it does only a 16 bit vme bus cycle. the
argument is 32 bits to - facilitate getting back a 16 bit unsigned
quantity short to Visual Basic - as VB has no short intrinsic unsigned
(__int16) __int16. in no way shape or form - does this do a 32 bits to vme.
- A long is necessary to hold an unsigned 16
bit quantity. (More on next section.) - (The models 6xx can do 32 bit VME operations and
there are a few additional calls for those
libraries. It is easy to move up to a 32 bit
model by cutting and pasting in new VBA code but
going down is only easy if 32 bit VME operations
are not needed/used in the rest of the sheet.)
30A Word on Notation (3)
- In one early sheet I found 2 possible wrappings
of WriteVMEli which I thought might be
interesting because the wrapper adds
functionality to return the last error string
instead of a 0 or 1 (failure, success
respectively). If you need a slightly different
style then there is enough here I think to morph
the arguments and return values to your needs!
This is from a module called io_32 in hv5_32.xls.
Its for a 40x under Win 9x, Bit3_32.Dll but
it would work the same under the equivalent NT
DLL, BNTDV40X.dll. - Function VB_Writew(Address As Long, lvalue As
Long) As Integer - Dim i As Integer
- i WriteVMEli(Address, lvalue)
- VB_Writew i
- End Function
- Function VB_Writews(Address As Long, lvalue As
Long) As String - Dim i As Integer
- Dim s As String 255 s is declared here
with space (a buffer) for 255 characters - i WriteVMEli(Address, lvalue)
- s String(255, 0) ' every character of
the string is now set to 0! - s VB_getlasterrs() ' this returns the
last error string from the bit3 C driver - VB_Writews s
- End Function
- So this notation is to try to indicate that
VB_writew() writes an unsigned word (returning
the usual and/or default integer 0,1 as a 16 bit
integer) while VB_Writews() writes an unsigned
word returning a string indicating an error or
ok! if no error was detected. Both do this by
wrapping the unsigned 16 bit word into a larger
signed 32 bit word or long. Why do we have to
package up an unsigned 16 bit quantity in a
(signed) long of 32 bits?
31Some Problems with VBA and Some Work Arounds
- Unfortunately VB(A) has no intrinsic unsigned
types. Everything is signed. An integer is
always 16 bits (0xffff) -32768 to 32767
(0x7fff) in VB(A) though other languages
(usually) provide a 16 bit unsigned integer 0 to
65535 (0xffff) - Long is also signed (0xffffffff)-2,147,483,648 to
2,147,483,647 (0x7fffffff) - Unsigned Long would be 0 to 4,294,967,295
0xffffffff) - Note something helpful to remember is that for
any signed quantity the largest positive value it
will hold is 0x7f in the high byte (on a 2s
compliment machine, which most computers are
including Wintel). - Assigning h8000 to a long doesnt work! One
gets 0xffff8000 and not 0x8000. (sign extension
problem on any hex value gt 0x7fff. This has been
a real pain! - Workarounds
- Promote everything to more Bits. Use a long to
hold 16 bits being careful to use decimal instead
of hex for values gt 0x7fff. - Pass it onto the C stack where the C argument is
unsigned and/or larger (more bits) as above.
E.g., if you need 0 to 65535 then use a long. If
gt 2 billion then use a double. Etc. (can use
arbitrary long integers with C and inline
assembly. I have an example of using gt 64 bits
using the ADDC instruction using double and 2
longs so that modulo operations still work on the
lower 32 bits. - Use strings! Can also pass it as a hex string and
convert it to binary where necessary. - VB(A) doesnt do true bit shifting some fixes
available in xutil.c and more elsewhere. - Conclusion/Summary Due to its extensibility
many/most limitations can be worked around. (One
that cant be is the programmers lack of
imagination and/or skill!)
32Concept of Lists
- B4 objects there was structured programming
with a fundamental tenet being that one function
(and/or subroutine) did one thing (in as
generalized a way as possible) (Just as there
are objects now) One can expand this to the
concept of a list. - Make One list do one thing. Maybe you have a
list for the first grocery store and a 2nd list
for another due to a sale at the second but the
first has better produce. Maybe another for the
Hardware store, etc. Maybe after all your lists
are made and organized you expedite them by
shopping. The idea is the same, make a list of
like VME read/writes, their addresses and values,
and then expedite or execute that list! (Make
the VME DTACK LEDs light!) - (FYI There are languages built around this list
concept including a now somewhat obsolete
language called Lisp which I think now is quite
dead due to its difficult to use of parenthesis
on top of parenthesis. It wasnt pretty but it
had power for those that could keep track of and
parse the parenthesis.) - Remember one of our first goals was to reduce the
number of lines of code? One way is the concept
of a list processor. Not new, many programs
(had) have file based list processors of various
sorts. The next slide is a simple 48 line VBA
subroutine based on the Bit3 module that will
process a VME list.
33A (simple) VBA VME List Processor (48 lines)
- Sub ListProc()
- Dim iRow As Integer
- Dim iMax As Integer
- Dim iplace As Integer
- Dim lAdd As Long
- Dim lVal As Long
- Dim iVal As Integer
- Dim iAddMod As Integer
- Const iROWSTART As Integer 16
- Call allAutoOff
- iplace VB_clrlatcherri ' clear the
latching bit3 status error flg - iRow iROWSTART - 3
- iMax Cells(iRow, 4).Value
- iMax iMax iROWSTART - 1
- For iRow iROWSTART To iMax
- lAdd Cells(iRow, 4).Value
- iAddMod Cells(iRow, 11).Value
- If iAddMod ltgt 0 Then ' if not 0 then send a
new add mod - iplace VB_setaddmoda(iAddMod)
34List of Lists (of Lists!)
- If a Workbook had several Worksheets in it with
each worksheet having one list then they could be
chained together for the cost of 2 lines of VBA
code per link. The following is a somewhat
artificial example to try to make things clearer - sub doAList
- Sheets(InitBoard1").Select
- call ListProc
-
- Sheets(InitBoard2).select
- call ListProc
- Sheets(DoSomething1).select
- call ListProc
- ..
- until all of the sheets are exhausted
- could make branching statements using if and
case based on values coming back in certain
cells. - could also do one list multiple times ect.
- Can call other sheets and load them
- Workbooks.Open FileName
"C\xls\xls_CTS\\List617_2.xls" - Sheets(Sheet VME1).select
- call ListProc
35List of Lists (of Lists!) 2
- Any list processor can be made Quiet, Silent,
or Invisible with Application.ScreenupdatingFals
e (It will also run faster!) - Just Remember to turn it on with
Application.ScreenupdatingTrue when you are
done! Else the worksheet will no longer paint the
screen again until it is reopened! (Not widely
used but it is available.) - A few sheets have a quiet or deferred error
handler. So they are quiet and dont check for
errors until all the lists have been completed.
Not widely used but is available and/or could be
done if desired. - List Processing Concept is very powerful. Saves
lots of programming time at some expense to run
time (only on slow machines!) Most widely used
with 1553 and Sequencer test where even a
pass/fail test was developed with it using a
procedural write- up. This technique has the
advantage of the same code and Worksheets being
used for debugging by the engineer and/or skilled
technician. Engineer made up most of the lists.
Took very little additional programmer time. No
time wasted on dead code because all code (and
Worksheets) is still in use (whenever a sequencer
does need repairing)!
36Types or Purpose of WorkSheets in Use
- What sheets do we have done that can be looked
at? (for examples, lifting/reusing code ect.)
Tons! (Megabytes! gt 5 Person years!) So many I
am out of quota on the servers so if you dont
see it there ask me! All kinds including but not
limited to (and in no particular order) - Small 1 Crate DAQs (both SASEQ and VRB,VRBC
based) for both Silicon chips and AFE boards.
(Way different sheets.) - FPGA reprograming A sheet for reprogramming
VRBs (downloads new firmware to battery backed
RAM that it will boot from.) Also a VRBC
download worksheet. These last two use the Bit3
to load new Altera programs to the FPGAs on the
board via VME Bit3. (Jamieson uses the
parallel port as an interface to his boards as
they are not VME based. He as also done some
JTAG testing using the parallel port as his
hardware is not VME based.) - Front Panel control and display substitue A High
Voltage Front Panel Display and Control Sheet(s).
- Front Panel control and display of the DZero
clock and statistical testing. (run for
months/years? In Feynman on a Win 9x box!. - General Engineering Control and Exercising VME,
CAMAC, 1553 List Processors for all of the
above and more including the DZero Clock (in
Feynman), and Mike Utes Sequencer(s). - Confirming VRB VRBC operation and
troubleshooting including engineering,
Statistical (and/or FPGA emulation!) functions.
Most recent sheet with many modules in it. Still
under development for CTS. (A work in progress.)
But a version allowed Ted Zmuda to come up with
VLPC firmware that ran the VRB 1 VRBC gt 9.1
billion times without failure. (It used VME
(Block Transfer DMA! my Bit3 driver!) at L1
rate gt 20Khz and L3 rate of 6 Kilohertz on
Lyns 2Gig machine (thanks Lyn!). Used Bit3 and
Parallel port for trigger and handshaking. Kept
track of everything and provided support for
triggering Teds Logic Analizer via the parallel
port. (A 24 bit (16Mega) wrapping problem could
be exercised and looked at quicker on the logic
analyzer then it could be simulated by the Altera
software.) So it could trap errors and trigger
things as well as do statistics. (9.1 billion
events takes 18 days. 0 errors. Way to go T.
Zmuda!) - 1553 Spy (Logic analyzer) Worksheet on a Laptop
(Shouas.) Uses the Ballard driver for their 1553
PCMCIA card. Some versions have the ability to
trigger a scope via the Parallel Port! - AFE characterization M. Matulik, P. Rubinov.
- Pass/Fail Production Testing (M. Matulik. and J.
Anderson)
37Random Hints, (words to the wise).
- Dont use spaces in object and/or sheet names.
(If nothing else it may be hard to distinguish
between a space and a _ due to some line on the
lower margin.) It almost works but sometimes..
Just dont! - VB is not case sensitive (it echoes case for
clarity after you declare it.) except in side of
quotes Sheet 1 is not the same as sheet 1 - When is VB not block structured? When the first
block of the if is commented out! May have to
change the sense of the if. (This recently
burned me!) - Best to be explicit in the if statement avoid C
usage of If. E.g. if (myVar) then may not
work. Best is to use if (myVargt0) or if
(myVarTrue) or if myVarFalse) which leads us
to the next thing. - Spell things out sequentially. In general dont
stack things up in (). Make a variable and
feed that to the next routine and/or set of ().
Allows type checking and since it gives the
parser less grief it gives you fewer gray hairs
as well. - For speed use With, End with blocks when
dealing with many instances of the same daughter
object. Supposedly it saves re-verification that
the parent object exists. (Unfortunately withs
do not nest.)
38A Word About Arguments (and Pointers)
- Although VB VBA does not EXPLICITLY support
pointers the default argument passing is by
Reference (or basically a pointer!) This is as
opposed to by copy. FORTRANs default is also
by reference as opposed to Cs default of by
copy. (C also support passing things by
reference as well when a functions argument
declaration(s) include the use of a pointer
((address) dereferencing operator) . - When passing arrays back and forth between C and
VB(A) one should be aware that C starts all
arrays at 0. The default for VB(A) is also 0.
However in VB(A) it can be changed via either
Option base 0 or Option base 1 at the top of
a module. When passing arrays back and forth
between C and VB(A) I think it very wise to
explicitly put Option Base 0 at the top of the
module. One less point of confusion for the
interpreter and for the author/reader/programmer.
It just keeps everyone on the same page. - Generally, when matching arguments to C code in a
DLL if it is not declared as a pointer (no )
then it typically needs the VB byval qualifier
which tells VB it is passed as a copy. (Usually)
Look in the .h file to see how to call it or
other documentation (book or whatever.). (An
exception is passing strings. For now just
pretend it is in the realm of magic. But if
you insist there is an example in the Bit3
library with VB_getlasterrs() with comments and a
reference. - There is a newer dialect of VB out called NET
VB which Im not talking about here. Of course
you know its default is the opposite of VB(A)
and we are in no way referring to that! This was
for a Web scripting environment as a Java
competitor. At the moment we are just working
locally so we are less concerned about security
and/or protection which is why they bit the
bullet and changed it all around! (VB and VBA
were built to be extended!) - Probably the most important thing to realize is
that passing a variable by reference into say a
subroutine is that if that sub you passed it into
changes it, then its changed in the callers
variable as well. This is good for passing
arrays as in FORTRAN and C. However when
overlooked it is the fas