Сентябрь 2014: эта страница устарела, оставлена для истории.

Осенью 2013 я преподаю ТСАНИ в НГУ у группы 11302 (физика плазмы). На этой странице исправляю ошибки, дополняю методички, отвечаю на вопросы студентов; короче, пишу свои замечания о курсе (возможно, на основе этой стринцы будут переизданы методички). Она будет пополняться, так что рекомендуется проверять её перед каждым занятием. Студенты могут сами предлагать материал для этой страницы.

Оглавление

Общие замечания

Материалы курса находятся на сайте НГУ и сайте ИЯФа (тут более свежая версия методички к работе 4, а также есть фильм, показываемый на первом занятии); есть ещё копия старых материалов. Документ «О курсовых работах» актуален и сегодня.

Рекомендую овладеть слепым десятипальцевым методом набора (не только для этого курса, а вообще для себя). Не забывайте про существование цифрового блока клавиатуры, клавиш Home, End и всяких сочетаний клавиш (Alt+Tab, Alt+F4, Ctrl+w...). Часто так быстрее, чем мышью.

Редактор кода «LabWindows/CVI», на мой взгляд, не очень удобен. Рекомендую «Notepad++». Так как прав на установку нет, скачивать архив zip.

Строгих критериев выставления оценок нет, но есть мнение, что примерно так:

Но есть и мнение, что в lab0 (стр. 16) всё правильно написано.

Язык Си

В первую очередь стоит сказать, что из-за недостатка времени курс ТСАНИ не учит хорошему стилю программирования, он даёт лишь представление о работе программиста-физика.

Не рекомендуется внимательно читать «Справку по Си» (FAQ_C.pdf): там есть ошибки, очень многое вообще отсутствует (препроцессор, тернарный оператор, перечисления) или объяснено недостаточно (структуры, указатели, операторы). Я знаю ровно 2 хорошие книги о языке Си: Керниган, Ритчи (Ричи) «Язык программирования Си» («The C Programming Language» 2-nd edition) и Харбисон, Стил «Язык программирования Си» («Язык Си с примерами», «C A Reference Manual» 5-th edition).

При создании нового проекта или файла обращайте внимание, в какой папке он создаётся. Для каждого проекта создавайте отдельную папку (d:\tsani\work\11302\name\lab1-1, d:\tsani\work\11302\name\lab1-2...).

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

Ознакомьтесь со страницей «C Data Types» справки «LabWindows/CVI». Учтите, что там перечислены не все используемые названия типов (например, uInt64 = unsigned __int64). Если не знаете, какого типа создать переменную или аргумент функции, подумайте прежде всего об int или double. Для символов следует использовать char (не unsigned char).

