#include <windows.h>
#include <winioctl.h>
#include <math.h>
#include <FCNTL.h>
#include <io.h>
#include <stdio.h>

#include "bulkioct.h"
#include "rtusbapi.h"
#include "usb3000.h"

DWORD GetSystemVersionMy(void);

//------------------------------------------------------------------------
// конструктор
//------------------------------------------------------------------------
CRTUSB3000::CRTUSB3000(HINSTANCE hInst)
{
	// идентификатор модуля DLL
	hInstance = hInst;
	// идентификатор устройства
	hDevice = INVALID_HANDLE_VALUE;
	// сбросим скорость работы модуля на шине USB
	UsbSpeed = RTUSB3000::INVALID_USB_SPEED;
	// вообще запись в ППЗУ модуля запрещена
	EnableFlashWrite = FALSE;
	// запись в служебную область ППЗУ модуля запрещена
	IsServiceFlashWrite = FALSE;
	// флажок разрешения выходных цифровых линий
	EnableTtlOut = FALSE;
	// инициализация полей size всех структур класса (в байтах)
	fi.size = sizeof(RTUSB3000::FLASH);
	ap.size = sizeof(RTUSB3000::INPUT_PARS);
	dp.size = sizeof(RTUSB3000::OUTPUT_PARS);
	// адрес начала сегмента кода драйвера в памяти программ DSP
	ProgramBaseAddress = 0x1000;
	// адрес начала сегмента данных драйвера в памяти программ DSP
	VarsBaseAddress = RTUSB3000::VarsBaseAddress;
	// оживим критичекую секцию для работы
	InitializeCriticalSection(&cs);
	// максимальный кол-во байтов для передачи по ContolPipe
	MAX_USB_BLOCK = 4096;
	// базовый индекс ошибок модуля
	BASE_ERROR_ID = 200;
	// флажок памяти данных для канала IDMA DSP
	DSP_DM = 0x4000;
	// флажок памяти программ для канала IDMA DSP
	DSP_PM = 0x0000;
	// номер последней ошибки
	LastErrorNumber = 0;
}

//------------------------------------------------------------------------
// деструктор
//------------------------------------------------------------------------
CRTUSB3000::~CRTUSB3000() { }




//========================================================================
// Функции общего назначения для работы с модулем USB3000
//========================================================================

//------------------------------------------------------------------------
// откроем виртуальный слот для доступа к USB модулю
//------------------------------------------------------------------------
BOOL WINAPI CRTUSB3000::OpenDevice(WORD VirtualSlot)
{
	// название модуля
	char ModuleName[10];
	// название драйвера
	char szDrvName[18];//, slot[4];

	// виртуальный слот доступен?
	if(hDevice != INVALID_HANDLE_VALUE) { CloseHandle(hDevice); hDevice = INVALID_HANDLE_VALUE; }

	// определим версию Windows
	DWORD SystemVersion = GetWindowsVersion();
	if((SystemVersion == UNKNOWN_WINDOWS_VERSION) ||
   	(SystemVersion == WINDOWS_95) || (SystemVersion == WINDOWS_98_OR_LATER) ||
		(SystemVersion == WINDOWS_NT)) { LastErrorNumber = 1; return FALSE; }

	// формируем название драйвера USB
	sprintf(szDrvName, "\\\\.\\Rtecusb%d", VirtualSlot);

	// откроем идентификатор для модуля
	hDevice = CreateFile(szDrvName, GENERIC_READ | GENERIC_WRITE, 0, NULL,
						                      OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);
	// проверим хэндл устройства
	if(hDevice == INVALID_HANDLE_VALUE)  { LastErrorNumber = 2; return FALSE; }
	// прочтём название модуля
	else if(!GetModuleName(ModuleName)) { LastErrorNumber = 45; CloseDevice(); return FALSE; }
	// проверим что это USB3000
	else if(strcmp(ModuleName, "USB3000")) { LastErrorNumber = 46; CloseDevice(); return FALSE; }
	// определим на какой скорости работает модуль
	else if(!GetUsbSpeed(&UsbSpeed)) { LastErrorNumber = 56; CloseDevice(); return FALSE; }
	// неправльно определили скорость работы модуля на шине USB
	else if(UsbSpeed >= RTUSB3000::INVALID_USB_SPEED) { LastErrorNumber = 57; CloseDevice(); return FALSE; }
	// прочитаем статус разрешения выходных цифровых линий из DSP
	if(!GET_VAR_WORD(RTUSB3000::D_ENABLE_TTL_OUT, (SHORT *)&EnableTtlOut)) { LastErrorNumber = 19; CloseDevice(); return FALSE; }
	// по умолчанию потоковый ввод данных из модуля запрещен
	ap.InputEnabled = FALSE;

	return TRUE;
}

//------------------------------------------------------------------------------
// освободим текущий виртуальный слот
//------------------------------------------------------------------------------
BOOL WINAPI CRTUSB3000::CloseDevice(void)
{
	BOOL status = TRUE;

	if(hDevice != INVALID_HANDLE_VALUE)
	{
		if(!CloseHandle(hDevice)) { LastErrorNumber = 3; status = FALSE; }
		hDevice = INVALID_HANDLE_VALUE;
	}

	return status;
}

//------------------------------------------------------------------------
// возвращается дескриптор устройства
//------------------------------------------------------------------------
HANDLE WINAPI CRTUSB3000::GetModuleHandle(void) { return hDevice; }

//------------------------------------------------------------------------
// возвращается текущая скорость работы шины USB
//------------------------------------------------------------------------
BOOL WINAPI CRTUSB3000::GetUsbSpeed(BYTE * const UsbSpeed)
{
	BOOL status;
	WORD InBuf[4];
	DWORD cbRet=0;

	// виртуальный слот доступен?
	if(hDevice == INVALID_HANDLE_VALUE) { LastErrorNumber = 4; return FALSE; }

	// захватим ресурс критической секции
	EnterCriticalSection(&cs);

	// формируем вендор получения названия модуля
	InBuf[0] = 1; InBuf[1] = V_GET_USB_SPEED; InBuf[2] = 0x0; InBuf[3] = 0x0;
	// засылаем сформированный вендор
	status = DeviceIoControl( hDevice, DIOC_SEND_COMMAND,
				            		&InBuf, sizeof(InBuf),
										UsbSpeed, 1/*bytes*/,
                       			&cbRet, NULL);

	// освободим ресурс критической секции
	LeaveCriticalSection(&cs);

	// все хорошо :)))))
	if(status) return TRUE;
	// ошибка выполнения DeviceIoControl()
	else { LastErrorNumber = 5; return FALSE; }
}

//------------------------------------------------------------------------------
// получим название модуля
//------------------------------------------------------------------------------
BOOL WINAPI CRTUSB3000::GetModuleName(PCHAR const ModuleName)
{
	BOOL status;
	char String[15];
	WORD InBuf[4];
	DWORD cbRet=0;

	// виртуальный слот доступен?
	if(hDevice == INVALID_HANDLE_VALUE) { LastErrorNumber = 4; return FALSE; }
	// захватим ресурс критической секции
	EnterCriticalSection(&cs);

	// формируем вендор получения названия модуля
	InBuf[0] = 1; InBuf[1] = V_GET_MODULE_NAME; InBuf[2] = 0x0; InBuf[3] = 0x0;
	// засылаем сформированный вендор
	status = DeviceIoControl( hDevice, DIOC_SEND_COMMAND,
				            		&InBuf, sizeof(InBuf),
										String, 15/*bytes*/,
                       			&cbRet, NULL);

	// освободим ресурс критической секции
	LeaveCriticalSection(&cs);

	// копируем строчку
	strncpy(ModuleName, String, 10);
	// последний символ должен быть 0 (на всякий случай)
	ModuleName[9] = '\0';

	// все хорошо :)))))
	if(status) return TRUE;
	// ошибка выполнения DeviceIoControl()
	else { LastErrorNumber = 5; return FALSE; }
}

//------------------------------------------------------------------------------
// получим серийный номер модуля
//------------------------------------------------------------------------------
BOOL WINAPI CRTUSB3000::GetModuleSerialNumber(PCHAR const SerialNumber)
{
	BOOL status;
	WORD InBuf[4];
	DWORD cbRet=0;

	// виртуальный слот доступен?
	if(hDevice == INVALID_HANDLE_VALUE) { LastErrorNumber = 4; return FALSE; }
	// захватим ресурс критической секции
	EnterCriticalSection(&cs);

	// формируем вендор получения серийного номера модуля
	InBuf[0] = 1; InBuf[1] = V_GET_MODULE_SERIAL_NUMBER; InBuf[2] = 0x0; InBuf[3] = 0x0;
	// засылаем сформированный вендор
	status = DeviceIoControl( hDevice, DIOC_SEND_COMMAND,
	                       		&InBuf, sizeof(InBuf),
					SerialNumber, 9/*bytes*/,
	                       		&cbRet, NULL);

	// освободим ресурс критической секции
	LeaveCriticalSection(&cs);

	// последний символ должен быть 0 (на всякий случай)
	SerialNumber[8] = '\0';

	// все хорошо :)))))
	if(status) return TRUE;
	// ошибка выполнения DeviceIoControl()
	else { LastErrorNumber = 5; return FALSE; }
}

//------------------------------------------------------------------------
// получение номера версии прошивки интерфейсной части
// модуля RTUSB3000 (микроконтроллер AVR)
//------------------------------------------------------------------------
BOOL WINAPI CRTUSB3000::GetAvrVersion(PCHAR const AvrVersion)
{
	BOOL status;
	char String[15];
	WORD InBuf[4];
	DWORD cbRet=0;

	// виртуальный слот доступен?
	if(hDevice == INVALID_HANDLE_VALUE) { LastErrorNumber = 4; return FALSE; }
	// захватим ресурс критической секции
	EnterCriticalSection(&cs);

	// формируем вендор получения названия модуля
	InBuf[0] = 1; InBuf[1] = V_GET_MODULE_NAME; InBuf[2] = 0x0; InBuf[3] = 0x0;
	// засылаем сформированный вендор
	status = DeviceIoControl( hDevice, DIOC_SEND_COMMAND,
				            		&InBuf, sizeof(InBuf),
										String, 15/*bytes*/,
                       			&cbRet, NULL);

	// освободим ресурс критической секции
	LeaveCriticalSection(&cs);

	// копируем строчку
	strncpy(AvrVersion, String+10, 5);
	// последний символ должен быть 0 (на всякий случай)
	AvrVersion[4] = '\0';

	// все хорошо :)))))
	if(status) return TRUE;
	// ошибка выполнения DeviceIoControl()
	else { LastErrorNumber = 5; return FALSE; }
}

