НИИЭТ К1921ВГ1Т : двоичный счетчик
Четырёхразрядный двоичный счётчик по кнопке
Введение
Двоичный счётчик показывает, как одно действие пользователя превращается в изменение состояния программы. Четыре светодиода позволяют представить 16 чисел; каждый из них отвечает за один разряд. Такая индикация полезна для отладки счётчиков событий и простых интерфейсов устройства.
Начнём с обработки кнопки. Её удержание и дребезг не должны увеличивать число несколько раз. После выделения одиночного события нажатия построим счётчик, разложим число на биты и проследим переход от 15 обратно к 0.
Постановка задачи и исходные данные
Исходное число равно 0. Каждое новое нажатие USER_BTN увеличивает число на единицу по модулю 16. LED1 соответствует младшему разряду, LED4 – старшему. Смоделируем 16 нажатий, включая переполнение счётчика. В тестовую последовательность включим дребезг первых 6 мс нажатия, чтобы оценивать работу реального интерфейса, а не только идеального импульса.
Для воспроизведения откройте эту папку в Engee. Расчётная модель находится в NIIET02_binary_sim.engee, версия для платы – в NIIET02_binary_target.engee. Кодовые ячейки ниже выполняются последовательно; путь к моделям определяется каталогом самого скрипта.
Алгоритм
Клик обновляет четырёхразрядный счётчик. В памяти хранится число; преобразователь битов выдаёт младший разряд первым.
Рисунок 1. Алгоритм работы устройства.
Построение модели в Engee
Сначала соберём ButtonEvent. Сырой вход USER_BTN равен 0 при нажатии и 1 при отпускании. Сравнение с нулём переводит его в логический признак «нажато». При шаге опроса SampleTime = 0.001 счётчик одинаковых отсчётов должен дойти до 20, прежде чем новое состояние будет принято. Задержка действует при нажатии и отпускании.
Из устойчивого состояния выделяется только переход 0→1. Сигнал Click длится один такт 1 мс. Он поступает в сумму с сохранённым числом, а CountWrap вычисляет остаток по модулю 16. Таким образом, долгое удержание не увеличивает число повторно.
CountByte переводит результат в UInt8. Далее Integer to Bit Converter настраивается как nBits = 4, bitOrder = LSB first, outDtype = UInt8. Demux разделяет вектор на четыре сигнала: первый соответствует LED1, четвёртый – LED4.
Рисунок 2. Расчётная модель NIIET02_binary_sim.
В расчётной модели блок Engee Function формирует сигнал кнопки с шагом 1 мс. Интервалы нажатий заданы в целых миллисекундах. В первые 6 мс каждого нажатия уровень чередуется, имитируя дребезг контакта.
Запуск расчёта
Теперь запустим сохранённую модель. Первая ячейка открывает её и задаёт исходные параметры примера. После engee.run станут доступны временные ряды измеряемых сигналов.
using Plots
gr(format=:png)
demo_dir = @__DIR__
model_name = "NIIET02_binary_sim"
if !any(m -> string(m.name) == model_name, engee.get_all_models())
engee.load(joinpath(demo_dir, model_name * ".engee"))
end
engee.open(model_name)
engee.run(model_name)
Считаем результаты моделирования. Каждый временной ряд содержит время и значение сигнала.
measurements = Dict(
"RawButton" => collect(NIIET02_binary_sim_RawButton),
"Click" => collect(NIIET02_binary_sim_Click),
"Pressed" => collect(NIIET02_binary_sim_Pressed),
"Count" => collect(NIIET02_binary_sim_Count),
"LED1" => collect(NIIET02_binary_sim_LED1),
"LED2" => collect(NIIET02_binary_sim_LED2),
"LED3" => collect(NIIET02_binary_sim_LED3),
"LED4" => collect(NIIET02_binary_sim_LED4)
)
nothing
Разбор результатов
Построим временные диаграммы, чтобы сопоставить входные события, внутреннее состояние и видимую индикацию.
state_data = measurements["Count"]
p_state = plot(state_data.time, state_data.value;
seriestype=:steppost, ylabel="Число", label=false, lw=1.8)
p_led = plot(; ylabel="LED + смещение", xlabel="Время, с")
for i in 1:4
led = measurements["LED" * string(i)]
plot!(p_led, led.time, Float64.(led.value) .+ 1.5(i-1);
seriestype=:steppost, label="LED" * string(i), lw=1.6)
end
input_data = measurements["RawButton"]
p_input = plot(input_data.time, input_data.value;
ylabel="Кнопка PB3", label=false, seriestype=:steppost)
niiet_overview = plot(p_input, p_state, p_led; layout=(3,1), size=(1100,850), margin=6Plots.mm)
Рисунок 3. Состояния и выходные сигналы устройства.
Несмотря на начальный дребезг каждого нажатия, число меняется один раз. Получена последовательность 0, 1, …, 15, 0; всего выделено 16 событий. Состояния светодиодов соответствуют разрядам: LED1 переключается при каждом увеличении, LED2 – через одно, LED3 – через три, LED4 – после перехода через 7.
При переполнении все разряды снова равны нулю. Увеличенный участок кнопки объясняет результат: быстрые изменения сырого входа не попадают в устойчивое состояние, а после подтверждения появляется один короткий Click.
Увеличим первое нажатие. Здесь особенно хорошо видны дребезг, задержка подтверждения устойчивого состояния и единственный импульс Click.
panels = []
for (key, title) in [("RawButton","Сырой вход"),
("Pressed","Устойчивое нажатие"), ("Click","Событие нажатия")]
signal = measurements[key]
window = 0.09 .<= signal.time .<= 0.17
push!(panels, plot(signal.time[window], signal.value[window];
ylabel=title, label=false, seriestype=:steppost))
end
plot!(panels[end]; xlabel="Время, с")
niiet_detail = plot(panels...; layout=(3,1), size=(1100,800), margin=6Plots.mm)
Рисунок 4. Обработка первого нажатия.
Перенос на НИИЭТ
После моделирования перейдём к плате K1921VG1T. В таргетной версии сигналы светодиодов поступают на цифровые выходы. Источник кнопки теперь является физическим входом USER_BTN. Математическая часть определяет те же состояния и переходы, которые мы только что рассмотрели на графиках.
Таблица 1. Параметры моделей.
| Параметр | Расчёт в Engee | Выполнение на плате |
|---|---|---|
| Решатель | Euler, фиксированный шаг | Euler, фиксированный шаг |
| Шаг | 1 мс | 1 мс |
| Начало | 0 с | 0 с |
| Конец | 2.3 с | 600 с |
Таблица 2. Подключение сигналов платы.
| Сигнал | Вывод микроконтроллера | Назначение |
|---|---|---|
| USER_BTN | PB3 | Кнопка, активный уровень 0 |
| LED1 | PC12 | Разряд 0, вес 1 |
| LED2 | PC13 | Разряд 1, вес 2 |
| LED3 | PC14 | Разряд 2, вес 4 |
| LED4 | PC15 | Разряд 3, вес 8 |
Для DI зададим m_port = B, m_input = 3, SampleTime = 0.001. Низкий уровень соответствует нажатию USER_BTN. Для DO выбираются порт C и выводы m_output = 12…15 в порядке LED1…LED4; на вход подаётся UInt8. Номера PA/PB/PC обозначают выводы микроконтроллера. Их расположение на разъёмах следует найти в схеме своей ревизии платы. Для измерения PWM осциллограф подключают между сигнальным выводом и общей землёй.
Перед сборкой настроим EDM-Target-NIIETK1921VG1T. Путь к тулчейну должен указывать на установленную инфраструктуру компьютера стенда. Настройки запуска приведены отдельно, в таблице 3.
Таблица 3. Параметры EDM-Target.
| Поле | Значение |
|---|---|
| Таргет | Niietk1921vg1t |
toolchain_root |
D:\DEMO\NIIET\niietk1291vg1t_infrastructure |
programmer_type |
jlink |
toolchain_via |
auto |
code_gen_path |
. |
com_port |
COM10 |
Для прошивки подключим J-Link и установим соединение с платой в Engee.Интеграциях. Пример запускается независимо от обмена измеренными сигналами с компьютером. Время выполнения таргетной модели задано как StopTime = 600 с; его можно увеличить перед новой сборкой.
Рисунок 5. Таргетная модель NIIET02_binary_target.
В следующей ячейке показана вся последовательность аппаратного запуска: открытие модели, выбор K1921VG1T, генерация кода, сборка, прошивка и старт. Для выполнения на подключённой плате установите PROGRAM_TARGET = true. При обычном повторном расчёте графиков оставьте значение false.
PROGRAM_TARGET = false
if PROGRAM_TARGET
target_name = "NIIET02_binary_target"
if !any(m -> string(m.name) == target_name, engee.get_all_models())
engee.load(joinpath(demo_dir, target_name * ".engee"))
end
engee.open(target_name)
model = engee.gcm()
target = Main.EngeeDeviceManager.Targets.NIIETK1921VG1T.Niietk1921vg1t()
Main.EngeeDeviceManager.Targets.generate_executable_code(target, model, false)
Main.EngeeDeviceManager.Targets.compile_model(target, model)
Main.EngeeDeviceManager.Targets.upload_model(target, model)
Main.EngeeDeviceManager.Targets.start_model(target, model, false)
end
Нажимайте USER_BTN и сопоставляйте светодиоды с двоичным представлением числа. Удержание кнопки должно сохранять число, а шестнадцатое нажатие возвращать 0. Приведённые графики получены при моделировании в Engee; результаты измерений физических выходов можно сопоставить с ними в том же масштабе времени.
Выводы
Счётчик становится удобным интерфейсом только после выделения одиночного события нажатия. Подавление дребезга отделяет физическую кнопку от логики счёта, а двоичное разложение связывает число с четырьмя светодиодами. После 16-го нажатия счётчик возвращается в нулевое состояние.