Чтобы случайно не выполнить присваивание (if (a = 1)) вместо сравнения (if (a == 1)), приобретите привычку ставить константу перед переменной (if (1 == a)). Но в случае двух переменных это не поможет :-(.

Далеко не всем элементам графического интерфейса следует назначать callback-функции. Для некоторых элементов можно назначить одну и ту же callback-функцию.

У меня на сайте (http://www.caesarion.ru/programming/#ccpp) есть раздел о языке Си, его тоже читайте, здесь я не буду дублировать то, что есть там (например, более полную таблицу типов данных в «LabWindows/CVI»).

Учтите, что в компьютерах, в отличие от математики, целые числа и числа с плавающей запятой (точкой) — это две большие разницы: «...Очень любопытные и странные явления: некоммутативность и неассоциативность арифметических операций, ноль со знаком, разность неравных чисел дает ноль...». Вот лишь пара ссылок: «Википедия» (обратите внимание на раздел «машинная эпсилон»), «Хабрахабр» (для нас важнее всего раздел «4. Подводные камни в арифметике с плавающей запятой»). Впрочем, «Сейчас арифметика с плавающей запятой почти совершенна. Практически всегда наивный подход сработает, и программа, не учитывающая все ее особенности, выдаст правильный результат, а описанные подводные камни касаются только экзотических случаев. Но нужно всегда оставаться бдительным: в таком вопросе как компьютерная математика легко наступить на грабли».

Указатели

Этот раздел можно считать дополнением к Lab 1.

Указатель (pointer) — это такой тип данных, который содержит адрес переменной или функции. Дальнейшее верно для переменных. По сути это просто беззнаковый целый тип (есть даже такое понятие, как арифметика указателей). Для взятия адреса переменной используют амперсанда (&i). Для разыменования (опосредования) указателя пишут звёздочку (*p). Ещё звёздочку используют для объявления/определения указателей (в том числе аргументов функций). Любой указательный тип можно привести к типу void* и наоборот. Адрес можно взять у любой переменной, в том числе у указателя. Можно сделать указатель на указатель, указатель на указатель на указатель и так далее. При работе с функциями указатели удобны тем, что сами они занимают мало места (в LabWindows/CVI 9.0 32 бита), но через них функция может прочитать и записать сколь угодно большой объём данных. Ссылок (reference) в языке Си нет, однако можно говорить, что указатель «ссылается».

Объявление int *p, i; эквивалентно объявлению int* p, i; и объявлениям int *p; int i;, но не объявлениям int *p; int *i;.

Конструкция int *p; *p = 1; допускается языком Си, но, если вы так написали, скорее всего, вы не понимаете, что делаете, и программа будет работать не так, как вы хотите. Правильно так: int *p; int i = 1; p = &i;. Или так: int *p; p = malloc(sizeof(int)); *p = 1; free(p);.

Пусть есть такая функция: void example(int *pi);, и вы хотите передать ей переменную i типа int. Можно сделать так: int *p; p = &i; example(p);. А можно так: example(&i);.

Подумайте, почему функция void example(int *pi) {int i = 0; pi = &i; return;} int* f(int i) {return &i;} или int* f(int i) {int j = i; return &j;} может вернуть адрес области памяти, в которой не содержится значения i или даже привести к ошибке (времени компиляции или времени исполнения), но может и заработать (в зависимости от компилятора, операционной системы, аппаратного обеспечения и ещё кучи параметров, которые не всегда можно учесть). Можете провести эксперимент: следующий код компилируется в «LabWindows 9.0» и выполняется без ошибок в «Windows 7», но первый printf выводит 123, а последний что-то другое.#include <ansi_c.h> int* f(int i); int main (int argc, char *argv[]) { int *i = f(123); printf("%d\n", *i); printf("%d\n", *i); getchar(); return 0; } int* f(int i) { int j = i; int addrj = (int)&j; return (void *)addrj; }

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

LabWindows/CVI

F1 — помощь. Если курсор стоит на библиотечной функции, откроется страница с описанием этой функции.

Ctrl+Shift+пробел (когда курсор стоит на функции) — помощь в выборе аргументов функции.

Каждому файлу графического интерфейса (.uir) соответствует одноимённый заголовочный файл (.h), причём по умолчанию он не включается в проект.

По невыясненной причине (похоже, проблема в «LabWindows/CVI». Обсуждение на форуме разработчика) иногда возникает ошибка времени исполнения обращения за пределы нестатического массива, объявленного в функции, при конфигурации debug. Можно предложить следующие способы решения проблемы:

Задержку можно сделать функцией Delay(double numberOfSeconds), но менее ресурсоёмка фукнция Sleep(DWORD dwMilliseconds), при этом надо вставить #include <windows.h>, причём до других #include.

DAQmx

Этот раздел можно считать дополнением к Lab 2.

(Пояснение к стр. 14) Задание (task) может содержать только однотипные каналы, то есть нельзя смешивать цифровые и аналоговые, выходные и входные. Однако выходные каналы можно как писать, так и читать (см. замечание на стр. 45).

Далее рассматриваем задания, состоящие из цифровых каналов (они предназначены для работы с портами ввода-вывода). Порты в нашем случае 8-битные (8-разрядные), поэтому и функции чтения-записи будем использовать 8-битные (в их названиях есть «U8»).

Линия (line) — это одна из ножек порта.

Есть 2 режима работы порта: весь порт представляет собой единое целое (во все его линии можно писать или читать только одновременно) или каждая линия независима от других, можно одновременно читать из одной линии и писать в другую. Некоторые устройства поддерживают только первый режим работы, но используемый в практикуме модуль NI PXI-6251 позволяет работать с линиями независимо. В дальнейшем рассматриваем именно такой режим.

Задание содержит линии косвенным образом: задание состоит из (витруальных) каналов ((virtual) channels), а уже они состоят из линий. Для добавления в задание каналов используются функции DAQmxCreateDIChan()/DAQmxCreateDOChan(), для чтения/записи DAQmxReadDigitalU8()/DAQmxWriteDigitalU8() (чтобы не писать по два раза, в дальнейшем будем говорить о входных каналах).

От аргумент dataLayout функции DAQmxReadDigitalU8() что-то зависит лишь в случае чтения нескольких значений каждой линии за один вызов функции, нам это не нужно (в Lab 2, про дальнейшее не знаю), так что не будем его рассматривать.

Создание каналов рассмотрим на примерах.

Пример 1. Пусть нужно добавить в пока ещё пустое задание task линии 0, 1, 2, 3 и 7 порта 0 и линию 3 порта 2 и прочитать их. Можно сделать так:DAQmxCreateDIChan(task, "PXI1Slot2/port0/line0:3, PXI1Slot2/port0/line7", "in1", DAQmx_Val_ChanForAllLines); DAQmxCreateDIChan(task, "PXI1Slot2/port2/line3", "in2", DAQmx_Val_ChanForAllLines); uInt8 data[2]; DAQmxReadDigitalU8(task, 1 /* numSampsPerChan */, 10.0, DAQmx_Val_GroupByChannel, data, 2 /* arraySizeInSamps */, NULL, NULL);

Создано два канала (один из 5 линий, другой из 1), поэтому массив должен состоять из как минимум 2 элементов и поэтому же 2 передаём аргументом arraySizeInSamps. После выполнения этого кода data[0] будет содержать значения из порта 0 (потому что сначала в задание добавили его), а data[1] из порта 2 (его добавили вторым). Номер бита соответствует номеру линии. Биты, соответствующие линиям, отсутствующим в канале (биты 4, 5, 6 для data[0] и биты 1:7 для data[1]), игнорируются.

Пример 2. Рассмотрим другой пример с теми же линиями:DAQmxCreateDIChan(task, "PXI1Slot2/port0/line0:3", "in1", DAQmx_Val_ChanForAllLines); DAQmxCreateDIChan(task, "PXI1Slot2/port2/line3", "in2", DAQmx_Val_ChanForAllLines); DAQmxCreateDIChan(task, "PXI1Slot2/port0/line7", "in3", DAQmx_Val_ChanForAllLines); uInt8 data[3]; DAQmxReadDigitalU8(task, 1 /* numSampsPerChan */, 10.0, DAQmx_Val_GroupByChannel, data, 3 /* arraySizeInSamps */, NULL, NULL);

Теперь задание состоит из трёх каналов. Несмотря на то, что в первом и последнем канале участвуют линии одного и того же порта, data[0] и data[2] никак не связаны: в data[0] значимыми битами являются только 0:3, а в data[2] только 7.

Пример 3. Не надо смешивать разные порты в одном канале:DAQmxCreateDIChan(task, "PXI1Slot2/port0, PXI1Slot2/port1", "in1", DAQmx_Val_ChanForAllLines);

Такой канал создастся, но для его использования нужны данные большей размерности (uInt16 вместо uInt8) и другая функция (с «U16» в названии вместо «U8»).

Пример 4. При условии, что канал содержит линии только одного порта, не имеет значение способ и порядок написания линий: "PXI1Slot2/port0" эквивалентно "PXI1Slot2/port0/line0:7", "PXI1Slot2/port0/line7:0", "PXI1Slot2/port0/line7, PXI1Slot2/port0/line6:0", "PXI1Slot2/port0/line7, PXI1Slot2/port0/line0, PXI1Slot2/port0/line6, PXI1Slot2/port0/line1, PXI1Slot2/port0/line2:5" и так далее.

Пример 5. До сих пор при создании каналов в качестве агрумента lineGrouping использовалось DAQmx_Val_ChanForAllLines, то есть один вызов функции DAQmxCreateDIChan() создавал ровно один канал, содержащий все перечисленные линии. Но можно создать по одному каналу на линию, если использовать DAQmx_Val_ChanPerLine. Соответственно, каждой линии нужен свой элемент массива:DAQmxCreateDIChan(task, "PXI1Slot2/port0/line0:3", "in1", DAQmx_Val_ChanPerLine); // создаст 4 канала по 1 линии в каждом DAQmxCreateDIChan(task, "PXI1Slot2/port0/line4:7", "in2", DAQmx_Val_ChanForAllLines); // создаст 1 канал из 4 линий uInt8 data[5]; DAQmxReadDigitalU8(task, 1 /* numSampsPerChan */, 10.0, DAQmx_Val_GroupByChannel, data, 5 /* arraySizeInSamps */, NULL, NULL);

Lab 0. Системы автоматизации экспериментальных установок

Линии связи не обязательно состоят из проводов. Бывают беспроводные (радиотелеграф, WiFi, Bluetooth), оптоволоконные...

Точное значение терминов «линия связи», «магистраль», «шина» мне неизвестно (хотя Lab 2 кое-что проясняет) но одно из определений шины говорит, что это когда все устройства подключены к ней одновременно (соответственно, в каждый момент времени только одно может писать, а все остальные читают). Причём необязательно они подключены к одному физическому проводу, например, USB и Ethernet на витой паре (в отличие от коаксиального) требуют, чтобы каждый провод соединял только два устройства; однако благодаря таким устройствам, как концентраторы (hub), шина может содержать гораздо больше двух устройств.

В наше время байт, как и написано в методичке, состоит из 8 бит, однако это не определение, а исторически сложившееся свойство. По определению байт — это наименьшая адресуемая единица оперативного запоминающего устройства (ОЗУ, память). Определение не устанавливает размер байта, существовали даже такие компьтеры, в которых часть памяти адресовалась по 8 бит, а часть по 1 биту. Если в байте 8 бит, его можно называть октетом; особенно часто это слово используется в описаниях сетевых протоколов (так как по сети передаются биты, а ОЗУ отсутствует). Половину октета называют «ниббл» (nibble, nybble) или «тетрада», хотя в разговорной речи встречается «полубайт».

Побитовые операции выполняются с числами одной разрядности. Если они разные, дополняем нулями слева (1 = b01 = b001 = b0001 = 0x1 = 0x01 = 0x001 = 0x0001 = 0x00000001).

Если a — однобитное число, то:

Последние два свойства используются для изменения на противоположные некоторых битов любого числа: пусть в октете b надо поменять биты 0, 1, 2 и 3, а 4, 5, 6 и 7 оставить неизменными. Делаем так: b ^ 0x0f.

Стоит выучить натуральные степени двойки хотя бы до 10: 1, 2, 4, 16, 32, 64, 128, 256, 512, 1024. Также полезны 15-я и 16-я степени: 32 768 и 65 536.

Об оценках за практикум (стр. 16—17, контрольный вопрос 1) написано выше (в общих замечаниях).

Пояснение к контрольному вопросу 8 на странице 17 и вопросу 2 на странице 23: все числа записаны в шестнадцатиричной системе счисления.

Уточнение вопроса 1 на странице 23: младшие 8 бит при этом должны остаться неизменными.

Пояснение к контрольному вопросу 3 на странице 23: все числа записаны в двоичной системе счисления.

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

Lab 1. Изучение среды программирования LabWindows/CVI

(Стр. 12, EVENT_COMMIT) В большинстве случаев (кнопки, переключатели, поля ввода) нужно использовать это и только это событие.

Практическое задание 1 (парабола)

Суть задания — вспомнить язык Си, в частности, объявления и определения переменных и функций, передачу аргументов функций (по значению), области видимости, инициализацию переменных, указатели.

(Стр. 5) Функция CalculateRoots() должна быть именно такой, как описана в методичке. В ней не должно быть ввода коэффициентов или вывода корней. Она должна только прочитать ровно 3 числа (A, B, C) и записать ровно 3 числа (через укзатели pD, pX1, pX2). Придумайте, как организовать работу как с действительными, так и с мнимыми корнями (на мой взгляд, это плохой стиль программирования, но таково задание).

Вам может понадобиться функция NotANumber().

Рекомендуется написать ещё одну функцию для расчёта вершины (возможный прототип void CalculateVertex(float a, float b, float d, float *xa, float *ya)). Обе функции желательно разместить в отдельном (не там, где main()) .c-файле, а прототипы в .h-файле.

Практическое задание 2 (синусоида)

(Стр. 10) Чтобы сгенерировать функцию-обработчик (callback) через контекстное меню элемента, необходимо сначала задать её имя в свойствах элемента, а программа не должна выполняться (если она была запущена и прервана, нажмите Ctrl+F12 или кнопку Stop (Terminate Execution)).

(К пункту 2 задания, стр. 14) Достаточно, чтобы 2 раза в секунду график просто строился заново (с нулевого времени и нулевой фазы). Однако желающие могут усложнить задачу: при перерисовке время на шкале должно увеличиваться (вам понадобится функция PlotXY()), фаза сохраняться, а изменение амплитуды происходить при нулевом напряжении.

(К пункту 5 задания, стр. 15) Под спектром понимается квадратный корень из суммы квадратов действительной и мнимой частей. О преобразовании Фурье можно почитать раздел «Frequency Analysis» в справке «LabWindows/CVI».

Lab 2. Магистрально-модульные системы автоматизации

Биты не всегда передаются по одному проводу. Иногда по двум — дифференциальным парам (RS-485, Ethernet на витой паре, USB, SATA).

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

Достоинства последовательных шин: 1) уменьшение взаимных наводок между проводниками шины (электромагнитная индукция) → можно повысить тактовую частоту; 2) физическое уменьшение линии связи (количество проводов, количество выводов микросхем (значительную часть цены микросхемы составляет корпус), место на плате под разъёмы, микросхемы) → уменьшение цены устройства.

