Описание пользователя
Глава 3. Использование стандартных модулей автокомпиляции
§3.6. Принципы создания автокомпилируемых моделей блоков
§3.6.2. Работа со статическими переменными блока
§3.6.2.6. Использование сигналов
Рассматривается работа с переменными сигнального типа, используемыми для передачи информации о факте наступления какого-либо события. Описываются особенности использования сигнала запуска Start и сигнала готовности Ready, которые есть у каждого простого блока.
Сигнальные переменные используются в RDS для передачи блокам информации о наступлении какого-либо события – нажатия кнопки, срабатывания устройства, обнаружения какого-либо специфического состояния схемы и т.п. Они очень похожи на логические – как и логические, сигнальные переменные могут принимать только значения 0 и 1 – но отличаются способом передачи значений по связям (подробнее см. в §2.5.2 руководства программиста). Этих отличий два:
- по связи передается только единичное значение сигнала-выхода, нулевое значение на входы соединенных блоков не передается;
- после передачи единичного значения сигнала-выхода оно автоматически сбрасывается в ноль (на соединенных с этим выходом входах остается единица).
Из-за такого поведения сигналов работа с сигнальными входами блоков отличается от работы с входами других типов: модель блока с сигнальным входом, обнаружив на этом входе единицу, должна, выполнив все необходимые действия, самостоятельно присвоить этому входу нулевое значение, подготовив его тем самым к приему следующей единицы. Действительно, нулевое значение сигнала по связи не передается, поэтому единица, поступившая на сигнальный вход, останется там навсегда, если блок-владелец этого входа принудительно не сбросит ее.
Задачи, решаемые с помощью сигналов, можно решать и при помощи логических переменных, но модели блоков при этом получаются несколько сложнее. В §2.5.2 руководства программиста рассмотрена задача подсчета числа нажатий пользователем кнопки, моделируемой блоком, и сложности, возникающие при создании блока-кнопки и блока-счетчика с использованием только логических переменных. Коротко повторим приведенные там рассуждения.
Если сделать выход блока-кнопки логическим и присваивать ему единицу при нажатии кнопки и ноль при отпускании, в блоке-счетчике необходимо будет увеличивать выход на единицу только при изменении значения логического входа с ноля на единицу. Просто увеличивать выход при единичном значении входа нельзя: если кнопка будет нажата в течение нескольких тактов расчета (а это весьма вероятно – такты следуют друг за другом очень быстро), на протяжении всех этих тактов на входе блока-счетчика будет единица, и в каждом такте его выход будет увеличиваться, хотя пользователь нажал на кнопку всего один раз. Таким образом, в блоке-счетчике необходимо отслеживать не само значение логического входа, а его изменение с нуля на единицу, для чего придется ввести дополнительную переменную для хранения значения входа на прошлом такте расчета: если в прошлом такте вход был нулем, а в текущем стал единицей, значит, кнопка была нажата, и выход нужно увеличивать. Таким образом, при решении задачи с помощью логических переменных модели получаются такими:
- в блоке-кнопке должно быть две реакции на события: при нажатии кнопки мыши логическому выходу нужно присваивать единицу, при отпускании – ноль;
- в блоке-счетчике необходимо ввести вспомогательную логическую переменную, в которую в конце реакции на такт расчета следует копировать текущее значение входа (то есть запоминать прошлое состояние входа), а в начале этой же реакции сравнивать значение входа с этой переменной и увеличивать выход, только если в переменной – ноль, а на входе – единица.
Теперь решим ту же задачу с помощью сигналов. В блоке-кнопке сделаем сигнальный выход, в который будем записывать единицу при нажатии кнопки (реакция на отпускание не понадобится). В блоке-счетчике сделаем сигнальный вход, при обнаружении единицы на котором будем увеличивать выход и сбрасывать вход снова на ноль. Никакой дополнительной переменной для хранения прошлого значения входа не понадобится. Действительно, когда блок-кнопка установит единичное значение выхода при нажатии, после передачи этого значения на вход счетчика выход автоматически сбросится в ноль, и останется нулем, сколько бы пользователь ни держал кнопку нажатой. Счетчик, отреагировав на единицу на входе, увеличит выход и сбросит свой вход, на котором, таким образом, тоже останется ноль до следующего нажатия кнопки. Модели с использованием сигналов будут такими:
- в блоке-кнопке будет только реакция на нажатие кнопки мыши, в которой сигнальному выходу присваивается единица;
- в блоке-счетчике в реакции на такт расчета при обнаружении единицы на сигнальном входе без каких-либо проверок вход сбрасывается на ноль, а выход увеличивается.
Можно видеть, что во втором случае модели получаются проще, поскольку при использовании сигналов RDS, фактически, берет на себя работу по отслеживанию изменения состояния переменной: можно сказать, что по связи передается только передний фронт сигнала. При присвоении выходу единицы эта единица уходит на соединенные входы, автоматически сбрасывается, и передача на этом заканчивается – выход готов к приему следующей единицы. Платой за упрощение моделей является необходимость принудительно сбрасывать сигнальные входы в ноль после их обработки.
Создадим модель блока-счетчика, похожую на описанную выше (кнопку с сигнальным выходом возьмем из стандартных библиотечных блоков). Наш блок по входному сигналу «Clk» будет изменять свой целый выход «Count» с 0 до 9, а при поступлении следующего сигнала сбросит «Count» в ноль, выдаст на выход «Carry» сигнал переноса и продолжит счет с нуля. Кроме того, у блока будет сигнальный вход «Reset», сбрасывающий счетчик (устанавливающий «Count» в ноль). Такая же модель, только без использования модуля автокомпиляции, подробно рассматривается §2.5.2 руководства программиста.
Блок будет иметь следующую структуру переменных:
| Имя | Тип | Вход/выход | Пуск | Начальное значение |
|---|---|---|---|---|
| Start | Сигнал | Вход | ✓ | 1 |
| Ready | Сигнал | Выход | 0 | |
| Reset | Сигнал | Вход | ✓ | 0 |
| Clk | Сигнал | Вход | ✓ | 0 |
| Count | int | Выход | 0 | |
| Carry | Сигнал | Выход | 0 |
Создадим новый блок с автокомпилируемой моделью, зададим для него запуск по сигналу и введем в его модель эту структуру переменных. На вкладке «» в правой части окна редактора введем следующий текст:
if(Reset) // Поступил сигнал Reset
{ Reset=0; // Сброс этого сигнала
Clk=0; // Сброс сигнала Clk
Count=0; // Обнуление счетчика
Carry=0; // Сброс сигнала переноса
}
else if(Clk) // Поступил сигнал Clk
{ Clk=0; // Сброс этого сигнала
Count++; // Увеличение счетчика
if(Count>=10) // Досчитали до 10
{ Count=0; // Обнуление счетчика
Carry=1; // Выдача сигнала переноса
}
}
В этой модели мы сначала проверяем поступление сигнала Reset. Если Reset – не ноль (то есть сигнал поступил), мы сбрасываем его вручную, чтобы подготовится к следующему приему сигнала, и вместе с ним обнуляем целый выход Count и все остальные сигнальные входы и выходы – сигнал Reset возвращает наш блок в исходное состояние. Если же Reset равен нулю, то есть сигнала сброса не было, мы проверяем поступление Clk. Если Clk – не ноль (поступил сигнал увеличения счетчика), мы сбрасываем его в ноль, подготавливаясь к поступлению и обработке следующего сигнала, и увеличиваем Count на единицу. Затем, если значение Count достигло десяти, мы сбрасываем его в ноль и присваиваем единицу сигналу переноса Carry.
Для тестирования этой модели можно собрать схему, изображенную на рис. 382. В ней – два блока с нашей моделью (Block10 и Block11), соединенных последовательно: выход переноса «Carry» блока Block10 соединен с входом счета «Clk» блока Block11. Когда Block10 досчитает до десяти и выдаст сигнал переноса, Block11 увеличит свой выход на единицу. При таком соединении выходы первого и второго блоков можно считать двумя разрядами двузначного десятичного числа – таким образом, вместе они могут досчитать до ста. Размножить созданный блок-счетчик можно скопировав его в буфер обмена нажатием Ctrl + C, а затем вставив его копию нажатием Ctrl + V, при этом на запрос модуля автокомпиляции, нужно ли создавать копию модели для вставляемого блока, следует ответить отрицательно (выбрать вариант «»).
Рис. 382. Тестирование модели счетчика сигналов
Запустив расчет и щелкая по кнопке «» в приведенной выше схеме можно будет увидеть, как значение на верхнем числовом индикаторе справа будет увеличиваться. Когда оно дойдет до девяти, следующий щелчок сбросит его в ноль и увеличит значение на нижнем индикаторе. Щелчок не кнопке «» установит оба индикатора в ноль.
Первые две переменных в каждом простом блоке всегда являются сигнальным входом и сигнальным выходом – по умолчанию они называются «Start» и «Ready» соответственно, но их, при желании, можно переименовать. В режиме расчета эти сигналы управляют работой блока и связей, подключенных к его выходам. Единица на входе «Start» запускает модель блока, если для блока не установлен запуск каждый такт (в противном случае модель будет запускаться всегда, какое бы значение вход «Start» ни имел). Единица на выходе «Ready» разрешает передачу данных выходов блока по связям. Модель может работать с этими переменными как с обычными сигналами. Следует помнить, что перед вызовом реакции модели на такт расчета RDS автоматически сбрасывает сигнал «Start» и взводит сигнал «Ready» (то есть считается, что блок уже сработал, и данные его выходов должны быть переданы по связям после такта). Если модели необходимо принудительно перезапустить себя в следующем такте независимо от срабатывания связей, присоединенных к входам блока и его настроек, она может самостоятельно взвести свой собственный сигнал «Start». Модель может также, при необходимости, запретить передачу данных своих выходов по связям, сбросив сигнал «Ready». Поскольку «Ready» автоматически взводится только при реакции на такт расчета, во всех остальных реакциях модель должна самостоятельно взвести этот сигнал, чтобы передать свои изменившиеся выходы по связям. Например, если блок изменяет свой выход по щелчку мыши, в реакции на щелчок необходимо присвоить «Ready» единицу, иначе новое значение выхода не будет передано (пример такой модели рассматривается в §3.6.11).
С сигналом «Start» мы уже сталкивались в предыдущих примерах: чтобы модели блоков принудительно запускались в самом первом такте расчета и обрабатывали исходные значения входов, мы давали этой переменной единичное значение по умолчанию. Однако, мы еще ни разу не подключали связи к этому входу. Такое подключение позволяет запускать реакцию модели на такт расчета по сигналу от другого блока – это позволяет либо организовать достаточно сложную логику работы схемы (примеры приведены в §1.4), либо просто ускоряет работу схемы (модель не запускается, когда это не нужно) и, в некоторых случаях, упрощает программу модели. Рассмотрим сначала использование сигнала запуска непосредственно для запуска модели блока.
В §3.2 и §3.6.2.1 была рассмотрена модель простого сумматора, выдающего на вещественный выход «y» сумму вещественных входов «x1» и «x2». Изменим немного эту модель так, чтобы суммирование осуществлялось только по команде от кнопки – то есть, по сигналу. Установим для сумматора запуск по сигналу на вкладке «» окна параметров блока и изменим структуру переменных модели следующим образом (можно изменить ее в существующей модели, или создать копию модели с новой структурой переменных, скопировав блок через буфер обмена клавишами Ctrl + C и Ctrl + V и ответив «» запрос модуля автокомпиляции):
| Имя | Тип | Вход/выход | Пуск | Начальное значение |
|---|---|---|---|---|
| Start | Сигнал | Вход | ✓ | 0 |
| Ready | Сигнал | Выход | 0 | |
| x1 | double | Вход | 0 | |
| x2 | double | Вход | 0 | |
| y | double | Выход | 0 |
По сравнению с моделью из §3.6.2.1, в эту структуру внесено два изменения. Во-первых, значение по умолчанию сигнала запуска «Start» изменено на ноль – нам не нужно, чтобы модель запускалась без команды в самом первом такте расчета. Во-вторых, флажки «» у входов «x1» и «x2» выключены – автоматический запуск модели при срабатывании связей, подключенных к этим входам, нам тоже не нужен. Программа модели блока останется прежней – «y=x1+x2;».
Рис. 383. Сумматор,
складывающий по сигналу
Соберем с новым блоком схему, изображенную на рис. 383. Входы сумматора «x1» и «x2» подключены к полям ввода, выход «y» – к индикатору, а сигнальный вход «Start» – к сигнальному выходу «Click» стандартной библиотечной кнопки. Запустив расчет, можно будет увидеть, что при изменении значений в полях ввода выход нового блока, в отличие от блока из §3.6.2.1, не изменяется – модель блока не запускается. Сумма входов появится на выходе только после нажатия кнопки, то есть после поступления единицы на вход «Start». Разумеется, сигнал запуска может подаваться не только с кнопки, его источником может быть выход любого другого блока. Так в схемах одни блоки могут управлять запуском других.
Рассмотрим теперь другое использование сигнала запуска, иногда позволяющее несколько упростить программу модели. Выше была рассмотрена модель блока-счетчика с переносом, которая запускалась по сигналам счета «Clk» (увеличить значение) и сброса «Reset» (обнулить счетчик). Можно переписать эту модель так, чтобы в качестве сигнала счета выступал сигнал запуска «Start». Мы переименуем его в «Clk», и в программе модели будем проверять значение «Reset»: если Reset==1, значит, модель запустилась из-за сигнала сброса, а если Reset==0, то она запустилась по какой-то другой причине – а другой причиной запуска может быть только поступление единицы на сигнал запуска блока. Новый блок будет иметь следующую структуру переменных:
| Имя | Тип | Вход/выход | Пуск | Начальное значение |
|---|---|---|---|---|
| Clk | Сигнал | Вход | ✓ | 0 |
| Ready | Сигнал | Выход | 0 | |
| Reset | Сигнал | Вход | ✓ | 0 |
| Count | int | Выход | 0 | |
| Carry | Сигнал | Выход | 0 |
Первый сигнальный вход блока, ранее называвшийся «Start», теперь переименован в «Clk», а старый сигнал «Clk» удален из структуры переменных. В новом блоке обязательно нужно задать запуск по сигналу, причины этого будут объяснены ниже. Новая модель блока будет выглядеть следующим образом:
if(Reset) // Поступил сигнал Reset
{ Reset=0; // Сброс этого сигнала
// Сброс сигнала Clk здесь не нужен – сигнал запуска блока сбрасывается автоматически
Count=0; // Обнуление счетчика
Carry=0; // Сброс сигнала переноса
return;
}
// Сигнал Reset не поступал, но модель запустилась - значит, поступил Clk.
// Сбрасывать Clk не нужно - сигнал запуска блока сбрасывается автоматически
Count++; // Увеличение счетчика
if(Count>=10) // Досчитали до 10
{ Count=0; // Обнуление счетчика
Carry=1; // Выдача сигнала переноса
}
Новая модель, за исключением комментариев, получилась короче. Если собрать из новых блоков схему, изображенную на рис. 382, она будет работать точно так же, как и схема со старыми блоками.
В новой модели счетчика следует обратить внимание на три важных момента. Во-первых, начальное значение сигнала запуска, то есть первой сигнальной переменной блока, теперь не единица, как в старой модели, а ноль. Мы теперь используем этот сигнал в качестве сигнала счета, поэтому единицу по умолчанию ему давать нельзя – в этом случае при первом запуске расчета счетчик увеличит свой выход лишний раз. Если бы нам необходимо было при первом запуске расчета обработать начальные значения входов блока, использовать сигнал запуска так, как мы это сделали, было бы нельзя – он должен был бы остаться выделенным сигналом, ответственным только за запуск модели в режиме расчета, чтобы мы могли дать ему единичное начальное значение.
Во-вторых, нигде в новой модели нет ни проверки, ни обнуления сигнала Clk. Проверять его значение бессмысленно, поскольку перед запуском модели сигнал запуска блока автоматически обнуляется. Сам запуск модели свидетельствует о том, что сигнал пришел либо на Clk, либо на Reset (у входа Reset установлен флажок в колонке «»). Reset автоматически не сбрасывается, поэтому, если модель запустилась из-за него, в нем останется единица (мы проверяем это в самом начале программы). Обнулять Clk не нужно по этой же причине – на момент вызова нашей реакции он уже обнулен.
В-третьих, в отличие от старой модели счетчика, новая будет работать только в том случае, если в параметрах блока установлен флажок «». Если установить флажок «», в каждом такте расчета модель будет запускаться, и, поскольку счетчик теперь увеличивает свой выход при каждом запуске, он будет считать непрерывно, независимо от поступления сигнала на вход Clk. В старой модели Clk был отдельным входом, значение которого проверялось в программе, а сам факт запуска модели ничего не значил, поэтому старая модель работала при любом способе запуска.
Теперь приведем пример использования выхода «Ready» для запрещения срабатывания связей, подключенных к выходу блока. Создадим модель блока, вставив который в разрыв связи, передающей вещественное значение, можно будет разрешать или запрещать передачу данных по этой связи. Такой блок есть в стандартной библиотеке RDS, но мы, в качестве примера, создадим его вручную.
Блок будет иметь вещественный вход «x», который будет копироваться в вещественный выход «y», если логический вход «Enable» не равен нулю, причем при Enable==0 значение выхода блока, оставшееся там с прошлых срабатываний, не должно передаваться по связи.
Структура переменных блока будет следующей:
| Имя | Тип | Вход/выход | Пуск | Начальное значение |
|---|---|---|---|---|
| Start | Сигнал | Вход | ✓ | 1 |
| Ready | Сигнал | Выход | 0 | |
| x | double | Вход | ✓ | 0 |
| Enable | Логический | Вход | ✓ | 0 |
| y | double | Выход | 0 |
Создадим новый блок с автокомпилируемой моделью, зададим для него запуск по сигналу и введем в его модель указанную структуру переменных. На вкладке «» в правой части окна редактора введем следующий текст:
if(Enable) // Передача разрешена
y=x;
else // Передача запрещена
Ready=0;
В этой программе при ненулевом Enable мы копируем x в y, а при нулевом запрещаем работу всех выходных связей, записав 0 в Ready. При запуске модели блока для реакции на такт расчета второму сигнальному выходу блока, то есть Ready, автоматически присваивается единица, поэтому передача данных выходов блока по связям по умолчанию разрешена, и при Enable!=0 нам не нужно предпринимать никаких действий для ее разрешения, как и во всех рассмотренных ранее моделях.
Рис. 384. Проверка блока
управления связью
Для проверки созданной модели можно собрать схему, изображенную на рис. 384. Здесь «Block12» и «Block13» – два одинаковых блока управления связью с нашей моделью, их входы «x» подключены к полям ввода, а выходы «y» – к одному и тому же числовому индикатору (обе связи соединены с его входом «v»). Входы «Enable» наших блоков соединены с выходами «Down» двух стандартных библиотечных кнопок, в настройках которых установлены флажки «». В этом режиме по первому щелчку мыши кнопка нажимается и остается нажатой, и ее выход «Down» получает значение 1, а по второму – снова переходит в отпущенное состояние, и выход «Down» обнуляется. Если запустить расчет, на индикаторе будет отображаться значение того поля ввода, для которого нажата соответствующая ему кнопка. Если изменять значение в поле ввода, подключенном к блоку, кнопка для которого не нажата (на рисунке – «Block13»), модель этого блока будет запускаться, поскольку у переменной «x» установлен флажок «», но, из-за того, что внутри модели в этом случае «Ready» обнуляется, выход «y» не передается по связи, и индикатор получает значение с другого соединенного с ним блока, выход «Ready» которого остался равным единице и, поэтому, связь сработала. Если нажать обе кнопки, будут срабатывать обе выходных связи, и какое именно значение будет отображаться на индикаторе, предсказать сложно.
В приведенных примерах мы никогда ни к чему не присоединяли выход «Ready». Его можно использовать не только для разрешения работы выходных связей, но и как признак того, что блок сработал – например, соединить его со входом «Start» следующего блока и, таким образом, задать последовательность работы блоков, если это необходимо. Пример подобной схемы приведен на рис. 11 в §1.4, здесь мы его рассматривать не будем, поскольку он не требует каких-либо специальных действий в программе модели – это стандартное для RDS поведение блоков.