//------------------------------------------------------------------------------
// освободим указатель на устройство
//------------------------------------------------------------------------------
BOOL WINAPI CRTUSB3000::ReleaseInstance(void)
{
	BOOL status = TRUE;

	// освободим ресурс критической секции
	DeleteCriticalSection(&cs);
	// освободим идентификатор виртуального слота
	if(!CloseDevice()) status = FALSE;
	// освободим указатель на устройство
	delete this;

	return status;
}


//------------------------------------------------------------------------
// сброс сигнального процессора DSP
//------------------------------------------------------------------------
BOOL WINAPI CRTUSB3000::RESET_DSP(void)
{
	BOOL status;
	WORD InBuf[4];
	DWORD cbRet=0;

	// виртуальный слот доступен?
	if(hDevice == INVALID_HANDLE_VALUE) { LastErrorNumber = 4; return FALSE; }
	// захватим ресурс критической секции
	EnterCriticalSection(&cs);

	// формируем вендор сброса DSP модуля
	InBuf[0] = 0; InBuf[1] = V_RESET_DSP; InBuf[2] = 0x0; InBuf[3] = 0x0;
	// засылаем сформированный вендор
	status = DeviceIoControl( hDevice, DIOC_SEND_COMMAND,
	                       		&InBuf, sizeof(InBuf),
                       			NULL, 0/*bytes*/,
                       			&cbRet, NULL);

	// освободим ресурс критической секции
	LeaveCriticalSection(&cs);

	// все хорошо :)))))
	if(status) return TRUE;
	// ошибка выполнения DeviceIoControl()
	else { LastErrorNumber = 5; return FALSE; }
}


//------------------------------------------------------------------------------
// функция загрузки драйвера DSP в модуль RTUSB3000
//------------------------------------------------------------------------------
BOOL WINAPI CRTUSB3000::LOAD_DSP(PCHAR const FileName)
{
	int	FileHandle;
	DWORD	NBytes;
	char  *CodeByte;
	WORD	*CodeWord;
	WORD 	PmBase, PmWords, DmBase, DmWords;
	char 	PatternHeadStr[]="crrtd";
	char	HeadStr[6];

	// виртуальный слот доступен?
	if(hDevice == INVALID_HANDLE_VALUE) { LastErrorNumber = 4; return FALSE; }
 	// осуществим сброс DSP модуля
	else if(!RESET_DSP()) { LastErrorNumber = 6; return FALSE; }

	if(FileName == NULL)
	{
		// найдем ресурс с кодом драйвера DSP
		HRSRC RsRes = FindResource(hInstance, "USB3000DSP", RT_RCDATA);
		if(RsRes == NULL) { LastErrorNumber = 7; return FALSE; }
		// загрузим его
		HGLOBAL RcResHandle = LoadResource(hInstance, RsRes);
		if(RcResHandle == NULL) { LastErrorNumber = 8; return FALSE; }
		// зафиксируем
		char  *pRcData = (char *)LockResource(RcResHandle);
		if(pRcData == NULL) { LastErrorNumber = 9; return FALSE; }
		// определим размер ресурса
		NBytes = SizeofResource(hInstance, RsRes);
		if(NBytes == NULL) { LastErrorNumber = 10; return FALSE; }

		// выделим массив в памяти компьютера под драйвер DSP
		CodeByte = new char[NBytes+2];
		if(CodeByte == NULL)  { LastErrorNumber = 11; return FALSE; }

		// скопируем данные ресурса
		for(DWORD i=0; i < NBytes; i++) CodeByte[i] = pRcData[i];
	}
	else
	{
		// откроем файл с кодом драйвера DSP
		FileHandle=open(FileName, O_BINARY | O_RDONLY);
		if(FileHandle == -1) { LastErrorNumber = 12; return FALSE; }

		// определим кол-во байт в файле с кодом драйвера DSP
		NBytes = filelength(FileHandle);
		if((LONG)NBytes == -1) { LastErrorNumber = 13; return FALSE; }

		// выделим память под драйвер DSP
		CodeByte = new char[NBytes+2];
		if(CodeByte == NULL)  { LastErrorNumber = 11; return FALSE; }

		// теперь спокойно прочитаем файл
		if(read(FileHandle, CodeByte, NBytes) == -1) { LastErrorNumber = 14; delete[] CodeByte; return FALSE; }

		// закроем файл
		close(FileHandle);
	}

	memcpy(HeadStr, CodeByte, 5); HeadStr[5] = '\0';
	if(strcmp(PatternHeadStr, HeadStr)) { LastErrorNumber = 15; delete[] CodeByte; return FALSE; }

	// указатель к WORD
	CodeWord = (WORD *)CodeByte;

	// базовый адрес памяти программ
	PmBase = CodeWord[6];
	// кол-во слов для памяти программ
	PmWords = CodeWord[7];
	// базовый адрес памяти данных
	DmBase = CodeWord[8];
	// кол-во слов для памяти данных
	DmWords = CodeWord[9];

	// загрузим всю память данных DSP
	if(!PUT_DM_ARRAY((WORD)DmBase, DmWords, (SHORT *)(CodeWord + 2*PmWords + 10))) { delete[] CodeByte;  LastErrorNumber = 16; return FALSE; }

	// загрузим память программ DSP, начиная с адреса 0х1
	if(!PUT_PM_ARRAY((WORD)(PmBase+1), MAKE_PM_BUFFER_RTUSBAPI(PmWords-1, (CodeWord + 12)))) { delete[] CodeByte;  LastErrorNumber = 17; return FALSE; }

	// загрузим нулевую ячейку памяти программ DSP, т.е. стартанем драйвер DSP
	if(!PUT_PM_ARRAY((WORD)PmBase, MAKE_PM_BUFFER_RTUSBAPI(1, CodeWord + 10))) { delete[] CodeByte;  LastErrorNumber = 17; return FALSE; }

	// освободим память
	delete[] CodeByte;

	// теперь найдем базовый адрес блока переменных в памяти программ DSP
	LONG PBA; if(!GET_PM_WORD(RTUSB3000::D_PROGRAM_BASE_ADDRESS, &PBA)) { LastErrorNumber = 18; return FALSE; }
	ProgramBaseAddress = (WORD)(PBA >> 8);

	return TRUE;
}

//------------------------------------------------------------------------
// проверка работоспособности загруженного модуля
//------------------------------------------------------------------------
BOOL WINAPI CRTUSB3000::MODULE_TEST(void)
{
	// виртуальный слот доступен?
	if(hDevice == INVALID_HANDLE_VALUE) { LastErrorNumber = 4; return FALSE; }

	// для начала проверим содержимое тестовых переменных драйвера DSP
	WORD TMode1; if(!GET_VAR_WORD(RTUSB3000::D_TEST_VAR1, (SHORT *)&TMode1)) { LastErrorNumber = 19; return FALSE; }
	WORD TMode2; if(!GET_VAR_WORD(RTUSB3000::D_TEST_VAR2, (SHORT *)&TMode2)) { LastErrorNumber = 19; return FALSE; }
	if((TMode1 != 0x5555) || (TMode2 != 0xAAAA)) { LastErrorNumber = 20;  return FALSE; }
	else
	{
		WORD Ready;
		WORD TimeOut = 40;

		// проинициируем переменную D_TEST_INTR в драйвере DSP
		if(!PUT_VAR_WORD(RTUSB3000::D_TEST_INTR_VAR, 0x77BB)) { LastErrorNumber = 21; return FALSE; }

		// ждём пока загруженный драйвер DSP не установит переменную D_READY в '1'
		do
		{
			Sleep(25);
			if(!GET_VAR_WORD(RTUSB3000::D_MODULE_READY, (SHORT *)&Ready)) { LastErrorNumber = 19; return FALSE; }
		} while((!Ready) && (TimeOut--));
		// выход по таймауту
		if(TimeOut == 0xFFFF)  { LastErrorNumber = 22; return FALSE; }

		// посылаем в DSP модуля тестовую команду
		if(!SEND_COMMAND(C_TEST)) { LastErrorNumber = 23; return FALSE; }

		Sleep(100);

		// если всё хорошо, то теперь из D_TEST_INTR должно прочитаться 0xAA55
		WORD TestLoadVar; if(!GET_VAR_WORD(RTUSB3000::D_TEST_INTR_VAR, (SHORT *)&TestLoadVar)) { LastErrorNumber = 19; return FALSE; }
		if(TestLoadVar != 0xAA55) { LastErrorNumber = 24; return FALSE; }
	}

	return TRUE;
}

//------------------------------------------------------------------------
// текущая версия загруженной драйвера DSP
//------------------------------------------------------------------------
BOOL WINAPI CRTUSB3000::GET_DSP_INFO(RTUSB3000::DSP_INFO * const DspInfo)
{
	WORD i;
	WORD dv;

	// считываем название устройства, для которого предназначен загруженный
	// драйвер DSP (9 символов + '\0')
	// для модуля USB3000 должна быть строчка "USB3000"
	for(i = 0x0; i < sizeof(DspInfo->Target)/2; i++)
		if(!GET_VAR_WORD((WORD)(RTUSB3000::D_TARGET + i), (SHORT *)&DspInfo->Target[2*i])) { LastErrorNumber = 19; return FALSE; }

	// считываем метку разработчика драйвера DSP (5 символов + '\0')
	// штатный драйвер DSP имеет метку "rtech"
	for(i = 0x0; i < sizeof(DspInfo->Label)/2; i++)
		if(!GET_VAR_WORD((WORD)(RTUSB3000::D_LABEL + i), (SHORT *)&DspInfo->Label[2*i])) { LastErrorNumber = 19; return FALSE; }

	// считаем версию драйвера DSP
	if(!GET_VAR_WORD(RTUSB3000::D_VERSION, (SHORT *)&dv)) { LastErrorNumber = 19; return FALSE; }
	DspInfo->DspMajor = (BYTE)(dv >> 8);
	DspInfo->DspMinor = (BYTE)(dv & 0xFF);

	return TRUE;
}

