nedoPC.org

Electronics hobbyists community established in 2002
Atom Feed | View unanswered posts | View active topics It is currently 28 Mar 2024 10:43



Reply to topic  [ 2 posts ] 
Анимированная dll-библиотека Proteus для EDUC-8 
Author Message
God
User avatar

Joined: 13 Nov 2010 04:06
Posts: 1345
Reply with quote
Какой колоссальный труд! Снимаю шляпу...

<hr>
Lavr: Спасибо, Виталий! :lol: Но скажу честно, что я это всё делал не торопясь и
с удовольствием, а вовсе не тужась так, чтобы кишки стремились покинуть чрево... :D
Я немного ускорился в начале лета, чтобы не оставлять недоделанного на отпуск,
а так - после того как я отрисовал схемы в привычном ГОСТ-овском виде, мне уже
стало примерно понятно, как и что работает и чем я буду тот или иной узел заменять.
Так что при "сборке" всё работало сразу или почти сразу, что было весьма приятно.

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

Но когда гаджет и по внешнему виду похож на то, что предполагается смоделировать,
то это уже приятно вдвойне, и поэтому я хочу уделить тут некоторое внимание процессу
создания динамической библиотеки Proteus с анимацией, поскольку динамические
библиотеки устройств мы уже разобрали как писать на примере процессора 580ВМ80
,
и, я думаю, моделирование различных передних панелей устройств многим будет интересно,
поскольку позволяет увидеть свой девайс в почти закоченном внешнем виде.

Тем более, что это оказалось не так трудно, как я себе представлял, и я заложил
в свою LEDPANEL.DLL некоторую универсальность, которая может помочь тем, кто хочет
смоделировать свой оригинальный индикатор, но не имеет возможности копаться долго
в хитросплетениях VSM.SDK или же и вовсе не дружит с C/C++...

Посмотрим, как это делается на примере фронтальной панели EDUC-8:

Начать отрисовку передней панели и индикатора лучше всего с вот такого "скелетного"
чертежа, где хорошо видно масштабную сетку, которую желательно выставить равной 100,
чтобы основные габариты были уложены в координаты, кратные 100, поскольку по умолча-
нию Proteus чаще всего начинает работать с такой сеткой = 100, то это позволит будущей
модели избежать возможных проблем с позиционированием.

Image

Ничего особо неприятного не будет, но вот модель с шагом 50 Proteus затруднится точно
поставить на узлы сетки 100, если не будет попадания в 50х50 = 100. Т.е. модель час-
тично останется за узлами сетки и неизвестно, попадут ли в узлы выводы, а если нет, то
разводка шин от них будет кривая. Можете взглянуть как это выглядит на выводах 7-сег-
ментых индикаторов
, т.к. они расположены кратно 50.

Модель рисуется из примитивов прямоугольник, линия, окружность и текст - выделены в
меню на рисунке красными контурами. Подробннее, как надо рисовать, мы уже разбирали
в старте топика о библиотеках Proteus - Proteus C++ DLL's.

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

После того как "скелетный" вид нарисован и удовлетворяет всем собственным предпочтениям,
следует сохранить проект и начать расстановку координатных маркеров. Лучше сохранять
проект на каждом этапе с новым названием, чтобы в случае ошибки вернуться к предыдущему
варианту, а не путаться с правкой. Скажем, сохранили как LEDPANEL_0, а расставляем
маркеры уже в проекте LEDPANEL_1 и т.д.

Маркер расставляется с помощью инструмента-"прицела", выделенного в меню синим контуром.
Первый маркер ставим в левый верхний угол модели, и он определит оси координат на панели
нашей модели. Если на маркер навести курсор мыши и ввести с клавиатуры латинский символ О
(Origin), то в нижней строке статуса Proteus координаты всех элементов будут изображаться
розовым цветом и отсчитываться от этого маркера в указанных осях координат (ось Y - вниз,
действительно имеет отрицательные значения).

Теперь надо расставить маркеры координат возле или на всех отображаемых индикаторах панели.
Эти маркеры нужны нам для "прицеливания" при определении координат индикаторов на панели.
Я не поставил маркеры для круглых "светодидов" по центру, т.к. цвет мешает прицелиться.
А без цвета было несколько затруднительно оценить примерный внешний вид.
Но вот у маленьких прямоугольных двухцветных индикаторов маркеры находятся сверху и по центру,
т.к. маркеры нарисованы в сетке кратной 50, то узел сетки = 100 - аккурат посредине.

Теперь довольно занудная процедура - надо записать все координаты элементов индикации
относительно маркера левого верхнего угла. Для этого активизируем этот маркер, наведя на
него курсор мыши и введя с клавиатуры латинский символ О (Origin - отключается так же: O),
и теперь прицеливаемся курсором мыши в расставленные ранее маркеры координат индикаторов.
Главное, Origin для маркера левого верхнего угла во время этих процедур не отключать, иначе
координаты, которые отображаются в нижней статусной строке Proteus, не будут правильными.
Координаты желательно занести в текстовый документ с какой-либо осмысленной нумерацией,
чтобы в дальнейшем точно знать к какому индикатору они относятся.

Я, например, сделал это следующим образом:
Code:
/--- подсветка нижнего ряда ---
/--- слева-направо (Х, У, Номер гр.символа индикатора (1...5) > LEDPANEL_1_х ... LEDPANEL_5_х)
400,-2000,5
800,-2000,5
1200,-2000,5
1600,-2000,5
2000,-2000,5
2400,-2000,5
2800,-2000,5
3200,-2000,5

4200,-2000,5
4600,-2000,5
5000,-2000,5

6000,-2000,5
6400,-2000,5
6800,-2000,5
7200,-2000,5
7650,-2000,5