Опечатка на странице 10: не NI-PXI6261, а NI-PXI6251 (на самом деле NI PXI-6251, тогда вообще везде надо исправлять).

Опечатка на странице 13, рисунок 9: не DO0/1 (digital — цифровой), а AO0/1 (analog — аналоговый) (это в бумажно виде; в PDF уже исправлено). В DO перепутано 0 и 1 в крейте и терминальном блоке. Для порта 2 не показаны линии прерываний.

Имеется демонстрационная программа для этой лабораторной работы (в D:\tsani\тесты). Она предполагает, что провода подключены следующим образом:

Практическое задание 1. Работа с портом ввода-вывода

Есть подозрение, что записывать 1 в port2/line0 вообще необязательно. Надо разобраться...

Кнопки и индикаторы лучше нумеровать с 0 до 7 снизу вверх, чтобы совпадали со светодиодами на плате.

На все конопки удобно назначить лишь одну callback-функцию и различать их по агрументу int control, а также использовать switch.

Для установки port2/line0 можно использовать отдельное задание в начале программы и сразу очистить его.

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

Таймер используйте только для реализации функционала кнопки Running Light.

Можно выделить несколько способов работы с кнопками и индикаторами:

  1. Каждой кнопке назначается своя функция обратного вызова (callback). Допустимый, но самый нерациональный способ.

  2. Всем кнопкам (кроме Running Light) назначается одна функция обратного вызова (callback), обращение к каждой кнопке и индикатору идёт по постоянному имени (constant name).

    Этот способ несколько громоздок, так как всё-таки придётся обращаться к каждой кнопке и индикатору, зато всё будет продолжать работать после изменения интерфейса. Насколько я понимаю, так и надо делать в «LabWindows/CVI».

  3. Обращение к каждой кнопке (индикатору) идёт по постоянному имени первой кнопки (индикатора) и смещению.

    Этот способ позволяет написать меньше всего кода. Работу с кнопками (индикаторами) можно производить в циклах. Получается что-то вроде массива кнопок (индикаторов).

    Есть лишь одна проблема: изменение графического интерфейса может всё изменить.

