Общий подход к использованию интерфейсных функций {#sect_gen_descr}
==============================================================
  Отличия при работе с модулям @lcard_modules {#sect_modules_diff}
  ==============================================================
  Отличие возможностей модулей  {#sect_modules_diff_features}
  -------------------------------------------
  Функциональные возможности модулей @lcard_modules очень схожи, однако есть существенные отличая.
  1. Основное отличие заключается в использованных интерфейсах --- PCI-Express для L-502 и USB или Ethernet для E-502 и E16. В связи с этим несколько отличается процедура установки связи с устройством. Кроме того, работа по интерфейсу Ethernet требует настройки дополнительных параметров.
  2. В E-502 используется дополнительно контроллер ARM Cortex-M4 для реализации логики интерфейсов (USB/Ethernet). У данного контроллера есть своя прошивка, которую можно обновлять и в которой могут быть реализованы дополнительные возможности (в первую очередь при работе через Ethernet). Рекомендуется всегда использовать последнюю версию прошивки, которую можно скачать по адресу https://lcard.ru/e502-m4.bin и обновить с помощью программы [X502Studio](https://gitlab.com/l-card/acq/devices/x502/x502studio). Кроме того, теоретически возможно создание пользователем своей прошивки для данного контроллера, так как "Л Кард" предоставляет исходные коды данной прошивки (git-репозиторий с исходниками прошивки можно найти по адресу https://gitlab.com/l-card/acq/devices/x502/e502/firmware/e502_m4), однако какого-либо руководства по данной прошивке "Л Кард" для этого не предоставляет. Также возможно рассмотрение предложений по заказу на доработку прошивки ARM Cortex-M4 под нужные пользователю задачи.
  3. Существует ограничение скорости передачи данных для E-502. Если L-502 позволяет одновременно осуществить ввод и вывод всех данных с аналоговых каналов и цифровых линий на максимальной скорости, то в модуле E-502 есть ограничения, которые зависят от используемого интерфейса:
    - при работе по USB суммарная максимальная скорость передачи составляет порядка 5 млн. отсчетов в секунду.  Т.е. допустимо использовать например ввод с АЦП и цифровых линий на 2 МГц и при этом вывод только на один канал ЦАП/DOUT на 1 МГц, или на два на 500 КГц. Если идет ввод только с АЦП на 2 МГц, то возможно использовать все три канала вывода на 1 МГц и т.д.       
      При этом это ограничение обусловлено ограничением скорости интерфейса между контроллером ARM Cortex-M4 и ПЛИС, а не самим интерфейсом USB.
    - при работе по Ethernet (TCP/IP) максимальная скорость уже ограничена скоростью передачи по сети по протоколу TCP. При работе модуля только на ввод скорость ограничена 2.5 млн. отсчетов в секунду. Скорость на вывод и влияние ее на скорость ввода будет уточнена в дальнейшем. Для вывода рекомендуется по возможности использовать циклический режим вывода. Следует также учитывать загрузку самой сети при передаче данных по Ethernet, так как она может сильно влиять на максимальную скорость передачи.
  4. В связи с ограничением скорости передачи в E-502 поддерживается возможность задать общий делитель для частоты вывода , который можно задать с помощью X502_SetOutFreqDivider() или X502_SetOutFreq(). В L-502 также была введена данная возможность в версии прошивки ПЛИС 0.5, таким образом для ее использования может быть необходимо обновить прошивку.
  5. При циклическом выводе в L-502 буфер находится в драйвере на ПК, а в E-502 хранение циклического буфера реализовано внутри памяти контроллера ARM Cortex-M4 модуля, что позволяет избежать загрузки интерфейса и ПК во время работы, однако приводит к ограничению размера буфера. Т.е. при работе по сети циклический вывод не влияет на ограничение интерфейса, однако ограничение в суммарную скорость в 5 млн. отсчетов в секунду для модуля сохраняется. Для версии прошивки ARM ниже 1.0.3 буфер вывода в 3 млн. отсчетов (суммарно на все каналы) делился на 2 равные части (одна используется для вывода сигнала, другая для загрузки следующего, чтобы можно было сменить сигнал без останова предыдущего), поэтому один сигнал ограничен  в 1.5 млн. отсчетов. Начиная с 1.0.3 буфер может делится произвольно (в зависимости от размера загружаемого сигнала), поэтому размер сигнала ограничен значением --- 3 млн. отсчетов минус размер выдаваемого сейчас сигнала (0 --- если при загрузке не идет генерация предыдущего). Таким образом, если не используется возможность смены сигнала на лету (т.е. всегда после X502_OutCycleSetup() идет X502_OutCycleStop() или X502_StreamsStop() до следующей загрузки сигнала), то можно для одного сигнала использовать все 3 млн. отсчетов.  Также следует учитывать, что передача сигнала в ARM контроллер занимает время и при необходимости выдачи сигнала одновременно с запуском сбора необходимо дождаться загрузки сигнала, что можно сделать например с помощью флага #X502_OUT_CYCLE_FLAGS_WAIT_DONE в X502_OutCycleSetup(). 
  6. При работе по интерфейсу Ethernet не реализована функция X502_GetSendReadyCount(), а функция X502_GetRecvReadyCount() гарантировано работает только под ОС Windows.
  7. При чтении значений цифровых входов для E-502 старшие линии DI14, DI15, DI16 объединены с линиями синхронизации DI_SYN2, CONV_IN и START_IN соответственно. При этом, так как в обоих модулях линии 17 и 18 объединены с DI_SYN1 и DI_SYN2, то значение 18-ой и 14-ой линии для E-502 всегда одинаковы.
  8. Настройки подтяжек для цифровых входов в E-502 отличаются от L-502 (см. описание типа #t_x502_pullups)
  9. В E-502 используется другая микросхема ЦАП, которая также планируется к использованию в последующих ревизиях модуля L-502.
  10. E16 по цоколевке сигнальных разъемов и диапазонам измерений входного сигнала это продолжение линейки модулей [E14](https://www.lcard.ru/products/external/e-440). API верхнего уровня сделано совместимым с e502api. Для задания частоты работы ЦАП и АЦП используется опорная частота 48 Мгц. Есть режим совместимости с модулем E14-440 и работы с [E14-440 API](https://www.lcard.ru/products/external/e-440?qt-ltab=6). В качестве интерфейсного процессора используется контроллер WCH32V307. Cигнальные разъемы по цоколевке несовместимы с E502!
  11. В E16 как и в Е14 реализован старт сбора по уровню аналогового сигнала - выше/ниже уровня, при пересечении границы уровня снизу-вверх/сверху-вниз.
  12. Идентификатор USB устройства для E16 (VID_2A52&PID_0E16) отличается от E502 и E14, поэтому в некоторых случаях придется установить новые драйвера.
  13. В E16 для циклического вывода (вывода из внутреннего буфера без внешней подкачки), доступен один буфер размером 11264 слов (в новых прошивках кол-во может измениться).
  14. В отличие от E502, в E16 нет выделенных выходов синхронизации START_OUT и CONV_OUT, но их роль могут исполнять выходы TRIG или INT, которые можно переключать на вход или на выход, при конфигурации "на вход" эти контакты можно использовать как START_IN или CONV_IN.
  15. Контакты 17 и 18 цифрового разъёма сделаны унифицированными, по умолчанию на них выведены +3,3В и GND как в Е14-440, при подаче команды включения реле (см. #t_x502_pullups) на эти выводы выводится +15 и -15В как в Е14-140.
 
 
  Общие и специализированные функции для работы с модулем {#sect_modules_diff_functions}
  ----------------------------------------------------------------
 В связи с тем, что большая часть функциональности модулей @lcard_modules совпадают, то большинство функций реализовано общими для обоих модулей. Все общие функции реализованы в библиотеке \filename{x502api}. При этом названия функций и констант начинаются с X502_, а типов с t_x502_. Соответственно, для работы с обоими модулями используется один и тот же тип описателя модуля #t_x502_hnd. 
 
 Основное отличие при работе, зависящее от интерфейса связи с модулем, заключается в процедуре установки самой связи. Эти функции реализованы в отдельных библиотеках \filename{l502api} и \filename{e502api} для модуля @lcard_modules соответственно. При установки связи, вся необходимая информация о том, как работать с модулем по нужному интерфейсу, сохраняется внутри непрозрачного описателя модуля, а пользователь после открытия связи может работать с модулем одинаково с использованием одних и тех же общих функций из \filename{x502api} независимо от типа модуля и используемого интерфейса. 
 
 Также в отдельную группу специализированных функций выделены функции по настройке интерфейса Ethernet, которые реализованы только в \filename{e502api} и описаны в разделе @ref func_eth_config.
 
  Совместимость проектов, разработанных до введения библиотеки x502api {#sect_l502_comapt}
  ----------------------------------------------------------------
  Для реализации полной совместимости с проектами, работающими только с модулем L-502 и разработанными до введения поддержки работы с модулем E-502 (которая введена с версии 1.1.0) и создания общей библиотеки, в \filename{l502api} реализованы функции из данной библиотеки предыдущих версий (1.0.x). При этом эти объявления этих функций и соответсвующих типов вынесены в отдельный файл "l502api_compat.h", который включается из "l502api.h" для сохранения совместимости. Эти типы определены через общие типы из "x502api.h", а функции реально только вызывают аналогичные общие функции из \filename{x502api}. Соответственно, проекты, разработанные для предыдущей версии без учета общих функций, должны корректно собираться и при новой версии библиотек. Главное отличие, которое следует учитывать, что если эти проекты распространяются с новой версией \filename{l502api}, то необходимо распространять и библиотеку \filename{x502api}, так как ее функции используются функциями новой версии \filename{l502api}.
  
  Так как данные функции и типы полностью повторяют большинство обобщенных функций (за исключением префиксов в названиях), то они не приводятся в данном документе.
  
  
  Общий алгоритм для работы с модулем {#sect_gen_alg}
  ============================================================== 
   Данный раздел описывает типичную последовательность вызова функций для работы с модулями @lcard_modules. Более подробно каждый шаг будет описан в последующих разделах.

Типичная последовательность вызовов имеет следующий вид:
 1. При работе с модулями по интерфейсам PCI-Express или USB получить список серийных номеров с помощью функций L502_GetSerialList() и E502_UsbGetSerialList(), соответственно, или получить список записей о модулях с помощью L502_GetDevRecordsList() и E502_UsbGetDevRecordsList(). При работе по Ethernet можно использовать функции обнаружения устройств в локальной сети, описанные в главе @ref sect_eth_browsing, или, если известен IP-адрес устройства, перейти сразу к пункту 2.
 2. Если в системе присутствует нужный модуль, создать описатель модуля с помощью X502_Create().
 3. Установить соединение с модулем. При использовании записей о устройстве открытие всегда выполняется с помощью X502_OpenByDevRecord(). При использовании серийного номера используются L502_Open() или E502_OpenUsb() для L-502 и E-502, подключенного по USB, соответственно. Чтобы установить связь с модулем по Ethernet с использованием IP-адреса, необходимо использовать функцию E502_OpenByIpAddr(). 
 4. При необходимости, получить дополнительную информацию о устройстве с помощью X502_GetDevInfo() (в частности для проверки наличия сигнально процессора BlackFin).
 5. При наличии сигнального процессора (и желании работать с его использованием) загрузить прошивку сигнального процессора с помощью X502_BfLoadFirmware().
 6. Установка параметров модуля с помощью [набора функций для изменения настроек модуля](@ref func_config) (названия функций начинаются с X502_Set)
 7. Передача установленных параметров в модуль с помощью X502_Configure().
 8. Работа с модулем в синхронном и/или асинхронном режиме (описана в последующих подразделах).
 9. Закрытие модуля X502_Close().
 10. Освобождение описателя модуля функцией X502_Free().
 
 
 Работа с модулем при синхронном вводе {#sect_gen_sync_in}
 ----------------------------------------------------------
 
 Типичная работа с модулем при синхронном вводе состоит из следующих шагов:
 1. Разрешение нужных синхронных потоков (АЦП и/или цифровых данных) с помощью X502_StreamsEnable().
 2. Запуск синхронных потоков X502_StreamsStart().
 3. Чтение принятых данных из модуля с помощью X502_Recv().
 4. Обработка прочитанных данных с помощью X502_ProcessData(), X502_ProcessAdcData() или X502_ProcessDataWithUserExt().
 5. При необходимости приема и обработки следующего блока, переход к пункту 3.
 6. Останов синхронных потоков с помощью X502_StreamsStop().
 
 
 Работа с модулем при синхронном потоковом выводе {#sect_gen_sync_out}
 --------------------------------------------------------------
 
 Типичная работа с модулем при синхронном выводе состоит из следующих шагов:
 1. Установка начальных значений для ЦАП с помощью асинхронного вывода X502_AsyncOutDac().
 2. Разрешение нужных синхронных потоков (каналы ЦАП, цифровые выходы) с помощью X502_StreamsEnable().
 3. Запуск предварительной загрузки данных на вывод с помощью X502_PreloadStart().
 4. Подготовка блока данных на запись с помощью X502_PrepareData().
 5. Запись подготовленного блока в модуль с помощью X502_Send().
 6. При необходимости повторить пункты 4. и 5. нужное количество раз. При этом общий размер предварительно загруженных данных не должен превысить размер буфера (по умолчанию 9 МСлов)
 7. Запуск синхронных потоков вызовом X502_StreamsStart().
 8. Каждый раз при необходимости подгрузить новые данные в буфер выполнить пункты 4. и 5.
 9. По завершению работы выполнить останов синхронных потоков с помощью X502_StreamsStop().
 
 Работа с модулем при циклическом выводе {#sect_gen_cycle_out}
 -----------------------------------------------------------------
 Для выставления циклического сигнала без подкачки типичная последовательность выглядит так:
 1. Установка начальных значений для ЦАП с помощью асинхронного вывода X502_AsyncOutDac().
 2. Разрешение нужных синхронных потоков (каналы ЦАП, цифровые выходы) с помощью X502_StreamsEnable().
 3. Выделение циклического буфера указанного размера с помощью X502_OutCycleLoadStart().
 4. Загрузка данных указанного размера для циклического вывода с помощью одного или нескольких вызовов X502_Send().
 5. Сделать загруженный сигнал активным с помощью X502_OutCycleSetup() с флагом #X502_OUT_CYCLE_FLAGS_WAIT_DONE.
 6. Запустить синхронный ввод-вывод через X502_StreamsStart().
 7. При необходимости вывести новый сигнал выполнить шаги 3.-5.
 8. По завершению работы остановить синхронный ввод-вывод с помощью X502_StreamsStop() или только циклический вывод через X502_OutCycleStop().

 
 Работа с модулем при асинхронном вводе-выводе {#sect_gen_async}
 -----------------------------------------------------------------
 
 Типичная работа при асинхронном вводе-выводе состоит из вызова одной из функций:
 - X502_AsyncInDig() - асинхронный ввод значений цифровых линий
 - X502_AsyncOutDig() - асинхронный вывод значений на цифровые линии
 - X502_AsyncOutDac() - асинхронный вывод значения на один из каналов ЦАП
 - X502_AsyncGetAdcFrame() - асинхронный прием одного кадра АЦП
 
 
 
 
  Создание и освобождение описателя модуля {#sect_descr}
  ==============================================================
   
  Вся работа с модулями @lcard_modules осуществляется через описатель модуля типа #t_x502_hnd. Описатель модуля представляет собой непрозрачный указатель на структуру, которая хранит всю информацию о модуле и состоянии соединения с ним. Пользователь не имеет прямого доступа к полям структуры и все действия с модулем выполняются посредством вызова соответствующих функций библиотеки, которые принимают описатель модуля в качестве первого параметра.

  Перед попыткой установить связь с модулем необходимо создать описатель, вызвав функцию X502_Create(), которая выделяет память под структуру, инициализирует ее поля значениями по умолчанию и возвращает указатель на нее --- описатель модуля.

  После того как работа с модулем завершена, выделенная функцией X502_Create() память должна быть освобождена посредством вызова X502_Free(). После освобождения описатель уже не может использоваться.
  
  Открытие связи с модулем {#sect_open}
  ==============================================================
 Общее описание процедуры установки связи с модулем  {#sect_open_gen}
----------------------------------------------------------------------
 
Для работы с модулем необходимо установить с ним соединение с использованием ранее созданного описателя.

Функции для установки соединения с модулем зависят от используемого интерфейса и описаны в последующих подразделах.

Если функция открытия связи с модулем выполнена успешно, то соединение с модулем установлено и дальше можно использовать любые функции управления модулем.  По завершению работы с модулем необходимо закрыть соединение с помощью X502_Close().

Если же функция открытия связи вернула ошибку, то это может соответствовать одной из следующих ситуаций:
 - Связь с модулем не удалось установить, т.к. модуль не найден или не отвечает корректно на запросы. В этом случае по завершению вызова функции описатель модуля остается в исходном состоянии, не связанном с каким-либо соединением и его можно использовать только для открытия нового соединения.
 - Связь с модулем установлена, но обнаружены проблемы в работе модуля. В этом случае соединение с модулем все равно остается открытым,  однако большая часть функций работы с модулем не доступна. 

 При необходимости различить эти два разных состояния можно с помощью функции X502_IsOpened(), которая проверяет, открыто ли соединение с модулем в данный момент для указанного описателя. В большинстве случаев явно различать эти ситуации не требуется и достаточно вызвать функцию X502_Close(), которая закроет соединение, если оно не закрыто, при любой ошибке открытия соединения. Если функцию закрытия не вызывать по ошибке открытия соединения, то соединение может остаться открытым до освобождения памяти описателя с помощью X502_Free() или до повторного открытия соединения с помощью данного описателя.
 
 Соединение может оставаться открытым при следующих ошибках:
  - #X502_ERR_FPGA_NOT_LOADED --- прошивка ПЛИС не была успешно загружена и ПЛИС находится в нерабочем состоянии. Ошибка актуальна только для модуля E-502, т.к. в модуле L-502 в ПЛИС реализован сам интерфейс PCI-Express и без загрузки прошивки ПЛИС модуль не будет даже определен в системе. В модуле E-502 прошивка ПЛИС загружается микроконтроллером   из Flash-памяти при старте модуля. Ошибка может возникать, если повреждена информация во Flash-памяти или по каким-то причинам возникла ошибка при ее чтении из Flash-памяти или при ее загрузке в ПЛИС. В данном режиме доступны только функции работы с Flash-памятью, функции управления сетевыми настройками модуля и функция перезагрузки прошивки ПЛИС (E502_ReloadFPGA()).
  - #X502_ERR_REF_FREQ_NOT_LOCKED --- ошибка захвата опорной частоты синхронизации, идущей от гальваноотвязанной части управления вводом-выводом. Хотя доступ к Flash-памяти и регистрам ПЛИС может работать нормально, любые функции ввода-вывода аналоговых или цифровых данных не могут быть осуществлены при данной ошибке.
 
 
 
Установка связи с модулем L-502 по интерфейсу PCI-Express  {#sect_open_pci}
----------------------------------------------------------------------
  
  Для начала работы с модулем необходимо установить с ним связь с помощью функции L502_Open(). Для различия модулей используется их серийные номера.

  Получить список серийных номеров всех найденных модулей L-502 можно с помощью функции L502_GetSerialList(). Данная функция принимает плоский массив, в который будут сохранены найденные серийные номера, и максимальное количество серийных номеров, которое можно сохранить в переданный массив. 
  
  В простейшем случае можно задать максимальное значение модулей и для серийных номеров использовать статически выделенный массив: 
  
  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~{.c}
  #define MAX_MODULES_CNT  16
  
  char serial_list[MAX_MODULES_CNT][X502_SERIAL_SIZE];
  int32_t get_list_res = L502_GetSerialList(serial_list, MAX_MODULES_CNT, 0, NULL);
  if (get_list_res<0) {
      /* Ошибка получения списка серийных номеров */
  } else if (get_list_res==0) {
  	   /* Не найдено ни одного модуля */
  }  else  {
  	   /* Найдено get_list_res модулей */  	   
  }  
  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

  В общем случае для работы с произвольным максимальным количеством модулей можно воспользоваться третьим параметром функции для получения количества найденных модулей в системе. При этом в качестве массива серийных номеров можно передать нулевой указатель и указать нулевой размер массива. После этого можно динамически выделить массив под полученное количество серийных номеров и повторно вызвать L502_GetSerialList() для получения серийных номеров всех модулей L-502:
  
   ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~{.c}
   uint32_t dev_cnt;
   int32_t res;
   
   /* Получаем количество модулей в системе */
   res  = L502_GetSerialList(NULL, 0, 0, &dev_cnt);
   if (res<0)  {
   		/* Ошибка получения списка серийных номеров */
   }  else if (dev_cnt==0)  {
   		/* Не найдено ни одного модуля */
   }  else {
       /* Выделяем плоский массив под dev_cnt серийных номеров размером 
          dev_cnt*L502_SERIAL_SIZE */
       t_x502_serial_list serial_list=
              (t_x502_serial_list)
              malloc(dev_cnt*X502_SERIAL_SIZE);
       if (serial_list == NULL)  {
       		/* Ошибка выделения памяти */
       }  else  {
       		res = L502_GetSerialList(serial_list, dev_cnt, 0,
       		                         NULL);
       		if (res>0) {
           		/* Получено res серийных номеров */
          	}
          	/* Освобождаем выделенный массив под серийные номера */
          	free(serial_list);
       }
   }
   ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

  Следует отметить, что с одним модулем одновременно может быть установлено только одно соединение. При попытке открыть модуль, с которым уже установлено соединение через другой описатель (возможно, в другой программе) L502_Open() вернет #X502_ERR_DEVICE_ACCESS_DENIED. При этом L502_GetSerialList() по умолчанию возвращает список всех серийных номеров модуля, включая те, с которыми уже установлено соединение. Если нужно получить список только тех устройств, с которыми еще не установлено соединения, то в L502_GetSerialList() можно передать флаг #X502_GETDEVS_FLAGS_ONLY_NOT_OPENED.

  Если в качестве серийного номера в L502_Open() передать нулевой указатель или пустую строку, то будет предпринята попытка открыть первый модуль, с которым удастся успешно установить соединение. Если ни с одним модулем установить соединение не удалось, то будет возвращена ошибка, полученная при попытки открыть последний модуль. То есть, при наличии двух модулей L-502 в системе, первый вызов L502_Open() установит соединение с первым модулем L-502, второй вызов - со вторым, а третий вернет уже ошибку доступа  #X502_ERR_DEVICE_ACCESS_DENIED.
  
Установка связи с модулем E-502 по интерфейсу USB {#sect_open_usb}
----------------------------------------------------------------------  
При работе по USB алгоритм установления связи абсолютно аналогичен открытию модуля L-502, с той разницей, что для получения списка серийных номеров используется функция E502_UsbGetSerialList(), а для открытия модуля E-502 по серийному номеру используется E502_OpenUsb().

Установка связи с модулем E-502 по интерфейсу Ethernet {#sect_open_eth}
------------------------------------------------------------------------
Установить связь с модулем по Ethernet можно как по явно заданному IP-адресу устройства, так и воспользоваться функциями обнаружения устройств в локальной сети, подробно описанными в главе @ref sect_eth_browsing.

При установке связи по IP-адресу достаточно вызвать функцию E502_OpenByIpAddr().

Следует отметить, что в отличие от других интерфейсов, для корректной работы по Ethernet должны быть настроены необходимые параметры, о чем описано в главе @ref sect_eth_config.


Установка связи с модулями с использованием записей о устройстве {#sect_open_devrec}
-------------------------------------------------------------
Одним из минусов функций открытия по серийному номеру является то, что в момент открытия необходимо знать, устройству, подключенному по какому интерфейсу, соответствует данный номер, чтобы выбрать соответствующую функцию для открытия устройства. Для избежания этого введен специальный тип #t_x502_devrec, соответствующий записи о найденном устройстве. Данный тип содержит информацию о найденном устройстве (название, серийный номер, интерфейс, флаги с поддерживающимися возможностями и т.п.), а также содержит всю необходимую информацию о том, как установить связь и работать с соответствующим устройством.  Соответственно, от устройства и интерфейса зависят только функции получения записей о устройстве, а установка связи осуществляется общей функцией X502_OpenByDevRecord().

Это позволяет получить все необходимые записи на отдельном этапе и сохранить их в общий массив, а в дальнейшем уже выполнить открытие связи по нужным записям, не разбирая отдельно, что это за устройство и по какому интерфейсу подключено. 

Особенностью является необходимость пользователю очищать память, выделенную на этапе инициализации записи об устройстве. Очистка памяти выполняется с помощью X502_FreeDevRecordList(). Запись об устройстве используется исключительно внутри X502_OpenByDevRecord() и при желании может быть освобождена сразу после вызова данной функции, если список записей больше не нужен. Также следует учесть, что перед тем, как проинициализированную ранее запись передавать в функцию для получения или инициализации новой записи (например для обновления списка), необходимо ее сперва очистить для избежания утечек памяти.

Доступны следующие функции для инициализации записей:
    - L502_GetDevRecordsList() инициализирует записи, соответствующие подключенным модулям L-502 по интерфейсу PCI-Express. По параметрам и использованию аналогична L502_GetSerialList() с учетом необходимости освобождать записи.
    - E502_UsbGetDevRecordsList() инициализирует записи, соответствующие подключенным модулям E-502 по интерфейсу USB. По параметрам и использованию аналогична E502_UsbGetSerialList() с учетом необходимости освобождать записи. 
    - E502_MakeDevRecordByIpAddr() инициализирует запись для установления соединения с модулем E-502 с заданным адресом по интерфейсу Ethernet. 
    - E502_MakeDevRecordByEthSvc() инициализирует запись для установления соединения с модулем E-502, соответствующем автоматически обнаруженному сервису, по интерфейсу Ethernet
    

Ниже приведен пример, который создает записи для всех найденных модулей, подключенных по интерфейсам USB и PCI-Express. Кроме того, предполагается, что в массиве <B> ip_addr_list </B> содержится <B> ip_cnt </B> адресов, для которых также создаются записи, а определение <B> TCP_CONNECTION_TOUT </B> задает таймаут в мс на подключение по сетевому интерфейсу. Затем предоставляется выбор нужного устройства, после чего с ним устанавливается связь и все записи после этого очищаются.

   ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~{.c}
    uint32_t ip_addr_list[] = { .... } ; /* список ip-адресов устройств  */
    uint32_t ip_cnt = ... ; /* размер этого списка */
    
    uint32_t pci_devcnt = 0; 
    uint32_t usb_devcnt = 0;
    int32_t fnd_devcnt = 0; /* общее кол-во найденных записей */
    t_x502_devrec *devrec_list = NULL; /* список записей о устройствах */
    t_x502_hnd hnd = NULL; /* описатель открытого устройства */

    /* получаем количество подключенных устройств по интерфейсам PCI и USB */
    L502_GetDevRecordsList(NULL, 0, 0, &pci_devcnt);
    E502_UsbGetDevRecordsList(NULL, 0, 0, &usb_devcnt);

    if ((pci_devcnt+usb_devcnt + ip_cnt) != 0) {
        /* выделяем память для массива для сохранения найденного 
            количества записей */
        devrec_list = malloc((pci_devcnt + usb_devcnt + ip_cnt) * 
                              sizeof(t_x502_devrec));

        if (devrec_list != NULL) {
            unsigned i;
            /* получаем записи о модулях L-502, но не больше pci_devcnt */
            if (pci_devcnt!=0) {
                int32_t res = L502_GetDevRecordsList(&devrec_list[fnd_devcnt],
                                                     pci_devcnt, 0, NULL);
                if (res >= 0) {
                    fnd_devcnt += res;
                }
            }
            /* добавляем записи о модулях E-502, подключенных по USB, в конец массива */
            if (usb_devcnt!=0) {
                int32_t res = E502_UsbGetDevRecordsList(&devrec_list[fnd_devcnt],
                                                        usb_devcnt, 0, NULL);
                if (res >= 0) {
                    fnd_devcnt += res;
                }
            }

            /* создаем записи для переданного массива ip-адресов */
            for (i=0; i < ip_cnt; i++) {
                if (E502_MakeDevRecordByIpAddr(&devrec_list[fnd_devcnt], 
                                               ip_addr_list[i], 0, 
                                               TCP_CONNECTION_TOUT) == X502_ERR_OK) {
                    fnd_devcnt++;
                }
            }
        }
    }

    if (fnd_devcnt != 0) {
      uint32_t dev_ind; 
      
      /* обработка списка и выбор нужного устройства, индекс которого 
        для примера сохраняется в dev_ind */
       .....
       
       
        if ( <устройство выбрано> )
            hnd = X502_Create();
            if (hnd==NULL) {
                /* Ошибка создания описателя модуля! */
            } else {
                /* устанавливаем связь с модулем по записи */
                int32_t err = X502_OpenByDevRecord(hnd, &devrec_list[dev_ind]);
                if (err != X502_ERR_OK) {
                    /* ошибка установления соединения */
                    X502_Free(hnd);
                    hnd = NULL;
                }
            }
        }

        /* освобождение ресурсов действительных записей из списка */
        X502_FreeDevRecordList(devrec_list, fnd_devcnt);
    }
    /* очистка памяти самого массива */
    free(devrec_list);
    
    
    if (hnd != NULL) {
        /* работа с модулем */

        X502_Close(hnd);
        X502_Free(hnd);
    }
 
   ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

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



  Режимы работы с сигнальным процессором и без него {#sect_modes}
  ==============================================================
  
  Модули L-502 и E-502 могут работать в двух режимах:
  - В штатном режиме (#X502_MODE_FPGA) вся обработка данных выполняется аппаратно в ПЛИС модуля, а управление модулем осуществляется путем прямой записи значений в регистры ПЛИС. В этом режиме доступны все штатные функции сбора данных, однако у пользователя нет возможности расширить функциональные возможности самого модуля. Этот режим доступен для всех модификаций L-502 и E-502.
  - В режим работы с сигнальным процессором (#X502_MODE_DSP) (отсутствует в E16) всё управление сбором данных выполняется сигнальным процессором BlackFin и все потоки данных на ввод и вывод идут через него. Таким образом, пользователь может путем создания модифицированной прошивки BlackFin реализовать дополнительные возможности (например, обратную связь в режиме реального времени). Этот режим доступен только для модификаций L-502-P-G, L-502-P-G-D и E-502-P-EU-D. Узнать о наличие сигнального процессора программно можно также по флагу #X502_DEVFLAGS_BF_PRESENT в [флагах в записи о устройстве](@ref t_x502_devrec::flags) или [в флагах информации о модуле](@ref t_x502_info::devflags), которая может быть получена после установления связи с модулем через функцию L502_GetDevInfo().
  
  При включении питания модуль всегда находится в штатном режиме работы без использования сигнального процессора. Для работы сигнального процессора необходимо предварительно загрузить в него программу (прошивку). Это можно сделать из файла формата ldr с помощью функции X502_BfLoadFirmware(). После этого модуль будет автоматически переведен в режим с сигнальным процессором.
    
  При необходимости, можно специально переключить режим работы с помощью X502_SetMode(). Это может потребоваться, например, если прошивка загружена в BlackFin по интерфейсу JTAG. Кроме того, следует иметь ввиду, что при открытии связи с устройством не производится изменение режима работы модуля. Т.е. если одна программа установила режим #X502_MODE_DSP, то при последующем открытии модуля из другой программы этот режим сохранится. В этой связи может понадобиться явно перевести модуль в штатный режим с помощью X502_SetMode(). Поэтому если программа не предполагает, что модуль мог работать в режиме #X502_MODE_DSP и выполнять какие-то функции, которые не нужно прерывать, а работает с модулем с "чистого листа", рекомендуется сразу после установки связи установить явно нужный режим работы.
 
  Все настройки модуля и работа с синхронным вводом-выводом должны выполняться после установки нужного режима.
 
  В любой момент можно узнать текущий режим работы с помощью X502_GetMode().


  Установка настроек модуля {#sect_config}
  ==============================================================
  
  Перед использованием модуля, как правило, необходимо выполнить настройку его параметров. Сперва все настройки записываются в поля структуры описателя модуля с помощью функций, начинающихся с X502_Set, которые будут описаны в последующих подразделах, после чего установленные параметры передаются в модуль с помощью X502_Configure().
  
    Настройка последовательности опроса каналов АЦП {#sect_config_adc_table}
    -------------------------------------------------------------------------
  
  Модули @lcard_modules представляют собой АЦП с последовательной коммутацией каналов. То есть измерение нескольких каналов происходит последовательно, путем переключения входного коммутатора АЦП. Как и в большинстве моделей "Л Кард" последовательность опроса каналов задается с помощью управляющей таблицы логических каналов АЦП. Всего таблица может содержать от одного до #X502_LTABLE_MAX_CH_CNT логических каналов.
  
  Каждый логический канал задает следующие параметры:
  - номер физического канала, с которого производится измерение. Номер физического канала задается, считая от 0, то есть 0 означает первый канал, 1 – второй и т.д. Таким образом, в дифференциальном режиме номер канала может быть от 0 до 15, а в режиме измерения с общей землей --- от 0 до 31.
  - режим измерения АЦП из #t_x502_lch_mode.
  - используемый диапазон измерения (из #t_x502_adc_range или #t_e16_adc_range).
  - коэффициент усреднения по заданному логическому каналу  (см. раздел @ref sect_config_lch_avg, не реализовано в Е16).
  
  Задать параметры логического канала с нужным номером можно с помощью функции X502_SetLChannel(), а количество логических каналов в управляющей таблице --- с помощью X502_SetLChannelCount().
  
  Например, необходимо измерить сперва напряжение входа X1 относительно общей земли для диапазона +/-10В, затем измерить значение на 16 канале в дифференциальном режиме (между входами X16 и Y16) с диапазоном +/-1В, а затем  измерить напряжение между Y1 и общей землей (17 канал в режиме с общей землей) с диапазоном +/-0.2В (Назначение выводов сигнального разъема и подключение сигналов к модулю описано в [\"Руководстве пользователя\"](http://www.lcard.ru/download/l-502_users_guide.pdf)). В этом случае настройка логической таблицы будет выглядеть следующим образом:
  
  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~{.c}
	/* устанавливаем 3 логических канала */
	int32_t err = X502_SetLChannelCount(hnd, 3);
	if (err == X502_ERR_OK) {
	 	/* первый логический канал соответствует измерению 1 канала
	 	      относительно общей земли */
	 	err = X502_SetLChannel(hnd,0,0,X502_LCH_MODE_COMM,X502_ADC_RANGE_10,0);
	}
	if (err == X502_ERR_OK) {
	 	 /* второй логический канал соответствует измерению 16 канала 
	 	 	в диф. режиме */
	 	 err = X502_SetLChannel(hnd,1,15,X502_LCH_MODE_DIFF,X502_ADC_RANGE_1, 0);
	}
	if (err == X502_ERR_OK) {
		/* третий логический канал - измерение 17-го канала 
		   относительно общей земли */
		err = X502_SetLChannel(hnd,2,16,X502_LCH_MODE_COMM, X502_ADC_RANGE_02, 0);
	}
  
  	if (err == X502_ERR_OK) {
  		/* установка других настроек */  		
  	} 
 	if (err == X502_ERR_OK) {
  		/* передаем настройки в модуль */
  		err = X502_Configure(hnd,0);
  	} 
  	
  	if (err != X502_ERR_OK) {
  		/* произошла ошибка при настройке параметров... */
  	}
  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  
  
  После завершения измерения с настройками, соответствующими последнему логическому каналу, следует измерение, соответствующее снова первому (с нулевым номером) логическому каналу. Последовательность измерений соответствующая одному проходу логической таблицы называется кадром.

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


  Настройка частоты синхронного ввода/вывода {#sect_config_freq}
  -------------------------------------------------------------------------

Все частоты потокового сбора и выдачи данных основываются на опорной частоте синхронизации. В качестве опорной частоты может использоваться внутренний источник частоты или внешний. В первом случае, опорная частота может быть 2МГц либо 1.5МГц. По умолчанию используется 2МГц. Изменить ее можно с помощью функции X502_SetRefFreq(). 

При внешней опорной частоты может использоваться сигнал с произвольной частотой до 1.5 МГц. При этом для того, чтобы функции библиотеки могли оптимально подобрать настройки передачи данных (если они не задаются вручную), а также для того, чтобы корректно работали функции подбирающие делители (см. ниже) для получения нужных частот ввода/вывода необходимо задать значение внешней опорной частоты, которая будет подана. Это значение можно задать с помощью функции X502_SetExtRefFreqValue().

Частота сбора АЦП получается с помощью деления значения опорной частоты на установленный коэффициент, который может быть в диапазоне от 1 до #X502_ADC_FREQ_DIV_MAX. Кроме того, как уже упоминалось [в предыдущем разделе](@ref sect_config_adc_table), между измерением последнего логического канала одного кадра и началом следующего кадра, может быть добавлена межкадровая задержка. Межкадровая задержка задается в виде количества периодов опорной частоты синхронизации. 

Делитель частоты сбора АЦП и количество периодов опорной частоты для межкадровой задержки можно задать явно с помощью функций X502_SetAdcFreqDivider() и X502_SetAdcInterframeDelay() соответственно. Вместо этих функций для удобства можно использовать функцию X502_SetAdcFreq(), которой можно передать значения частоты сбора АЦП и частоты кадров в Герцах, а функция сама рассчитает нужный делитель и значение межкадровой задержки, чтобы полученные частоты были наиболее близки к указанным. При этом функция вернет реально установившиеся значения частот. 

Под частотой сбора АЦП (f_acq) понимается величина, обратная времени одного преобразования, соответствующего одному логическому каналу. Под частотой кадров (f_frame) понимается величина, обратная времени от начала измерения первого логического канала одного кадра до начала измерения первого логического канала следующего кадра. Это частота соответствует частоте сбора для одного логического канала.

Ниже приведена диаграмма, иллюстрирующая на примере сбора при заданных трех логических каналах, как определяются упомянутые выше частоты.

\image latex doc/images/adc_frame.jpeg "Диаграмма сбора АЦП для трех логических каналов" width=0.6\textwidth
\image html adc_frame.jpeg "Диаграмма сбора АЦП для трех логических каналов"

Если межкадровая задержка не нужна, то можно передать нулевой указатель в качестве второго параметра X502_SetAdcFreq(), тогда будет использоваться всегда нулевая межкадровая задержка (т.е. измерение следующего кадра начнется сразу после завершения предыдущего).

Помимо синхронного ввода с АЦП модули @lcard_modules позволяют осуществлять синхронный ввод с цифровых входов (количество зависит от типа модуля, что описано в разделе @ref sect_modules_diff_features). Также как и для синхронного сбора данных АЦП, частота синхронного цифрового ввода определяется как опорная частота, деленная на коэффициент, который можно установить с помощью X502_SetDinFreqDivider(). Так же можно вызывать функцию X502_SetDinFreq(), чтобы она рассчитала этот коэффициент для получения наиболее близкой частоты к указанной. Для синхронного ввода цифровых линий нет ни логической таблицы (так как каждый раз считывается значение всех цифровых входов и передается в виде одного слова), ни межкадровой задержки --- при запуске синхронного ввода все измерения выполняются через одинаковые промежутки времени. При этом частота для ввода с цифровых линий может отличаться от частоты сбора АЦП.

Также модули @lcard_modules позволяют осуществить синхронный вывод параллельно на два канала ЦАП (опция) и цифровые выводы. При этом максимальная частота вывода каждого канала в два раза меньшей значения опорной частоты. Для модуля E-502, а также L-502 с прошивкой ПЛИС версии 0.5 или выше, можно установить делитель частоты вывода (относительно опорной частоты в диапазоне от #X502_OUT_FREQ_DIV_MIN до #X502_OUT_FREQ_DIV_MAX) либо явно с помощью функции X502_SetOutFreqDivider(), либо вызвать функцию X502_SetOutFreq(), чтобы она подобрала такой делитель так, чтобы частота была наиболее близка к заданной. При этом частота задается общая для всех потоков вывода.

  
  Коэффициент усреднения для логического канала (не реализовано в Е16) {#sect_config_lch_avg}
  -------------------------------------------------------------------------
  
Реально микросхема АЦП всегда работает на частоте равной опорной частоте синхронизации. В случае, если частота сбора АЦП установлена меньше, чем опорная частота синхронизации, то на одно измерение значения логического канала приходится n измерений АЦП (n = f_acq/f_ref --- отношение установленной частоты сбора АЦП к опорной частоте дискретизации).

При отключенном усреднении, первые n-1 измерений отбрасывается, что увеличивает время установления сигнала. При необходимости можно использовать несколько последних отсчетов (navg) для получения результирующего значения. Тогда результирующего значение будет являться средним между navg последними измерениями, однако это сокращает соответственно время на установления сигнала. Естественно navg всегда меньше либо равно n. Кроме того navg не может превышать максимального значения, равного #X502_LCH_AVG_SIZE_MAX.

Значение navg задается последним параметром функции X502_SetLChannel(). Значение равное 1 означает отсутствие усреднения. Значения равное 0 означает, что коэффициент усреднения может быть выбран по усмотрению библиотеки. В текущей реализации значение 0 аналогично значению 1, но это может быть изменено в последующих версиях.


  Настройка режимов синхронизации {#sect_config_sync_mode}
  -------------------------------------------------------------------------
  
  По умолчанию в качестве опорной частоты синхронизации используется внутренняя частота модуля, а запуск всех синхронных измерений осуществляется при выполнении функции X502_StreamsStart().
  
  Однако, при необходимости, возможно задать как внешний источник опорной частоты, так и внешний сигнал запуска синхронного сбора/выдачи данных.
  
  Для этого могут быть использованы входы цифрового разъема DI_SYN1 и DI_SYN2 (может использоваться как фронт, так и спад одного из этих сигналов), либо также может использоваться разъем синхронизации для организации синхронного сбора данных по принципу ведущий-ведомые.
  В модуле E16 для этих же целей используются контакты INT (19 контакт цифрового разъема) и TRIG (20 контакт аналогового разъема), при этом в E16 нет выделенных портов (START/CONV)_(IN/OUT), а TRIG и INT могут быть настроены как на вход, так и на выход и играть роль входов или выходов сигналов START и CONV.
  
  Выбор внешнего сигнала для задания опорной частоты синхронизации задается с помощью X502_SetSyncMode(), а условие запуска с помощью функции X502_SetSyncStartMode(). Следует отметить, что если задано внешнее событие запуска, то для того, чтобы модуль перешел в режим ожидания этого события, необходимо вызвать X502_StreamsStart().
  
  Останов синхронного сбора/выдачи данных всегда осуществляется программно с помощью X502_StreamsStop().
  
  При использовании разъема синхронизации для организации сбора данных по принципу ведущий-ведомые, для ведущего модуля источником опорной частоты синхронизации остается внутренняя частота (режим #X502_SYNC_INTERNAL), а каждый ведомый модуль использует опорную частоту и/или признак запуска сбора от внешнего мастера, т.е. для каждого ведомого модуля должен быть установлен режим #X502_SYNC_EXTERNAL_MASTER.

  
  
  Синхронный и асинхронный режимы работы {#sect_sync_async}
  ===========================================================================
  
  Для модулей @lcard_modules доступны следующие данные на ввод:
  - отсчеты с АЦП 
  - значения цифровых входов 
  
  Также модуль может быть использована для вывода:
  - отсчетов на первый канал ЦАП
  - отсчетов на второй канал ЦАП
  - значений на цифровые выходы
  
  Таким образом, имеется 2 канала на ввод и 3 канала на вывод.
  
  Каждый из этих каналов может работать как в синхронном режиме, так и асинхронно. При этом каждый канал может быть настроен индивидуально, то есть можно выполнять, например, асинхронный ввод цифровых линий на фоне синхронного потокового сбора с АЦП или выводить на один канал ЦАП сигнал в синхронном потоковом режиме, в то время как в другой выставлять значения асинхронно. Единственное исключение --- невозможно осуществить асинхронный ввод с АЦП на фоне синхронного сбора данных с цифровых входов. 
  
 Также существует ограничение использования асинхронного режима вместе с синхронным в случае запуска синхронного ввода-вывода по внешнему условию. В момент времени, когда модуль ожидает внешний сигнал запуска синхронного ввода-вывода (т.е. после того, как была вызвана функция X502_StreamsStart(), но до того, как возникло внешнее условие старта), асинхронный ввод или вывод невозможен.  
  
  Асинхронный режим работы {#sect_async_mode}
  -------------------------------------------------------------------------
  
  При включении питания все каналы находятся в асинхронном режиме.
  В асинхронном режимы при вызове функции асинхронного ввода/вывода производится однократный ввод или вывод указанной информации. При этом задержка от вызова функции до непосредственно момента измерения данных для ввода или выставления указанного значения на выходе для вывода точно не определена. Также точно не может быть определена задержка между двумя последовательными операциями ввода/вывода. 
  
  Плюсом асинхронного режима является простота его использования --- достаточно одного вызова требуемой функции:
  - X502_AsyncInDig() - асинхронный ввод значений цифровых линий
  - X502_AsyncOutDig() - асинхронный вывод значений на цифровые линии
  - X502_AsyncOutDac() - асинхронный вывод значения на один из каналов ЦАП
  
  Для однократного ввода данных с АЦП используется функция X502_AsyncGetAdcFrame(), которая выполняет ввод одного кадра данных АЦП. В отличии от других функций асинхронного ввода-вывода, перед вызовом данной функции необходимо выполнить настройку модуля: необходимо задать управляющую таблицу АЦП (см. @ref sect_config_adc_table). Измерение логических каналов внутри кадра происходит синхронно с заданной частотой сбора АЦП. Асинхронным является ввод самих кадров, то есть задержка между измерением кадров при последовательном вызове X502_AsyncGetAdcFrame() не определена.
  
  Например, код для выполнения однократного измерения с 7-го физического канала в дифференциальным режиме с диапазоном +/-0.5В может выглядеть следующим образом:
  
  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~{.c}
	/* устанавливаем 1 логический канал в управляющей таблице */
	int32_t err = X502_SetLChannelCount(hnd, 1);
	if (err == X502_ERR_OK) {
	 	/* логический канал соответствует измерению 7 канала 
	 		в диф. режиме */
	 	err = X502_SetLChannel(hnd,0,6,X502_LCH_MODE_DIFF,L502_ADC_RANGE_05,0);
	}
 	if (err == X502_ERR_OK) {
  		/* передаем настройки в модуль */
  		err = X502_Configure(hnd,0);
  	}  
  	if (err == X502_ERR_OK) {
  		/* Считываем кадр данных АЦП из одного отсчета */
  		double val;
  		err = X502_AsyncGetAdcFrame(hnd, 
  			    X502_PROC_FLAGS_VOLT, 1000, &val);
  		if (err == X502_ERR_OK) {
  			/* верно считали значение val */
  		}  		
  	}
  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  
  Синхронный режим работы {#sect_sync_mode}
  -------------------------------------------------------------------------
  
  
  В синхронном режиме ввод или вывод данных осуществляется с заданной частотой, то есть время между соседними измерениями или выводом соседний отсчетов определено. Частоты сбора для каждого канала задаются относительно общей опорной частоты синхронизации (подробнее см. главу [\"Настройка частоты синхронного ввода/вывода\"](@ref sect_config_freq)) и запуск синхронного ввода-вывода для всех каналов осуществляется одновременно.
  
  Для запуска синхронного режима, необходимо сперва с помощью функции X502_StreamsEnable() разрешить синхронный режим по требуемым каналам, а затем запустить синхронный ввод/вывод по всем разрешенным каналам с помощью X502_StreamsStart().
  
  При синхронном вводе модуль производит измерения с заданной частотой и сам передает данные по интерфейсу в буфер (для L-502 этот буфер находится в драйвере и передается модулем с использованием BusMaster DMA, а для E-502 --- буфер выделяется бибилотекой). Принятые в буфер данные могут быть прочитаны программой с помощью X502_Recv().   
  
  Аналогично, для синхронного вывода, модуль сам по мере необходимости считывает данные из буфера  и выводит считанные отсчеты с заданной частотой. Данные в буфер драйвера должны быть предварительно записаны с помощью X502_Send(). При этом, если к моменту вывода очередного отсчета данные в буфер драйвера не поступили, то будет выведено предыдущее значение.
  
  В драйвере или библиотеке выделяется всего два буфера --- один на прием и один на передачу. То есть значения для синхронного ввода с цифровых линий и отсчеты АЦП передаются одним потоком, также одним потоком передаются все данные на вывод. Каждый отсчет передается в виде 32-битного слова, содержащего дополнительную информацию, включающую в себя признак, к какому типу данных относится данный отсчет.
  
  Разбор принятых данных на отсчеты АЦП и значения цифровых выводов осуществляется с помощью X502_ProcessData(). Помимо этого, данная функция также может осуществить перевод отсчетов АЦП в Вольты.
  Следует учесть, что в отличие от некоторых других изделий "Л Кард", применение калибровочных коэффициентов осуществляется аппаратно и значения уже приходят в виде 24-битных отсчетов с уже примененными коэффициентами.
  
  В 32-битном слове, соответствующем отсчету АЦП, передается дополнительно режим измерения и номер физического канала. X502_ProcessData() сравнивает эти значения с теми, которые были заданы при настройке управляющей таблицы, чтобы убедиться в корректности принимаемых данных. При этом X502_ProcessData() ожидает, что с ее помощью будут обрабатываться все принятые данные.

  Данные от АЦП приходят в том порядке, в котором производятся измерения, т.е. сперва измерения соответствующие всем логическим каналам первого кадра, затем второго и т.д. В этом же порядке X502_ProcessData() возвращает и преобразованные отсчеты АЦП. При этом в X502_ProcessData() можно передавать и нецелое количество кадров (например, если запущен синхронный ввод с цифровых линий, то заранее сложно определить, сколько в принятом блоке данных содержится отсчетов с цифровых линий, а сколько отсчетов с АЦП), в этом случае X502_ProcessData() обработает и выдает все отсчеты, включая отсчеты нецелого кадра, а при следующем ее вызове проверяет, что отсчеты АЦП начинаются с логического канала, следующего за последним обработанным до этого каналом. Какой логический канал ожидается следующим для обработки можно узнать с помощью функции X502_GetNextExpectedLchNum().
  
  Например, пусть в управляющей таблице АЦП установлено 7 логических каналов. Если был принят блок данных от модуля содержащий 5 отсчетов АЦП (и произвольное количество значений цифровых входов, если включен синхронный ввод с цифровых линий) и обработан с помощью X502_ProcessData(), то X502_ProcessData() вернет преобразованные 5 отсчетов АЦП, соответствующие логическим каналом с индексами 0,1,2,3,4. Следующий логический канал, который ожидается для обработки --- логический канал с индексом 5, поэтому X502_GetNextExpectedLchNum() вернет значение 5. Если обработать следующий блок данных, содержащий 5 следующих отсчетов, то X502_ProcessData() вернет отсчеты соответствующие каналам с индексами 5,6,0,1,2. Т.е. с помощью вызова X502_GetNextExpectedLchNum() можно узнать, какому логическому отсчету будет соответствовать первый элемент выходного массива при последующем вызове X502_ProcessData().
  
  Следует учесть, что по умолчанию буфер в драйвере или библиотеке рассчитан на количество отсчетов, которое будет введено за 4с непрерывного сбора. Если вовремя не считывать данные с помощью X502_Recv(), то произойдет переполнение буфера в драйвере и часть данных, для которых не нашлось в буфере места, будет потеряна. При последующем появлении места в буфере, в то место в потоке, где произошел разрыв непрерывного потока данных, будет вставлено слово, представляющее собой сообщение о переполнении буфера. Если X502_ProcessData() во входном массиве обнаружит это слово, то функция вернет ошибку #X502_ERR_STREAM_OVERFLOW. При этом, как и в случае возникновения других ошибок обработки, все отсчеты, которые были до возникновения ошибки будут обработаны и возвращены в выходных массивах (размеры которых будут соответственно обновлены).
  
  Аналогично, для формирования общего потока на вывод в требуемом формате используется функция X502_PrepareData(), принимающая данные из трех массивов и сохраняющая их во внешний массив. Если какой-либо из источников не должен использоваться, то в качестве массива передается нулевой указатель. Для каналов, которые не были настроены на синхронный режим с помощью X502_StreamsEnable(), входной массив на анализируется и данные из него не используются..
  
  Следует отметить, что если для синхронного ввода инициализация потока передачи происходит по X502_StreamsStart(), так как данные начнут поступать только после запуска синхронного ввода, то с синхронным выводом дело обстоит несколько иначе. Так как по X502_StreamsStart() уже должна начаться выдача синхронных данных, то часть данных уже должна быть загружена в модуль. Таким образом, после разрешения синхронного вывода по нужным каналам с помощью X502_StreamsEnable() и до запуска синхронного вывода с помощью X502_StreamsStart() необходимо осуществить предзагрузку части данных синхронного потока. Для этого следует вызвать функцию X502_PreloadStart(), по которой в драйвере или библиотеке будет выделен буфер на передачу и инициализирован поток на передачу, а затем записать часть синхронных данных в буфер драйвера с помощью X502_PrepareData() и X502_Send(). Если этого не сделать, то синхронный вывод начнется лишь после того, как данные будут записаны в модуль и не будет привязан к началу синхронного сбора/выдачи данных. 
  
  Кроме того, для синхронной выдачи данных на ЦАП рекомендуется предварительно установить начальные значения на ЦАП с помощью функции асинхронного вывода. В противном случае при начале синхронного вывода может быть небольшой переходный процесс от значения на ЦАП, которое было до запуска синхронного вывода, до выставления первых нужных значений, т.к. ЦАП имеет свой фильтр и ограничения на скорость изменения сигнала.
  
  
  Циклический вывод {#sect_cycle_out}
  -------------------------------------------------------------------------
  Для модуля L-502, начиная с версии 1.0.4 драйвера и библиотеки, а также для модуля E-502 (начиная с версии 1.1.0), введена поддержка циклического вывода на ЦАП и цифровые выводы. Этот режим позволяет загрузить сигнал полностью в буфер внутри драйвера (для L-502) или процессора Cortex-M4 (для E-502), содержимое которого будет циклически выводиться без необходимости дальнейшей подкачки.
  
  Данные для загрузки в циклический буфер подготавливаются также как и для потокового вывода с помощью X502_PrepareData() и записываются с помощью X502_Send() и могут содержать комбинацию данных на оба канала ЦАП и на цифровые выводы. Циклический вывод является вариантом синхронного вывода и для его работы нужно разрешить нужные потоки на вывод через X502_StreamsEnable() и должен быть запущен синхронный ввод-вывод через X502_StreamsStart(). Также как и с обычным потоковым выводом, часть каналов может использоваться для вывода циклического сигнала, а часть --- асинхронно. Однако нельзя часть каналов вывода использовать в циклическом режиме, а часть в потоковом режиме с подкачкой (естественно, циклический режим на вывод можно использовать с потоковым на ввод).
  
  Для вывода циклического сигнала используется двойная буферизация --- то есть может быть выделено два буфера, пока из одного сигнал циклически выводится, в другой может подгружаться следующий сигнал. Смена сигнала происходит по концу периода предыдущего. При этом после записи одного сигнала необходимо, чтобы успела пройти смена сигналов перед тем как можно будет загружать следующий, в противном случае функция X502_OutCycleLoadStart() вернет ошибку (что можно использовать как признак, что буфер еще не готов для загрузки нового сигнал). Проверку завершения смены сигнала можно сделать  при соответствующих версиях ПО (см. описание функции X502_OutCycleCheckSetupDone())  и явно с помощью функции X502_OutCycleCheckSetupDone() или используя флаг #X502_OUT_CYCLE_FLAGS_WAIT_DONE во время вызова X502_OutCycleSetup(), чтобы функция вернула управление только после выполнения смены сигналов.
  
  Для загрузки сигнала сперва вызывается функция X502_OutCycleLoadStart(), в которой задается размер циклического буфера, который будет использован для хранения отсчетов всех используемых каналов вывода. Например, если нужно использовать два канала ЦАП, на каждый из которых вывести сигнал из 1000 точек, то размер должен быть указан 2000. После этого отсчеты загружаются как и при потоковом выводе с подкачкой с помощью функций X502_PrepareData() и X502_Send(). При этом суммарно должно быть записано ровно столько же отсчетов, сколько было указано при вызове X502_OutCycleLoadStart(). После загрузки по вызову X502_OutCycleSetup() происходит переключение на загруженный буфер, при этом, в зависимости от текущего состояния, это приводит к следующему:
  - если синхронный ввод-вывод не запущен (не было вызова X502_StreamsStart()) то начинается предзагрузка циклического сигнала в модуль, однако реально выдача сигнала начнется только при вызове X502_StreamsStart() (или по внешнему условию запуска). Это позволяет привязать начало вывода первого отсчета циклического сигнала к началу ввода. При этом необходимо вызывать X502_OutCycleSetup() с флагом #X502_OUT_CYCLE_FLAGS_WAIT_DONE, чтобы гарантировать, что загрузка сигнала завершится до X502_StreamsStart() (актуально в первую очередь для E-502, где передача данных идет по интерфейсу и может занимать значительное время).
  - если синхронный ввод-вывод запущен, но не было выведено ни одного циклического сигнала до этого, то по X502_OutCycleSetup() начинается вывод циклического сигнала.
  - если синхронный ввод-вывод запущен и уже выводится предыдущий циклический сигнал, то после вызова X502_OutCycleSetup() в драйвере (или модуле для E-502) будет взведен флаг о необходимости переключить сигналы. После этого события по достижению конца предыдущего циклического буфера будет произведена смена буферов вывода. Таким образом это позволяет производить смену сигнала всегда в известной точке. При смене сигнала происходит освобождение старого буфера и только после реальной смены можно будет вызвать следующий раз X502_OutCycleLoadStart() для загрузки следующего сигнала. При использовании флага #X502_OUT_CYCLE_FLAGS_WAIT_DONE, функция вернет управление только в момент, когда сама смена уже произойдет (если эта возможность поддерживается ПО).
  
  Останов циклического вывода можно осуществить одним из следующий способов:
  - X502_OutCycleStop() останавливает циклический вывод после вывода последней точки на границе циклического буфера. То есть эта функция используется, чтобы циклический вывод был завершен точно в известной точке и на выходах остались значения, соответствующие последним отсчетам в циклическом сигнале. При этом сама функция не дожидается останова вывода, если не указан флаг #X502_OUT_CYCLE_FLAGS_WAIT_DONE.
  - X502_StreamsDisable() с указанием всех используемых каналов вывода приводит к немедленному завершению вывода и освобождением всех буферов. На выходах останутся значения, которые были в момент вызова. После этого можно будет заново разрешить и проинициализировать вывод как в циклическом так и в потоковом режиме с подкачкой.
  - X502_StreamsStop() приводит к немедленному останову всех потоков и останову генерации опорной частоты синхронизации. На выходе остаются те значения, которые были в момент вызова функции.
  
  
  
  Размер буфера и шаг для синхронного режима {#sect_sync_mode_buf}
  -------------------------------------------------------------------------
  
  В данном разделе приводится дополнительная информация о том, как можно настроить дополнительные параметры, управляющие передачей потока данных в синхронном режиме между модулем и ПК. Эти параметры по умолчанию настраиваются библиотекой автоматически. Предполагается, что большинству пользователей должны подойти автоматически настраиваемые параметры и данный раздел не является обязательным. Однако для случаев, когда автоматически определенные параметры не подходят, пользователь может задать их самостоятельно. Для этого в этом разделе приводится описание, как выбирается размер буфера и шаг библиотекой, что означают эти параметры и как их можно настроить вручную.  
  
  Как уже было сказано [в предыдущем разделе](@ref sect_sync_mode) прием и передача синхронных данных осуществляется через буфера в драйвере или библиотеке -- один буфер на прием, один на передачу.
  
  Выделение буфера на ввод осуществляется по X502_StreamsStart(), если был разрешен хотя бы один источник для синхронного ввода. Выделение буфера на вывод осуществляется по X502_PreloadStart(). 
  
  При этом размер буфера определяется автоматически библиотекой в зависимости от установленной частоты передачи данных. Размер буфера рассчитывается так, чтобы его хватило на 4 секунды при синхронном вводе и на 3 секунды при синхронном выводе. 
  
  Вторым параметром, характеризующим передачу, является шаг передачи.
  Для модуля L-502 этот параметр определяет шаг прерываний. Передача данных между буфером драйвера и модулем осуществляется непосредственно самим модулем по DMA. При этом, чтобы драйвер мог узнать о том, что данные были записаны в буфер или прочитаны из него, при передаче определенного количества отсчетов модуль генерирует прерывание. То есть реально драйвер "узнает" о том, что были переданы данные только после того как будет передано заданное количество отсчетов, называемое в данной главе шагом прерываний (Точнее сказать, не позже, чем будет передано заданное количество отсчетов, так как драйвер может прочитать значение счетчика переданных данных из модуля и по другим условиям).
  
  Таким образом, малый шаг прерываний позволяет драйверу раньше узнает о принятых или переданных данных, но приводит к большей загрузке системы. Библиотека рассчитывает шаг прерываний так, чтобы прерывания происходили с частотой 64 раза в секунду.
  
  Для модуля E-502 этот параметр определяет используемый размер запроса по USB. Кроме того при вводе данные ставятся на передачу в ПК при из размере равным шагу, но при этом при отсутствие поступления новых данных могут быть поставлен на передачу и меньший объем данных.
  
  Если пользователя по каким-либо причинам не устраивают эти значения он может настроить их вручную с помощью функций X502_SetStreamBufSize() и X502_SetStreamStep(). Эти функции должны быть вызваны до инициализации потоков передачи (до X502_StreamsStart() или X502_PreloadStart()).
  
  В частности, случаями когда значения библиотеки могут не устроить, могут быть следующие:
  - Пользователь использует свою прошивку BlackFin и использует каналы синхронных данных для передачи пользовательских данных, которые сильно изменяют скорость передачи данных. В этом случае библиотека не может правильно определить частоту передачи, так как не знает скорости передачи пользовательских данных.
  - Пользователь изменяет каналы, которые используются в синхронном режиме, на лету (после X502_StreamsStart()) и при этом скорости передачи по этим каналам существенно отличаются. Так как расчет размера буфера выполняется при инициализации канала, то он осуществляется только по тем каналам, которые были разрешены на тот момент. Если, например, был разрешен только синхронный сбор с АЦП на относительно небольшой частоте, то буфер будет выделен также небольшой. При этом, если после запуска сбора данных будет разрешен синхронный ввод с цифровых линий на частоте 2МГц, то вероятнее всего этот буфер окажется недостаточного размера, и с большой вероятностью произойдет его переполнение.
  Если же оба этих потока были разрешены изначально, а потом синхронный ввод цифровых линий будет запрещен, то рассчитанный изначально шаг прерывания будет слишком большим и данные от медленного потока АЦП будут обновляться с большими задержками. Если же частоты каналов соизмеримы, то включение/отключение одного из них не приведет к существенному изменению параметров.
  Изменение шага прерывания и размера буфера при запущенном сборе данных на текущий момент невозможно. 


Особенности работы по интерфейсу Ethernet и настройка сетевых параметров {#sect_eth_config}
============================================================================
  Если USB-интерфейс в модуле E-502 всегда работает и не требует дополнительной конфигурации, то для работы по интерфейсу Ethernet необходимо выполнить настройку параметров интерфейса и разрешить данный интерфейс. 
  Следует отметить, что при разрешенном Ethernet-интерфейсе с модулем можно работать как по USB, так и по Ethernet. При этом сбор/генерация данных могут выполняться одновременно только по одному интерфейсу (по которому пришла команда на запуск сбора/выдачи).
  
  Основными параметрами для работы по Ethernet являются:
    - IP-адрес устройства. Записывается как 4 цифры от 0 до 255 (модуль поддерживает только протокол IPv4), разделенные точками (например, 192.168.0.10). Состоит из адреса подсети и адреса устройства внутри подсети. Последний должен быть уникальным внутри подсети.
    - Маска подсети. Определяет какая часть адреса относится к адресу подсети, а какая к адресу устройства. Маска 255.255.255.0 означает, что первые 3 цифры (192.168.0) обозначают адрес подсети, последняя цифра (10) - адрес устройства в подсети.
    - IP-адрес шлюза. Используется только когда модуль E-502 и хост, с которого выполняется управление модулем, находятся в разных подсетях. Модуль передает пакеты по адресу шлюза, если адрес назначения находится не в той же подсети, что и модуль. При работе в локальной сети не используется.
    - MAC-адрес модуля (6 цифр от 0 до 255, которые записываются в 16-ричном формате). Физический адрес устройства, который должен быть уникален внутри локальной сети. В "Л Кард" для каждого модуля прописывается свой заводской MAC-адрес, который нельзя изменить. Однако, при необходимости, пользователь может задать свой пользовательский MAC адрес и разрешить его использование вместо заводского. При этом всегда есть возможность вернутся к заводскому MAC-адресу, запретив пользовательский.
    - Имя экземпляра устройства. Уникальное имя данного экземпляра в виде строки (до 64 английских символов или 32 русских). Используется  для возможности автоматического обнаружения модулей в локальной сети (подробнее в главе @ref sect_eth_browsing).
    
  Для работы в первую очередь необходимо задать правильные IP-параметры (адрес, маску и, при необходимости, адрес шлюза), подробнее о чем описано в [соответствующем разделе FAQ](http://lcard.ru/support/faq/tcpip_settings).
  
  IP-параметры модуля E-502 могут быть установлены 3-мя способами:
    - Заданы вручную (статические параметры). Пользователь сам должен позаботиться о том, чтобы адрес принадлежал нужной подсети и был уникальный в ней.
    - Получены автоматически от DHCP-сервера. Если задано автоматическое получение адреса и в локальной сети присутствует DHCP-сервер, то модуль делает запрос к нему и использует выделенные DHCP-сервером IP-адреса.
    - Может использоваться автоматически получаемый локальный (link-local) адрес (в соответствии с [RFC3927](https://tools.ietf.org/html/rfc3927)).  Адрес выбирается случайным образом в диапазоне от 169.254.1.0 до 169.254.254.255 и проверяется, что в сети нет другого устройства с таким адресом (если есть, то идет попытка выбора следующего адреса и т.д.). Это делает возможным подключение к устройству в локальной сети без специальной конфигурации. Следует однако отметить, что т.к. адрес действителен только в одной сети, то два разных устройства в разных сетях могут иметь одинаковый link-local адрес, что приводит к тому, что если у ПК несколько активных интерфейсов (и на обоих используется link-local адрес или наоборот обычный адрес), то хост не знает на каком интерфейсе искать нужное устройство. Т.е. для подключения по link-local адресу на ПК должен быть либо один активный сетевой интерфейс, либо на нужном интерфейсе должен использоваться link-local адрес, а на другом статический  или полученный по DHCP адрес.
 
 При включении автоматического получения адреса модуль выбирает себе link-local адрес (и проверяет его уникальность), параллельно выполняя поиск в сети DHCP-сервера. Если DHCP сервер не обнаружен, то используется link-local адрес. Как только будет обнаружен DHCP-сервер, то предпочтение отдается полученному от него адресу (в частности при старте в сети с DHCP сервером модуль может некоторое время до получения адреса от него использовать link-local адрес). Подобный алгоритм используется при  автоматическом получении адреса в частности в ОС Windows, а также и во многих дистрибутивах Linux (иногда предоставляя возможность отдельного разрешения DHCP и link-local адреса). Следует также иметь ввиду, что автоматически получаемый адрес модуль проверяет на уникальность в сети, поэтому существует задержка в несколько секунд от подключения к сети модуля до назначения адреса.
 Автоматическое получение адреса не требует дополнительных настроек, однако при этом неизвестен адрес устройства со стороны ПК для установления соединения. Для решения этой проблемы возможно использование процедуры поиска устройств в локальной сети, описанной [в следующем разделе](@ref sect_eth_browsing).
        
  Изменить сетевые настройки можно с помощью программы [X502Studio](https://gitlab.com/l-card/acq/devices/x502/x502studio).
    
  Также изменение сетевых настроек модуля возможно программным образом через API библиотеки. Для этого существует отдельный тип описателя конфигурации #t_e502_eth_config_hnd. Для изменения конфигурации нужно выполнить следующие шаги:
  1. Создать описатель конфигурации с помощью E502_EthConfigCreate().
  2. Прочитать текущую конфигурацию устройства с помощью E502_EthConfigRead() (соединение с модулем должно быть установлено)
  3. Можно получить нужные параметры с помощью функций E502_EthConfigGetXXX() и/или установить новые значения с помощью функций E502_EthConfigSetXXX().
  4. После завершения изменений можно записать измененную конфигурацию в модуль с помощью E502_EthConfigWrite(). Модуль сохранит новую конфигурацию в энергонезависимой памяти, запрещает Ethernet-интерфейс, после чего снова его переинициализирует уже с новыми параметрами. При этом, если соединение с устройством было выполнено по Ethernet, то для дальнейшей работы нужно разорвать соединение и установить заново (используя новые параметры)
  
  Для избежания непреднамеренного изменения конфигурации по сети, конфигурация может быть защищена простым паролем. Пока пароль не установлен в качестве пароля нужно передавать пустую строку. Установка нового пароля выполняется аналогично любым другим изменениям параметров конфигурации (с помощью E502_EthConfigSetNewPassword()). 
  
  В случае, если пароль забыт, то можно установить соединение по USB и изменить конфигурацию (включая пароль), введя в качестве текущего пароля серийный номер модуля.
  
Обнаружение модулей в локальной сети {#sect_eth_browsing}
===========================================================================
 В отличие от интерфейсов USB и PCI-Express, для интерфейса Ethernet нет стандартной возможности определения подключенных устройств. Однако есть ряд протоколов, реализация которых позволяет обнаружить устройства заданного типа в локальной сети. Для этой возможности модуль E-502 поддерживает протоколы mDNS (в соответствии с [RFC6762](https://tools.ietf.org/html/rfc6762)) и DNS-SD ([RFC6763](https://tools.ietf.org/html/rfc6763)). 
 В соответствии с ними, каждое устройство при подключении объявляет набор сервисов, который оно поддерживает. 
 
 Чтобы различать экземпляры устройств, поддерживающие одинаковый тип сервисов, у каждого экземляра есть свое уникальное имя. Это имя задается во время конфигурации модуля. Если оно не установлено, то в качестве имени экземпляра используется \"E502_\<серийный_номер\>\", однако пользователь может задать свое имя, характеризующее назначение конкретного модуля для более наглядной идентификации. Кроме имени экземпляра у каждого сервиса может быть набор текстовых параметров, описывающих данный экземпляр (для E-502 это параметры задающие имя типа устройства (поле devname, значение равно всегда E-502) и серийный номер (поле serial).
 
 В соответствии с этим протоколом у хоста в локальной сети есть возможность найти все экземпляры заданного сервиса в сети.
 Для обнаружения должна быть запущена соответствующая служба (или демон), отслеживающая изменения наличия устройств в сети, а уже функции e502api работают с данной службой. В ОС Linux в качестве реализации данного протокола используется демон Avahi, который включен в большинство современных дистрибутивов и входит в стандартную установку (или  нужно установить соответствующий пакет вручную). 
 В ОС Windows используется служба Bonjour, которая штатно не установлена, однако установщик включен в @lpcie_sdk и данная служба будет установлена при выборе соответствующего пункта (следует отметить, что так как служба является отдельным продуктом, который может использоваться и другим ПО, то она не удаляется автоматически при удалении @lpcie_sdk. При необходимости следует вручную удалить службу через установку и удаление программ в "Панели управления").
 
 Использование данного API позволяет автоматически обнаруживать подключенные в локальной сети устройства, во многом подобно другим интерфейсам. 
 Однако существуют следующие особенности:
 - Обнаружение модуля связано с посылкой пакетов и приемом ответов, которые могут быть потеряны при определенных условиях и потребовать переповторов. Это выполняется на уровне протокола и невидимо для пользователя, однако надо иметь ввиду, что обнаружение устройства может потребовать некоторого времени.
 - Так как отключение отслеживается на уровне протокола, нет оповещения о физическом отключении кабеля. Соответственно при выключении питания или выдергивании кабеля отключение модуля может быть не обнаружено на протяжении длительного времени.
 
 Для поиска устройств следует вызвать E502_EthSvcBrowseStart(), после чего использовать полученный [контекст поиска устройств в сети](@ref t_e502_eth_svc_browse_hnd) для последующих вызовов E502_EthSvcBrowseGetEvent(). Каждый вызов возвращает информацию максимум об одном событии и немедленно возвращает управление, как только оно произошло. Каждому новому обнаруженному модулю соответствует событие #E502_ETH_SVC_EVENT_ADD. В случае изменения параметров (например адреса) приходит событие #E502_ETH_SVC_EVENT_CHANGED, а при исчезновении (при условии описанных выше особенностей) --- #E502_ETH_SVC_EVENT_REMOVE. Для каждого события возвращается [описатель сетевого сервиса](@ref t_e502_eth_svc_record_hnd), по которому можно определить, какому модулю соответствует событие (узнать имя экземпляра и серийный номер модуля), а также запросить IP-адрес модуля. Для каждого события (кроме случая, когда событие не обнаружено и возвращен код #E502_ETH_SVC_EVENT_NONE) этот описатель необходимо освободить с помощью E502_EthSvcRecordFree() как только он станет ненужным.
 В простейшем случае можно вызвать E502_EthSvcBrowseGetEvent() пока не будет обнаружено появление нужного устройства или не истечет таймаут на поиск устройства. При желании также можно использовать периодический вызов E502_EthSvcBrowseGetEvent() для постоянного мониторинга устройств в сети.
 В любом случае, когда поиск устройств закончен, необходимо вызвать E502_EthSvcBrowseStop().
 
 Установить связь с модулем по [описателю сетевого сервиса](@ref t_e502_eth_svc_record_hnd) можно как вручную по полученному адресу через E502_EthSvcRecordResolveIPv4Addr(), так создать запись о устройстве с помощью E502_MakeDevRecordByEthSvc() для последующего открытия через X502_OpenByDevRecord(). 
 

\tableofcontents