//------------------------------------------------------------------------
// посылка в драйвер DSP модуля номера команды
//------------------------------------------------------------------------
BOOL WINAPI CRTUSB3000::SEND_COMMAND(WORD Command)
{
	WORD InBuf[4];
	DWORD cbRet=0;
	WORD TimeOut = 5000;

	// виртуальный слот доступен?
	if(hDevice == INVALID_HANDLE_VALUE) { LastErrorNumber = 4; return FALSE; }
	// зашлем номер команды
	else if(Command >= C_LAST_COMMAND) { LastErrorNumber = 25; return FALSE; }

	// вендор посылки командного прерывания в DSP
	InBuf[0] = 0; InBuf[1] = V_COMMAND_IRQ; InBuf[2] = 0x0; InBuf[3] = 0x0;
	// передадим в драйвер DSP номер требуемой команды
	if(!PUT_VAR_WORD(RTUSB3000::D_COMMAND, Command)) { LastErrorNumber = 21; return FALSE; }

	// захватим ресурс критической секции
	EnterCriticalSection(&cs);

	// теперь сгенерим командное прерывание в DSP модуля
	if(!DeviceIoControl(hDevice, DIOC_SEND_COMMAND, &InBuf, sizeof(InBuf), NULL, 0, &cbRet, NULL)) { LastErrorNumber = 5; LeaveCriticalSection(&cs); return FALSE; }

	// освободим ресурс критической секции
	LeaveCriticalSection(&cs);

	// проверим, что данная команда опознана драйвером DSP,
	// т.е в переменной D_COMMAND должен вернуться ноль
	do
	{
	   if(!GET_VAR_WORD(RTUSB3000::D_COMMAND, (SHORT *)&Command)) { LastErrorNumber = 19; return FALSE; }
	} while(Command && (TimeOut--));

	// проверим был ли таймаут выпролнения команды
	if(TimeOut == 0xFFFF) { LastErrorNumber = 26; return FALSE; }
	else return TRUE;
}

//------------------------------------------------------------------------------
// функция выдачи строки с последней ошибкой выполнения интерфейсной функции
//------------------------------------------------------------------------------
int WINAPI CRTUSB3000::GetLastErrorString(LPTSTR const lpBuffer, DWORD nSize)
{
	return LoadString(hInstance, BASE_ERROR_ID + LastErrorNumber, lpBuffer, nSize);
}




//========================================================================
// функции для работы в режиме ввода данных из модуля в РС
//========================================================================

//------------------------------------------------------------------------
// получение текущих параметров работы модуля в режиме ввода данных
//------------------------------------------------------------------------
BOOL WINAPI WINAPI CRTUSB3000::GET_INPUT_PARS(RTUSB3000::INPUT_PARS * const ap)
{
	WORD i, SclockDiv, InterKadrDelay;
	SHORT AdcOffsetCoef;
   WORD  AdcScaleCoef;
	double DspClockout;

	// виртуальный слот доступен?
	if(hDevice == INVALID_HANDLE_VALUE) { LastErrorNumber = 4; return FALSE; }
	// проверим размеры передаваемой и внутренней структур типа RTUSB3000::INPUT_PARS
	else if(this->ap.size != ap->size) { LastErrorNumber = 27; return FALSE; }
	// проверим поле Название в структуре типа RTUSB3000::FLASH (на всякий случай)
	else if(strcmp((char *)fi.Name, "USB3000")) { LastErrorNumber = 28; return FALSE; }
	// работает ли ввода данных
	else if(!GET_VAR_WORD(RTUSB3000::D_INPUT_ENABLED, (SHORT *)&ap->InputEnabled)) { LastErrorNumber = 19; return FALSE; }
	// используется ли корректировка входных аналоговых данных
	else if(!GET_VAR_WORD(RTUSB3000::D_CORRECTION_ENABLED, (SHORT *)&ap->CorrectionEnabled)) { LastErrorNumber = 19; return FALSE; }
	// используемый вид тактовых импульсов для ввода данных: внутренние или внешние
	else if(!GET_VAR_WORD(RTUSB3000::D_INPUT_CLOCK_SOURCE, (SHORT *)&ap->InputClockSource)) { LastErrorNumber = 58; return FALSE; }

	ap->InputEnabled &= 0xFFFFL;
	ap->CorrectionEnabled &= 0xFFFFL;

	// тип вводимых с модуля даных (АЦП или ТТЛ)
	if(!GET_VAR_WORD(RTUSB3000::D_INPUT_TYPE, (SHORT *)&ap->InputType)) { LastErrorNumber = 19; return FALSE; }

	// параметры синхронизации ввода данных с АЦП
	if(!GET_VAR_WORD(RTUSB3000::D_SYNCHRO_TYPE, (SHORT *)&ap->SynchroType)) { LastErrorNumber = 19; return FALSE; }
	else if(!GET_VAR_WORD(RTUSB3000::D_SYNCHRO_AD_TYPE, (SHORT *)&ap->SynchroAdType)) { LastErrorNumber = 19; return FALSE; }
	else if(!GET_VAR_WORD(RTUSB3000::D_SYNCHRO_AD_MODE, (SHORT *)&ap->SynchroAdMode)) { LastErrorNumber = 19; return FALSE; }
	else if(!GET_VAR_WORD(RTUSB3000::D_SYNCHRO_AD_CHANNEL, (SHORT *)&ap->SynchroAdChannel)) { LastErrorNumber = 19; return FALSE; }
	else if(!GET_VAR_WORD(RTUSB3000::D_SYNCHRO_AD_POROG, (SHORT *)&ap->SynchroAdPorog)) { LastErrorNumber = 19; return FALSE; }

	// управляющая таблица
	if(!GET_VAR_WORD(RTUSB3000::D_CONTROL_TABLE_LENGHT, (SHORT *)&ap->ChannelsQuantity)) { LastErrorNumber = 19; return FALSE; }
	else if(ap->ChannelsQuantity > 128) { LastErrorNumber = 29; return FALSE; }
	for(i=0; i < ap->ChannelsQuantity; i++) if(!GET_VAR_WORD((WORD)(RTUSB3000::D_CONTROL_TABLE + i), (SHORT *)&ap->ControlTable[i])) { LastErrorNumber = 19; return FALSE; }

	// временные параметры работы АЦП
	DspClockout = RTUSB3000::DSP_CLOCK_OUT; // тактовая частота DSP в кГц
	if(fabs(DspClockout - 72000.0) > 0.00001) { LastErrorNumber = 30; return FALSE; }
	if(!GET_VAR_WORD(RTUSB3000::D_INPUT_RATE, (SHORT *)&SclockDiv)) { LastErrorNumber = 19; return FALSE; }
	ap->InputRate = DspClockout/(2.*(WORD)(SclockDiv + 1.0));
	if(!GET_VAR_WORD(RTUSB3000::D_INTER_KADR_DELAY, (SHORT *)&InterKadrDelay)) { LastErrorNumber = 19; return FALSE; }
	ap->InterKadrDelay = (WORD)(InterKadrDelay + 1.)/ap->InputRate; // межкадровая задержка в мс
	ap->ChannelRate = 1./((ap->ChannelsQuantity - 1.)/ap->InputRate + ap->InterKadrDelay);

	// FIFO буфер АЦП
	if(!GET_VAR_WORD(RTUSB3000::D_INPUT_FIFO_BASE_ADDRESS, (SHORT *)&ap->InputFifoBaseAddress)) { LastErrorNumber = 19; return FALSE; }
	if(!GET_VAR_WORD(RTUSB3000::D_CUR_INPUT_FIFO_LENGTH, (SHORT *)&ap->InputFifoLength)) { LastErrorNumber = 19; return FALSE; }

	// считаем калибровочные коэффициенты для АЦП
	for(i = 0x0; i < 0x8; i++)
	{
		if(!GET_VAR_WORD((WORD)(RTUSB3000::D_ADC_ZERO + i), (SHORT *)&AdcOffsetCoef)) { LastErrorNumber = 19; return FALSE; }
		ap->AdcOffsetCoef[i] = AdcOffsetCoef;
		if(!GET_VAR_WORD((WORD)(RTUSB3000::D_ADC_SCALE + i), (SHORT *)&AdcScaleCoef)) { LastErrorNumber = 19; return FALSE; }
		ap->AdcScaleCoef[i] = (double)AdcScaleCoef/(double)0x8000;
	}

	// сохраним все параметры ввода во внутренней структуре типа RTUSB3000::INPUT_PARS
	this->ap = *ap;
	// уффф!!!
	return TRUE;
}