/--- подсветка PC
300,-300,1
700,-300,1
1100,-300,1
1500,-300,1
1900,-300,1
2300,-300,1
2700,-300,1
3100,-300,1
/--- подсветка MA
300,-700,3
700,-700,3
1100,-700,3
1500,-700,3
1900,-700,3
2300,-700,3
2700,-700,3
3100,-700,3
/--- подсветка MB
300,-1100,4
700,-1100,4
1100,-1100,4
1500,-1100,4
1900,-1100,4
2300,-1100,4
2700,-1100,4
3100,-1100,4
/--- подсветка AC
300,-1500,2
700,-1500,2
1100,-1500,2
1500,-1500,2
1900,-1500,2
2300,-1500,2
2700,-1500,2
3100,-1500,2
/--- подсветка КОП
4500,-300,1
4500,-700,2
4500,-1100,3
4500,-1500,4
4900,-300,4
4900,-700,3
4900,-1100,2
4900,-1500,1
/--- подсветка РЕЖИМА
7100,-300,1
7100,-700,2
7100,-1100,3
7100,-1500,4

Координаты следует точно проверить, иначе позже индикатор нас удивит, высветив элемент
в совсем неожиданном месте! :lol:

Теперь из "скелетной" модели сделаем корпус элемента. Сохраним проект с новым названием,
выделим весь рисунок контуром с нажатой правой кнопкой мыши (в Proteus v7.xx - левой
кнопкой мыши) и командой copy, выделенной на следующем рисунке красным контуром в верхнем
меню, получаем копию рисунка на второй половине листа. На этом рисунке выделяем контуром
и копируем ниже рисунка 8 круглых индикаторов ВМЕСТЕ С МАРКЕРАМИ и 3 прямоугольных инди-
катора
ВМЕСТЕ С МАРКЕРАМИ - они нам понадобятся позже.

Image

С самогО рисунка удаляем все круглые индикаторы ВМЕСТЕ С МАРКЕРАМИ, оставляем только
маркер левого верхнего угла. Раскрашиваем тело и окна модели в нужные нам цвета.
Этот рисунок будет представлять собой модель в неактивном состоянии - так мы её будем
видеть, когда симуляция не включена, поэтому окна светодиодов - тёмного коричневого
цвета, контуров диодов не видно, маленькие прямоугольные индикаторы - также неактивного
тёмного коричневого цвета. Цвет панели я выбрал серый.

Этот рисунок будет стартовым при анимации модели библиотекой LEDPANEL.DLL поэтому в
Proteus принято группировать графические элементы анимированной модели под общим именем
с индексами. Этот элемент по этому принципу получает название LEDPANEL_C, видимо от
английского "Corpus" - корпус, тело.
Так и обозначим его ниже с помощью текстового скрипта (выделен синим контуром в меню
слева): LEDPANEL_C - это чтобы не забыть самим. В создаваемый под этим именем графичес-
кий символ текстовый скрипт LEDPANEL_C - НЕ ВХОДИТ !!!


Теперь, когда рисунок тела готов, создаем из него графический элемент библиотеки графи-
ческих символов Proteus:

Image

Выделяем весь рисунок вместе с маркером левого верхнего угла НО БЕЗ НИЖНЕГО ТЕКСТОВОГО
СКРИПТА
контуром с нажатой правой кнопкой мыши (в Proteus v7.xx - левой кнопкой мыши)
в верхнем меню Edit применяем к рисунку опцию Send to back - "тело" всегда должно быть
позади всего, что на нем нарисуют. И, не снимая выделения с рисунка, в меню Library
выбираем опцию Make Symbol. И вот уже в окне этой опции вписываем название этого графи-
ческого символа: LEDPANEL_C, указываем галочку Graphic и помещаем элемент в библиотеку
пользователя - USERSYM. Подтверждаем 'OK', и элемент LEDPANEL_C сохранён в библиотеке.

Теперь следует подготовить вид включенного "тела". Наш проект анимации относится по типу
к Bitwise, где у элементов подразумевают наличие двух состояний "включено" и "выключено",
или "активно" и "не активно", поэтому все остальные графические элементы модели будут иметь
два индекса у имени. Сейчас создаем вид включенного, но "неактивного" "тела" под именем
LEDPANEL_0_0. Парой ему будет графический элемент включенного и "активного" "тела" под
именем LEDPANEL_0_1. Скажем у LEDPANEL_C - экран неактивный тёмный, включенный он приобре-
тает более яркий цвет, а когда он "активен" цвет экрана совсем светлый, но это всё довольно
условные понятия и всё зависит от того, что и как мы выведем в программе библиотеки DLL.
Эти индексированные имена просто дают конкретную связь между рисунком графического элемента
и командой вывода элемента на экран, а какую связь - рассмотрим чуть позже.

Чаще всего элемент LEDPANEL_0_0 призван стереть или закрасить сразу всё, что происходило на
передней панели. Я изобразил его вот так:

Image

Это - вся передняя панель, кроме самогО корпуса панели. Но в окнах я обозначил контуры потухших
круглых индикаторов, а маленькие прямоугольные индикаторы стали светлее. На анимации это вы-
глядит так, будто панель включилась. А вот элемент LEDPANEL_0_1 я использовал более своеобразно,
поскольку нижние маленькие прямоугольные индикаторы у нас имеют 3 состояния: красный, синий и
"не горит", так вот LEDPANEL_0_1 и будет - "не горит". Модель-то типа Bitwise! Тристабильных
состояний индикатора в ней нет, так что задействуем элемент LEDPANEL_0_1 нетрадиционно.