В данной работе следует использовать второй способ, при использовании третьего задание засчитываться не будет. Если же вы хотите совместить достоинства второго (всё продолжает работать после изменений) и третьего (можно работать со сколь угодно большим числом элементов) подходов, создавайте элементы не в редакторе графического интерфейса, а в коде программы (читайте раздел Library Reference → User Interface Library → Overview → Creating a Graphical User Interface справки «LabWindows/CVI»).

Практическое задание 2. Работа с ЦАП и АЦП

Контрольный вопрос к заданию: расскажите о коммутации сигналов (AO0, AO1, AI0, AI1).

Работу с ЦАП и АЦП лучше вынести в отдельный файл (или даже два), чтобы использовать его в следующем задании. Подумайте об именах файла и функций, учитывая, что будет работа и с другими ЦАП и АЦП (например, используйте префикс ni_adc_, ni_dac_).

Практическое задание 3

Контрольный вопрос к заданию: расскажите о коммутации сигналов (AO0, AO1, AI0, AI1, Vin1, Vin2, Vout1, Vout2).

Это задание состоит из 5 частей, причём следующие основываются на предыдущих (и на практическом задании 2). Можно разбить функции следующим образом:

Для большей наглядности кода при работе с портом 2 можно использовать препроцессор:#define CONTROL_DEFAULT 6 // ALE = 0; /READ = /WRITE = 1 #define CONTROL_ALE 7 // ALE = /READ = /WRITE = 1 #define CONTROL_WRITE 2 // ALE = 0; /READ = 1; /WRITE = 0 #define CONTROL_READ 4 // ALE = 0; /READ = 0; /WRITE = 1