//------------------------------------------------------------------------
// заполнение требуемых параметров работы модуля в режиме ввода данных
//------------------------------------------------------------------------
BOOL WINAPI CRTUSB3000::SET_INPUT_PARS(RTUSB3000::INPUT_PARS * const ap)
{
	WORD i;
	SHORT AdcOffsetCoef;
   WORD  AdcScaleCoef;
	double DspClockout;
	double InputRateLimit;

	// сбросим флажок разрешения потокового ввода данных из модуля
	// во внутренней структуре типа RTUSB3000::INPUT_PARS
	this->ap.InputEnabled = FALSE;

	// виртуальный слот доступен?
	if(hDevice == INVALID_HANDLE_VALUE) { LastErrorNumber = 4; return FALSE; }
	// проверим размеры передаваемой и внутренней структур типа RTUSB3000::INPUT_PARS
	else if(this->ap.size != ap->size) { LastErrorNumber = 27; return FALSE; }
	// проверим поле Название в структуре типа RTUSB3000::FLASH (на всякий случай)
	else if(strcmp((char *)fi.Name, "USB3000")) { LastErrorNumber = 28; return FALSE; }

	// разрешение корректировки входных аналоговых данных
	if(!PUT_VAR_WORD(RTUSB3000::D_CORRECTION_ENABLED, (SHORT)(ap->CorrectionEnabled))) { LastErrorNumber = 21; return FALSE; }
	// определимся с видом тактовых импульсов для ввода данных: внутренние или внешние
	else if(!PUT_VAR_WORD(RTUSB3000::D_INPUT_CLOCK_SOURCE, (SHORT)(ap->InputClockSource))) { LastErrorNumber = 58; return FALSE; }

	// проверим требуемое кол-во логических каналов для ввода данных
	if(ap->ChannelsQuantity == 0x0) { LastErrorNumber = 48; return FALSE; }
	else if(ap->ChannelsQuantity > 128) { LastErrorNumber = 49; return FALSE; }
	// передадим в драйвер DSP кол-во логических каналов для ввода данных
	if(!PUT_VAR_WORD(RTUSB3000::D_CONTROL_TABLE_LENGHT, ap->ChannelsQuantity)) { LastErrorNumber = 21; return FALSE; }

	// нужно определить тип ввода данных: только АЦП, только ТТЛ или смешанный
	if(!FindoutInputType(ap)) { LastErrorNumber = 50; return FALSE; }
	// тип ввода MIXED_DATA пока не поддерживается :(((
	else if((ap->InputType == RTUSB3000::EMPTY_DATA) ||
				(ap->InputType == RTUSB3000::MIXED_DATA)) { LastErrorNumber = 51; return FALSE; }
	else if(!PUT_VAR_WORD(RTUSB3000::D_INPUT_TYPE, ap->InputType)) { LastErrorNumber = 21; return FALSE; }

	// формирование необходимой управляющей таблицы
	if(!ControlTableCreation(ap)) { LastErrorNumber = 52; return FALSE; }
	// загрузка управляющей таблицы
	for(i = 0x0; i < ap->ChannelsQuantity; i++)
	   	if(!PUT_VAR_WORD((WORD)(RTUSB3000::D_CONTROL_TABLE + i), (SHORT)ap->ControlTable[i])) { LastErrorNumber = 21; return FALSE; }

	// параметры синхронизации ввода данных с АЦП
	if(ap->SynchroType >= RTUSB3000::INVALID_INPUT_SYNCHRO) { LastErrorNumber = 31; return FALSE; }
	else if(!PUT_VAR_WORD(RTUSB3000::D_SYNCHRO_TYPE, ap->SynchroType)) { LastErrorNumber = 21; return FALSE; }
	else if(ap->SynchroType == RTUSB3000::ANALOG_SYNCHRO)
	{
	   if(!PUT_VAR_WORD(RTUSB3000::D_SYNCHRO_AD_TYPE, ap->SynchroAdType)) { LastErrorNumber = 21; return FALSE; }
	   if(!PUT_VAR_WORD(RTUSB3000::D_SYNCHRO_AD_MODE, ap->SynchroAdMode)) { LastErrorNumber = 21; return FALSE; }
	   if(!PUT_VAR_WORD(RTUSB3000::D_SYNCHRO_AD_CHANNEL, ap->SynchroAdChannel)) { LastErrorNumber = 21; return FALSE; }
	   if(!PUT_VAR_WORD(RTUSB3000::D_SYNCHRO_AD_POROG, ap->SynchroAdPorog)) { LastErrorNumber = 21; return FALSE; }
	}

	// определим предел частоты ввода данных(в кГц) в зависимости от скорости работы USB
	if(UsbSpeed == RTUSB3000::USB11) InputRateLimit = 500.0;
	else if(UsbSpeed == RTUSB3000::USB20)
	{
		// определим предел частоты для разных типов ввода данных (в кГц)
		if(ap->InputType == RTUSB3000::MIXED_DATA) InputRateLimit = 1000.;
		else if(ap->InputType == RTUSB3000::ADC_DATA) InputRateLimit = 3000.;
		else if(ap->InputType == RTUSB3000::TTL_DATA) InputRateLimit = 6000.;
	}
	else if(UsbSpeed >= RTUSB3000::INVALID_USB_SPEED) { LastErrorNumber = 57; return FALSE; }

	// временнЫе параметры работы АЦП
	DspClockout = RTUSB3000::DSP_CLOCK_OUT; // тактовая частота DSP в кГц
	if(fabs(DspClockout - 72000.0) > 0.00001) { LastErrorNumber = 30; return FALSE; }
	ap->InputRate = fabs(ap->InputRate);
	if(ap->InputRate > InputRateLimit) ap->InputRate = InputRateLimit;
	if(ap->InputRate < 0.1) ap->InputRate = 0.1;
	double SCLOCK_DIV = DspClockout/(2.*ap->InputRate) - 0.5;
	if(SCLOCK_DIV > 65500.) SCLOCK_DIV = 65500.;
	ap->InputRate = DspClockout/(2.*(WORD)(SCLOCK_DIV+1.0));
	if(!PUT_VAR_WORD(RTUSB3000::D_FIRST_SAMPLE_DELAY, (SHORT)(DspClockout/ap->InputRate + 5.5))) { LastErrorNumber = 21; return FALSE; }
	if(!PUT_VAR_WORD(RTUSB3000::D_INPUT_RATE, (SHORT)SCLOCK_DIV)) { LastErrorNumber = 21; return FALSE; }
	ap->InterKadrDelay = 0.0;			// пока межкадровой задержки нет :(((
	ap->InterKadrDelay = fabs(ap->InterKadrDelay);
	if((1./(ap->InputRate)) > (ap->InterKadrDelay)) ap->InterKadrDelay = 1./ap->InputRate;
	double kadrdelay = ap->InterKadrDelay*ap->InputRate - 0.5;
	if(kadrdelay > 65500.) kadrdelay = 65500.;
	ap->InterKadrDelay = (WORD)(kadrdelay + 1.)/ap->InputRate;
	if(!PUT_VAR_WORD(RTUSB3000::D_INTER_KADR_DELAY, (SHORT)kadrdelay)) { LastErrorNumber = 21; return FALSE; }
	ap->ChannelRate = 1.0/((ap->ChannelsQuantity - 1.0)/ap->InputRate + ap->InterKadrDelay);

	//===== параметры FIFO буфера АЦП =====
	// базовый адрес FIFO буфера АЦП всегда равен 0x0
	ap->InputFifoBaseAddress = 0x0;
	// длина FIFO буфера АЦП должна находиться в диапазоне от 0x400(1024)
	if(ap->InputFifoLength < 0x400) ap->InputFifoLength = 0x400;
	// и вплоть до 0x3000 (12288)
	else if(ap->InputFifoLength > 0x3000) ap->InputFifoLength = 0x3000;
	// а также длина FIFO буфера АЦП должна быть кратна...
	if(ap->InputFifoLength > 0x800) ap->InputFifoLength -= (WORD)(ap->InputFifoLength%2048);
	else ap->InputFifoLength -= (WORD)(ap->InputFifoLength%1024);
	// передадим в драйвер DSP используемую длину FIFO буфера ввода данных
	if(!PUT_VAR_WORD(RTUSB3000::D_INPUT_FIFO_LENGTH, ap->InputFifoLength)) { LastErrorNumber = 21; return FALSE; }

	// загрузим калибровочные коэффициенты для АЦП
	for(i = 0x0; i < 0x8; i++)
	{
		if(ap->AdcOffsetCoef[i] >= 0) AdcOffsetCoef = (SHORT)(ap->AdcOffsetCoef[i] + 0.5);
		else AdcOffsetCoef = (SHORT)(ap->AdcOffsetCoef[i] - 0.5);
		if(!PUT_VAR_WORD((WORD)(RTUSB3000::D_ADC_ZERO + i), AdcOffsetCoef)) { LastErrorNumber = 21; return FALSE; }

		AdcScaleCoef = (WORD)(ap->AdcScaleCoef[i] * (double)0x8000 + 0.5);
		if(!PUT_VAR_WORD((WORD)(RTUSB3000::D_ADC_SCALE + i), AdcScaleCoef)) { LastErrorNumber = 21; return FALSE; }
	}

	// сохраним все параметры ввода во внутренней структуре типа RTUSB3000::INPUT_PARS
	this->ap = *ap;

	// если до сюда добрались - значит всё в порядке с параметрами ввода,
	// т.е. можно установить флажок разрешения потокового ввода данных из модуля
	// используется в ф. START_READ(), READ_KADR() и ReadData()
	this->ap.InputEnabled = TRUE;
	// уффф!!!
	return TRUE;
}

//------------------------------------------------------------------------
// запуск потокового ввода данных
//------------------------------------------------------------------------
BOOL WINAPI CRTUSB3000::START_READ(void)
{
	WORD InBuf[4];
	DWORD cbRet = 0;

	// виртуальный слот доступен?
	if(hDevice == INVALID_HANDLE_VALUE) { LastErrorNumber = 4; return FALSE; }
	// проверим установлен ли флажок разрешения потокового ввода данных из модуля
	// это является признаком правильности установки параметров потокового ввода
//	else if(this->ap.InputEnabled == FALSE)  { LastErrorNumber = 47; return FALSE; }

	// захватим критическую секцию
	EnterCriticalSection(&cs);

	// формируем вендор старта ввода данных из модуля (для AVR)
	InBuf[0] = 0; InBuf[1] = V_START_READ; InBuf[2] = (WORD)(0x0 | DSP_DM); InBuf[3] = (WORD)(ap.InputFifoLength/2.0);
	// шлём вендор старта ввода данных (для AVR)
	if(!DeviceIoControl(hDevice, DIOC_SEND_COMMAND, &InBuf, sizeof(InBuf), NULL, 0, &cbRet, NULL)) { LastErrorNumber = 5; LeaveCriticalSection(&cs); return FALSE; }

	// освободим критическую секцию
	LeaveCriticalSection(&cs);

	// теперь зашлём команду старта ввода в драйвер DSP модуля
	if(!SEND_COMMAND(C_START_READ)) { LastErrorNumber = 23; return FALSE; }
	else return TRUE;
}

//------------------------------------------------------------------------
// останов потокового ввода данных
//------------------------------------------------------------------------
BOOL WINAPI CRTUSB3000::STOP_READ(void)
{
	DWORD cbRet=0;

	// виртуальный слот доступен?
	if(hDevice == INVALID_HANDLE_VALUE) { LastErrorNumber = 4; return FALSE; }
	// команда в драйвер DSP для останова ввода данных
	else if(!SEND_COMMAND(C_STOP_READ)) { LastErrorNumber = 23; return FALSE; }

	// захватим критическую секцию
	EnterCriticalSection(&cs);

	// вендор очистки канала чтения bulk'а
	if(!DeviceIoControl(hDevice, DIOC_RESET_PIPE3/*reset Read Pipe*/, NULL, NULL, NULL, NULL, &cbRet, NULL)) { LastErrorNumber = 5; LeaveCriticalSection(&cs); return FALSE; }

	// освободим критическую секцию
	LeaveCriticalSection(&cs);

	return TRUE;
}

