Tek-Tips is the largest IT community on the Internet today!

Members share and learn making Tek-Tips Forums the best source of peer-reviewed technical information on the Internet!

  • Congratulations strongm on being selected by the Tek-Tips community for having the most helpful posts in the forums last week. Way to Go!

FORTRAN I/O to USB devices 1

Status
Not open for further replies.

PaulWDent

Technical User
May 18, 2012
13
US
I have seen a lot of unresolved questions on how to do FORTRAN I/O to a USB device.
All suggested solutions are for your FORTRAN program to call a mass of ugly-C code

that in turn calls a mass of ugly Windows APIs. But it ought to be as simple as

executing a FORTRAN OPEN statement to get a FORTRAN unit number.
I am well on the way to figuring out how to do that, but there are still a few
gaps in my knowledge. So I post what I have found so far in the hope that it may
inspire other FORTRAN programmers to persevere and help fill in the blanks.

It is already possible in FORTRAN to do I/O to a serial or parallel port simply by

OPEN(UNIT=12,FILE="COM1") or OPEN(UNIT=12,FILE="LPT1)

"COM1" and "LPT1" are aliasses known to the system for the serial and parallel
ports respectively.
Compaq Visual Fortran and I guess IVF too publish the following list of I/O device

names:

Device Description
CON Console (standard output)
PRN Printer
COM1 Serial port #1
COM2 Serial port #2
COM3 Serial port #3
COM4 Serial port #4
LPT1 Parallel Port #1
LPT2 Parallel Port #2
LPT3 Parallel Port #3
LPT4 Parallel Port #4
NUL NULL device. Discards all output; contains no input
AUX Serial port #1
LINE 1 Serial port #1
USER 1 Standard output
ERR 1 Standard error
CONOUT$ Standard output
CONIN$ Standard input

You see nothing about USB devices there.
The reason is, different things can be plugeed into a USB port at different times.
So names for USB devices have to be created ad-hoc, and are very long strings.

You can find the first part of the required string to be used as a filename in
a FORTRAN OPEN statement as follows (at least in XP):

Go to Start, right click on My Computer, and select Properties from the dropdown

list. Select Hardware in the window that opens, then Device Manager.
The screen that opens shows all hardware devices with human-recognizable names.
Double click on your desired USB device and then choose "details" in the window
that opens next.
You will then see a "Device Instance ID" displayed.
This is the first part of the string you will need.
You can't cut and paste it, so I took a screenshot and trimmed it in Paint before
saving it as a .jpg.

The second part of the string that has to be appended to the first part,
I believe is the address of a registry entry that contains the driver filename.
You can get the second part of the string as follows:

Download Winobj.exe from here


then run Winobj.exe

In the left pane of the window that opens, select the folder "Globals"

In the right pane you will now have all the names in the system nametable listed.

Scroll down until you find the string (or strings) whose first part matches the

Device Instance Id you found above. You now have the whole string that has to be

assigned to a Fortran character variable to be used as the filename in an OPEN

statement.
I find two matches, and I believe one is for I and one is for O. You have to

replace the # characters that separate parts of the string by the \ character.
You also may have to preface the string with \\.\ or \\?
So Fortran I/O to a USB device should boil down to something as simple as

CHARACTER*64 USBIN,USBOUT
USBIN="\\.\USB\...........\............\{;;;;;;;;;;;;;;;;;;;}"
USBOUT="\\.\USB\...........\............\{;;;;;;;;;;;;;;;;;;;}"
OPEN(UNIT=12,FILE=USBOUT)
OPEN(UNIT=13,USBIN)
WRITE(12,100)BUFOUT
READ(13,200)BUFIN

where of course the formats 100 and 200 and the structure of BUFIN and BUFOUT will

be dependent on what your USB device is expecting.

I write the strings as I am seeing them. You do not have to add additional {}

brackets; they should be in the namestring already. But you do have to switch the #

characters to \ characters.

I have also determined that the entries in the nametable only exist as long as your

USB device is connected, and disappear when it is diconnected. However, when it is

reconnected, the whole namestring fortunately is identical to what it was before.

However, I can't guarantee that it would be the same on a different machine.

So a big question, if you want to write portable programs
(which I don't need to do at present), is how the program
itself could find these strings. Even more fundamental:
How do you even describe to the program what to look for?
I can't imagine needing a program so smart that you could
just ask it to "find me a GPIB-USB adapter".
This sounds like the "service discovery" operation that Bluetooth
performs, that enables it look for a device in a predefined class,
such as "Printer".
Are there such device classes, defined by predetermined strings,
and is "GPIB adapter" one of them?
This is the gap in my knowledge that I hope others can fill in.
Preferably with code (in FORTRAN!)
 
??
Excuse me, which problem are you solving ?
This code opened and read a file from an USB-stick and printed the result to console:
Code:
program test
open (unit = 10, file = 'I:\test.txt')
rewind 10
do
	read (10,*, iostat = i) irslt
	if (i .ne. 0) exit
	write (*,*) irslt
enddo

stop
end

Norbert

The optimist believes we live in the best of all possible worlds - the pessimist fears this might be true.
 
The op is trying to communicate with a USB device that isn't mapped to the file system. For instance a Vellman 8055D (
Once you have the name of the device, everything is under your control. The problem is getting the name of the device. Most devices come with a DLL which you can use and with F2003, you have a C to Fortran interface so it is quite easy to use them. The problem arises when you are trying to read/write directly to the USB device. For instance, on a memory stick, it is possible to find the manufacturer by reading the device. This cannot be obtained from the file system interface.
 

Well, clearly, this is out of my jurisdiction in that we don't seem to be talking Fortran...I didn't even understand what the request was or what the problem is...I am with Norbert on this one..."I:\test.txt"

All I am going to say is: Use the right tool for the right job.

In other words, I would have a different program (C? batch?) that does all the search and possibly map the USB to an actual drive letter BEFORE launching the Fortran program.

Fortran (formula translator) was invented to automate formula evaluations, calculations, number crunching...not as a system implementation language, this latter was what 'C' was invented for (and latter used to re-write unix).

But, like I said, I may be missing the point...just don't drawn in a cup of water and use the right tool for th e right job.

Regards,

Germán

 
Give over - Fortran can be used for anything. You must have read Datamation July, 1983. "Real Programmers Don't Use Pascal". Totally politically incorrect but a good read and a good laugh. I like the part that says
you can write Fortran in any language
Those were the days when C was only 11 years old and when everything was done in Fortran: device drivers, graph plotting, word processing, body scanners etc. We used to use equivalence statements for memory mapped devices.

If you look at [URL unfurl="true"]http://www.forensicswiki.org/wiki/USB_History_Viewing[/url]
under Device Information, it will give you some of the answers. You don't have to go into regedit, you can query from the cmd prompt using
Code:
reg query HKLM\System\CurrentControlSet\Enum\USBSTOR
and chase around the GUIDs that way. There is quite a lot on how to trouble shoot USB devices in USB Troubleshooting
 
Yes, I can have my BMW 740i rebuildt to haul 20 tons of freight across the country. And I can have a John Deere tractor modified for a comfortable 1000 miles trip through the Rocky Mountains. Question is what effort would be involved and as to the quality of the results.

For me, fortran is a tool to get things computed and done. Let the user select his options and set his prameters, then have the computer read the data, chew and digest them, and finally save and display the results. Finish.

As Paul said, you need some C-code to do certain things in fortran. So why use fortran anyway to do such a job ?

Just my two cents.

Norbert


The optimist believes we live in the best of all possible worlds - the pessimist fears this might be true.
 
C is about as meaningful to me as Urdu.I speak French German Dutch English and Swedish, and Fortran and Assembler fit into my language template, but C does not.

Many programming problems boil down to something being undocumented, so you have to hack it.

I have made a little more progress with my hacking.

If you download Winobj.exe from Microsoft and run it, and look at the folder "Global", on the right hand side of the right hand pane there are more short names prefaced by /Device/ which you can use in a FORTRAN open statement. The name given for USB port 4 that my GPIB adapter is on is simply USBPD0-4. However you have to go through the steps I previously mentioned to know that that is the device you are looking for. The short names that are revealed by winobj.exe seem to work in a Fortran Open statement, as I can send and receive strings to/from any of them.
However, my next problem was that I had no clue what strings to send out to get the GPIB to do its thing. The manufacturer couldn't enderatnd what I was talking about when I asked if they had a document describing the protocol on the USB side. The protocol on the GPIB side is well documented.

Then I realized I am just too much of a low-level bit-bashing guy. The software guys have gone way beyond that to another level of hardware abstraction that I hadn't expected. What they have done is to create driver routines that take named I/O to the next level on the USB, that is, beyong a USB port, to an Endpoint. So if you can discover the name associated with a USB Endpoint, when you use that in a Fortran Open statement, you will be linked to a driver that directs I/O strings beyond just a USB port, and to an Endpoint. The driver is equipped with the knowledge about what control strings (e.g. addresses and handshakes) have to be sent along the USB to address each Endpoint. My GPIB adapter seems to have three endpoints: Control (ouptut from the host), Data Input and Data Output. But the level of abstraction in the driver has been taken even beyond that; the driver seems to knows what strings to send to those GPIB adapter endpoints to get beyond the GPIB adapter to a particular instrument on the GPIB, and associates a device name with each instrument, like "DMM". However, those names are not revealed by Winobj.exe. If that level of abstraction was fully "Window-ized", they would be and things might be easier. But that level may be proprietary.


It's a bit like telephone hacking in the old days, when we used to first dial the German 800 number Frankfurt Dial-a-Disk, and then use a blue box to send BCD-modulated tones down the line to clear down this free call while leaving us connected to an international trunk exchange, from where we could dial free anywhere in the world once we had hacked the BCD tone codes.
 
Have you tried the reg thing? It lists out all the devices that have ever been registered on your machine.
 
I tried the reg query. It only lists memory devices that have been attached to the USB
 
Like I wondered before....are we talking Fortran? did we ever? None of this sounds to me like Fortran...or German, Dutch, English, or Swedish :)

I am not trashing the thread, continue by all means...I am saying.

Maybe, the OP could be getting better help in some other thread with people knowledgeable on the more correct field...hardware? usb interfaces, Windows? etc...

Germán
 
Yes we are talking Fortran

The issue is "What does one write in Fortran to get a reading from a Digital Multimeter, for example."

In automatic test equipment, which is my application, we might want to create a program that, for example, sets up an instrument like a power supply or signal generator or both to stimulate a Unit Under Test, then reads back some measurements on its performance, using some other instruments like a DMM, frequency counter, oscilloscope, spectrum analyzer, network analyzer, etc. A number of measurements may be taken, and then lots of calculation performed with them. Like Statistics, or FFTs. The calculation is one task, and display and/or filing of the results is another task, and Fortran is ideal for both the calculation and display/filing.
The I/O to control the instruments ought to be one-liners. Every instrument is controllable by just outputting a short ASCII string to it. But that requires getting a unit number from an OPEN statement; and that in turn requires that you can fill in the blanks in FILE=".........". The latter is what I am striving to achieve. The drivers for the instruments are available, but the interfaces are normally documented only in terms of what you would write in "C" or VBA, using handle=iopen(.....) statements, which return a "handle", and not a Fortran unit number. And thereafter, the programs that use them are not simple one-liners, but pages of incomprehensible code with weird variable names and types that you can't even see because they are in a precompiled include file. That gets me on to the subject of dsassemblers: I Googled disassemblers, and there are purported to be several free ones, but not a single of one of them could be donwloaded and made to work. Either broken links or stupid bugs like non-windows compatible file names used in a windows app, or they would only work in DOS from a command line and did not translate to mnemonics, but only showed hex codes.


I know many people in my position would jump on the use of Fortran If the latter was solved, because it is a language preferred by engineers to write custom programs, and some customers, like some DOD customers, require it. It is more easily readable, understandable and maintainable by others that may have to do so. Sometimes these programs are used for decades - beyond the life of the original programmer
 
"What does one write in Fortran to get a reading from a Digital Multimeter, for example."
Every instrument is controllable by just outputting a short ASCII string to it

Now we get some sense into it.
That is what I did a very long time ago while working on my thesis. And I used Fortran 77 for this. But this was not an USB-port at that time.

Could you post the piece of C-code or of the instructions on how you should adress this instrument ? As far as I can see there are some windows-functions, I think, to get a device handle and then send messages to this device. If you are using the Compaq compiler, this is not too difficult.

Norbert


The optimist believes we live in the best of all possible worlds - the pessimist fears this might be true.
 
I hear you...but this is out of my realm of knowledge.

So:
"and that in turn requires that you can fill in the blanks in FILE=".........". The latter is what I am striving to achieve."

What I am trying to understand is if you are trying to figure out "what to fill in with" strictly from within a Fortran program or whether it does not matter how you figure it out for as long as once you plug in Fortran, it works.

I know that the unix operating system makes it a point that anything should look like a file, even hardware ports. Then, again, like I said before, unix was written in C.

So, are you open to figure out the "name" of the unit to be open in anyway you can? Whether it involves a C program or something else? or are you really trying to keep it Fortran?

Also, does matlab have the ability to connect to devices like the ones you are talking about, maybe via one of its toolboxes? Is it possible to talk to somebody in their forum?...would they tell? I know the backbone of matlab (LAPACK,etc) is written in Fortran 90; but I don't know about the rest.

Germán
 
I take it from one of your posts, that you have the Compaq-compiler at your disposal.
Then ee this here:
Physical Devices
I/O statements that do not refer to a specific file or I/O device read from standard input and write to standard output. Standard input is the keyboard, and standard output is the screen (console). To perform input and output on a physical device other than the keyboard or screen, you specify the device name as the filename to be read from or written to.Some physical device names are determined by the host operating system; others are recognized by Visual Fortran. Extensions on most device names are ignored.

This is followed by a list of port names that does not copy too well into this thread.

For more information see the description of the open statement.

This is from the Compaq compiler documentation and seems to be a Compaq feature. At least it does not seem to be part of the standard fortran.

Looks like you connect your device to any of the ports you have at your disposal and then just open it to write to or read from. Apparently the name of the port is sufficient, you do not need any more spec.

Code:
character char
open (unit = 10, file = 'LPT1', action = 'READWRITE')
...
...
write (10,'(a1)') 'a'    ! for sending the letter 'a'
read (10,*) char

Should be worth a try.

Norbert




The optimist believes we live in the best of all possible worlds - the pessimist fears this might be true.
 
Okay, now, digging deeper, I see that USB is completely different in usage to the LPTx and COMx interfaces.

Well, time goes on...

Norbert


The optimist believes we live in the best of all possible worlds - the pessimist fears this might be true.
 
Yes you are up to speed with me now Norbert. I did post the list of device names that the Compaq manual recites. Then was heartened to find, that by running the winobj.exe program, I could discover a low more device names that are available on a particular machine, that Compaq couldnlt have know about in advance. I think this should be great interest to FORTRAN programmers wanting to do I/O to more than just the the standard devices. I'll dig up what I can find on how to address an instrument in 'c'.

Salgerman: MATLAB does not advertize any capability to drive GPIB devices, but LABVIEW does.
LABVIEW can also call FORTRAN programs. However, LABVIEW is a major, expensive and bulky software package. I would prefer to avoid having to buy, install and learn LABVIEW just to solve an I/O problem.
 
I know that works. It will send the letter 'a' to my printer, but not to an instrument which is connected via a USB-GPIB adapter to the USB. To do that, I need to know what to put in place of LPT1.

I know that if I put 'USBD0-4' in place of 'LPT1' the string will get as far as the USB port to which the GPIB-USB adapter is connected. I have tried that. But it won't come out on the GPIB bus because there is microprocessor in the GPIB-USB adapter which is doing protocol translation from the protocol used on the USB to GPIB protocol. The latter is well defined, but the protocol on the USB side is undocumented. I know it is something of this form:

If you want to address an instrument with a GPIB address "xx" on the GPIB bus, then you first have to send a control string to USB endpoint 7 that signifies "here is the GPIB address', followed by another string to endpoint 6 containing the address. Then you have to send another control byte to endpoint 7 that indicates "prepare to receive output" followed by output to endpoint 6, or else a string that signifies "prepare to take a measurement and deliver an output string", followed by read to endpont 82.

This protocol is undocumented, and I just learned the above using a USB snooper. The snooper however intercepts messages to the driver, not bytes on the USB. So, although I can put strings out on the USB, I have no idea what they need to be to address "endpoint 6, 7 or 82".
According to some USB experts, I shouldn't need to know either, as the driver should get me directly to an endpoint. It even gets me direct to an instrument, as long as I know the name to put in FILE="......." in the open statement.
According to what I have read, nobody should ever need to address a USB port; instead you target the endpoint directly and the driver should handle directly addressing an endpoint.

The annoying thing is that ALL of this is taken care of in the installed driver, and I just don't know how to assign a Fortran I/O unit number to the instruments, or even to the "endpoints"

Now wouldn't we be so impressed if they had just put an instrument name in the device nametable like "DMM" for digital multimeter? Then all we would need to write is
CHARACTER*512 RESULT
OPEN(13, FILE="DMM")
WRITE(13)"IDN?/n"
READ(13)RESULT

All I am missing is what to put in place of "DMM"

Hej! I have an idea! Maybe I can run though all combinations of letters and numbers up to length 5, and when I try to do a read, trap the "non-existent file error" to continue! That would be another way to enumerate all named devices.
 
Here is some 'C' code that puports to do the job, but it elcts more questions than it answers:
I add me commenst in caps tp each line to indicate my understanding or lack thereof of what it does, and end up pruning it down to the barebones:

#include <stdio.h> // for printf() THIS IS NNEDED BECAUSE 'C' DOESN'T HAVE
I/O BUILT IN FOTRAN DOESNLT NEED THIS
#include "sicl.h" // Standard Instrument Control Library routines I HAVE THE FILE
BUT THERE IS NO CODE IN IT - ONLY DECLARATIONS

#define DEVICE_ADDRESS "gpib0,0" // Modify this line to match your setup HOW? IS THIS NOT SIMPLY
THE SAME PROBLEM THAT I HAVE IN FORTRAN

void main(void) // I GUESS THIS SAYS "I AM NOT A SUBROUTINE"
{
INST id; // device session id WHAT DATA TYPE IS THIS?
char buf[256] = { 0 }; // read buffer for idn string THIS LOOKS STRAIGHTFORWARD

#if defined(__BORLANDC__) && !defined(__WIN32__) DON'T NEED
_InitEasyWin(); // required for Borland EasyWin programs. DON'T NEED
#endif DON'T NEED

// Install a default SICL error handler that logs an error message and
// exits. On Windows 95 view messages with the SICL Message Viewer,
// and on Windows NT use the Windows NT Event Viewer.

ionerror(I_ERROR_EXIT); // I GUESS THIS A CALL TO A SUBSROUTINE WITH ONE
// ARGUMENTS ADDRESS. THE ARGUMENT ADDRESS IF FOR
//UNLEES I CAN FIND T IN THE INCLUDE FILE sicl.h

// Open a device session using the DEVICE_ADDRESS

id = iopen(DEVICE_ADDRESS); //WHO SET THE VARIABLE DEVICE_ADDRESS, AND TO WHAT?
//WHAT IS ITS TYPE? CHARCETR STRING, OR WHAT?
//I BELIEVE WHAT IT RETURNS id IS A HANDLE - THE


// Set the I/O timeout value for this session to 1 second
itimeout(id, 1000);

// Write the *RST string (and send an EOI indicator) to put the instrument
// in a known state.
iprintf(id, "*RST\n");

// Write the *IDN? string and send an EOI indicator, then read
// the response into buf.
// For WIN16 programs, this will only work with the Large memory model
// since ipromptf expects to receive far pointers to the format strings.

ipromptf(id, "*IDN?\n", "%t", buf);
printf("%s\n", buf);

iclose(id);

// For WIN16 programs, call _siclcleanup before exiting to release
// resources allocated by SICL for this application. This call is a
// no-op for WIN32 programs.
_siclcleanup();
}
 
Here is some C code puporting to solve the problem,
but for me it raises more questions than it answers
I add my own comments in caps


#include <stdio.h> // for printf() BECAUSE C DOESN'T HAVE I/O BUILT IN
// FORTRAN DOESN'T NEED THIS
#include "sicl.h" // Standard Instrument Control Library routines
//THAT WOULD BE INTERESTING EXCEPT THAT THIS FILE CONTAINS
//NO CODE, ONLY DECLARATIONS

#define DEVICE_ADDRESS "gpib0,0" // Modify this line to match your setup
//HOW? ISN'T THIS THE SAME QUESTION I HAVE IN FORTRAN?

void main(void) //I GUESS THIS SAYS "I NEED NO ARGUMENTS"
{
INST id; // device session id WHAT DATA TYPE IS THIS
char buf[256] = { 0 }; // read buffer for idn string SEEMS STRAIGHTFORWARD

#if defined(__BORLANDC__) && !defined(__WIN32__) DON'T NEED THIS
_InitEasyWin(); // required for Borland EasyWin programs. DON'T NEED THIS
#endif DON'T NEED THIS

// Install a default SICL error handler that logs an error message and
// exits. On Windows 95 view messages with the SICL Message Viewer,
// and on Windows NT use the Windows NT Event Viewer.
ionerror(I_ERROR_EXIT); // I GUESS THIS IS A CALL TO A SUBROTUINE WITH A SINGLE
//ARGUMENT. BUT WHAT IS THE ARGUMENT'S TYPE?

// Open a device session using the DEVICE_ADDRESS
id = iopen(DEVICE_ADDRESS); //WHO SETS "DEVICE_ADDRESS", TO WHAT, AND WHAT IS ITS TYPE?
//IS IT LIKE A FORTRAN PARAMETER THAT WAS SET TO THE
//STRING "gpib0,0" ABOVE?
//id SEEMS TO BE A I/O UNIT NUMBER, BUT NOT THE SAME AS FORTRAN
//AND IT WAS DEFINED ABOVE AS A "SESSION ID"

// Set the I/O timeout value for this session to 1 second
itimeout(id, 1000); //WELL IT DOES SEEM TO BE BEING USED AS A SESSION ID HERE

// Write the *RST string (and send an EOI indicator) to put the instrument
// in a known state.
iprintf(id, "*RST\n"); //BUT NOW IT'S BEING USED AS AN I/O UNIT NUMBER OR HANDLE
//I COULD WRITE WRITE(id)"*RST\n" IN FORTRAN TOO, IF I KNEW
//WHAT id WAS!

// Write the *IDN? string and send an EOI indicator, then read
// the response into buf.
// For WIN16 programs, this will only work with the Large memory model
// since ipromptf expects to receive far pointers to the format strings.

ipromptf(id, "*IDN?\n", "%t", buf); //IS ipromptf A STANDARD 'C' FUNCTION?
printf("%s\n", buf); //IS %s\n" some kind of format control like 16F12.5?
iclose(id); // THIS I CAN RELATE TO" CLOSE(id) !

// For WIN16 programs, call _siclcleanup before exiting to release
// resources allocated by SICL for this application. This call is a
// no-op for WIN32 programs.
_siclcleanup(); //NOT NEEDED FOR WIN32
} //AT LEAST THE FINAL CURLY BRACKET IS NOT 10 PAGES AWAY


So let's boil this down to the barebones:
/**************************************************************************
#include <stdio.h> //gets C's non-native I/O library
#include "sicl.h" //Gets some vendor-supplied declarations
INST id; //Wht's the type? Integer? What length?
char buf[256] = { 0 };
id = iopen("gpib0,0"); //This string is not a Windows device name listed
// in the name table
//If I write OPEN(13, file="gpib0,4") (because my GPIB-USB
//adapter is port 4), it still doesn't work
itimeout(id, 1000);
iprintf(id, "*RST\n");
ipromptf(id, "*IDN?\n", "%t", buf);
printf("%s\n", buf);
iclose(id);
/**************************************************************************

Needless to say, the C-code does not compile to a program that executes, and neither
does Agilent's VBA exambles.

Does anybody know what the C function iopen does with the string argument,
and what it returns? Seems like the string would have to be a valid file
or system device name?
 
Status
Not open for further replies.

Part and Inventory Search

Sponsor

Back
Top