Подумайте над типом аргумента *data (знаковость, битность, массив ли это или указатель) в функции Write(board, address, *data). Вы можете вообще использовать целочисленный тип, а не указатель. В любом случае выбор нужно будет обосновать при сдаче.

Как минимум 2 человека из 8 испытывали следующую проблему с чтением регистра: вместо данных читался адрес (хотя на индикаторе магистрали данные были правильные). Возможно, функция чтения магистрали вызывалась при неостановленном задании записи, а модуль NI PXI-6251 «сильнее», чем Avalon-плата ЦАП-АЦП, поэтому в момент чтения на магистрали, действительно, был выставлен адрес, но потом он сменялся на данные, что и отображал индикатор.

Lab 3. Распределённые системы управления и последовательные шины передачи данных

(Стр. 4, USB) Имеется в виду версия 2.0, а скорость указана максимальная мгновенная. Максимальная средняя меньше из-за протокола (про другие шины не знаю, возможно, тоже).

(Стр. 4, Ethernet) Насколько мне известно, минимальная скорость составляет 1 Мб/с. Максимальная скорость зависит от среды передачи и длины, уже есть 10 Гб/с. Максимальная длина тоже зависит. Есть варианты и на 15 м (10 Гб/с витая пара), и на 40 км (оптика). А для 100BASE-T и 1000BASE-T стандарт разрешает 100 м.