Опять сохраняем проект с новым именем, чтобы безболезненно вернуться к старому в случае ошибки,
удаляем у рисунка серый корпус, но оставляем на месте маркер левого верхнего угла. Накладываем копией
со "скелетной" модели контуры круглых индикаторов, предварительно удалив их координатные
маркеры и заполнение цветом. Нижние маленькие прямоугольные индикаторы заполняем тёмно-красным
цветом (он светлее тёмно-коричнегого). Кликнув текстовый скрипт - меняем название на:
LEDPANEL_0_0 - опять же это просто для себя, чтобы не путаться, в графический элемент скрипт не
входит.
Снова выделяем весь рисунок вместе с маркером левого верхнего угла НО БЕЗ НИЖНЕГО ТЕКСТОВОГО
СКРИПТА
контуром с нажатой правой кнопкой мыши (в Proteus v7.xx - левой кнопкой мыши) и в
верхнем меню Edit применяем к рисунку опцию Send to back - "активное тело" всегда должно
быть позади всего, что на нем нарисуют. И, не снимая выделения с рисунка, в меню Library
снова выбираем опцию Make Symbol. В окне этой опции вписываем название этого графического
символа: LEDPANEL_0_0, указываем галочку Graphic и помещаем элемент в библиотеку пользова-
теля - USERSYM. Подтверждаем 'OK', и элемент LEDPANEL_0_0 сохранён в библиотеке.

Теперь создадим графические символы всех "активных" и "не активных" индикаторов, для чего
раскрасим заливкой цветом их модели, ранее сохраненные ниже. Мне понадобилось 8 + 3 эле-
мента
, поскольку мне было необходимо для круглых индикаторов 4-х цветов и прямоугольных
двух цветов в одной позиции, плюс один символ - на погашенный.
Цвета "активных" и "не активных" индикаторов хорошо видны на рисунке, для оранжевого был
выбран цвет "не активный" также тёмно-красный, т.к. тёмно-оранжевый уж больно противный
цвет. Все индикаторы подписаны текстовыми скриптами индексированными именами общей группы
LEDPANEL_x_x. Напоминаю, что текстовые скрипты подписи НЕ ВХОДЯТ в графический символ!!!

Создаем все графические символы индикаторов. Для чего последовательно обводим каждый из
них, как на рисунке, БЕЗ НИЖНЕГО ТЕКСТОВОГО СКРИПТА контуром с нажатой правой кнопкой
мыши (в Proteus v7.xx - левой кнопкой мыши) и в верхнем меню Edit применяем к рисунку
опцию Bring to front: индикаторы всегда должны быть над "телом" панели.
Опять же, не снимая выделения с рисунка, в меню Library снова выбираем опцию Make Symbol.
В окне этой опции вписываем название соответствующее текстовому скрипту каждого графического
символа: LEDPANEL_х_х, указываем галочку Graphic и помещаем элемент в библиотеку пользова-
теля - USERSYM. Подтверждаем 'OK', и элементы LEDPANEL_х_х последовательно сохраняются в
библиотеке USERSYM.
Всё в точности, как мы уже делали ранее для элементов LEDPANEL_C и LEDPANEL_0_0.

После того, как все операции проделаны, убедимся, что у нас есть в наличии все созданные
нами графические элементы и мы нигде не ошиблись. Иначе придется вернуться на пару шагов
назад и снова повторить создание элементов.
С этой целью в меню слева нажимаем пиктограмму S на зеленом фоне ([S]-2D Graphics Symbols
Mode
) после чего должен появиться список графических символов модели, где следует их всех
просмотреть, нет ли дублирования и не забыт ли какой-либо элемент.

Image

Также следует убедиться, что все элементы действительно занесены в графическую библиотеку
USERSYM Proteus. Для этого нажимаем выделенную красным контуром на рисунках выше кнопку L
(Library), и в открывшемся окне в библиотеке USERSYM должны находиться все те же названия
графических элементов.

Image

Если всё верно, то графические элементы для анимации модели подготовлены, и следует приступить
к расстановке выводов модели. В этом процессе надо быть очень внимательным, поскольку нуме-
рация выводов во внутреннем представлении Proteus происходит по мере их появления на листе
модели. Эта внутренняя нумерация никак не связана с названием выводов и присвоенным им нами
номерам, так что этот процесс желательно провести безошибочно расставляя номера на выводах
также по мере их появления на листе, чтобы внутренняя нумерация и присвоенные нами номера
сразу совпали. Позже ошибку можно будет выявить только с отладочной версией DLL, и при большом
количестве выводов исправлять ошибку весьма трудно.

Расставлять выводы будем у изображения "тела", которое мы сохраняли как LEDPANEL_C :

Image

Для расстановки выводов выбираем в левом меню Proteus пиктограмму, похожую на изображение
операционного усилителя
(треугольник, два зеленых вывода спереди и один - сзади: Device pin.)
Тип вывода - DEFAULT (иногда удобнее SHORT, но он скрывает автоматически подписи имен выводов).
Главная особенность расстановки, как мы видим на рисунке выше, что для нижних индикаторов
необходимо выставить по 2 вывода, но мы их расставляем не подряд. Сначала - выводы 0, 1...59,
для всех индикаторов логический "0" на этих выводах - признак включения и отрисовки яркого
графического элемента
. Но для маленьких прямоугольных индикаторов - это отрисовка яркого
красного графического элемента
, а вот когда выводы 0, 1...59 расставлены и последовательно
пронумерованы, то снизу расставляем выводы с номерами 60, 61...74.
Логический "0" на выводах 60, 61...74 - отрисовка яркого синего графического элемента.

Image

Немного о том, что является причиной строгости в нумерации: это очень упрощает алгоритм
программы анимации - номер вывода внутри библиотеки сразу будет индексом массива состояний
индикаторов. Это исключает необходимость в структуре case и любой перекодировке.
По номеру вывода устанавливаем в массив true или false и сразу высвечиваем нужное состояние
индикатора в виде соответствующего графического символа.
Кстати - смотрел на картинку и сам увидел вроде как ошибку: после номера вывода 73 у меня
проставлен следующий номер 75, а не 74... но ведь работает верно! :o А у Proteus - его свой
внутренний номер всё равно - 74, не смотря на то, что промаркирован как 75, поскольку вывод
выставлен на лист 74-м по порядку. Порядок здорово всё упрощает!
Мне, правда, говорили на Kazus.ru, что нельзя закладыватся на столь неопределённую и не
описанную в хелпах фичу... я, было, призадумался - но при всех тестах сбоев не наблюдал
в 3-х версиях Windows, 4-х разных Proteus и 4-х разных компьютерах. Так что оставил всё -
как есть, поскольку быстро, просто и надежно работает!

