Эта страница заменяет прошлогоднюю страницу ТСАНИ-2013 до работы 2 включительно. Дальнейшие работы (3, 4) пока что смотрите там.

Оглавление

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

Основные изменения в 2014 по сравнению с 2013: библиотека DAQmx заменена на (вернее, обёрнута в) TSANI, переписаны методички к работам 1, 2, справочные материалы. Последние версии методичек лежат в электронном виде на компьютерах.

Работы 0...4 = оценка «удовлетворительно». «Хорошо» — по согласованию с преподавателем (например, выполнены задания со звёздочкой). «Отлично» = работы 0...4 выполнены до конца ноября + по согласованию с преподавателем сделаны 1-2 экспериментальные работы, причём одна оформлена как курсовая.

О курсовых работах.

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

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

При создании нового проекта или файла обращайте внимание, в какой папке он создаётся. Прежде всего создайте папку d:\tsani\work\<номер группы>\<фамилия>\. В ней для каждого задания создавайте отдельную папку. Желательно хранить все программы до получения зачёта.

Компьютеры не подключены к сети, а диски доступны на запись всем (то есть любой может случайно стереть все ваши данные), но USB-порты работают, поэтому после каждого занятия делайте резервные копии на свои флешки.

«Корзина» не работает (несмотря на ярлык на рабочем столе), файлы удалаются сразу!

Язык Си и LabWindows/CVI

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

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

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

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

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

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

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

printf принимает переменные, а scanf — их адреса. Аналогично SetCtrlVal и GetCtrlVal.

В старых версиях Си объявлять переменные можно было только в начале блока. Сейчас такое ограничение снято, и LabWindows/CVI поддерживает современные стандарты, но его можно настроить на старые версии. Поэтому в случае ошибки из-за объявления переменных в середине блока (Unrecognized statement, Undeclared identifier) перенесите их в начало или установите флажок Options→Build Options→C Language Options→Build with C99 extensions.

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

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

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

Каждому файлу графического интерфейса (.uir) соответствует одноимённый заголовочный файл (.h), причём по умолчанию он не включается в проект. Но его стоит открыть, чтобы видеть имена элементов графического интерфейса. Обновляется .h-файл каждый раз при сохранении .uir-файла. Численные идентификаторы являются служебной информацией, они могут измениться, поэтому использовать можно только имена. Пример: #define PANEL 1: на 1 вообще не смотрим. Для нас существует лишь PANEL.

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

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

Указатели

Этот раздел можно считать дополнением к 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);.

Подумайте, почему функция int* f(int i) {return &i;} или int* f(int i) {int j = i; return &j;} может вернуть адрес области памяти, в которой не содержится значения i или даже привести к ошибке (времени компиляции или времени исполнения), но может и заработать (в зависимости от компилятора, операционной системы, аппаратного обеспечения и ещё кучи параметров, которые не всегда можно учесть). Можете провести эксперимент: следующий код компилируется в «LabWindows/CVI 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; }

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

Библиотека ТСАНИ

