НИИЭТ К1921ВГ1Т : Синусоидальная ШИМ
Синусоидальная ШИМ на одном канале
Введение
Широтно-импульсная модуляция позволяет представить медленно меняющийся сигнал последовательностью быстрых импульсов. Частота импульсов задаёт несущую, а длительность высокого уровня внутри каждого периода определяется заполнением. Чтобы получить синусоидальную огибающую, достаточно изменять заполнение по синусоидальному закону.
Построим пример для сигнала 50 Гц и несущей 20 кГц. В Engee отдельно увидим закон изменения заполнения и увеличенный участок импульсов. Затем перенесём управление заполнением на аппаратный PWM-выход PA4.
Постановка задачи и исходные данные
Частота синусоиды равна 50 Гц, её период – 20 мс. Частота ШИМ равна 20 кГц, период несущей – 50 мкс. Заполнение задаём в процентах от 0 до 100 и обновляем через 100 мкс. За один период синусоиды получим 200 значений заполнения и 400 периодов аппаратной несущей.
Для воспроизведения откройте эту папку в Engee. Расчётная модель находится в NIIET04_spwm_sim.engee, версия для платы – в NIIET04_spwm_target.engee. Кодовые ячейки ниже выполняются последовательно; путь к моделям определяется каталогом самого скрипта.
Алгоритм
Синусоида масштабируется в проценты заполнения. Ограничение защищает диапазон входа готового аппаратного блока.
Q – преобразование в UInt32, используемое перед PWM-блоком. Вторая формула описывает расчётный наблюдатель импульсов; реальная несущая формируется периферией платы.
Рисунок 1. Алгоритм работы устройства.
Построение модели в Engee
Источник Ref_A задаёт нормированную синусоиду. В Sine Wave частота вводится в радианах в секунду, поэтому для 50 Гц нужно Frequency = 2π·50, а не 50. Остальные настройки: Amplitude = 1, Phase = 0, SampleTime = 0.0001.
Пара Gain и Bias переводит диапазон −1…1 в проценты: Gain = 50, Bias = 50. Saturation с LowerLimit = 0, UpperLimit = 100 ограничивает допустимое заполнение. Преобразование в UInt32 с RndMeth = Floor даёт целое число процентов.
Для наблюдения импульсов в расчётной модели время умножается на 20000, после чего берётся остаток по модулю 1. Сравнение этой пилы с заполнением, делённым на 100, создаёт виртуальный PWM. При шаге расчёта 5 мкс на период несущей приходится 10 отсчётов: этого достаточно, чтобы связать огибающую с шириной импульсов.
Рисунок 2. Расчётная модель NIIET04_spwm_sim.
Запуск расчёта
Теперь запустим сохранённую модель. Первая ячейка открывает её и задаёт исходные параметры примера, чтобы значения в коде можно было сопоставить с настройками блоков. После engee.run станут доступны временные ряды измеряемых сигналов.
using Plots
gr(format=:png)
demo_dir = @__DIR__
model_name = "NIIET04_spwm_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)
f_signal = 50.0
f_pwm = 20_000.0
for phase in ["A"]
engee.set_param!(model_name * "/Ref_" * phase,
"Frequency" => "2*pi*$(f_signal)")
end
engee.set_param!(model_name * "/CarrierFrequency", "Gain" => string(f_pwm))
engee.run(model_name)
Считаем результаты моделирования. Каждый временной ряд содержит время и значение сигнала.
measurements = Dict(
"DutyA" => collect(NIIET04_spwm_sim_DutyA),
"PWMA" => collect(NIIET04_spwm_sim_PWMA),
"ReferenceA" => collect(NIIET04_spwm_sim_ReferenceA)
)
nothing
Разбор результатов
На первом графике сопоставим опору с заполнением.
reference = measurements["ReferenceA"]
duty = measurements["DutyA"]
p_ref = plot(reference.time, reference.value; ylabel="Опора A", label=false)
p_duty = plot(duty.time, duty.value;
xlabel="Время, с", ylabel="Заполнение A, %", label=false)
niiet_overview = plot(p_ref, p_duty; layout=(2,1), size=(1100,650), margin=6Plots.mm)
Рисунок 3. Управляющие сигналы и заполнение ШИМ.
За 0,06 с видны три периода синусоиды. Заполнение проходит весь диапазон 0…100%, а при переходе опоры через ноль равно 50%. На увеличенном участке частота импульсов остаётся около 20 кГц; меняется их ширина.
До преобразования в UInt32 закон заполнения совпадает с расчётной синусоидой. Целочисленный вход PWM даёт ступени по одному процентному пункту. Эти ступени относятся к разрешению команды заполнения; шаг 5 мкс на графике импульсов связан с сеткой моделирования.
Рассмотрим 0,5 мс импульсов. На этом коротком окне хорошо различим период несущей 50 мкс. На физическом выходе такие импульсы формирует PWM-периферия микроконтроллера.
panels = []
for phase in ["A"]
pwm = measurements["PWM" * phase]
window = 0.001 .<= pwm.time .<= 0.0015
push!(panels, plot(pwm.time[window], pwm.value[window];
ylabel="PWM " * phase, label=false, seriestype=:steppost))
end
plot!(panels[end]; xlabel="Время, с")
niiet_detail = plot(panels...; layout=(1,1), size=(1100,400), margin=6Plots.mm)
Рисунок 4. Импульсы расчётной ШИМ на увеличенном участке.
Перенос на НИИЭТ
После моделирования перейдём к плате K1921VG1T. В таргетной версии заполнение в процентах поступает в аппаратную PWM-периферию. Математическая часть определяет те же состояния и переходы, которые мы только что рассмотрели на графиках.
Таблица 1. Параметры моделей.
| Параметр | Расчёт в Engee | Выполнение на плате |
|---|---|---|
| Решатель | Euler, фиксированный шаг | Euler, фиксированный шаг |
| Шаг | 5 мкс | 100 мкс |
| Начало | 0 с | 0 с |
| Конец | 0.06 с | 600 с |
Таблица 2. Подключение сигналов платы.
| Сигнал | Вывод микроконтроллера | Назначение |
|---|---|---|
| PWM A | PA4 | Заполнение фазы A, 20 кГц |
В PWM укажем m_port = A, m_pin = 4, m_freq = 20000. Вход duty_percent принимает UInt32: 0 означает 0%, 100 – 100%. Частоту несущей формирует периферия, а модель обновляет заполнение каждые 100 мкс. Номера 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 с; его можно увеличить перед новой сборкой.
.png)
Рисунок 5. Таргетная модель NIIET04_spwm_target.
В следующей ячейке показана вся последовательность аппаратного запуска: открытие модели, выбор K1921VG1T, генерация кода, сборка, прошивка и старт. Для выполнения на подключённой плате установите PROGRAM_TARGET = true. При обычном повторном расчёте графиков оставьте значение false.
PROGRAM_TARGET = false
if PROGRAM_TARGET
target_name = "NIIET04_spwm_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
Для сравнения с расчётом наблюдайте PA4 осциллографом. На короткой развёртке видна несущая 20 кГц; для оценки огибающей заполнения нужен интервал не менее 20 мс. Приведённые графики получены при моделировании в Engee; результаты измерений физических выходов можно сопоставить с ними в том же масштабе времени.
Выводы
Синусоидальная ШИМ связывает две временные шкалы: сигнал 50 Гц и несущую 20 кГц. Масштабирование синусоиды задаёт заполнение, а сравнение с несущей объясняет форму импульсов. При переносе на плату те же проценты управляют аппаратной PWM-периферией.