Теперь остается предпоследний шаг перед созданием активной модели - отредактировать её
текстовый скрипт, в котором ранее было написано только LEDPANEL_C. При компиляции модели
ТЕКСТОВЫЙ СКРИПТ ОБЯЗАТЕЛЬНО ДОЛЖЕН БЫТЬ ВЫДЕЛЕН АКТИВНЫМ ВМЕСТЕ В КАРТИНКОЙ И ВЫВОДАМИ!

В начале скрипта идут следующие строки:
Code:
{*DEVICE}
NAME=LEDPANEL
{PREFIX=U}
{ACTIVE=LEDPANEL,6,BITWISE,DLL}
{HELP=DISPLAYS>POPUP,1}

{*PROPDEFS}
{MODDLL="VSM Model",READONLY STRING}
{PRIMITIVE="PRIMITIVE",HIDDEN STRING}

{ALEVEL="ALEVEL",HIDDEN STRING}
{INVERT="INVERT",HIDDEN STRING}
{TTRIGMIN="Trigger Time",HIDDEN STRING}
{BITWISE="BITWISE",HIDDEN STRING}
{NUM="NUM",HIDDEN STRING}

{A0="A0",HIDDEN STRING}
{A1="A1",HIDDEN STRING}
{A2="A2",HIDDEN STRING}
{A3="A3",HIDDEN STRING}
{A4="A4",HIDDEN STRING}
{A5="A5",HIDDEN STRING}
{A6="A6",HIDDEN STRING}
{A7="A7",HIDDEN STRING}
{A8="A8",HIDDEN STRING}
{A9="A9",HIDDEN STRING}
{A10="A10",HIDDEN STRING}
{A11="A11",HIDDEN STRING}
{A12="A12",HIDDEN STRING}
{A13="A13",HIDDEN STRING}
{A14="A14",HIDDEN STRING}
{A15="A15",HIDDEN STRING}
{A16="A16",HIDDEN STRING}
{A17="A17",HIDDEN STRING}
{A18="A18",HIDDEN STRING}
{A19="A19",HIDDEN STRING}
{A20="A20",HIDDEN STRING}
{A21="A21",HIDDEN STRING}
{A22="A22",HIDDEN STRING}
{A23="A23",HIDDEN STRING}
{A24="A24",HIDDEN STRING}
{A25="A25",HIDDEN STRING}
{A26="A26",HIDDEN STRING}
{A27="A27",HIDDEN STRING}
{A28="A28",HIDDEN STRING}
{A29="A29",HIDDEN STRING}
{A30="A30",HIDDEN STRING}
{A31="A31",HIDDEN STRING}
{A32="A32",HIDDEN STRING}
{A33="A33",HIDDEN STRING}
{A34="A34",HIDDEN STRING}
{A35="A35",HIDDEN STRING}
{A36="A36",HIDDEN STRING}
{A37="A37",HIDDEN STRING}
{A38="A38",HIDDEN STRING}
{A39="A39",HIDDEN STRING}
{A40="A40",HIDDEN STRING}
{A41="A41",HIDDEN STRING}
{A42="A42",HIDDEN STRING}
{A43="A43",HIDDEN STRING}
{A44="A44",HIDDEN STRING}
{A45="A45",HIDDEN STRING}
{A46="A46",HIDDEN STRING}
{A47="A47",HIDDEN STRING}
{A48="A48",HIDDEN STRING}
{A49="A49",HIDDEN STRING}
{A50="A50",HIDDEN STRING}
{A51="A51",HIDDEN STRING}
{A52="A52",HIDDEN STRING}
{A53="A53",HIDDEN STRING}
{A54="A54",HIDDEN STRING}
{A55="A55",HIDDEN STRING}
{A56="A56",HIDDEN STRING}
{A57="A57",HIDDEN STRING}
{A58="A58",HIDDEN STRING}
{A59="A59",HIDDEN STRING}

{*INDEX}
{CAT=Optoelectronics}
{SUBCAT=LED Panels Display}
{MFR=ExUSSR}
{DESC=Interactive LED Panel for EDUC-8}

Я их всех подробно разбирать не буду, поскольку большинство стандартных параметров разобрали
в топике Proteus C++ DLL's

Для использования в других моделях важна строка:
{ACTIVE=LEDPANEL,6,BITWISE,DLL} - у модели 6 пар изображений LEDPANEL_0_х ... LEDPANEL_5_х
группы LEDPANEL, изображения парные. Если у модели их иное число, то 6 надо изменить!

А вот о вновь веденных параметрах специально для этой модели - скажу:
{ALEVEL="ALEVEL",HIDDEN STRING} - параметр ALEVEL (Active LEVEL) "1" или "0" (всегда "0").

{INVERT="INVERT",HIDDEN STRING} - параметр INVERT (pin) надо ли инвертировать значение вывода
этот параметр в модели не учитывается, заложен для дальнейшего развития.

{TTRIGMIN="Trigger Time",HIDDEN STRING} - параметр времени переключения - также в модели не
учитывается, переключение мгновенное, заложен для дальнейшего развития.

{BITWISE="BITWISE",HIDDEN STRING} - параметр двух состояний для индикаторных элементов,
учитывается и важен, так как определяет порядок индексов графических символов.

{NUM="NUM",HIDDEN STRING} - параметр числа выводов и количества индикаторов - не может быть
более 60
, т.к. максимальный размер массива состояний в модели заложен 60 (0...59).

{A0="A0",HIDDEN STRING} - параметры через которые вводятся ранее записанные в текстовый
... файл координаты индикаторов и указатели на их графические символы:
{A59="A59",HIDDEN STRING} (1...5) <==> LEDPANEL_1_х ... LEDPANEL_5_х

