Активная модель, в отличие от рассмотренных в предыдущих статьях (Разработка компонента для протеуса и Создаем модель 1-Wire анализатора для Proteus), может работать с экраном, мышкой и клавиатурой. Например, активными моделями являются различные индикаторы (светодиоды, самисегментые, ЖК и т. д.).
Для примера, создадим цифровую модель RGB светодиода. Для этого в новом проекте поместим прямоугольник в рабочую область. Нарисуем кружок в центре этого прямоугольника и добавим выводы с именами R, G, B и GND. Скрипт модели, должен быть таким:
{*DEVICE} NAME=led3color {PREFIX=LED} {ACTIVE=led3color,1,DLL} {*PROPDEFS} {MODDLL="VSM Model DLL",READONLY STRING} {PRIMITIVE="Primitive Type",HIDDEN STRING} {*COMPONENT} {MODDLL=Led_3Color.dll} {PRIMITIVE=DIGITAL,Led_3Color}
После этого нужно скомпилировать модель (в меню "Library" кликнуть по пункту "Make Device") и она станет доступной в библиотеке протеуса.
Подробнее о создании графической части модели можно прочитать в Разработка компонента для протеуса.
Получилась такая модель:
Теперь приступим к программной части модели. Код активной модели немного отличается от ранее рассмотренных, тем что интерфейс состоит из двух интерфейсов - IACTIVEMODEL и IDSIMMODEL. Первый отвечает за активную часть модели (графика и ввод), а второй, за цифровую часть (обмен данными через выводы модели). В этом случае так же, экспортируемые из DLL процедуры createdsimmodel() и deletedsimmodel() заменены на createactivemodel() и deleteactivemodel().
Код DLL.
XIncludeFile "vsm.pbi" Macro mClassPrivate ; Приватные перемнные класса IACTIVEMODEL. *component.ICOMPONENT *instance.IINSTANCE ; Приватные переменные класса IDSIMMODEL. *inst.IINSTANCE *ckt.IDSIMCKT *PinR.IDSIMPIN *PinG.IDSIMPIN *PinB.IDSIMPIN *PinGND.IDSIMPIN ; Остальные приватные переменные. Color.l new_flag.b EndMacro Structure Class_IDSIM *vt[1] ;pointer to virtual table as first entry. mClassPrivate ; Приватные перемнные. EndStructure Structure Class_IACTIVE *vt[2] ;pointer to virtual table as first entry. mClassPrivate ; Приватные перемнные. EndStructure EnableExplicit EnableASM Declare ModelNEW() Declare animate(element.i, *newstate.ACTIVEDATA) ProcedureCDLL.i createactivemodel(device.s, *ils.ILICENCESERVER) Protected *Result=0 If *ils\Authorize($80808081) *Result=ModelNEW() ; Создание объекта. EndIf ProcedureReturn *Result EndProcedure ProcedureCDLL deleteactivemodel(*model.Class_IACTIVE) If *model ;ClearStructure(*model, Class_IACTIVE) FreeMemory(*model) ; Освобождение памяти, занимаемой объектом. EndIf EndProcedure Procedure ModelNEW() ; Создание объекта. Protected *Class.Class_IACTIVE=0 *Class = AllocateMemory(SizeOf(Class_IACTIVE)) If *Class ;InitializeStructure(*Class, Class_IACTIVE) *Class\vt[0] = ?IACTIVE_Funct ; Функции класса IACTIVEMODEL. *Class\vt[1] = ?IDS_Funct ; Функции класса IDSIMMODEL. EndIf ProcedureReturn *Class EndProcedure ;- Процедуры класса ;{ ;- IACTIVEMODEL. ; Инициализация модели. ; В качестве параметра передается указатель на интерфейс IComponent. Procedure initialize(*cpt.ICOMPONENT) Protected *This.Class_IACTIVE, s.s, c.l, i MOV *This, ecx *This\component = *cpt *This\new_flag = #True *This\Color = 0 EndProcedure ; ISIS вызывает эту функцию, когда создается аналоговая модель. ; Имя модели передается в качестве параметра. ; Например, если мы присвоили в сценарии параметра "PRIMITIVE=.ANALOG.ammeter", ; будет передано в функцию слово "AMMETER". Procedure.i getspicemodel(*sPrimitive) Protected *This.Class_IACTIVE MOV *This, ecx ProcedureReturn 0 ; Это не аналоговая модель. EndProcedure ; Функция будет вызвана, когда ISIS создает цифровую модель, ; которая соответствует активной модели. ; Напрмиер, если в сценарии будет "PRIMITIVE=.DIGITAL.display", ; эта функция будет вызвана с параметром "DISPLAY". Procedure.i getdsimmodel(*sPrimitive) Protected *This.Class_IACTIVE MOV *This, ecx ProcedureReturn *This+SizeOf(Integer) ; Ссылка на объект IDSIMMODEL. EndProcedure ; Эта функция вызывается, при копировании или масштабировании компонента (модели). ; Это отличается от функции animate() с точки зрения того факта, ; что необходимо, полностью перерисовать компонент. ; Кроме того, в качестве параметра текущее состояние передается функции. ; Минимальная реализация функции такова: *This\component\drawstate(state). Procedure Plot_IACTIVE(state.ACTIVESTATE) Protected *This.Class_IACTIVE MOV *This, ecx *This\component\drawstate(state) MOV ecx, *This ; Thiscall вызов. animate(0, 0) ; Вызов функции перерисовки содержимого модели. EndProcedure ; Функция вызывается для анализа события, генерируется внутри электрического аналога, ; (интерфейс ISPICEMODEL или IDSIMMODEL) для соответствующей активной модели. ; Активные события создаются ядром PROSPICE в результате ; извлечения значения функций IDSIMMODEL\indicate(), ; которая вызывается при каждом копировании экрана. ; Это обеспечивает общий механизм, с помощью которого ; электрический аналог может взаимодействовать с графикой частью модели. ; Зачастую используется этот механизм, если вы планируется использовать ; примитивы RTVPROBE, RTIPROBE или RTDPROBE для того, чтобы принять простое ; измерения, которые вы затем использовать в графической модели. ; Параметер "element" - элемент графической модели, для которой генерируется событие. ; Эта дает возможность использовать несколько примитивов RTxPROBE, ; которые расположены в модели для того, чтобы проводить измерения одной моделью. ; Параметр *newstate содержит данные о событии. Procedure animate(element.i, *newstate.ACTIVEDATA) Protected *This.Class_IACTIVE MOV *This, ecx If *This\new_flag = #True ; Перерисока лишь если это необходимо. *This\new_flag = #False *This\component\setbrushcolour(*This\Color) ; Цвет заливки. *This\component\drawcircle(500, -500, 300) ; Рисуем круг. EndIf EndProcedure ; Эта функция вызывается при нажатии кнопок клавиатуры, ; а так же при перемещении или нажатии кнопок мышки. ; Функция вызывается только когда указатель мыши он находится над компонентом (моделью). ; Если функция возвращает значение #True, это означает, что она "захватывает" фокус ; и получает поток событий клавиатуры и мыши, пока не вернет #False. ; Параметры функци: key - код виртуальной клавиши Windows, ; х и у - координаты указателя мыши относительно левого верхнего угла модели, ; flags - флаги нажатых кнопок мыши: 1 - левая, 2 - правая. Procedure.b actuate_IACTIVE(key.w, x.i, y.i, flags.l) Protected *This.Class_IACTIVE MOV *This, ecx ProcedureReturn #False EndProcedure ;- IDSIMMODEL. ; Функция isdigital. ; Она должна вернуть «1», если вывод с данным именем является цифровым, и «0», если нет. ; У нас нет других выводов, кроме аналоговых, поэтому мы всегда возвращаем «1». Procedure isdigital(*PinName) Protected *This.Class_IDSIM MOV *This, ecx ProcedureReturn 1 ; В компоненте все выводы цифровые. EndProcedure ; Функция setup. ; Ей передаются интерфейсы объектов IINSTANCE – интерфейс экземпляра, ; и IDSIMCKT – интерфейс схемы. ; В этой функции мы должны проинициализировать внутренние переменные, ; а также сохранить на будущее ссылки на IINSTANCE и IDSIMCKT. ; Также, в этой функции чаще всего сохраняются адреса интерфейсов выводов ; (через вызов функции *This\inst\getdsimpin). Procedure setup(*instance.IINSTANCE, *dsimckt.IDSIMCKT) Protected *This.Class_IDSIM MOV *This, ecx *This\inst = *instance ; Сохраняем указатели на объекты. *This\ckt = *dsimckt ; Сопоставляем выводы модели с интерфейсами. *This\PinR = *This\inst\getdsimpin("R", #True) *This\PinG = *This\inst\getdsimpin("G", #True) *This\PinB = *This\inst\getdsimpin("B", #True) *This\PinGND = *This\inst\getdsimpin("GND", #True) EndProcedure ; Функция runctl вызывается при смене режима симуляции – например, ; когда мы приостанавливаем процесс симуляции. ; Это удобно для того, чтобы остановить внутренние таймеры ; или проделать другие необходимые действия. Procedure runctrl(mode.RUNMODES) Protected *This.Class_IDSIM MOV *This, ecx EndProcedure ; Функция actuate вызывается, если у нас приводятся в действие актюаторы – стандартные ; элементы управления компонентом. Они бывают двух видов – «больше/меньше» и «нажато/отпущено». Procedure actuate_IDS(time.REALTIME, newstate.ACTIVESTATE) Protected *This.Class_IDSIM MOV *This, ecx EndProcedure ; Функция indicate вызывается каждый раз при перерисовке экрана. ; Если мы возвращаем значение FALSE, то эта функция больше не будет вызываться. Procedure indicate(time.REALTIME, *aData.ACTIVEDATA) Protected *This.Class_IDSIM MOV *This, ecx ProcedureReturn #True EndProcedure ; Функция simulate вызывается при каждом изменении состояния выводов. ; Небольшое замечание – она вызывается также когда мы изменяем состояние собственного вывода, ; так что необходимо точно устанавливать, произошло ли какое-либо входное событие и какое именно. Procedure simulate(time.ABSTIME, mode.DSIMMODES) Protected *This.Class_IDSIM MOV *This, ecx ; Формируем цвет в зависимости от логических уровней на входах модели. *This\Color = RGB(ishigh(*This\PinR\istate())*255, ishigh(*This\PinG\istate())*255, ishigh(*This\PinB\istate())*255) *This\new_flag = #True EndProcedure ; Функция callback вызывается при наступлении определенного времени, ; которое задается вызовом IDSIMCKT::setcallback. Её удобно использовать для тактовых генераторов. Procedure callback(time.ABSTIME, eventid.EVENTID) Protected *This.Class_IDSIM MOV *This, ecx EndProcedure DataSection IACTIVE_Funct: Data.i @initialize() Data.i @getspicemodel() Data.i @getdsimmodel() Data.i @Plot_IACTIVE() Data.i @animate() Data.i @actuate_IACTIVE() IDS_Funct: Data.i @isdigital() Data.i @setup() Data.i @runctrl() Data.i @actuate_IDS() Data.i @indicate() Data.i @simulate() Data.i @callback() EndDataSection ;}
Если внимательно посмотреть, то видны уже знакомые по предыдущим статьям процедуры ,такие как isdigital(), setup() и т. д. Они образуют класс IDSIMMODEL.
В код так же добавлены новые, такие как initialize(), getspicemodel() и т. д. Они образуют класс IACTIVEMODEL.
В функции initialize() производится инициализация модели. В ней нужно сохранить указатель на объект ICOMPONENT, который в дальнейшем понадобится.
В функции getspicemodel() возвращается 0 в место указателя на объект ISPICEMODEL, поскольку аналоговая часть в этой модели не реализована. Модель цифровая, поэтому в следующей на ней процедуре getdsimmodel() возвращается указатель на объект IDSIMMODEL.
Функция Plot_IACTIVE() (на самом деле ее имя Plot, но поскольку в PureBasic есть функция с аналогичным именем, эту пришлось переименовать) вызывается протеусом при перерисовке экрана, например, при изменении масштаба и в ней должна производится перерисовка модели.
Функция animate() вызывается протеусом при необходимости перерисовки содержимого модели. В ней и рисуется активная часть светодиода. Для этого указывается цвет свечения и рисуется круг в центре модели с этим цветом. Код цвета задается в функции simulate(), вызываемой при изменении состояния выводов модели. В ней анализируется логичекий уровень каждого вывода и на основе этого создается соответствующий код цвета.
Скомпилируйте код в DLL и сохраните с именем Led_3Color.dll. После чего скопируйте в папку MODELS протеуса и если все сделано правильно, то модель будет доступна в библиотеке и ее можно будет использовать в проектах.
Модель во время симуляции.
На входах R и B установлены высокие логические уровни, а при смешивании красного и синего цвета получается розовый, что видим на скриншоте.
Но эта модель может отобразить только 8 цветов, в зависимости от логических уровней на входах R, G и B.
Чтобы расширить поддерживаемый цветовой диапазон, модель была доработана, таким образом, чтобы цвет зависел не только от наличия или отсутствия логических уровней на входах, но и от скважности импульсов (подразумевается что на входы модели светодиода поступает импульсное напряжение). Тем более что многие микроконтроллеры аппаратно могут изменять скважность импульсов, используя технологию ШИМ - широтно-импульсная модуляция. По английски это звучит как pulse-width modulation (PWM).
Прежде всего определимся что такое ШИМ (PWM) и как оно работает. Принцип работы по сути прост. При неизменной частоте, изменяется скважность - отношение длительности положительного полупериода импульса к отрицательному.
Значит для того чтобы модель определила скважность, необходимо измерить длительность положительного полупериода и отрицательного, после чего сравнить их и вычислить длительность которого больше и насколько. На основании этих данных, отобразить соответствующий цвет. На словах вроде все просто, но как реализовать, особенно точный подсчет времени? Протеус предоставляет все необходимое для этого. Во первых, информирует о любых изменениях состояния выводов модели, а во вторых, предоставляет счетчик времени с разрешающей способностью до единиц пикосекунд! Но вряд ли современные компьютеры (о суперкомпьютерах речь не идет) могут дать подобную точность при симуляции в режиме реального времени. В лучшем случае с точность будет до единиц наносекунд. Но в нашем случае, достаточна точность на порядок хуже.Рассмотрим доработанный код (файл dLed_PWM.pb в папке Src\DLL).
Код:XIncludeFile "vsm.pbi" Structure PinParam *Pin.IDSIMPIN OldState.b OldTime.ABSTIME[2] Color.a EndStructure Macro mClassPrivate ; Приватные перемнные класса IACTIVEMODEL. *component.ICOMPONENT *instance.IINSTANCE ; Приватные переменные класса IDSIMMODEL. *inst.IINSTANCE *ckt.IDSIMCKT PinR.PinParam PinG.PinParam PinB.PinParam PinGND.PinParam ; Остальные приватные переменные. Color.l new_flag.b EndMacro Structure Class_IDSIM *vt_IDSIM ;pointer to virtual table as first entry. mClassPrivate ; Приватные переменные. EndStructure Structure Class_IACTIVE *vt_IACTIVE ;pointer to virtual table as first entry. *vt_IDSIM mClassPrivate ; Приватные переменные. EndStructure EnableExplicit EnableASM Declare ModelNEW() Declare animate(element.i, *newstate.ACTIVEDATA) ProcedureCDLL.i createactivemodel(device.s, *ils.ILICENCESERVER) Protected *Result=0 If *ils\Authorize($80808081) *Result=ModelNEW() ; Создание объекта. EndIf ProcedureReturn *Result EndProcedure ProcedureCDLL deleteactivemodel(*model.Class_IACTIVE) If *model ;ClearStructure(*model, Class_IACTIVE) FreeMemory(*model) ; Совобождение памяти, занимаемой объектом. EndIf EndProcedure Procedure ModelNEW() ; Создание объекта. Protected *Class.Class_IACTIVE=0 *Class = AllocateMemory(SizeOf(Class_IACTIVE)) If *Class ;InitializeStructure(*Class, Class_IACTIVE) *Class\vt_IACTIVE = ?IACTIVE_Funct ; Функции класса IACTIVEMODEL. *Class\vt_IDSIM = ?IDS_Funct ; Функции класса IDSIMMODEL. EndIf ProcedureReturn *Class EndProcedure Procedure PinColor(*This.Class_IDSIM, *Pin.PinParam, time.ABSTIME) Protected Temp.f Protected st.b = ishigh(*Pin\Pin\istate()) ; Если сюда попали в первый раз, то запоминаем. ; состояние пина и текущее время, затем выходим. If *Pin\OldTime[0] = 0 *Pin\OldState = st *Pin\OldTime[0] = time *Pin\OldTime[1] = time ProcedureReturn EndIf ; Состояние пина не менялось больше 100 миллисекунд - сброс по таймауту. If (time - *Pin\OldTime[0]) / 100000000000 > 0 If st *Pin\Color=255 Else *Pin\Color=0 EndIf *Pin\OldTime[0]=time *Pin\OldTime[1]=time *Pin\OldState = st ProcedureReturn EndIf ; Если текущее состояние пина равно предыдущему, то выходим. If *Pin\OldState = st ProcedureReturn EndIf If st=0 ; Спад импульса. If *Pin\OldTime[0] = *Pin\OldTime[1] ; Это начало измрения. *Pin\OldTime[0]=time Else ; Длительность полуериодов измерена. ; Вычисление скажности и перевод в диапазон 0...255. Temp = 255/(((Time-*Pin\OldTime[0]) / (Time-*Pin\OldTime[1]))) If Temp>255 *Pin\Color = 255 ElseIf Temp<0 *Pin\Color = 0 Else *Pin\Color = Int(Temp) EndIf *Pin\OldTime[0]=time ; Запоминает текущее время. *Pin\OldTime[1]=time EndIf Else ; Фронт импульса *Pin\OldTime[1]=time ; Запоминаем вемя отрицательного полупериода. EndIf *Pin\OldState = st EndProcedure ;- Процедуры класса ;{ ;- IACTIVEMODEL. ; Инициализация модели. ; В качестве параметра передается указатель на интерфейс IComponent. Procedure initialize(*cpt.ICOMPONENT) Protected *This.Class_IACTIVE, s.s, c.l, i MOV *This, ecx *This\component = *cpt *This\new_flag = #True *This\Color = $7F7F7F EndProcedure ; ISIS вызывает эту функцию, когда создается аналоговая модель. ; Имя модели передается в качестве параметра. ; Например, если мы присвоили в сценарии параметра "PRIMITIVE=.ANALOG.ammeter", ; будет передано в функцию слово "AMMETER". Procedure.i getspicemodel(*sPrimitive) Protected *This.Class_IACTIVE MOV *This, ecx ProcedureReturn 0 ; Это не аналоговая модель. EndProcedure ; Функция будет вызвана, когда ISIS создает цифровую модель, ; которая соответствует активной модели. ; Напрмиер, если в сценарии будет "PRIMITIVE=.DIGITAL.display", ; эта функция будет вызвана с параметром "DISPLAY". Procedure.i getdsimmodel(*sPrimitive) Protected *This.Class_IACTIVE MOV *This, ecx ProcedureReturn *This+OffsetOf(Class_IACTIVE\vt_IDSIM);SizeOf(Integer) ; Ссылка на объект IDSIMMODEL. EndProcedure ; Эта функция вызывается, при копировании или масштабировании компонента (модели). ; Это отличается от функции animate() с точки зрения того факта, ; что необходимо, полностью перерисовать компонент. ; Кроме того, в качестве параметра текущее состояние передается функции. ; Минимальная реализация функции такова: *This\component\drawstate(state). Procedure Plot_IACTIVE(state.ACTIVESTATE) Protected *This.Class_IACTIVE MOV *This, ecx *This\component\drawstate(state) *This\new_flag = #True MOV ecx, *This ; Thiscall вызов. animate(0, 0) ; Вызов функции перерисовки содержимого модели. EndProcedure ; Функция вызывается для анализа события, генерируется внутри электрического аналога, ; (интерфейс ISPICEMODEL или IDSIMMODEL) для соответствующей активной модели. ; Активные события создаются ядром PROSPICE в результате ; извлечения значения функций IDSIMMODEL\indicate(), ; которая вызывается при каждом копировании экрана. ; Это обеспечивает общий механизм, с помощью которого ; электрический аналог может взаимодействовать с графикой частью модели. ; Зачастую используется этот механизм, если вы планируется использовать ; примитивы RTVPROBE, RTIPROBE или RTDPROBE для того, чтобы принять простое ; измерения, которые вы затем использовать в графической модели. ; Параметер "element" - элемент графической модели, для которой генерируется событие. ; Эта дает возможность использовать несколько примитивов RTxPROBE, ; которые расположены в модели для того, чтобы проводить измерения одной моделью. ; Параметр *newstate содержит данные о событии. Procedure animate(element.i, *newstate.ACTIVEDATA) Protected *This.Class_IACTIVE MOV *This, ecx If *This\new_flag = #True ; Перерисока лишь если это необходимо. *This\new_flag = #False *This\component\setbrushcolour(*This\Color) ; Цвет заливки. *This\component\drawcircle(500, -500, 300) ; Рисуем круг. EndIf EndProcedure ; Эта функция вызывается при нажатии кнопок клавиатуры, ; а так же при перемещении или нажатии кнопок мышки. ; Функция вызывается только когда указатель мыши он находится над компонентом (моделью). ; Если функция возвращает значение #True, это означает, что она "захватывает" фокус ; и получает поток событий клавиатуры и мыши, пока не вернет #False. ; Параметры функци: key - код виртуальной клавиши Windows, ; х и у - координаты указателя мыши относительно левого верхнего угла модели, ; flags - флаги нажатых кнопок мыши: 1 - левая, 2 - правая. Procedure.b actuate_IACTIVE(key.w, x.i, y.i, flags.l) Protected *This.Class_IACTIVE MOV *This, ecx ProcedureReturn #False EndProcedure ;- IDSIMMODEL. ; Функция isdigital. ; Она должна вернуть «1», если вывод с данным именем является цифровым, и «0», если нет. ; У нас нет других выводов, кроме аналоговых, поэтому мы всегда возвращаем «1». Procedure isdigital(*PinName) Protected *This.Class_IDSIM MOV *This, ecx ProcedureReturn 1 ; В компоненте все выводы цифровые. EndProcedure ; Функция setup. ; Ей передаются интерфейсы объектов IINSTANCE – интерфейс экземпляра, ; и IDSIMCKT – интерфейс схемы. ; В этой функции мы должны проинициализировать внутренние переменные, ; а также сохранить на будущее ссылки на IINSTANCE и IDSIMCKT. ; Также, в этой функции чаще всего сохраняются адреса интерфейсов выводов ; (через вызов функции *This\inst\getdsimpin). Procedure setup(*instance.IINSTANCE, *dsimckt.IDSIMCKT) Protected *This.Class_IDSIM MOV *This, ecx *This\inst = *instance ; Сохраняем указатели на объекты. *This\ckt = *dsimckt ; Сопоставляем выводы модели с интерфейсами. *This\PinR\Pin = *This\inst\getdsimpin("R", #True) *This\PinG\Pin = *This\inst\getdsimpin("G", #True) *This\PinB\Pin = *This\inst\getdsimpin("B", #True) *This\PinGND\Pin = *This\inst\getdsimpin("GND", #True) *This\ckt\setcallback(20000000000, *This, $20) EndProcedure ; Функция runctl вызывается при смене режима симуляции – например, ; когда мы приостанавливаем процесс симуляции. ; Это удобно для того, чтобы остановить внутренние таймеры ; или проделать другие необходимые действия. Procedure runctrl(mode.RUNMODES) Protected *This.Class_IDSIM MOV *This, ecx EndProcedure ; Функция actuate вызывается, если у нас приводятся в действие актюаторы – стандартные ; элементы управления компонентом. Они бывают двух видов – «больше/меньше» и «нажато/отпущено». Procedure actuate_IDS(time.REALTIME, newstate.ACTIVESTATE) Protected *This.Class_IDSIM MOV *This, ecx EndProcedure ; Функция indicate вызывается каждый раз при перерисовке экрана. ; Если мы возвращаем значение FALSE, то эта функция больше не будет вызываться. Procedure indicate(time.REALTIME, *aData.ACTIVEDATA) Protected *This.Class_IDSIM MOV *This, ecx If *This\new_flag = #True *aData\Type = #ADT_DSIMDATA *aData\dsimdata\numpins=4 EndIf ProcedureReturn #True EndProcedure ; Функция simulate вызывается при каждом изменении состояния выводов. ; Небольшое замечание – она вызывается также когда мы изменяем состояние собственного вывода, ; так что необходимо точно устанавливать, произошло ли какое-либо входное событие и какое именно. Procedure simulate(time.ABSTIME, mode.DSIMMODES) Protected *This.Class_IDSIM MOV *This, ecx PinColor(*This, *This\PinR, time) PinColor(*This, *This\PinG, time) PinColor(*This, *This\PinB, time) *This\Color = RGB(*This\PinR\Color, *This\PinG\Color, *This\PinB\Color) *This\new_flag = #True EndProcedure ; Функция callback вызывается при наступлении определенного времени, ; которое задается вызовом *This\ckt\setcallback(). Её удобно использовать для тактовых генераторов. Procedure callback(time.ABSTIME, eventid.EVENTID) Protected *This.Class_IDSIM, *x MOV *This, ecx If eventid=$20 PinColor(*This, *This\PinR, time) PinColor(*This, *This\PinG, time) PinColor(*This, *This\PinB, time) *This\Color = RGB(*This\PinR\Color, *This\PinG\Color, *This\PinB\Color) *This\new_flag = #True ; Запускаем таймер. Через 0.2 секунды будет вызнана процедура setcallback() с кодом $20. *This\ckt\setcallback(time+200000000000, *This, $20) EndIf EndProcedure DataSection IACTIVE_Funct: Data.i @initialize() Data.i @getspicemodel() Data.i @getdsimmodel() Data.i @Plot_IACTIVE() Data.i @animate() Data.i @actuate_IACTIVE() IDS_Funct: Data.i @isdigital() Data.i @setup() Data.i @runctrl() Data.i @actuate_IDS() Data.i @indicate() Data.i @simulate() Data.i @callback() EndDataSection ;}Изменения в основном коснулись кода процедур simulate() и callback(). В них, перед расчетом отображаемого цвета, вызывается три раза процедура PinColor() для каждого входа в отдельности. Именно в ней измеряется скважность импульсов и вычисляется цвет. В начале процедуры PinColor(), используя макрос ishigh() из файла vsm.pbi, определяется установлен ли высокий логический уровень на входе (какой именно это вход, зависит от того, указатель на какую структуру был передан процедуре во втором аргументе *Pin). Затем выясняется первый ли это вызов процедуры для данной структуры и если первый, то запоминается текущее состояние входа и текущее время. Если же это не первый вызов, то проверяется не превышен ли таймаут 100 миллисекунд по которому определяется что это не ШИМ, а обычные логические уровни. Необходимо для того чтобы изменять цвет можно было не только методом ШИМ, но и как в предыдущем коде, путем изменения логических уровней. Т. е. сделано для универсальности. Но это так же накладывает ограничения на минимальную частоту ШИМ, которая должна быть не ниже 10 Гц.
Если таймаут не превышен, то сравнивается текущий логический уровень на входе, с предыдущим и если они равны, то производится выход из процедуры. А вот если не равны, то определяется фронт это (на входе логическая единица) или спад (на входе логический ноль). Логика измерения времени такова: По спаду (переход от логической единицы к логическому нулю) сохраняется текущее время от начала симуляции (если не забыли, разрешающая способность до единиц пикосекунд). По фронту (переход от логического нуля к единице), так же сохраняется время. При следующем спаде импульса, вычисляется длительность положительного полупериода (когда была логическая единица на входе) и общая длительность импульса. На основании этих данных, вычисляется соотношение длительности положительного и отрицательного полупериода импульса, т. е. скважность. Далее скважность переводится в диапазон значений от 0 до 255 и записывается в поле Color структуры *Pin.Есть еще одна особенность о которой стоит упомянуть, поскольку о ней почти нигде не написано. По умолчанию, графика модели обновляется несколько раз в секунду. И при динамическом изменении ШИМ видим не плавные переходы цвета, а слайд-шоу.
Чтобы протеус прорисовывал модель чаще, нужно ему об этом сообщить. Для этого в процедуре indicate() записываются данные в структуру с именем *aData типа ACTIVEDATA. В данном случае, это произвольные данные, только для того чтобы протеус перерисовал содержимое модели. Но при необходимости, через структуру можно передать необходимые данные, которые будет переданы в процедуру animate() которая в одном из аргументов принимает аналогичную структуру.Поучилось следующее.
На скриншоте видно в виртуальном осциллографе соотношение длительности положительных полупериодов по отношению к отрицательных на всех трех входах модели светодиода. Наибольшая длительность положительного полупериода у зеленого сегмента светодиода. Немного меньше у красного и еще меньше у синего. Поэтому результатирующий цвет получился светло зеленый.
В папке Proteus находится пример использования и библиотека, расположенная в папках LIBRARY и MODELS. Для установки модели, эти папки (LIBRARY и MODELS) нужно скопировать в папку с протеусом.
В папке Src находятся исходники DLL-библиотеки и графической части модели, а так же исходный текст прошивки (папка Bascom) для доработанной модели светодиода с поддержкой ШИМ.
Все файлы в имени которых присутствует слово PWM относятся к доработанному варианту.
Модель создавалась и тестировалась в Proteus 7.8 SP2. С другими версиями, работоспособность не проверялась.
DLL компилировалась в PureBasic 5.11 Windows x86.