//------------------------------------------------------------------------
// ввод кадра отсчетов с логических каналов модуля
//------------------------------------------------------------------------
BOOL WINAPI CRTUSB3000::READ_KADR(SHORT * const Data)
{
	WORD ChannelsQuantity;

	// виртуальный слот доступен?
	if(hDevice == INVALID_HANDLE_VALUE) { LastErrorNumber = 4; return FALSE; }
	else if(strcmp((char *)fi.Name, "USB3000")) { LastErrorNumber = 28; return FALSE; }
	// остановим АЦП
	else if(!STOP_READ()) { LastErrorNumber = 32; return FALSE; }
	// проверим установлен ли флажок разрешения потокового ввода данных из модуля
	// это является признаком правильности установки параметров потокового ввода
	else if(this->ap.InputEnabled == FALSE)  { LastErrorNumber = 47; return FALSE; }
	// проверим параметры работы АЦП (на всякий случай)
	else if(!GET_VAR_WORD(RTUSB3000::D_CONTROL_TABLE_LENGHT, (SHORT *)&ChannelsQuantity)) { LastErrorNumber = 19; return FALSE; }
	else if(ChannelsQuantity != ap.ChannelsQuantity)  { LastErrorNumber = 33; return FALSE; }
	else if(ChannelsQuantity > ap.InputFifoLength)  { LastErrorNumber = 34; return FALSE; }
	// команда в драйвер DSP для ввода кадра отсчетов с АЦП
	else if(!SEND_COMMAND(C_READ_KADR)) { LastErrorNumber = 23; return FALSE; }
	// теперь можно прочитать полученные данные
	else if(!GET_DM_ARRAY(0x0, ap.ChannelsQuantity, Data)) { LastErrorNumber = 35; return FALSE; }

	return TRUE;
}

//------------------------------------------------------------------------
// однократный ввод с заданного логического канала модуля
//------------------------------------------------------------------------
BOOL WINAPI CRTUSB3000::READ_SAMPLE(WORD Channel, SHORT * const Sample)
{
	// виртуальный слот доступен?
	if(hDevice == INVALID_HANDLE_VALUE) { LastErrorNumber = 4; return FALSE; }
	// остановим АЦП
	else if(!STOP_READ()) { LastErrorNumber = 32; return FALSE; }
	// логический номер канала АЦП для однократного ввода
	else if(!PUT_VAR_WORD(RTUSB3000::D_INPUT_CHANNEL, Channel)) { LastErrorNumber = 21; return FALSE; }
	// команда в драйвер DSP для однократного ввода с АЦП
	else if(!SEND_COMMAND(C_READ_SAMPLE)) { LastErrorNumber = 23; return FALSE; }
	// теперь прочитаем полученное значение с АЦП
	else if(!GET_VAR_WORD(RTUSB3000::D_INPUT_SAMPLE, Sample)) { LastErrorNumber = 19; return FALSE; }

	return TRUE;
}

//------------------------------------------------------------------------
// потоковое чтение данных из модуля
//------------------------------------------------------------------------
BOOL WINAPI CRTUSB3000::ReadData(SHORT * const lpBuffer, DWORD * const nNumberOfWordsToRead, LPDWORD lpNumberOfBytesRead, LPOVERLAPPED lpOverlapped)
{
	BOOL status;

	// виртуальный слот доступен?
	if(hDevice == INVALID_HANDLE_VALUE) { LastErrorNumber = 4; return FALSE; }
	// число отсчетов должно находиться в диапазоне от 0x200(512)
	else if(*nNumberOfWordsToRead < 0x200) *nNumberOfWordsToRead = 0x200;
	// и вплоть до 0x100000(1024*1024)
	else if(*nNumberOfWordsToRead > (1024*1024)) *nNumberOfWordsToRead = 1024*1024;
	// также число отсчетов должно быть кратно 0x200(512)
	*nNumberOfWordsToRead -= (*nNumberOfWordsToRead)%512;

	// передаём системе асинхронный запрос на ввод данных из модуля
	status = ReadFile(hDevice, (LPVOID)lpBuffer, 2*(*nNumberOfWordsToRead), lpNumberOfBytesRead, lpOverlapped);

	// все хорошо :)))))
	if(status) return TRUE;
	// ошибка выполнения ReadFile()
	else { LastErrorNumber = 53; return FALSE; }
}




//========================================================================
// функции для работы в режиме вывода данных из РС в модуль
//========================================================================

//------------------------------------------------------------------------
// получение текущих параметров работы модуля в режиме вывода данных
//------------------------------------------------------------------------
BOOL WINAPI CRTUSB3000::GET_OUTPUT_PARS(RTUSB3000::OUTPUT_PARS * const dp)
{
	WORD SclkDiv;
	double DspClockout;

	// виртуальный слот доступен?
	if(hDevice == INVALID_HANDLE_VALUE) { LastErrorNumber = 4; return FALSE; }
	else if(this->dp.size != dp->size) { LastErrorNumber = 36; return FALSE; }
	else if(strcmp((char *)fi.Name, "USB3000")) { LastErrorNumber = 28; return FALSE; }
	else if(!GET_VAR_WORD(RTUSB3000::D_OUTPUT_ENABLED, (SHORT *)&dp->OutputEnabled)) { LastErrorNumber = 19; return FALSE; }

	// временные параметры работы ЦАП
	DspClockout = RTUSB3000::DSP_CLOCK_OUT; // тактовая частота DSP в кГц
	if(fabs(DspClockout - 72000.0) > 0.00001) { LastErrorNumber = 30; return FALSE; }

	if(!GET_VAR_WORD(RTUSB3000::D_OUTPUT_SCLK_DIV, (SHORT *)&SclkDiv)) { LastErrorNumber = 19; return FALSE; }
	double SCLK = DspClockout/(2.0*(SclkDiv + 1.0));

	WORD RFS_DIV;
	if(!GET_VAR_WORD(RTUSB3000::D_OUTPUT_RATE, (SHORT *)&RFS_DIV)) { LastErrorNumber = 19; return FALSE; }
	dp->OutputRate = SCLK/(RFS_DIV + 1.0);

	// параметры FIFO буфера АЦП
	if(!GET_VAR_WORD(RTUSB3000::D_OUTPUT_FIFO_BASE_ADDRESS, (SHORT *)&dp->OutputFifoBaseAddress)) { LastErrorNumber = 19; return FALSE; }
	if(!GET_VAR_WORD(RTUSB3000::D_CUR_OUTPUT_FIFO_LENGTH, (SHORT *)&dp->OutputFifoLength)) { LastErrorNumber = 19; return FALSE; }

	// сохраним все параметры вывода во внутренней структуре типа RTUSB3000::OUTPUT_PARS
	this->dp = *dp;

	return TRUE;
}

//------------------------------------------------------------------------
// заполнение требуемых параметров работы модуля в режиме вывода данных
//------------------------------------------------------------------------
BOOL WINAPI CRTUSB3000::SET_OUTPUT_PARS(RTUSB3000::OUTPUT_PARS * const dp)
{
	WORD SclkDiv;
	double DspClockout, SCLK;

	// виртуальный слот доступен?
	if(hDevice == INVALID_HANDLE_VALUE) { LastErrorNumber = 4; return FALSE; }
	else if(this->dp.size != dp->size) { LastErrorNumber = 36; return FALSE; }
	else if(strcmp((char *)fi.Name, "USB3000")) { LastErrorNumber = 28; return FALSE; }

	// временнЫе параметры работы ЦАП
	DspClockout = RTUSB3000::DSP_CLOCK_OUT; // тактовая частота DSP в кГц
	if(fabs(DspClockout - 72000.0) > 0.00001) { LastErrorNumber = 30; return FALSE; }

	if(!GET_VAR_WORD(RTUSB3000::D_OUTPUT_SCLK_DIV, (SHORT *)&SclkDiv)) { LastErrorNumber = 19; return FALSE; }
	SCLK = DspClockout/(2.0*(SclkDiv + 1.0));

	dp->OutputRate = fabs(dp->OutputRate);
	if(dp->OutputRate < SCLK/65535.0) dp->OutputRate = SCLK/65535.0;

	WORD RFS_DIV = (SHORT)(SCLK/(dp->OutputRate)-0.5);
	dp->OutputRate = SCLK/(RFS_DIV + 1.0);
	if(!PUT_VAR_WORD(RTUSB3000::D_OUTPUT_RATE, RFS_DIV)) { LastErrorNumber = 21; return FALSE; }

	//===== параметры FIFO буфера ЦАП =====
	// базовый адрес FIFO буфера ЦАП всегда равен 0x3000
	dp->OutputFifoBaseAddress = 0x3000;
	// длина FIFO буфера ЦАП должна находиться в диапазоне от 0x80(128)
	if(dp->OutputFifoLength < 0x80) dp->OutputFifoLength = 0x80;
	// и вплоть до 0xF80(3968)
	else if(dp->OutputFifoLength > 0xF80) dp->OutputFifoLength = 0xF80;
	// а также длина FIFO буфера ЦАП должна быть кратна 0x80(128)
	dp->OutputFifoLength -= (WORD)(dp->OutputFifoLength%128);
	if(!PUT_VAR_WORD(RTUSB3000::D_OUTPUT_FIFO_LENGTH, dp->OutputFifoLength)) { LastErrorNumber = 21; return FALSE; }

	// сохраним все параметры вывода во внутренней структуре типа RTUSB3000::OUTPUT_PARS
	this->dp = *dp;

	return TRUE;
}

//------------------------------------------------------------------------
// запуск потокового вывода данных
//------------------------------------------------------------------------
BOOL WINAPI CRTUSB3000::START_WRITE(void)
{
	WORD InBuf[4];
	DWORD cbRet=0;

	// виртуальный слот доступен?
	if(hDevice == INVALID_HANDLE_VALUE) { LastErrorNumber = 4; return FALSE; }
	// а есть ли ЦАП на данном экземпляре модуля
	else if(fi.IsDacPresented == FALSE)  { LastErrorNumber = 37; return FALSE; }

	// захватим ресурс критической секции
	EnterCriticalSection(&cs);

	// формируем вендор старта передачи данных в модуль (для AVR)
	InBuf[0] = 0; InBuf[1] = V_START_WRITE; InBuf[2] = (WORD)(0x3000 | DSP_DM); InBuf[3] = (WORD)(dp.OutputFifoLength/2.0);
	// шлём вендор старта вывода данных (для AVR)
	if(!DeviceIoControl(hDevice, DIOC_SEND_COMMAND, &InBuf, sizeof(InBuf), NULL, 0, &cbRet, NULL)) { LastErrorNumber = 5; LeaveCriticalSection(&cs); return FALSE; }

	// освободим ресурс критической секции
	LeaveCriticalSection(&cs);

	// теперь зашлём команду в драйвер DSP для останова вывода данных
	if(!SEND_COMMAND(C_START_WRITE)) { LastErrorNumber = 23; return FALSE; }
	else return TRUE;
}

