Serial Port protocol for PCGU1000

I’m developing a test system that needs to automate the PCGU1000. I’ve gotten the “FGULink_DLL” API (rev 5) working in a standalone program (written in C#) on a Windows 7 64-bit machine, but when I integrate it into a larger program, it crashes with a random program error that I can’t debug. In addition, there are other problems with this set-up, in that it doesn’t support Windows 7 64 bit easily (the drivers have to be fiddled with) and it doesn’t allow using UAC – the application must run as Administrator, which is a security issue for us.

I noticed that the device uses the SiLabs virtual com port interface, so there must be a way to simply configure the device using the com port instead of the DLL. If the communication is simply serial commands, then a much simpler interface that doesn’t require a DLL or special drivers or Administrator privilege (and supports multiple devices simultaneously) should be possible. Is there documentation on the serial protocol between the PC and PCGU1000? I could sniff the RS232 data and reverse engineer it, but that would be a large undertaking.

thanks,
Mark

It’s really hard to debug this problem, but if I do just the simplest call to the API (like call GenStatus()), moments later the application crashes within ntdll.dll

“Unhandled exception at 0x77606d5e in OEM Tester.exe: 0xC0000005: Access violation writing location 0x00d70e24”

It varies from run to run. Once I saw that the call stack showed a recursive call to itself (or the call stack was corrupted). However, if I comment out the single call to GenStatus(), then there is no crash.

The weird thing is that GenStatus returns the “OK” code without crashing. I can also call GenReady and it returns true. However, moments later I get the crash.

mscorwks.dll!6fa7a110()
[Frames below may be incorrect and/or missing, no symbols loaded for mscorwks.dll]
mscorwks.dll!6fa7f296()
mscorwks.dll!6fa88a87()
mscorwks.dll!6fa81df5()
mscorwks.dll!6faee3a6()
<etc… hundreds of lines deleted >
mscorwks.dll!6fcd1245()
msvcr80.dll!743ae16c()
msvcr80.dll!743abe2d()
mscorwks.dll!6fcd1245()
msvcr80.dll!743ae16c()

[quote]It’s really hard to debug this problem, but if I do just the simplest call to the API (like call GenStatus()), moments later the application crashes within ntdll.dll

“Unhandled exception at 0x77606d5e in OEM Tester.exe: 0xC0000005: Access violation writing location 0x00d70e24”

It varies from run to run. Once I saw that the call stack showed a recursive call to itself (or the call stack was corrupted).[/quote]We are unable to reproduce the issue.

[quote]However, if I comment out the single call to GenStatus(), then there is no crash. [/quote]We’ll investigate this function more detailed.

[quote]I’ve gotten the “FGULink_DLL” API (rev 5) working in a standalone program (written in C#) on a Windows 7 64-bit machine, but when I integrate it into a larger program, it crashes with a random program error that I can’t debug. In addition, there are other problems with this set-up, in that it doesn’t support Windows 7 64 bit easily (the drivers have to be fiddled with) and it doesn’t allow using UAC – the application must run as Administrator, which is a security issue for us.[/quote]The UAC does not allow to read the COM port number from registry.
This is the registry key where the COM port number is located:
HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Enum\USB\VID_10CF&PID_1010\0001\Device Parameters

As an alternative solution you may run the program in compatibility mode for Windows XP.

[quote]Is there documentation on the serial protocol between the PC and PCGU1000?[/quote]Sorry, the serial communication is not documented. The only document is the “upper level” DLL document.
In this thread there is a simplified block diagram and a short description of the generator operation:
viewtopic.php?f=11&t=4239

[quote]I could sniff the RS232 data and reverse engineer it, but that would be a large undertaking.[/quote]Via the COM port is first sent the FPGA data (54900 bytes) extracted from the file pcgu1001.bit.
Then all the settings and the waveform data (8192 bytes) are sent to the generator:
DC offset, amplitude, frequency range, relay state, amplitude correction, LED status, noise on/off, sweep on/off, sweep increment, low pass filter setting…

There are other methods for retrieving the COM port number, without using the registry. I’ve pasted my code below. It would be nice if you could use this instead of the registery, since there is no reason why you need to run in Admin mode just to talk to the COM port!

You need to supply bool ValidVidPidPair(uint16_t vid, uint16_t pid); This should check for the VID/PID of the virtual com port you are looking for. I use this for both FTDI and virtual serial ports.

#include <windows.h>
#include <set>
#include <string>

...

	HDEVINFO hDevInfo = SetupDiGetClassDevs(NULL,0,0,DIGCF_PRESENT|DIGCF_ALLCLASSES);

	if ( hDevInfo ){
		while (TRUE){
			SP_DEVINFO_DATA DeviceInterfaceData;
			ZeroMemory(&DeviceInterfaceData, sizeof(DeviceInterfaceData));
			DeviceInterfaceData.cbSize = sizeof(DeviceInterfaceData);
			if (!SetupDiEnumDeviceInfo(hDevInfo, i, &DeviceInterfaceData)){
				// SetupDiEnumDeviceInfo failed
				break;
			}


			DWORD dataType;
			DWORD actualSize = 0;
			wchar_t dataBuf[MAX_PATH + 1];

			if (SetupDiGetDeviceRegistryProperty(hDevInfo,&DeviceInterfaceData, SPDRP_HARDWAREID,
			   &dataType,(PBYTE) dataBuf, sizeof(dataBuf),&actualSize)){
				 DWORD vid;
				 DWORD pid;
				 DWORD rev;
                 bool ok = false;
				 if (swscanf_s(dataBuf,TEXT("USB\\Vid_%04X&Pid_%04X&Rev_%04X"),&vid,&pid,&rev) == 3) {
                     ok = true;
                 } else
				 if (swscanf_s(dataBuf,TEXT("USB\\VID_%04X&PID_%04X&REV_%04X"),&vid,&pid,&rev) == 3) {
                     ok = true;
                 }
				 if (ok) {
					 if (ValidVidPidPair(vid,pid))  {

						 // Get the service name associated with this USB device.
						 // We support usbser and FTD2XX right now.
						 if (SetupDiGetDeviceRegistryProperty(hDevInfo,&DeviceInterfaceData,SPDRP_SERVICE,
							 &dataType, (PBYTE) dataBuf, sizeof(dataBuf), &actualSize)) {

						     wprintf(TEXT("INFO: Device Service Name: %s VID/PID:%04X/%04X\n"),dataBuf,vid,pid);
							 std::wstring comportname;

							 if (wcscmp(dataBuf,TEXT("usbser")) == 0) {
								 if (SetupDiGetDeviceRegistryProperty(hDevInfo,&DeviceInterfaceData,SPDRP_FRIENDLYNAME,
									 &dataType, (PBYTE) dataBuf, sizeof(dataBuf), &actualSize)) {

									 std::wstring friendlyname(dataBuf);
									 size_t x = friendlyname.find(TEXT("(COM"));
									 if (x >=0) {
										 size_t y = friendlyname.find(TEXT(")"),x);
										 assert(y>x+4);
										 comportname = TEXT("\\\\.\\");
										 comportname += friendlyname.substr(x+1,y-x-1);

									 }
								 }
							 }
							 else if (wcscmp(dataBuf,TEXT("FTD2XX")) == 0) {
								 comportname = dataBuf;
                                                         } 
                                                         else if (wcscmp(dataBuf,TEXT("FTDIBUS")) == 0) {
								 comportname = TEXT("FTD2XX") ;
							 }

// do something with comportname here

 						 }
					 }
				 }
                        }
			i++;
		}
	}
	SetupDiDestroyDeviceInfoList(hDevInfo);

Thank you very much for posting this code snippet!
Indeed, it would be a big benefit to avoid the use of the Admin mode!

The only minor problem is that the whole PCGU1000 software is written in Delphi.
I’ll immediately start to check how to convert this code to Delphi…

I’ve reverse engineered the serial protocol and written my own C#.NET assembly to control the signal generator. Seems to work fine. It’s self-contained with FPGA bit file, so it is easy to deploy (doesn’t need the 4 external files) and doesn’t require admin privilege.

Congratulations - you did it!
Looks great!

I’m also getting soon the COM port detection to work without the registry access and without the admin mode…

Thanks again.