(Стр. 4, Ethernet) Количество адресуемых устройств указано для протокола интернета (IP), который может применяться поверх Ethernet. Сам по себе Ethernet Version 2 использует для адресации 48-битный MAC-адрес, причём 1 бит используется как признак широковещательной посылки, остальные биты тоже имеют различные значения.

(Стр. 5, I2C) I2C = Inter-Integrated Circuit. Ещё этот (или слегка модифицированный) протокол обзывают по-другому, например, TWI (Two-wire Serial Interface, двухпроводной последовательный интерфейс).

(Стр. 11, 13) Рисунок 7, а не 8 (или у рисунков поменять номера).

(Стр. 7) «Если получатель не может получить или отправить следующий байт (к примеру, если он занят обработкой сигнала), то он может установить линию SCL в низкое состояние». Не байт, а бит. То есть отправитель после каждого поднятия SCL должен ждать, чтобы она действительно поднялась (на стр. 17-18 об этом написано, но только для контроллеров. А на самом деле так для всех устройств). Но ACK посылается не на каждый бит, а на байт.

(Стр. 10). «...в этой работе используется шина I2C-1». На самом деле шину можно поменять, переключив провод из одного разъёма в другой. Рекомендуется написать программу для работы с обеими шинами.

(Стр. 10). «Сигналы SDAout и SCLout управляют ключами: когда на них подана логическая единица — ключи замкнуты, соответственно на шинах SDA и SCL устанавливается низкий сигнал». Это верно для зелёных разъёмов (клеммников), но сейчас подключение идёт через чёрные (телефонные), а в них всё наоборот.