После того, как переменные определены, заполняем их значениями, ранее сохраненными в текстовом
файле, учитывая соответствие, что активный вход 0 "зажигает" элемент A0, вход 1 "зажигает"
элемент A1, ... и т.д.
Code:
{NUM=60}
{A0=400,-2000,5}
{A1=800,-2000,5}
{A2=1200,-2000,5}
{A3=1600,-2000,5}
{A4=2000,-2000,5}
{A5=2400,-2000,5}
{A6=2800,-2000,5}
{A7=3200,-2000,5}
{A8=4200,-2000,5}
{A9=4600,-2000,5}
{A10=5000,-2000,5}
{A11=6000,-2000,5}
{A12=6400,-2000,5}
{A13=6800,-2000,5}
{A14=7200,-2000,5}
{A15=7650,-2000,5}
{A16=3100,-300,1}
{A17=2700,-300,1}
{A18=2300,-300,1}
{A19=1900,-300,1}
{A20=1500,-300,1}
{A21=1100,-300,1}
{A22=700,-300,1}
{A23=300,-300,1}
{A24=3100,-700,3}
{A25=2700,-700,3}
{A26=2300,-700,3}
{A27=1900,-700,3}
{A28=1500,-700,3}
{A29=1100,-700,3}
{A30=700,-700,3}
{A31=300,-700,3}
{A32=3100,-1100,4}
{A33=2700,-1100,4}
{A34=2300,-1100,4}
{A35=1900,-1100,4}
{A36=1500,-1100,4}
{A37=1100,-1100,4}
{A38=700,-1100,4}
{A39=300,-1100,4}
{A40=3100,-1500,2}
{A41=2700,-1500,2}
{A42=2300,-1500,2}
{A43=1900,-1500,2}
{A44=1500,-1500,2}
{A45=1100,-1500,2}
{A46=700,-1500,2}
{A47=300,-1500,2}
{A48=4500,-300,1}
{A49=4500,-700,2}
{A50=4500,-1100,3}
{A51=4500,-1500,4}
{A52=4900,-1500,1}
{A53=4900,-1100,2}
{A54=4900,-700,3}
{A55=4900,-300,4}
{A56=7100,-300,1}
{A57=7100,-700,2}
{A58=7100,-1100,3}
{A59=7100,-1500,4}

После того, как переменные определены, заполняем их значениями, ранее сохраненными в текстовом
Есть один нобязательный параметр {POWERPIN}, логический "0" или уровень GND на нём - выключает
активную индикацию модели, при остальных вариантах индикация работает.
По умолчанию:
{POWERPIN=76}
поскольку вывод питания модели LEDPANEL действительно 76, но если библиотеку LEDPANEL.DLL
использовать для анимации других моделей, номер вывода питания можно изменить этим параметром.

Текстовый скрипт тщательно подготовлен, и это очень упростит нам последний этап компиляции
модели, поскольку придется нажимать лишь кнопку "Далее".

Выделяем весь рисунок вместе с маркером левого верхнего угла и ВМЕСТЕ С НИЖНИМ ТЕКСТОВЫМ
СКРИПТОМ
контуром с нажатой правой кнопкой мыши (в Proteus v7.xx - левой кнопкой мыши)
в верхнем меню Proteus выбираем и нажимаем пиктограмму Make Device.

Image

Порядок окон компиляции модели и назначение опций в них подробно описаны в топике Proteus C++ DLL's
и, как я уже сказал выше, все параметры внесены нами заранее, единственное, что придется
выбрать - это библиотеку Proteus, где будет храниться модель. Выбираем - USERDVC.
Позже можно будет создать любую собственную библиотеку и перенести модель в неё.

Если компиляция прошла успешно - у нас в панели компонентов проекта появится собственный
компонент - LEDPANEL. Можем поместить его на лист, чтобы оценить на вид свою работу.

Image

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

Image

После запуска симуляции Proteus выдаст ошибку, что LEDPANEL.DLL не найдена - это верно! 8)
Мы её ещё не написали! И о том, как это сделать, я расскажу далее...

Я ничего не рассказал о модели клавиатуры, поскольку нарисовал её и указал параметры
для её стандартной библиотеки Proteus - KEYPAD.DLL - используя материалы сайта Kazus.ru:
9.6. KEYPAD.DLL - матричные клавиатуры на любой вкус.
<hr>
Теперь перейдём к программным изобразительным средствам, предоставляемым VSM.SDK для
анимированных моделей Proteus.
И прежде всего - как будут отображаться приготовленные нами графические символы:

Image

Для этого существуют функции следующего типа:
Code:
component->drawsymbol(-1); //--- Рисует LEDPANEL_C,  (-1) - его номер
component->drawsymbol(0);  //--- Рисует LEDPANEL_0_0, (0) - его номер
component->drawsymbol(1);  //--- Рисует LEDPANEL_0_1, (1) - его номер

остальные графические символы - "спрайты", поскольку их координаты и номера заносились
в массив 0, 1...59 при инициаллизации модели, выводились на экран вот так:
Code:
component->drawsymbol(dx[j],dy[j],0,0,sp[j]*2+1); //--- "зажигаем" активный индикатор
component->drawsymbol(dx[j],dy[j],0,0,sp[j]*2);   //--- "тушим" НЕ активный индикатор

Подробнее об этих функциях лучше прочитать в VSMSDK.HLP, поскольку я его пересказывать
не намерен, а написан он весьма понятно, хотя кратко и примеров маловато.
Я не из вредности это делаю, а просто опыт мне подсказывает, что после чтения разных
чужих пересказов, я всё же внимательно VSMSDK.HLP сам прочитал, и "процесс пошел"... :wink:

Кроме вывода наших "спрайтов" на экран, VSM.SDK предоставляет следующие изобразительные
функции:
Code:
Vector drawing services:
HGFXSTYLE ICOMPONENT::creategfxstyle (CHAR *name)
VOID ICOMPONENT::selectgfxstyle (HGFXSTYLE style)
VOID ICOMPONENT::setpenwidth (INT w)
VOID ICOMPONENT::setpencolour (COLOUR c)
VOID ICOMPONENT::setbrushcolour (COLOUR c)
VOID ICOMPONENT::drawline (INT x1, INT y1, INT x2, INT y2)
VOID ICOMPONENT::drawbox (INT x1, INT y1, INT x2, INT y2)
VOID ICOMPONENT::drawbox (BOX &bx)
VOID ICOMPONENT::drawcircle (INT x, INT y, INT radius)
VOID ICOMPONENT::drawbezier (POINT *p, INT numpoints)
VOID ICOMPONENT::drawpolyline (POINT *p, INT numpoints)
VOID ICOMPONENT::drawpolygon (POINT *p, INT numpoints)
VOID ICOMPONENT::drawsymbol (INT symbol)
VOID ICOMPONENT::drawsymbol (INT x, INT y, INT rot, INT mir, INT symbol)
VOID ICOMPONENT::drawstate (ACTIVESTATE state)
VOID ICOMPONENT::getsymbolarea (INT symbol, BOX *area)
BOOL ICOMPONENT::getmarker (CHAR *name, POINT *pos, INT *rot, INT *mir);

Text output services:
HTEXTSTYLE ICOMPONENT::createtextstyle (CHAR *name)
VOID ICOMPONENT::selecttextstyle (HTEXTSTYLE style)
VOID ICOMPONENT::settextfont (CHAR *name)
VOID ICOMPONENT::settextsize (INT h)
VOID ICOMPONENT::setbold (BOOL f)
VOID ICOMPONENT::setitalic (BOOL f)
VOID ICOMPONENT::setunderline (BOOL f)
VOID ICOMPONENT::settextcolour (COLOUR c)

VOID ICOMPONENT::drawtext (INT x, INT y, INT rot, INT jflags, CHAR *text, ...)

То есть, как мы видим, рисуй - нехочу... пиши - не хочу! :roll:

Я из всех этих функций использовал только следующие:
Code:
//--- инициаллизация переменных для работы с текстом ------------
// Get origin and style for readout text
   BOX textbox; //--- struct BOX  { LONG x1, y1, x2, y2; };
// Устанавливаем расположение спрайта -1 или 0 или 1 ...
//
   cpt->getsymbolarea(0, &textbox); //--- "0" - LEDPANEL_0_0
// на котором будет отображаться выводимый текст
// textorg - struct POINT { LONG x, y; };
   textorg.x = (textbox.left + textbox.right)/10; // ближе к левому краю
   textorg.y = (textbox.top + textbox.bottom)/4;  // ближе к верху
// Создаем текстовый стиль, в котором будет выводиться текст
//
   textstyle = cpt->createtextstyle(NULL); // NULL == "ACTIVE READOUT"

// Initial readout: начальная инициаллизация текстовой переменной readout
   strcpy(readout, " Test: ");
// выводим текст
   component->drawtext(280, -100, 0, TXJ_LEFT|TXJ_MIDDLE, readout);

// рисуем рамку заполненную серым цветом
   component->setpencolour (GREY);
   component->drawbox (1100, -1830, 2500, -1950);

// выводим НАДПИСЬ ДЛЯ ПОЛЬЗОВАТЕЛЯ ОБ АНИМАЦИИ
   component->setbold (TRUE);
   component->settextsize (180);
   component->settextcolour (0x0000ff);

// выводим текстовые символы текущего значения at textorg.x, textorg.y,
   strcpy(readout, "If you want  to  force  Front Panel  animation,");
   component->drawtext(280, -300, 0, TXJ_LEFT|TXJ_MIDDLE, readout);

Здесь я просто показал примеры использования некоторых из функций VSM.SDK, поскольку
примеров их применения в VSMSDK.HLP нет, а не всё, порой, сразу очевидно...

Но все эти обширные возможности нельзя применять там, где вздумается, а лишь сугубо в двух
конкретных вызовах, отвечающих в структуре DLL за графические возможности.
И первая из них это:
Code:
// ************************************************************
// P L O T
// отвечает за отрисовку раз за кадр, когда экран перерисовывается
// Proteus раз за кадр, при изменении масштаба, при отрисовке после
// выхода из-под другого окна... и т.д.
// ************************************************************
//
VOID LEDPANEL::plot (ACTIVESTATE state)
{
 //--- Рисуем компоненты основного изображения
   component->drawsymbol(-1);

 //--- Рисуем дисплей активным (видны диоды)
   component->drawsymbol(0);

 //--- Обновляем все элементы индикации
   ...
}

То есть PLOT - это по сути REPAINT после того, как вид анимируемой модели испорчен
какими-то внешними событиями и нуждается в восстановлении целиком.
PLOT никогда и ниоткуда не вызывают прямым образом - он активируется сам в нужных
случаях.

Вторая, и это главная функция вывода графики модели:
Code:
// ************************************************************
// A N I M A T E
// отвечает за мгновенную отрисовку - то, что случилось СЕЙЧАС!
// если в свойствах PROTEUS выключена анимация, то этот вызов
// не срабатывает автоматически и его нужно вызвать принудительно.
//
// Но обычно вызов происходит в конце кадра симуляции из ф-ции
// indicate (), если она указала тип данных в ACTIVEDATA и вернула
// в этой структуре данные такого типа, подтвердив вызов animate ()
// возвратом TRUE.
// ************************************************************
//
VOID LEDPANEL::animate (INT element, ACTIVEDATA *data)
{

//--- вот здесь-то и нужно рисовать всю анимацию модели

    if (bootstrt)
       {//--- если это самый первый вход в анимацию
      if (data->type == ADT_PINSTATE)
         {//--- с указанием состояния выводов
          component->drawsymbol(0);  //--- отрисуем LEDPANEL_0_0 "включилось"!
          bootstrt = FALSE; //--- и больше её рисовать не будем
         }
      }
//--- ну и т.д.
  ...
}