Для использований библиотеки ТСАНИ проекты надо создавать так: File→New→Project from Template→User-Interface Application(TSANI) (по сути это просто User-Interface Application + подключены файлы tsani.h и tsani.lib и сделан #include <tsani.h>). Для желающих увидеть внутренности библиотек: User-Interface Application(TSANI.SRC) (вместо *.lib будут добавлены *.c).

(по состоянию на 16.09.2014) Стр. 48: функция ni6251Close() в настоящее время не работает, так что не используйте её (возможно, потому что в .c-файле не указан int или void).

(по состоянию на 23.09.2014) Если в маске стоит 0 (чтение), потом что-то записали в регистр записи, а потом переключить маску на 1 (запись), то на выходе должно установиться напряжение в соответствии с тем, что записали в регистр записи. Однако библиотека ТСАНИ устроена по-другому, поэтому следует сначала устанавливать маску, а лишь потом писать в регистр записи. И вообще, перед каждым portOut на всякий случай стоит делать portMask.

(по состоянию на 02.12.2014) Библиотека позволяет работать только с ai0, ai1, но в работе 8 (термостат) нужен ai2. Возможно, достаточно создать проект с исходниками и исправить константу с 2 на 3.

Справочные материалы по практикуму ТСАНИ

В следующем издании надо пронумеровать страницы. В разделе 6.3 добавить как минимум символы \n, \t. Добавить поясения к примерам форматирования строк.

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

Стр. 3 (и далее): в коде программы заменить английские кавычки (#include “file1.h”) на машинописные (#include "file1.h").

Стр. 4: параметры main необязательны; вместо *[] может быть **.

Стр. 4: локальные: если нет static. Физически из памяти могут и не удаляться. Другое дело — область видимости.

Стр. 4: прототип не обязательно в самом начале, лишь бы раньше вызова. И можно через include.

Стр. 4: это не объявление, а определение.

Стр. 5: идентичность и даже совместимость необязательна, необходима преобразуемость. Если void — то return; (без каких-либо значений). return может понадобиться не в конце функции даже если не void.

Стр. 6: однострочный комментарий можно продлить склейкой строк \.

Стр. 6: строкового типа на уровне языка не существует. Булев входит в целочисленные. Ещё есть указатели, составные (массивы и структуры), объединения, функции и void. double не подтип float (впрочем, слова «подтип» вообще нет в стандарте, так что можно считать и подтипом) и не обязан отличаться от него.

Стр. 7: таблица приведена для какой-то конкретной реализации (включая LabWindows/CVI 9.0), а не языка Си вообще. Забыт short (2 байта в LabWindows/CVI 9.0).

Стр. 8: список ключевых слов несколько иной.

Стр. 9: не операторы, а операции. Таблицу лучше обновить по Харбисону, Стилу.

Стр. 12: при сдвиге вправо тоже нули. Но в других языках может быть по-другому.

Стр. 14: указатель не обязан содержать (корректный) адрес другой переменной. А ещё есть указатели на функции. Присваивание значения делать необязательно.

Стр. 14: функция может изменять значения глобальных переменных. Но это плохой стиль.

Стр. 16: не объявлять, а создавать (выделять память).

Стр. 17: игнорирование после \0 (и необходимость наличия этого символа в строке) — соглашение, а не свойство языка.

Стр. 20: для записи файл открывается через w, а не r.

Стр. 21: else может не быть.

Стр. 21: лучше сказать, что выполнение не передаётся БЛОКУ, а продолжается с МЕТКИ. Так будет понятнее, почему без break выполнятся и следующие «блоки». default обычно ставят в конец, но не всегда. Можно несколько меток подряд.

Стр. 22: for часто используют для выполнения заданного числа раз. Забыт do while.

Стр. 62: стоит добавить Харбисон, Стил Язык Си с примерами Бином 2011.

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: все числа записаны в двоичной системе счисления.

Предлагаю ещё один контрольный вопрос: как оперировать с числами разной разрядности (ABC|12)? Как такие операнды будут представлены в 16-битных регистрах процессора?

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

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

В следующем издании: исправить ошибки (русский язык, типографика). Убрать номер страницы с титульного листа. Стр. 3: «состоит из следующих составных частей»: тавтология, лучше убрать «составных». «локальную сеть или Интернет» — лучше просто «сеть» (или «интернет» с маленькой буквы). Стр. 5: «проект приложения» — привычнее «(новый) проект/программа». Стр. 7: «на рабочем столе» — я бы написал «на экране». Стр 8: «функция-обработчик событий» — по сути верно, но устоявшийся термин — «функция обратного вызова». Практические задания: разбить обратно на 2 программы. Задание 1: сейчас почти все забывают находить вершину. В проекте-шаблоне на компьютерах убрать всё лишнее (комментарии), а в методичке полностью привести его код с объяснениями. Добавить функции GetCtrlVal, SetCtrlVal и вообще примеров. Добавить теорию о Фурье-анализе.

Стр. 3: опечатка: не GRIB, а GPIB.

(не мой комментарий; я не понял, в чём проблема) Стр. 7: «Область после загрузки главного окна в память...» Переформулировать, а то некоторые студенты думают, что там всё и нужно делать (отображение графиков...).

Стр. 10—11: «Для каждого элемента управления обычно создается отдельная Callback-функция». Далеко не всегда! Иногда одна на несколько элементов. Иногда вообще не создаётся.

Стр. 11—12. В рамках ТСАНИ лучше всегда использовать только EVENT_COMMIT. Исключение — таймер (EVENT_TIMER_TICK) и панель (EVENT_CLOSE).

Стр. 13. Практических заданий не 1 и не 6, а 2. То есть надо написать две программы, причём первая консольная, а вторая разбита на 5 частей (можно показывать преподавателю каждую часть отдельно).

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

Многие студенты и преподаватели забывают, что помимо корней надо ещё и вершину найти.

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

Необязательно объявлять именно указатели (float *pD), а потом выделять и освобождать память (malloc/free). Можно объявить переменную (float D) и передавать в функцию CalculateRoots её адрес (&D).

На мой взгляд, зависимость смысла переменных pX1, pX2 от значения дискриминанта — плохой стиль, но таково задание. Кроме того, задание не совсем корректно (непонятно, как функция CalculateRoots может сообщить о любых корнях). Предлагаю сделать функцию не void, а int, чтобы сообщать о количестве корней. Либо более сложный вариант: ввести параметр int *any: если он true, то корни любые. А чтобы сообщить об отсутствии корней, можно использовать значение «не число» (NaN). В языке Си нет стандартной функции для этого, но в LabWindows/CVI есть: NotANumber() (нужно #include "toolbox.h").

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

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

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

Часть 2: под заготовкой-проектом имеется в виду File→New→Project from Template→User-Interface Application (вместо этого почему-то многие поняли, будто надо взять uir-файл из тестовой программы). Позволю себе слегка упростить задание: диапазоны осей (1000 мс, ±15 В) можно задать в редакторе графического интерфейса. В коде должно быть сначала заполнение массива значениями, а потом вывод этого массива на график. Вам понадобится функция PlotY (для желающих — PlotXY).

Часть 3: так как введённые значения амплитуды и частоты понадобятся только при очередной перерисовке графика, необязательно делать callback на поля ввода, вместо этого можно читать их в таймере. Аналогично в 4-й части задания.

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

Часть 5: можно использовать функции стандартной библиотеки Си: fopen, fprintf, fclose. Но полной документации по ним в LabWindows/CVI нет, в отличие от OpenFile, WriteFile, CloseFile.

Часть 6: под спектром понимается квадратный корень из суммы квадратов действительной и мнимой частей. О преобразовании Фурье можно почитать раздел «Frequency Analysis» в справке «LabWindows/CVI».

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

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

Не забудьте о разделе «Библиотека ТСАНИ».

(не мой комментарий; я не понял, в чём проблема) Стр. 16, пояснение к рисунку: что-то про неодновременность подключения регистров записи/чтения к выводам.

(стр. 20) Из-за особенностей библиотеки ТСАНИ в примере надо поменять местами portOut и portMask.

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

(стр. 18, ещё одно ограничение) Функция обратного вызова таймера должна отрабатывать как можно быстрее, в ней не должно быть задержек (Delay). Она должна вызываться каждые 250 мс. (некоторые студенты вызывали таймер раз в 2 секунды, а внутри него 7 раз делали Delay(0.25), в результате программа обрабатывала события лишь 1/8 времени своей работы. Так нельзя)

(для преподавателей, к заданию 1): поступило предложение исключить требование «Состояние индикаторов всегда должно соответствовать состоянию светодиодов» с аргументированием, что это занимает слишком много времени. Либо я чего-то не понял, либо единственный способ сделать это — вообще отказаться от индикаторов в графическом интерфейсе, а оставить только кнопки. В принципе можно, но ведь много лет до сих пор были и индикаторы, и светодиоды и никто не жаловался... Требуется обсуждение!

Задание 3-1: формулировка неверная: на самом деле нужно написать библиотеку для работы с Avalon (функции инициализации, read и write) и проверить её путём записи и чтения регистра. Так как сама шина 16-битная, интерфейс должен позволять работать с 16-битными числами. От студента требуется объяснить, почему прочитанное не всегда равно записанному. (при нынешней формулировке многие студенты вообще не создают функции, а просто копируют куски кода, меняя лишь числовые константы для обращения к разным регистрам)


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

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

Valid HTML 4.01 Strict Valid CSS! Последнее изменение этой страницы: December 03 2014 04:30:34 UTC