• Автор
    Тема
  • #122460
    no_img
    starostinalex
    Участник

    ZetLab ZET 220 с интерфейсом USB нужно использовать как многоканальный АЦП (количество подключенных каналов не менее 8). Я взял пример с сайта ZetLab Test_Zadc_C++ и немного его переделал:

    #include <windows.h> 
    #include <conio.h>
    #include <stdio.h>
    #include <iostream>
    #include <string>
    #include <sstream>
    #include "zadc_int.h"	
    
    using namespace std;
    
    int ErrorMessage(std::string MessageError)
    {
    	std::cout << MessageError << std::endl;
    	std::cin.get();
    	exit(EXIT_FAILURE);
    }
    
    template<typename T, long n>  
    inline long arraySize(const T(&arr)[n])
    {
    	return n;
    }
    
    template<typename T>
    inline string NumToStr(T num)
    {
    	ostringstream stream;
    	stream << num;
    	string str = stream.str();
    	return str;
    }
    
    int main(int argc, char* argv[])
    {
    	setlocale(LC_ALL, "");
    	long numChannelsOn[] = {1, 4, 15}; // 2, 5 и 16 каналы
    	//long numChannelsOn[] = {1, 15}; // программа работает правильно
    	long typeDevice = 16;						// Тип устройства
    	long numberDSP = 0;						// Порядковый номер сигнального процессора
    	long enable;							// Флаг поддерживается / не поддерживается
    	long numChannelsADC;					// Количество включенных каналов АЦП
    	long numWordsADC;						// Количество слов (по два байта) в одном отсчете АЦП
    	short *pBuffer16ADC = NULL;				// Указатель на начало буфера драйвера для АЦП с разрядностью не более 16 бит
    	long *pBuffer32ADC = NULL;				// Указатель на начало буфера драйвера для АЦП с разрядностью более 16 бит 
    	long sizeBufferADC;						// Размер буфера драйвера в словах
    	long pointerADC = 0;					// Указатель на текущий элемент заполнения буфера драйвера (кратен 2)
    	long pointerADC_old = 0;				// Предыдущее значение указателя на буфер драйвера
    	// Открытие устройства
    	if(ZOpen(typeDevice, numberDSP) != 0 ) 
    		ErrorMessage("Не удалось открыть АЦП");
    	// Проверка возможности АЦП	
    	if(ZGetEnableADC(typeDevice, numberDSP, &enable) != 0 && enable != 0) 
    		ErrorMessage("Устройство не может работать как АЦП");
    	std::cout << "Устройство может работать как АЦП"  << std::endl;
    		// Определение количества каналов	
    	if(ZGetQuantityChannelADC(typeDevice, numberDSP, &numChannelsADC) != 0)
    		ErrorMessage("Не удалось определить общее количество каналов: " + NumToStr(numChannelsADC));
    	std::cout << "АЦП имеет " +  NumToStr(numChannelsADC) + " каналов" << std::endl;
    	// Отключение всех каналов
      enable = 0;	
    	for(long i = 0; i < numChannelsADC; i++) 
    	{
    		if(ZSetInputADC(typeDevice, numberDSP, i, enable) != 0)
    			std::cout << "Не удалось отключить канал: " + NumToStr(i) << std::endl;
    	}
    	long NumChannelsOn = arraySize(numChannelsOn); // число включенных каналов
    	// включение нужных каналов	
      enable = 1;	
    	for(long i = 0; i < NumChannelsOn; i++)
    	{
    		if(ZSetInputADC(typeDevice, numberDSP, numChannelsOn[i], 1) != 0)
    			ErrorMessage("Не удалось включить канал: " + NumToStr(numChannelsOn[i]));
    		else
    		  std::cout << "Включен канал: " + NumToStr(numChannelsOn[i] + 1) << std::endl;
      }
    	// проверка правильности определения количества включенных каналов	
    	if(ZGetNumberInputADC(typeDevice, numberDSP, &numChannelsADC) != 0)
    		ErrorMessage("Не удалось определить количество включенных каналов");
    	if(NumChannelsOn != numChannelsADC)
    		ErrorMessage("Количество нужных каналов " + NumToStr(NumChannelsOn) + " не соответствует " 
    								                              + NumToStr(numChannelsADC) + " включенным каналам");
    	std::cout << "Количество заданных каналов "    + NumToStr(NumChannelsOn) + 
    							 " равно " + NumToStr(numChannelsADC) + " - результату выполнения функции ZGetNumberInputADC "  << std::endl;
    	double *ardAmpl = new double[NumChannelsOn];
    	for (long i = 0; i < NumChannelsOn; i++)
    	{
    		if (ZGetAmplifyADC(typeDevice, numberDSP, numChannelsOn[i], &ardAmpl[i]) != 0)
    			ErrorMessage("Не удалось определить усиление канала: " + NumToStr(numChannelsOn[i]));
    		else
    			std::cout << "Усиление канала " + NumToStr(numChannelsOn[i] + 1) + " равно " + NumToStr(ardAmpl[i]) << std::endl;
    	}
    	// Опрос веса младшего разряда АЦП включенных каналов устройства
    	double *ardResol = new double[numChannelsADC];
    	for (long i = 0; i < NumChannelsOn; i++)
    	{
    		if (ZGetDigitalResolChanADC(typeDevice, numberDSP, numChannelsOn[i], &ardResol[i]) != 0)
    			ErrorMessage("Не удалось определить разрешение канала: " + NumToStr(i));
    		else
    			std::cout << "Разрешение канала " + NumToStr(numChannelsOn[i] + 1) + " равно " + NumToStr(ardResol[i]) << std::endl;
    	}
    	// Опрос количества слов в одном отсчета АЦП устройства
    	if (ZGetWordsADC(typeDevice, numberDSP, &numWordsADC) != 0)
    		ErrorMessage("Не удалось определить количество слов в одном отсчете АЦП");
    	else
    		std::cout << "В одном отсчета АЦП " + NumToStr(numWordsADC) + " слова" << std::endl;
    	// Запрос буфера АЦП
    	if (ZGetBufferADC(typeDevice, numberDSP, (void**) &pBuffer16ADC, &sizeBufferADC) != 0)
    		ErrorMessage("Не удалось определить буфер устройства");
    	pBuffer32ADC = (long*) pBuffer16ADC;
    	std::cout << "Указатель на буфер pBuffer32ADC равен " + NumToStr(pBuffer32ADC) << std::endl;
    	 //Запрос на останов АЦП
    	if(ZStopADC(typeDevice, numberDSP) != 0)
    		ErrorMessage("Не удалось остановить устройство"); 
    	 //Запрос на запуск АЦП
      if(ZStartADC(typeDevice, numberDSP) != 0)
    		ErrorMessage("Не удалось запустить устройство");
    	double *volt = new double[numChannelsADC];
    	std::cout.setf(ios::fixed);
    	std::cout.width(10); // задает ширину поля
    	std::cout.precision(5);  // задает количество знаков после десятичной точки
    	// Цикл чтения данных АЦП
    	while(!_kbhit())
    	{
    		// Задержка обновления данных АЦП для отображения
    		Sleep(500);
    		// Запрос указателя на текущий элемент буфера
    		if(ZGetPointerADC(typeDevice, numberDSP, &pointerADC) != 0)
    			ErrorMessage("Не удалось получить указатель на буфер устройства");
    		// Если новые данные в буфер от АЦП не поступили, то перейти в начало цикла и подождать
    		if(pointerADC == pointerADC_old)
    			continue;
    		// Обновление предыдущего значения указателя
    		pointerADC_old = pointerADC;
    		//Переход на отсчет первого включенного канала последнего кадра АЦП
    		if(pointerADC - numWordsADC * numChannelsADC < 0)
    			pointerADC = sizeBufferADC + pointerADC - numWordsADC * numChannelsADC;
    		else 
    			pointerADC = pointerADC - numWordsADC * numChannelsADC; 
    		volt[0] = ardResol[0] * (pBuffer32ADC[pointerADC / numWordsADC]) / ardAmpl[0];
    		cout << volt[0] << endl;
    		for(long i = 1; i < NumChannelsOn; i++)
    		{ 
    			//Переход на следующий отсчет АЦП
    			pointerADC += numWordsADC;
    			//При выходе за границу буфера, осуществляется переход в начало
         
    			if(pointerADC >= sizeBufferADC)
    				pointerADC = pointerADC - sizeBufferADC;
    			volt[i] = ardResol[i] * (pBuffer32ADC[pointerADC / numWordsADC]) / ardAmpl[i];
    			cout << volt[i] << endl;
    		} // for
    		cout << " ---------------------------------------------" << endl;
    	} // while
    	// Запрос на останов АЦП
    	if (ZStopADC(typeDevice, numberDSP) != 0)
    		ErrorMessage("Не удалось остановить устройство");
    	// Запрос на освобождение буфера АЦП
    	if(pBuffer16ADC != NULL)
    	{
    		if(ZRemBufferADC(typeDevice, numberDSP, (void**) &pBuffer16ADC) != 0)
    			ErrorMessage("Не удалось освободить буфер устройства");
    	}
    	// Отключение от драйвера устройства
    	if(ZClose(typeDevice, numberDSP) != 0)
    			ErrorMessage("Не удалось отключить устройство");
    	delete [] volt;
    	delete [] ardResol;
    	delete [] ardAmpl;
    
    	if(!_kbhit())
    		_getch();
    
    	std::cout << "Press any key for exit..." << std::endl;
    	_getch();
    
     	return 0;
    }

    Проверку правильности работы программы я сделал на 3 каналах: второй канал подключил к одной батарейке (1,5 В), 5 и 16 каналы — соотвественно к 2 и 4 батарейкам (3 и 6 В). Значения напряжение определяются правильно, но вывод значений не соответствует порядку каналов:

    6.53385
    1.45083
    3.19009
     ---------------------------------------------
    3.20306
    6.51513
    1.44911
     ---------------------------------------------
    1.45500
    3.19388
    6.50739
     ---------------------------------------------
    6.53386
    1.45084
    3.19009
     ---------------------------------------------
    3.20306
    6.51515
    1.44911
     ---------------------------------------------
    1.45500
    3.19388
    6.50740

    Мне нужно строить графики и при таком выводе это невозможно. Если использовать только 2 канала (2 и 15) вместо трех, то вывод идет нормально:

    1.45499
    6.47789
     ---------------------------------------------
    1.45494
    6.47786
     ---------------------------------------------
    1.45501
    6.47784
     ---------------------------------------------
    1.45497
    6.47788

    Подскажите, пожалуйста, как исправить программу, чтобы при количестве каналов более двух порядок вывода значений напряжения был правильным.

  • Автор
    Ответы
  • #122461

    Добрый день!
    Проблема в том, что значение pointerADC не обязательно кратно количеству каналов, т.е. после запроса текущего значения pointerADC нужно понять на значение какого из каналов он указывает: 1, 2 или 3. При двух каналах все хорошо, потому что значение pointerADC всегда кратно 2.

    #122473
    no_img
    starostinalex
    Участник

    «После запроса текущего значения pointerADC нужно понять на значение какого из каналов он указывает: 1, 2 или 3».
    А как это понять? Что нужно сделать в программе?

    #122482

    Для реализации этого алгоритма нужно ввести свое значение PointerADC и читать по нему. Такой пример:
    1) PointerAdc_Real = 0, PointerAdc_User = 0
    2) PointerAdc_Real = 4 => PointerAdc_User = 3.
    3) PointerAdc_Real = 8 => PointerAdc_User = 6
    4) PointerAdc_Real = 12 => PointerAdc_User = 12
    На первом шаге оба указателя равны 0. На всех последующих шагах указатель в буфере изменился, но свой указатель можно изменить только на число, кратное количеству каналов.

    #122504
    no_img
    starostinalex
    Участник

    Я плохо понял Ваше объяснение. Где нужно ввести свое значение pointerADC (в цикле while или где-нибудь еще) и как оно будет связано с получением указателя функцией ZGetPointerADC? Указатель Real — это получение указателя функцией ZGetPointerADC? Почему такие соотношения чисел для real и user? Хорошо бы Вы пояснили это в коде. Я разместил проект на яндекс-диске https://yadi.sk/d/UEpUEY6aeM2A9g

    #122507

    В программе происходит чтение значения PointerADC, т.е. того индекса в буфере драйвера, куда будут копироваться драйвером вновь пришедшие данные от АЦП. Соответственно все данные до этого индекса — это те данные, которые от АЦП уже пришли. Буфер циклический, поэтому данные все время будут затираться, но поскольку размер буфера довольно большой, то программа успеет их скопировать и обработать.

    АЦП присылает данные порциями, которые могут быть не кратны количеству каналов, поэтому в общем случае текущее значение PointerADC может не указывать на отсчет по первому включенному каналу. Именно для этого pointerADC нужно выравнивать на количество включенных каналов. А это значит, что в программе нужно завести еще одну переменную (PointerAdc_User) и в ней хранить это выравненное значение и читать данные именно по нему.

    В приведенном выше примере PointerAdc_Real — это то, что вернула функция, PointerAdc_User — это пересчитанное значение. После старта АЦП обе переменные (user и real) равны 0, т.е. никаких данных в буфер еще не поступало. Теперь предположим, что Real стал равен 4, а следовательно мы должны пересчитать User относительно того, что у нас включено 3 канала, поэтому User у нас становится равным 3 и читать из буфера мы будем именно относительно User, а не относительно Real. И так далее.

    Помощь в написании кода программы доступна только при запросе по электронной почте info@zetlab.com при наличии лицензии ZETLAB STUDIO.

    #122541
    no_img
    starostinalex
    Участник

    Из описания я не понял, с помощью ZETLAB STUDIO можно работать с любыми приборами или только с приборами фирмы ZETLAB. Исходя из цены, можно предположить, что только с приборами фирмы ZetLab. Я правильно понял, что студия может работать только с приборами фирмы Zetlab?

    #122555

    При использования библиотеки Zadc.dll, ZETLAB STUDIO поддерживает только устройства производства ZETLAB. А любые графические элементы можно использовать с устройствами сторонних производителей.

    #122568
    no_img
    starostinalex
    Участник

    Спасибо за ответ, понятно.

Для ответа в этой теме необходимо авторизоваться.

Авторизация
*
*

Потеряли пароль?

Политика конфиденциальности персональных данных

Регистрация
*
*
*

Политика конфиденциальности персональных данных

Генерация пароля