Вот на особенности работы Proteus с вызовом ANIMATE и основана работа моей DLL.
Дело в том, что когда в верхнем меню Proteus 'System' выбрать опцию 'Animated
Circuits Config
' и в ней выставить галочку 'Show Logic State of Pins?', то сам
Proteus будет выставлять на выводах нашей модели логические состояния в виде
цветных квадратиков: красных, синих, серых и желтых.
Эти логические состояния определены в Proteus как:
Code:
State Type    Keyword Description
-----------------------------------------------------------------------
Power High      PHI   Logic 1 power rail.
Strong High     SHI   Logic 1 active output.
Weak High       WHI   Logic 1 passive output.
Floating        FLT   Floating output - high-impedance.
Weak Undefined  WUD   Mid voltage from analogue source.
Contention      CON   Mid voltage from digital conflict.
Weak Low        WLO   Logic 0 passive output.
Strong Low      SLO   Logic 0 active output.
Power Low       PLO   Logic 0 power rail.

И самое приятное, что при включенной анимации Proteus сам вызывает ANIMATE,
подавая ей на вход номер вывода в своём внутреннем представлении INT element
и его состояние: ACTIVEDATA *data
Остается лишь проверить состояние на низкий логический уровень:
Code:
BOOL LEDPANEL::itactive(INT element, ACTIVEDATA *data)
{
  if((data->stateval == WLO)||(data->stateval == PLO)||(data->stateval == SLO))
    {
     return TRUE;
    }
 return FALSE;
}

и отрисовать индикаторы, как уже обсуждалось выше: INT element - это индекс координат
и номера "спрайта" индикатора в массиве 0, 1...59.

Конечно, если 'Show Logic State of Pins?' выключено, то и анимация нашей панели не
работает. В этом случае она выведет предупреждение, что анимацию надо включить, хотя
и без анимации передней панели вся модель EDUC-8 работает совершенно нормально, только
без индикации.


27 Jun 2014 09:02
Profile
Supreme God
User avatar

Joined: 21 Oct 2009 08:08
Posts: 7777
Location: Россия
Reply with quote
Я не стал делать ничего более сложного, поскольку анимация логического состояния
выводов у меня и так всегда включена - если "пины заморгали", значит что-то работает,
а не повисло наглухо по неизвестной причине.

Ну а теперь всё же о том, как делать сложно и по-хорошему...