//------------------------------------------------------------------------
// останов вывода данных
//------------------------------------------------------------------------
BOOL WINAPI CRTUSB3000::STOP_WRITE(void)
{
	DWORD cbRet=0;

	// виртуальный слот доступен?
	if(hDevice == INVALID_HANDLE_VALUE) { LastErrorNumber = 4; return FALSE; }
	// а есть ли ЦАП на данном экземпляре модуля
	else if(fi.IsDacPresented == FALSE)  { LastErrorNumber = 37; return FALSE; }
	// команда в драйвер DSP для останова вывода данных
	else if(!SEND_COMMAND(C_STOP_WRITE)) { LastErrorNumber = 23; return FALSE; }

	// захватим ресурс критической секции
	EnterCriticalSection(&cs);

	// вендор очистки канала записи bulk'а
	if(!DeviceIoControl(hDevice, DIOC_RESET_PIPE1/*reset Write Pipe*/, NULL, NULL, NULL, NULL, &cbRet, NULL)) { LastErrorNumber = 5; LeaveCriticalSection(&cs); return FALSE; }

	// освободим ресурс критической секции
	LeaveCriticalSection(&cs);

	return TRUE;
}

//------------------------------------------------------------------------
// потоковая передача данных ЦАП в модуль
//------------------------------------------------------------------------
BOOL WINAPI CRTUSB3000::WriteData(SHORT * const lpBuffer, DWORD * const nNumberOfWordsToWrite, LPDWORD lpNumberOfBytesWritten, LPOVERLAPPED lpOverlapped)
{
	BOOL status;

	// виртуальный слот доступен?
	if(hDevice == INVALID_HANDLE_VALUE) { LastErrorNumber = 4; return FALSE; }
	// число отсчетов должно находиться в диапазоне от 0x40(64)
	else if(*nNumberOfWordsToWrite < 0x40) *nNumberOfWordsToWrite = 0x40;
	// и вплоть до 0x100000(1024*1024)
	else if(*nNumberOfWordsToWrite > (1024*1024)) *nNumberOfWordsToWrite = 1024*1024;
	// также число отсчетов должно быть кратно 0x40(64)
	*nNumberOfWordsToWrite -= (*nNumberOfWordsToWrite)%64;

	// передаём системе асинхронный запрос на вывод данных в модуль
	status = WriteFile(hDevice, (LPVOID)lpBuffer, 2*(*nNumberOfWordsToWrite), lpNumberOfBytesWritten, lpOverlapped);

	// все хорошо :)))))
	if(status) return TRUE;
	// ошибка выполнения WriteFile()
	else { LastErrorNumber = 53; return FALSE; }
}

//------------------------------------------------------------------------
// однократный вывод отсчёта
//------------------------------------------------------------------------
BOOL WINAPI CRTUSB3000::WRITE_SAMPLE(WORD Channel, SHORT * const Sample)
{
	WORD Data;

	// виртуальный слот доступен?
	if(hDevice == INVALID_HANDLE_VALUE) { LastErrorNumber = 4; return FALSE; }
	// а есть ли ЦАП на данном экземпляре модуля
	else if(fi.IsDacPresented == FALSE)  { LastErrorNumber = 37; return FALSE; }
	else if(Channel > 1) { LastErrorNumber = 38; return FALSE; }

	// ограничения кода ЦАП
	if((*Sample) < -2048) *Sample = -2048;
	else if((*Sample) > 2047) *Sample = 2047;

	//
	Data = (SHORT)((*Sample) + 2047);
	Data = (WORD)(Data & 0xFFF);
	Data |= (WORD)(Channel << 15) | (WORD)(0x1 << 14);
	if(!PUT_VAR_WORD(RTUSB3000::D_OUTPUT_SAMPLE, Data)) { LastErrorNumber = 21; return FALSE; }

	// команда в драйвер DSP для однократного вывода на ЦАП
	if(!SEND_COMMAND(C_WRITE_SAMPLE)) { LastErrorNumber = 23; return FALSE; }
	return TRUE;
}





//========================================================================
// функции для работы с ТТЛ линиями на внешнем цифровом разъёме
//========================================================================

//************************************************************************
// функция разрешения выходных линий внешнего цифрового разъёма
//************************************************************************
BOOL WINAPI CRTUSB3000::ENABLE_TTL_OUT(BOOL EnableTtlOut)
{
	// виртуальный слот доступен?
	if(hDevice == INVALID_HANDLE_VALUE) { LastErrorNumber = 4; return FALSE; }
	// флажок разрешения выходных цифровых линий
	this->EnableTtlOut = EnableTtlOut;
	// сохраним флажок разрешения выходных цифровых линий в DSP
	if(!PUT_VAR_WORD(RTUSB3000::D_ENABLE_TTL_OUT, (SHORT)EnableTtlOut)) { LastErrorNumber = 19; return FALSE; }
	// пошлем команду в DSP
	else if(!SEND_COMMAND(C_ENABLE_TTL_OUT)) { LastErrorNumber = 23; return FALSE; }

	return TRUE;
}

//************************************************************************
// функция вывода на выходные линий внешнего цифрового разъёма
//************************************************************************
BOOL WINAPI CRTUSB3000::TTL_OUT(WORD TtlOut)
{
	// виртуальный слот доступен?
	if(hDevice == INVALID_HANDLE_VALUE) { LastErrorNumber = 4; return FALSE; }
	// проверим признак разрешения/запрещения выходных цифровых линий
	else if(!EnableTtlOut) { LastErrorNumber = 39; return FALSE; }
	// передадим значения, которые должны быть выставлены на цифровых линиях
	else if(!PUT_VAR_WORD(RTUSB3000::D_TTL_OUT, (SHORT)(TtlOut))) { LastErrorNumber = 21; return FALSE; }
	// пошлем команду в DSP
	else if(!SEND_COMMAND(C_TTL_OUT)) { LastErrorNumber = 23; return FALSE; }

	return TRUE;
}

//************************************************************************
// функция чтения входных линий внешнего цифрового разъёма
//************************************************************************
BOOL WINAPI CRTUSB3000::TTL_IN(WORD * const TtlIn)
{
	// виртуальный слот доступен?
	if(hDevice == INVALID_HANDLE_VALUE) { LastErrorNumber = 4; return FALSE; }
	// пошлем команду в DSP
	else if(!SEND_COMMAND(C_TTL_IN)) { LastErrorNumber = 23; return FALSE; }
	// получим значения с цифровых входных линиий
	else if(!GET_VAR_WORD(RTUSB3000::D_TTL_IN, (SHORT *)TtlIn)) { LastErrorNumber = 19; return FALSE; }
	// у нас 10 входных цифровых линий   
	*TtlIn = (WORD)((*TtlIn) & 0x03FF);

	return TRUE;
}




//========================================================================
// функции для работы с пользовательским ППЗУ модуля
//========================================================================

//------------------------------------------------------------------------
// разрешение/запрещение режима записи в ППЗУ модуля
//------------------------------------------------------------------------
BOOL WINAPI CRTUSB3000::ENABLE_FLASH_WRITE(BOOL EnableFlashWrite)
{
	this->EnableFlashWrite = EnableFlashWrite;

	return TRUE;
}

//------------------------------------------------------------------------
// запись структуры типа FLASH в пользовательское ППЗУ
//------------------------------------------------------------------------
BOOL WINAPI CRTUSB3000::PUT_FLASH(RTUSB3000::FLASH * const fi)
{
	WORD InBuf[4];
	DWORD cbRet=0;

	// виртуальный слот доступен?
	if(hDevice == INVALID_HANDLE_VALUE) { LastErrorNumber = 4; return FALSE; }
	// размер структуры должен быть равен 256 байтам
	else if(fi->size != sizeof(RTUSB3000::FLASH)) { LastErrorNumber = 40; return FALSE; }
	// проверим можно ли писать в ППЗУ
	else if(!EnableFlashWrite) { LastErrorNumber = 41; return FALSE; }

	fi->CRC16 = 0x0;
	fi->CRC16 = CalculateCrc16((BYTE *)(fi) + 2, sizeof(RTUSB3000::FLASH) - 2);

	// захватим ресурс критической секции
	EnterCriticalSection(&cs);

	// формируем вендор записи пользовательского ППЗУ
	InBuf[0] = 0; InBuf[1] = V_PUT_FLASH; InBuf[2] = (WORD)(0x1E00); InBuf[3] = 0x0;
	// засылаем вендор записи пользовательского ППЗУ
  	if(!DeviceIoControl(hDevice,DIOC_SEND_COMMAND, &InBuf, sizeof(InBuf), fi, 256/*bytes*/, &cbRet, NULL)) { LastErrorNumber = 5; LeaveCriticalSection(&cs); return FALSE; }

	// освободим ресурс критической секции
	LeaveCriticalSection(&cs);

	// скопируем во внутренную структуру типа RTUSB3000::FLASH
	this->fi = *fi;
	// запретми запись в ППЗУ модуля
	EnableFlashWrite = FALSE;

	return TRUE;
}

