Найти в Дзене
ПЛИСовод.

FPGA: Мигалка с ШИМ.

Сегодня я сделаю мигалку на FPGA, которая будет плавно менять яркость светодиода. Итак, цель запустить Linux на FPGA где-то там, пока в дали, а пока я продолжаю просматривать видеоматериалы про ПЛИС. Первый на сегодня рассказал мне о счётчике на Verilog. И это было чертовски скучно, так как счётчики я делать научился давно. А вот второй видеоматериал меня прямо поразил: Поразил он меня тем, что для демонстрации используется моя плата. Неожиданно, но приятно. Не придётся городить огород, чтобы запускать их примеры у себя. Хотя, повторять счётчик я не стану. Лучше сделаю что-нибудь по-интереснее. Например, из счётчика можно сделать ШИМ, и заставить яркость светодиода пульсировать: Циклически плавно повышаться и понижаться. Для этого понадобится уже упомянутый счётчик, который станет сердцем ШИМ. Тактировать его буду прямо от кварца на плате. У ШИМ будет вход, с которым будет производиться сравнение текущего значения счётчика. Если значение счётчика меньше, чем значение входа, то светодио

Сегодня я сделаю мигалку на FPGA, которая будет плавно менять яркость светодиода.

Вот такая схема получится в результате.
Вот такая схема получится в результате.

Итак, цель запустить Linux на FPGA где-то там, пока в дали, а пока я продолжаю просматривать видеоматериалы про ПЛИС. Первый на сегодня рассказал мне о счётчике на Verilog.

И это было чертовски скучно, так как счётчики я делать научился давно. А вот второй видеоматериал меня прямо поразил:

Поразил он меня тем, что для демонстрации используется моя плата. Неожиданно, но приятно. Не придётся городить огород, чтобы запускать их примеры у себя.

Хотя, повторять счётчик я не стану. Лучше сделаю что-нибудь по-интереснее. Например, из счётчика можно сделать ШИМ, и заставить яркость светодиода пульсировать: Циклически плавно повышаться и понижаться.

Для этого понадобится уже упомянутый счётчик, который станет сердцем ШИМ. Тактировать его буду прямо от кварца на плате. У ШИМ будет вход, с которым будет производиться сравнение текущего значения счётчика. Если значение счётчика меньше, чем значение входа, то светодиод будет гореть. Иначе - нет. Тогда получается, что если на входе число 0, то счётчик никогда не будет его меньше - и светодиод гореть не будет вообще. А если единичка, то светодиод будет загораться на один такт из всего цикла.

Значение "все единички" будет обозначать максимальную яркость, но даже при таком значении всё-таки на один такт из цикла светодиод погаснет. При ожидаемой длине цикла более чем в 1000 тактов, не велика потеря.

Всё что я сейчас описывал, является классической реализацией ШИМ, которую постигают все начинающие ардуинщики. Единственная сложность, о которой не все ардуинщики знают - это необходимость удержания входного значения на протяжении всего цикла счёта, с 0 до максимума. Менять входное значение нужно строго в тот же момент, в которых происходит смена максимального значения на 0. В микроконтроллерах, для достижения подобного поведения, используется пара: предзагрузочный и теневой регистры. В той-же самой ATmega328 (сердце Arduino UNO и Arduino Nano) данная опция вшита и неотключаема, а в STM32 её можно включать и выключать, используя биты OCxPE в регистрах TIMx_CCMRx. Суть механизма простая - данные со входа в реальности используются только в тот самый момент обновления, именно тогда они загружаются во внутренний теневой регистр. А ШИМ постоянно сравнивает значение счётчика именно с ним - с этим самым теневым регистром.

Вот только я делаю не микроконтроллер (пока), и такие сложности мне ни к чему. Единственное - блок управления ШИМ должен выполнять требование - удерживать управляющее значение всё необходимое время.

А сделаю я так: ШИМ, при достижении последнего своего значения, будет выставлять строб готовности. Ровно на 1 такт. Этот строб будет ловить "контроллер пульсации" (не знаю, как назвать это правильно), и инкрементировать (или декрементировать, в зависимости от текущего направления) свой управляющий регистр. Само значение регистра окажется изменённым ровно к нужному моменту и просуществует в таком состоянии до конца периода шим.

Вбиваю в консольке

make waves

И в папку output скидывается результат симуляции:

Симуляция на Icarus Verilog, отображённая в VSCode.
Симуляция на Icarus Verilog, отображённая в VSCode.

Верхняя полоска - это значение управляющего регистра. Вторая - это значение регистра внутри ШИМ. Ну а последняя - это сам сигнал шим, подаваемый на светодиод.

Хотя, специально для симуляции я немного схитрил, сделав счётчики (что управляющего, что ШИМ) 3-х битными. Если бы не это, разглядывать выходной сигнал было бы почти невозможно. А вот для заливки на плату, счётчики делаю 12-битными, что должно дать задержку в:

4096 * (4096 * 2 - 2) / 48 000 000 ≈ 0.7 секунд на мигание.

Остаётся вбить

make flash

И прошивка собирается в контейнере и заливается на платку. Результат вживую:

Ну и ссылка на проект:

https://github.com/alexey-asmodean/fpga-learn/tree/master/02_pulsar