Здесь очень важна следующая функция, которая программно, но весьма косвенным образом
вызывает ANIMATE, передавая ей нужные параметры. И эта функция:
Code:
// ***************************************************************
// I N D I C A T E
// ***************************************************************
//---- Эта функция вызывается PROSPICE в конце каждого кадра анимации. Она позволяет
//---- электрической модели возможность передать информацию в графическую модель.
//
//  A model must return TRUE on the first call to its indicate function
//  if it wishes to receive any further calls.
//  If it returns FALSE, its indicate function will not be called at any
//  subsequent time.
//  Модель должна возвратиться TRUE в первом вызове своей функции indicate,
//  если она хочет получить дальнейшие вызовы.
//  Если она возвращает FALSE, то функция indicate в последующем вызываться
//  ни ISPICE, ни IDSIM не будет.
//
//  If the model does not assign a data type to the ACTIVEDATA structure,
//  no data will be transmitted to the parent indicator, even if the indicate
//  function returns TRUE.
//  Модель передает данные в IACTIVEMODEL::animate через структуру ACTIVEDATA.
//  Если модель не назначает тип данных в структуре ACTIVEDATA, никакие данные
//  не будут переданы родительсому индикатору или графической модели, даже если
//  функция indicate возвращает TRUE.
//
//  Структура ACTIVEDATA:
/*
struct ACTIVEDATA

 { ACTIVEDATATYPES type;   // See below:
   union
    { INT intval;          // Integer or flag value
      DOUBLE realval;      // real value
      STATE stateval;      // DSIM state
      DOUBLE wireinfo[2];  // Voltage and current pair
      SPICEDATA spicedata; // Frame data block from SPICE
      DSIMDATA dsimdata;   // Frame data point from DSIM
      VOID *userinfo;      // Pointer to user data
    };
 };

The structure includes an anonymous union, a C++ feature which allows easy
access to the payload with syntax of the form
Структура включает анонимный союз, специальную возможность C++, которая
позволяет обеспечить легкий доступ к содержимому с синтаксисом в форме:

if (event->type == ADT_REAL)
   value = event->realval;

The ACTIVEDATATYPES enumeration specifies the data type of a particular event.
Перечисление ACTIVEDATATYPES определяет тип данных конкретного события.

enum ACTIVEDATATYPES
 { ADT_VOID=-1,    // No data is present
   ADT_REAL,       // Data is a floating point number.
   ADT_BOOLEAN,    // Data is a flag value.
   ADT_INTEGER,    // Data is an integer.
   ADT_STATE,      // Data is a DSIM state.
   ADT_USER=100    // Data type is user defined.
 };
It specifies the data type contained in an ACTIVEDATA structure.
Оно определяет тип данных содержащийся в ACTIVEDATA структуре.

The ADT_VOID value can be used to cause an event to be generated but without
passing any data.
This is quite common in models that implement both graphical and electrical
functionality since the data measured by the electrical is directly available
to the graphical model.

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

BOOL LEDPANEL::indicate (REALTIME time, ACTIVEDATA *data)
{//--- назначение типа данных вызовет функцию animate()...
if (bootstrt) //--- при старте
   {
  if (z == 0)
     {
      z++; //--- первый вход == "будем передавать"
      return TRUE;
     }
      else
     {//-------- второй вход == "надпись для юзера"
      data->type = ADT_STATE;
      data->stateval = PLO;
      return TRUE;
     }
   }
    else
   {
    return FALSE; //--- больше ничего не передаем
   }
}

Сначала я её никак не использовал, заглушив по умолчанию return FALSE;. Но она мне
всё же понадобилась, чтобы отследить отсутствие анимации и вывести собщение для
пользователя.
То есть, я один раз передал из нее в ANIMATE данные типа ADT_STATE - состояние
вывода: PLO низкое, и в функции ANIMATE вставил проверку:
Code:
if (data->type == ADT_STATE)
   {//--- первый вход в ANIMATE, вызванный из INDICATE
      bootstrt = FALSE;

    // выводим НАДПИСЬ ДЛЯ ПОЛЬЗОВАТЕЛЯ ОБ НЕОБХОДИМОСТИ ВКЛЮЧЕНИ АНИМАЦИИ
          ...

   }

Сам Proteus вызывает ANIMATE с типом данных ADT_PINSTATE, поэтому свой вызов
мы отличаем от вызова в процессе анимации самим Proteus.

Но если есть необходимость работать без удобных подпорок со стороны Proteus, то тут
придется всё делать сложнее...
Для начала придется в функции
Code:
VOID LEDPANEL::setup (IINSTANCE *iinstance, IDSIMCKT *idsimckt)
{//--- Начальные установки при создании экземпляра модели

//--- связываем переменные имён контактов с выводами данных --------------
//---------- входы от клавиатуры
    pI1 = instance->getdsimpin("I1,i1", TRUE);
    pI2 = instance->getdsimpin("I2,i2", TRUE);
    pI3 = instance->getdsimpin("I3,i3", TRUE);
    pI4 = instance->getdsimpin("I4,i4", TRUE);
    pI5 = instance->getdsimpin("I5,i5", TRUE);
    pI6 = instance->getdsimpin("I6,i6", TRUE);
    pI7 = instance->getdsimpin("I7,i7", TRUE);
    pI8 = instance->getdsimpin("I8,i8", TRUE);
    pI9 = instance->getdsimpin("I9,i9", TRUE);
    pI10 = instance->getdsimpin("I10,i10", TRUE);
    pI11 = instance->getdsimpin("I11,i11", TRUE);
    pI12 = instance->getdsimpin("I12,i12", TRUE);
    pI13 = instance->getdsimpin("I13,i13", TRUE);
    pI14 = instance->getdsimpin("I14,i14", TRUE);
    pI15 = instance->getdsimpin("I15,i15", TRUE);

    pI16 = instance->getdsimpin("I16,i16", TRUE);
    pI17 = instance->getdsimpin("I17,i17", TRUE);
    pI18 = instance->getdsimpin("I18,i18", TRUE);
    pI19 = instance->getdsimpin("I19,i19", TRUE);
    pI20 = instance->getdsimpin("I20,i20", TRUE);
    pI21 = instance->getdsimpin("I21,i21", TRUE);
    pI22 = instance->getdsimpin("I22,i22", TRUE);
    pI23 = instance->getdsimpin("I23,i23", TRUE);
    pI24 = instance->getdsimpin("I24,i24", TRUE);
    pI25 = instance->getdsimpin("I25,i25", TRUE);
    pI26 = instance->getdsimpin("I26,i26", TRUE);
    pI27 = instance->getdsimpin("I27,i27", TRUE);
    pI28 = instance->getdsimpin("I28,i28", TRUE);
    pI29 = instance->getdsimpin("I29,i29", TRUE);
    pI30 = instance->getdsimpin("I30,i30", TRUE);
    pI31 = instance->getdsimpin("I31,i31", TRUE);
    ...
//- и так - все 76 выводов
    ...
}


Потом состояния этих выводов отслеживаются в вызове:
Code:
// ***************************************************************************
// S I M U L A T E
// здесь рисовать НЕЛЬЗЯ! Только слушать состояния входов и изменять выходы
// ***************************************************************************
//---- Эта функция вызывается DSIM если любая из цепей к которым подсоединены
//---- входы модели изменяет своё состояние в определенное время.
//
VOID LEDPANEL::simulate (ABSTIME stime, DSIMMODES smode)
{
// здесь что-то типа...
   if((pI1->istate()==SLO)||(pI1->istate()==WLO))
       cur_state(1) = 0;
   else if((pI2->istate()==SLO)||(pI2->istate()==WLO))
       cur_state(2) = 0;
   else if((pI3->istate()==SLO)||(pI3->istate()==WLO))
       cur_state(3) = 0;
   else
       ...
  return;
}

И после этого уже в функции INDICATE данные о состоянии выводов из массива cur_state(i)
передавать в функцию ANIMATE с типом данных data->type = ADT_STATE; и состоянием
data->stateval = XXX;

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

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

Это проект: LCD TG19264A.
В архиве - описание создания модели в *.doc Word на английском, но мы более сложный
пример разобрали здесь, так что, я думаю, по аналогии всё станет понятно.
Проект С++ обширно прокомментированный мной на русском, полностью рабочий, поскольку
в папке Release лежит свеже откомпилированная и проверенная в работе LCD19264A.DLL .
И проект Proteus, в котором библиотека LCD19264A.DLL использована так, как это описано
в документе по создания модели.

И, наконец, - два варианта библиотеки LEDPANEL.DLL в архиве.
Первый вариант - рабочий LEDPANEL.DLL;
второй вариант - отладочный LEDPANEL_DEB.DLL, в котором есть окно отладки, позволяющее
проследить взаимодействие функций библиотеки и передаваемую ими информацию.

Image

Поскольку в модели привязка к LEDPANEL.DLL, то для отладочных целей его следует пере-
именовать в LEDPANEL.BAK, a LEDPANEL_DEB.DLL - переименовать в LEDPANEL.DLL, если,
конечно, Вам позволят это сделать права администратора или отсутствие оных... :lol:

Вот, собственно, и всё, что я хотел "кратко" изложить о создании анимированных VSM-моделей,
используемых в среде Proteus. :wink:

Желаю удачи!!! :lol:

<hr>

_________________
iLavr


Last edited by Lavr on 04 Jul 2014 12:48, edited 1 time in total.



28 Jun 2014 20:15
Profile
Display posts from previous:  Sort by  
Reply to topic   [ 2 posts ] 

Who is online

Users browsing this forum: No registered users and 7 guests


You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot post attachments in this forum

Search for:
Jump to:  
Powered by phpBB® Forum Software © phpBB Group
Designed by ST Software.