//------------------------------------------------------------------------
// чтение структуры типа FLASH из пользовательского ППЗУ
//------------------------------------------------------------------------
BOOL WINAPI CRTUSB3000::GET_FLASH(RTUSB3000::FLASH * const fi)
{
	BYTE *ptr;
	WORD InBuf[4];
	WORD crc;
	DWORD cbRet=0;

	// виртуальный слот доступен?
	if(hDevice == INVALID_HANDLE_VALUE) { LastErrorNumber = 4; return FALSE; }
	// размер структуры должен быть равен 256 байтам
	else if(fi->size != sizeof(RTUSB3000::FLASH))  { LastErrorNumber = 40; return FALSE; }

	// попробуем выделить буфер под массив размером 256 байтов
	ptr = new BYTE[256];
	if(!ptr) { LastErrorNumber = 54; return FALSE; }

	// захватим ресурс критической секции
	EnterCriticalSection(&cs);

	// формируем вендор записи пользовательского ППЗУ
	InBuf[0] = 1; InBuf[1] = V_GET_FLASH; InBuf[2] = (WORD)(0x1E00); InBuf[3] = 0x0;
	// засылаем вендор записи пользовательского ППЗУ
  	if(!DeviceIoControl(hDevice,DIOC_SEND_COMMAND, &InBuf, sizeof(InBuf), ptr, 256/*bytes*/, &cbRet, NULL)) { LastErrorNumber = 5; delete[] ptr; LeaveCriticalSection(&cs); return FALSE; }

	// освободим ресурс критической секции
	LeaveCriticalSection(&cs);

	// проверим CRC считанного из ППЗУ байтового массива
	crc = (WORD)(((WORD)ptr[1] << 8) | ptr[0]);
	if(crc != CalculateCrc16((BYTE *)(ptr)+2, sizeof(RTUSB3000::FLASH)-2)) { LastErrorNumber = 55; delete[] ptr; return FALSE; }
	// всё в порядке - копируем в структуру типа RTUSB3000::FLASH
	memcpy(fi, ptr, sizeof(RTUSB3000::FLASH));
	// сохраняем во внутренней структуре типа RTUSB3000::FLASH
	this->fi = *fi;

	// освободим ресурс
	delete[] ptr;

	return TRUE;
}






//========================================================================
// функции для работы памятью DSP модуля
//========================================================================

//------------------------------------------------------------------------
// запись слова в область переменных в памяти программ драйвера DSP
//------------------------------------------------------------------------
BOOL WINAPI CRTUSB3000::PUT_VAR_WORD(WORD Address, SHORT Data)
{
	// виртуальный слот доступен?
	if(hDevice == INVALID_HANDLE_VALUE) { LastErrorNumber = 4; return FALSE; }
	else if((Address < VarsBaseAddress) || (Address > ProgramBaseAddress)) { LastErrorNumber = 42; return FALSE; }
	else if(!PUT_PM_WORD(Address, (LONG)Data<<8)) { LastErrorNumber = 43; return FALSE; }
	return TRUE;
}

//------------------------------------------------------------------------
// чтение слова из область переменных  в памяти программ драйвера DSP
//------------------------------------------------------------------------
BOOL WINAPI CRTUSB3000::GET_VAR_WORD(WORD Address, SHORT * const Data)
{
	LONG PmData;

	// виртуальный слот доступен?
	if(hDevice == INVALID_HANDLE_VALUE) { LastErrorNumber = 4; return FALSE; }
	else if((Address < VarsBaseAddress) || (Address > ProgramBaseAddress)) { LastErrorNumber = 42; return FALSE; }
	else if(!GET_PM_WORD(Address, &PmData)) { LastErrorNumber = 44; return FALSE; }
	*Data = (SHORT)(PmData >> 8);
	return TRUE;
}

//------------------------------------------------------------------------
// запись слова в ячейку памяти данных драйвера DSP
//------------------------------------------------------------------------
BOOL WINAPI CRTUSB3000::PUT_DM_WORD(WORD Address, SHORT Data)
{
	BOOL status;
	WORD InBuf[4];
	DWORD cbRet=0;

	// виртуальный слот доступен?
	if(hDevice == INVALID_HANDLE_VALUE) { LastErrorNumber = 4; return FALSE; }

	// захватим ресурс критической секции
	EnterCriticalSection(&cs);

   // формируем вендор записи слова в память данных DSP
	InBuf[0] = 0; InBuf[1] = V_PUT_ARRAY; InBuf[2] = (WORD)(Address | DSP_DM); InBuf[3] = 0x0;
	// засылаем сформированный вендор
	status = DeviceIoControl( hDevice,DIOC_SEND_COMMAND,
                             &InBuf,sizeof(InBuf),
                             &Data, 2/*bytes*/,
                             &cbRet,NULL);

	// освободим ресурс критической секции
	LeaveCriticalSection(&cs);

	// все хорошо :)))))
	if(status) return TRUE;
	// ошибка выполнения DeviceIoControl()
	else { LastErrorNumber = 5; return FALSE; }
}

//------------------------------------------------------------------------
// чтение слова из ячейки памяти данных драйвера DSP
//------------------------------------------------------------------------
BOOL WINAPI CRTUSB3000::GET_DM_WORD(WORD Address, SHORT * const Data)
{
	BOOL status;
	WORD InBuf[4];
	DWORD cbRet=0;

	// виртуальный слот доступен?
	if(hDevice == INVALID_HANDLE_VALUE) { LastErrorNumber = 4; return FALSE; }

	// захватим ресурс критической секции
	EnterCriticalSection(&cs);

   // формируем вендор чтения слова из памяти данных DSP
	InBuf[0] = 1; InBuf[1] = V_GET_ARRAY; InBuf[2] = (WORD)(Address | DSP_DM); InBuf[3] = 0x0;
	// засылаем сформированный вендор
	status = DeviceIoControl( hDevice,DIOC_SEND_COMMAND,
                             &InBuf,sizeof(InBuf),
                             Data, 2/*bytes*/,
                             &cbRet,NULL);

	// освободим ресурс критической секции
	LeaveCriticalSection(&cs);

	// все хорошо :)))))
	if(status) return TRUE;
	// ошибка выполнения DeviceIoControl()
	else { LastErrorNumber = 5; return FALSE; }
}

//------------------------------------------------------------------------
// запись слова в ячейку памяти программ драйвера DSP
//------------------------------------------------------------------------
BOOL WINAPI CRTUSB3000::PUT_PM_WORD(WORD Address, LONG Data)
{
	BOOL status;
	WORD InBuf[4];
	DWORD cbRet=0;

	// виртуальный слот доступен?
	if(hDevice == INVALID_HANDLE_VALUE) { LastErrorNumber = 4; return FALSE; }

	// захватим ресурс критической секции
	EnterCriticalSection(&cs);

   // формируем вендор записи слова в память программ DSP
	InBuf[0] = 0; InBuf[1] = V_PUT_ARRAY; InBuf[2] = (WORD)(Address | DSP_PM); InBuf[3] = 0x0;
	// засылаем сформированный вендор
	status = DeviceIoControl( hDevice,DIOC_SEND_COMMAND,
                             &InBuf, sizeof(InBuf),
                             &Data, 4/*bytes*/,
                             &cbRet,NULL);

	// освободим ресурс критической секции
	LeaveCriticalSection(&cs);

	// все хорошо :)))))
	if(status) return TRUE;
	// ошибка выполнения DeviceIoControl()
	else { LastErrorNumber = 5; return FALSE; }
}

//------------------------------------------------------------------------
// чтение слова из ячейки памяти программ драйвера DSP
//------------------------------------------------------------------------
BOOL WINAPI CRTUSB3000::GET_PM_WORD(WORD Address, LONG * const Data)
{
	BOOL status;
	WORD InBuf[4];
	DWORD cbRet=0;

	// виртуальный слот доступен?
	if(hDevice == INVALID_HANDLE_VALUE) { LastErrorNumber = 4; return FALSE; }

	// захватим ресурс критической секции
	EnterCriticalSection(&cs);

   // формируем вендор чтения слова из памяти программ DSP
	InBuf[0] = 1; InBuf[1] = V_GET_ARRAY; InBuf[2] = (WORD)(Address | DSP_PM); InBuf[3] = 0x0;
	// засылаем сформированный вендор
	status = DeviceIoControl( hDevice,DIOC_SEND_COMMAND,
                             &InBuf,sizeof(InBuf),
                             Data, 4/*bytes*/,
                             &cbRet,NULL);

	// освободим ресурс критической секции
	LeaveCriticalSection(&cs);

	// все хорошо :)))))
	if(status) return TRUE;
	// ошибка выполнения DeviceIoControl()
	else { LastErrorNumber = 5; return FALSE; }
}

//------------------------------------------------------------------------
// запись массива слов в область памяти данных драйвера DSP
//------------------------------------------------------------------------
BOOL WINAPI CRTUSB3000::PUT_DM_ARRAY(WORD BaseAddress, WORD NPoints, SHORT * const Data)
{
	BOOL error = FALSE;
	WORD InBuf[4];
	WORD BlockQuantity, RestDataPoints;
	DWORD cbRet = 0x0;

	// виртуальный слот доступен?
	if(hDevice == INVALID_HANDLE_VALUE) { LastErrorNumber = 4; return FALSE; }

	BlockQuantity = (WORD)((sizeof(SHORT)*NPoints)/MAX_USB_BLOCK);
	RestDataPoints = (WORD)((sizeof(SHORT)*NPoints)%MAX_USB_BLOCK); // в байтах

	InBuf[0] = 0; InBuf[1] = V_PUT_ARRAY; InBuf[3] = 0x0;
	for(WORD i=0; i < BlockQuantity; i++)
	{
		InBuf[2] = (WORD)((BaseAddress + i*MAX_USB_BLOCK/sizeof(SHORT)) | DSP_DM);
		EnterCriticalSection(&cs);
		if(!DeviceIoControl(hDevice,DIOC_SEND_COMMAND, &InBuf, sizeof(InBuf), Data + i*MAX_USB_BLOCK/sizeof(SHORT), MAX_USB_BLOCK/*bytes*/, &cbRet, NULL)) error = TRUE;
		LeaveCriticalSection(&cs);
      if(error) break;
	}

	if(!error && RestDataPoints)
	{
		InBuf[2] = (WORD)((BaseAddress + BlockQuantity*MAX_USB_BLOCK/sizeof(SHORT)) | DSP_DM);
		EnterCriticalSection(&cs);
		if(!DeviceIoControl(hDevice,DIOC_SEND_COMMAND, &InBuf, sizeof(InBuf), Data + BlockQuantity*MAX_USB_BLOCK/sizeof(SHORT), RestDataPoints/*bytes*/, &cbRet, NULL)) error = TRUE;
		LeaveCriticalSection(&cs);
	}

	if(error) { LastErrorNumber = 5; return FALSE; }
	return TRUE;
}