На плате имеются индикаторы линий D1..D4. Для чёрных разъёмов горящий индикатор означает землю на линии, погашенный — питание. Для зелёных — наоборот.

D1D2D3D4
SDA-1SCL-1SDA-2SCL-2

Записанное значение (которое может не совпадать с состоянием линии) можно отслеживать по индикатору магистрали Avalon (возможно, предварительно придётся записать 1 в port2/line0), причём там (для чёрных разъёмов) горящий индикатор означает питание, а погашенный землю (для зелёных наоборот?).

(Стр. 14). «6) послать сигнал STOP (этот шаг можно опустить);». У меня без STOP не заработало (подозреваю, что тестер нарушает протокол и не понимает REPEATED START).

(Стр. 15, работа с Тестером I2C). «Перед началом работы установите Тестер I2C в режим ведомый с помощью программы». Для этого сначала выберите режим (а также адрес и частоту таймера), а потом поставьте флажок «Применить настройки».

(Стр. 16, работа с термодатчиком). Прежде всего убедитесь, что у вас есть термодатчик (их на всех не хватает).

(Стр. 19, дополнительные задания, пункт 4). Скорость обмена — это количество удачных чтений температуры в единицу времени. Необходимо убрать все задержки в программе (#define DELAY 0) и выполнить измерения для двух случаев: без второго контроллера шины (тестера I2C) и с ним.

(Стр. 21) «#define» не процедура, а директива препроцессора. Для отслеживания состояний линий по индикаторам на плате задержку следует сделать гораздо больше; для реальной же работы (в том числе для отслеживания состояний шины через монитор шины тестера) её можно вообще убрать: обращение к портам и так происходит с достаточно большой задержкой, чтобы тестер успел среагировать (если всё же не успевает, увеличьте частоту таймера. У меня на 100 Гц успевал).

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

Lab 4. Цифрово-аналоговые и аналого-цифровые преобразователи

Практические задания

Для графиков следует использовать Graph, а не Strip Chart, чтобы можно было изменять масштаб: в редакторе интерфейса в свойствах элемента Control Settings → Zoom Style: Zoom to Rectangle. При работе программы Ctrl + LMB + move = увеличить; Ctrl + RMB = уменьшить; Ctrl + Shift + LMB + move = передвинуть (LMB — левая кнопка мыши; RMB — правая; move — двигать мышь).

Чтобы отрисовка происходила быстрее, следует выключить автомасштабирование и установить пределы осей. Для ЦАП надо использовать PlotPoint(), для АЦП можно PlotXY().

Нужно как построить все возможные графики, так и вывести численные значения ошибок (отдельно отрицательных и положительных; как в вольтах, так и в LSB; указать код или напряжение). Числа можно выводить в консоль. У графиков, в соответствии с определением характеристики преобразования, одна ось должна быть в вольтах, а другая в кодах (причём для ЦАП и АЦП это разные оси).

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

После нахождения смещения (ошибки нуля) необходимо вычесть его из всех точек; далее находится ошибка масштаба, и всё опять корректируется в соответствии с ней. Лишь после этого находятся дифференциальная и интегрльная нелинейности, причём они не должны быть целочисленными.

Для наглядности лучше строить все передаточные функции (идеальную, идеальную для АЦП с бесконечной разрядностью, измеренную, измеренную с учётом ошибки нуля, измеренную с учётом ошибки нуля и масштаба) на одном элементе Graph, но добавить возможность включать и выключать их. Остальные графики (интегральная и дифференциальная нелинейности, для АЦП — абсолютная погрешность) также можно построить на одном Graph (но другом).

Практическое задание 1

В разных источниках связь LSB и FS описывается по-разному: где-то коэффициент 2N (в методичке на с. 4 (причём там это подчёркнуто) и в глоссарии «Maxim integrated»), где-то 2N-1 (в фильме). Как на самом деле в нашем случае, я точно не знаю; будем следовать методичке и считать, что 2N, причём напряжение 3,3 В соответствует коду 256.

Есть мнение, что коду 0 и коду 1 соответствует одно и то же напряжение (теоретически 0), поэтому код 0 вообще надо выбросить из рассмотрения.

Практическое задание 2

Контрольные вопросы для допуска к выполнению задания: в каких пределах вы собираетесь подавать напряжения и с каким шагом (сколько напряжений)? Чему равен LSB АЦП Avalon? Чему равен LSB ЦАП NI PXI-6251?

Avalon-АЦП построен на микроконтроллере ATmega64, поэтому рекомендуется прочитать страницы 239 (последний раздел) — 242 (до дифференциальных каналов) его документации. В частности, обратите внимание, что последний переход происходит при -1,5 LSB от максимума, а не -0,5. Поэтому LSB = FS/2N = 2,5 мВ (а не FS/(2N-1) = 2,50244... мВ), а напряжению 2,56 В соответствует код 1024, а не 1023.

При расчёте нелинейностей и абсолютной погрешности необходимо брать передаточную характеристику идеального АЦП с бесконечной разрядностью.

Дополнительные задания: найдите дифференциальную нелинейность. Найдите абсолютную погрешность (absolute accuracy). Абсолютная погрешность находится точно так же, как интегральная, но берутся сырые данные (без учёта ошибок нуля и смещения).

Дополнительное задание к обеим частям

Для каждой точки проведите N измерений и усредните их. Убедитесь, что шум уменьшается как корень из N.


Значок граммар-наци Система Orphus Используйте «Орфус» для краткой анонимной обратной связи. Пишите на site@caesarion.ru чем больше, тем лучше: любые отзывы, пожелания, комментарии, исправления грамматических ошибок, предложения — всё, что связано с этим сайтом, как с содержанием, так и с технической стороной.

Сохранить эту страницу в «Архиве интернета»

Valid HTML 4.01 Strict Valid CSS! Последнее изменение этой страницы: September 04 2014 09:16:46 UTC