//------------------------------------------------------------------------
// чтение массива слов из области памяти данных драйвера DSP
//------------------------------------------------------------------------
BOOL WINAPI CRTUSB3000::GET_DM_ARRAY(WORD BaseAddress, WORD NPoints, SHORT * const Data)
{
	BOOL error = FALSE;
	WORD InBuf[4];
	WORD BlockQuantity, RestDataPoints;
	DWORD cbRet = 0x0;

	// виртуальный слот доступен?
	if(hDevice == INVALID_HANDLE_VALUE) { LastErrorNumber = 4; return FALSE; }

	BlockQuantity = (WORD)((sizeof(SHORT)*NPoints)/MAX_USB_BLOCK);
	RestDataPoints = (WORD)((sizeof(SHORT)*NPoints)%MAX_USB_BLOCK); // в байтах

	InBuf[0] = 1; InBuf[1] = V_GET_ARRAY; InBuf[3] = 0x0;
	for(WORD i=0; i < BlockQuantity; i++)
	{
		InBuf[2] = (WORD)((BaseAddress + i*MAX_USB_BLOCK/sizeof(SHORT)) | DSP_DM);
		EnterCriticalSection(&cs);
		if(!DeviceIoControl( hDevice, DIOC_SEND_COMMAND, &InBuf,sizeof(InBuf), Data + i*MAX_USB_BLOCK/sizeof(SHORT), MAX_USB_BLOCK/*bytes*/, &cbRet, NULL)) error = TRUE;
		LeaveCriticalSection(&cs);
      if(error) break;
	}

	if(!error && RestDataPoints)
	{
		InBuf[2] = (WORD)((BaseAddress + BlockQuantity*MAX_USB_BLOCK/sizeof(SHORT)) | DSP_DM);
		EnterCriticalSection(&cs);
		if(!DeviceIoControl(hDevice, DIOC_SEND_COMMAND, &InBuf, sizeof(InBuf), Data + BlockQuantity*MAX_USB_BLOCK/sizeof(SHORT), RestDataPoints/*bytes*/, &cbRet, NULL)) error = TRUE;
		LeaveCriticalSection(&cs);
	}

	if(error) { LastErrorNumber = 5; return FALSE; }
	return TRUE;
}

//------------------------------------------------------------------------
// запись массива слов в область память данных драйвера DSP
//------------------------------------------------------------------------
BOOL WINAPI CRTUSB3000::PUT_PM_ARRAY(WORD BaseAddress, WORD NPoints, LONG * const Data)
{
	BOOL error = FALSE;
	WORD InBuf[4];
	WORD BlockQuantity, RestDataPoints;
	DWORD cbRet=0;

	// виртуальный слот доступен?
	if(hDevice == INVALID_HANDLE_VALUE) { LastErrorNumber = 4; return FALSE; }

	BlockQuantity = (WORD)((sizeof(LONG)*NPoints)/MAX_USB_BLOCK);
	RestDataPoints = (WORD)((sizeof(LONG)*NPoints)%MAX_USB_BLOCK); // в байтах

	InBuf[0] = 0; InBuf[1] = V_PUT_ARRAY; InBuf[3] = 0x0;
	for(WORD i=0; i < BlockQuantity; i++)
	{
		InBuf[2] = (WORD)((BaseAddress + i*MAX_USB_BLOCK/sizeof(LONG)) | DSP_PM);
		EnterCriticalSection(&cs);
		if(!DeviceIoControl(hDevice, DIOC_SEND_COMMAND, &InBuf, sizeof(InBuf), Data + i*MAX_USB_BLOCK/sizeof(LONG), MAX_USB_BLOCK/*bytes*/, &cbRet, NULL)) error = TRUE;
		LeaveCriticalSection(&cs);
      if(error) break;
	}

	if(!error && RestDataPoints)
	{
		InBuf[2] = (WORD)((BaseAddress + BlockQuantity*MAX_USB_BLOCK/sizeof(LONG)) | DSP_PM);
		EnterCriticalSection(&cs);
		if(!DeviceIoControl(hDevice, DIOC_SEND_COMMAND, &InBuf, sizeof(InBuf), Data + BlockQuantity*MAX_USB_BLOCK/sizeof(LONG), RestDataPoints/*bytes*/, &cbRet, NULL)) error = TRUE;
		LeaveCriticalSection(&cs);
	}

	if(error) { LastErrorNumber = 5; return FALSE; }
	return TRUE;
}

//------------------------------------------------------------------------
// чтение массива слов из области памяти данных драйвера DSP
//------------------------------------------------------------------------
BOOL WINAPI CRTUSB3000::GET_PM_ARRAY(WORD BaseAddress, WORD NPoints, LONG * const Data)
{
	BOOL error = FALSE;
	WORD InBuf[4];
	WORD BlockQuantity, RestDataPoints;
	DWORD cbRet=0;

	// виртуальный слот доступен?
	if(hDevice == INVALID_HANDLE_VALUE) { LastErrorNumber = 4; return FALSE; }

	BlockQuantity = (WORD)((sizeof(LONG)*NPoints)/MAX_USB_BLOCK);
	RestDataPoints = (WORD)((sizeof(LONG)*NPoints)%MAX_USB_BLOCK); // в байтах

	InBuf[0] = 1; InBuf[1] = V_GET_ARRAY; InBuf[3] = 0x0;
	for(WORD i=0; i < BlockQuantity; i++)
	{
		InBuf[2] = (WORD)((BaseAddress + i*MAX_USB_BLOCK/sizeof(LONG)) | DSP_PM);
		EnterCriticalSection(&cs);
		if(!DeviceIoControl(hDevice, DIOC_SEND_COMMAND, &InBuf, sizeof(InBuf), Data + i*MAX_USB_BLOCK/sizeof(LONG), MAX_USB_BLOCK/*bytes*/, &cbRet, NULL)) error = TRUE;
		LeaveCriticalSection(&cs);
      if(error) break;
	}

	if(!error && RestDataPoints)
	{
		InBuf[2] = (WORD)((BaseAddress + BlockQuantity*MAX_USB_BLOCK/sizeof(LONG)) | DSP_PM);
		EnterCriticalSection(&cs);
	   if(!DeviceIoControl(hDevice, DIOC_SEND_COMMAND, &InBuf, sizeof(InBuf), Data + BlockQuantity*MAX_USB_BLOCK/sizeof(LONG), RestDataPoints/*bytes*/, &cbRet, NULL)) error = TRUE;
		LeaveCriticalSection(&cs);
	}

	if(error) { LastErrorNumber = 5; return FALSE; }
	return TRUE;
}



//------------------------------------------------------------------------------
// попробуем получить версию используемой Windows
//------------------------------------------------------------------------------
DWORD CRTUSB3000::GetWindowsVersion(void)
{
	OSVERSIONINFO osvi;

	ZeroMemory(&osvi, sizeof(OSVERSIONINFO));
	osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);

	if(!GetVersionEx(&osvi))
	{
		osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
		if(!GetVersionEx((OSVERSIONINFO *)&osvi)) return UNKNOWN_WINDOWS_VERSION;
	}

	switch (osvi.dwPlatformId)
	{
		case VER_PLATFORM_WIN32_NT:
			if(osvi.dwMajorVersion <= 4 ) return WINDOWS_NT; 		// WinNT
			else if((osvi.dwMajorVersion == 5) && (osvi.dwMinorVersion == 0)) return WINDOWS_2000; 			// Win2000
			else if((osvi.dwMajorVersion == 5) && (osvi.dwMinorVersion == 1)) return WINDOWS_XP; 				// WinXP
			else if((osvi.dwMajorVersion == 5) && (osvi.dwMinorVersion == 2)) return WINDOWS_2003_SERVER; 	// Win2003 Server
			else if(osvi.dwMajorVersion == 6) return WINDOWS_VISTA; 	// Vista
			else return UNKNOWN_WINDOWS_VERSION;

		case VER_PLATFORM_WIN32_WINDOWS:
			if((osvi.dwMajorVersion > 0x4) || ((osvi.dwMajorVersion == 0x4)&&(osvi.dwMinorVersion > 0x0))) return WINDOWS_98_OR_LATER; // Win98 or later
			else return WINDOWS_95; 											// Win95

		case VER_PLATFORM_WIN32s: return WINDOWS_32S; 					// Win32s
	}

	return UNKNOWN_WINDOWS_VERSION;
}

//------------------------------------------------------------------------
// вспомогательная функция
//------------------------------------------------------------------------
LONG *CRTUSB3000::Transform_Buffer(WORD PmPoints, LONG * const Buffer)
{
	for(WORD i=0; i < PmPoints; i++) Buffer[i] = ((Buffer[i] & 0x0000FFFF) << 8) | ((Buffer[i] & 0x00FF0000) >> 16);
	return Buffer;
}

//------------------------------------------------------------------------
// вспомогательная функция определения типа потокового ввода данных
// пока реализация следующая:
//    если в управляющей таблице обнаружен хоть один канал с ТТЛ вводом,
//    тогда модуль будет работать в режиме сбора данных только с ТТЛ линий
//------------------------------------------------------------------------
BOOL CRTUSB3000::FindoutInputType(RTUSB3000::INPUT_PARS * const ap)
{
	WORD i;

	ap->InputType = RTUSB3000::EMPTY_DATA;
	for(i = 0x0; i < ap->ChannelsQuantity; i++)
	{
		// ввод с цифровых линий
		if(ap->ControlTable[i] & 0x8)	{ ap->InputType |= RTUSB3000::TTL_DATA; break; }
		// ввод с АЦП
		else ap->InputType |= RTUSB3000::ADC_DATA;
	}
	return TRUE;
}

//------------------------------------------------------------------------
// вспомогательная функция формирования управляющей таблицы
// эта функция пока никак не используется :(((
//------------------------------------------------------------------------
#pragma argsused
BOOL CRTUSB3000::ControlTableCreation(RTUSB3000::INPUT_PARS * const ap)
{
	return TRUE;
}

//----------------------------------------------------------------
// вспомогательная функция для вычисление CRC16
//----------------------------------------------------------------
WORD CRTUSB3000::CalculateCrc16(BYTE *ptr, WORD NBytes)
{
	WORD w = 0x0;
	WORD i, j;

	for(i = 0x0; i < NBytes; i++)
	{
		w ^= (WORD)((WORD)(ptr[i]) << 0x8);
		for(j = 0x0; j < 8; j++)
		{
			if(w & (WORD)0x8000) w = (WORD)((w << 0x1) ^ 0x8005);
			else w <<= 0x1;
		}
	}
	return w;
}

