Pull to refresh

Программирование ПЛИС. Плавное изменение яркости светодиодов на Spartan-3E Starter Kit с использованием ШИМ (PWM)

Reading time 9 min
Views 62K
Эта статья ориентирована на новичков в программировании ПЛИС на языке VHDL и тех, кто хочет научиться это делать. Ранее на хабре уже была рассмотрена статья с аналогичной задачей, реализованной на PIC-контроллере. А в этой статье речь пойдет об изменении яркости свечения светодиода с помощью ПЛИС.
Итак, цель работы: Освоить понятие ШИМ и применить его в изменении яркости светодиода. Для реализации воспользоваться языком программирования VHDL в среде разработки Xilinx ISE Project Navigator v12.3.


Перейдем к реализации цели


Для реализации нам потребуется какая-нибудь железка с ПЛИС, я выбрал имеющуюся под рукой платку Spartan-3E Starter Kit (DataSheet). Так же необходимо установить Xilinx ISE Project Navigator (у меня установлена версия 12.3). В принципе, все готово работе. Осталось только подключить питание к платке и соединить ее по USB с компьютером для последующего программирования.

Часть 1. Теория изменения яркости светодиода.


Яркость светодиода можно регулировать подачей на него разных значений постоянного напряжения (например, переменным резистором). Но на нашей плате установлены светодиоды без переменных резисторов, которые могут принимать значение ‘1’ и светиться в полную яркость, либо ‘0’. Так как же тогда сделать регулировку яркости у такого столь простого девайса? Ответ – ШИМ. Вся суть в том, что мы будем «моргать» этим светодиодом настолько быстро, что моргание даже не будет заметно нашему глазу, а мы просто будем видеть тускло светящийся светодиод. Если сказать точнее, то просто у светодиода есть переходный процесс при зажигании, то есть загорается он не мгновенно. Именно этим мы пользуемся, подавая единицу на очень короткий промежуток времени, так чтобы светодиод не успел загореться в полную яркость.

Часть 2. Создание нового проекта.


Загружаем ISE Project Navigator и тыкаем File -> New Project. Пишем имя проекта (у меня shim_habr), выбираем директорию для сохранения и снизу выбираем Top-level source type: HDL. Жмем Next.



Далее выбираем ПЛИС. В моем случае Family: Spartan3E; Device: XC3S500E; Package: FG320; Speed: -4. Все эти данные можно увидеть на самой микросхеме, либо посмотреть в даташите.



Далее выбираем Preferred Language: VHDL, жмем Next и потом Finish.



Проект создан. Теперь нужно добавить в него модуль, в котором мы будем описывать логику работы светодиода. Слева вверху находим кнопку New Source и жмем на нее:


В появившемся окне выбираем VHDL Module и пишем любое имя файла (можно одноименное с проектом shim_habr). Жмем Next.


Теперь нам нужно задать используемые в проекте ножки. Этого можно не делать сейчас и пропустить этот шаг, а потом написать все руками. Но поскольку для нашего проекта нам потребуется всего лишь три ножки, то я ввел их прямо здесь. Итак, нам потребуется опорная частота от установленного на плате 50 МГц кварца, подключенного к ПЛИС к ножке с именем C9, а так же мы будем использовать два светодиода так же уже установленных на плате. Допустим, это будут два правых светодиода, подключенных к ножкам ПЛИС под именами E12 и F12. Назовем ножку кварца clk, установим Direction: IN т.к. мы будем считывать частоту, а ножки со светодиодами – led1 и led2 со значением Direction: OUT, т.к. мы будем ими управлять.


Жмем Next, потом Finish. Видим открывшийся текстовый редактор с уже заполненной заготовкой I/O портов проекта.
entity shim_habr is
    Port ( clk : in  STD_LOGIC;
           led1 : out  STD_LOGIC;
           led2 : out  STD_LOGIC);
end shim_habr;

Как я уже говорил, можно было пропустить предыдущий шаг и ввести все это вручную. Далее нам нужно сопоставить имена портов с именами ножек ПЛИС. Тыкаем правой кнопкой на имени файла shim_habr.vhd в иерархии и выбираем пункт New Source.



В открывшемся окне выбираем Implementation Constrains File и называем этот файл pin. Жмем Next, затем Finish.



В открывшийся пустой файл пишем следующее:
NET "clk" LOC = "C9";
NET "led1" LOC = "F12";
NET "led2" LOC = "E12";

Сохраняем.
Номера ножек можно посмотреть в даташите, либо в нашем случае нам упростили поиск — номера можно посмотреть прямо на плате рядом с нужной периферией:



Часть 3. Программирование постоянной яркости светодиода.


Переключаемся в файл shim_habr.vhd и пишем код:
architecture Behavioral of shim_habr is
 
constant clk_freq  : integer := 50_000_000; -- частота кварца
constant shim_freq : integer := 10_000;     -- частота ШИМ
constant max_count : integer := clk_freq / shim_freq; -- разрядность ШИМ
 
signal count: integer range 0 to max_count := 0; -- счетчик делителя частоты
constant porog: integer := max_count / 48; -- ширина импульса логической единицы
 
begin
 
process(clk)
begin
  if rising_edge(clk) then
    if count = max_count then
      count <= 0;
    else
      count <= count + 1;
    end if;
  end if;
end process;
 
led1 <= '1when count < porog else '0';
led2 <= '1';
 
end Behavioral;

Теперь разберемся, что делает этот код. Вначале обратите внимание на строчку led2 <= '1'. Мы зажигаем второй светодиод в полную яркость, подавая туда логическую единицу, чтобы нам было с чем сравнивать яркость свечения первого светодиода. Далее смотрим на объявленные регистры и константы. Константа clk_freq хранит частоту кварца в герцах; shim_freq это частота ШИМ в герцах. Соответственно, чтобы получить нужный нам период ШИМ, необходимо поделить тактовую частоту на частоту ШИМ, и мы получим число тактов главного кварца соответствующее периоду ШИМ. По сути это будет являться разрядностью ШИМ. Результат деления записываем в константу max_count. Далее создаем счетчик count, который будет циклично считать от 0 до max_count на частоте 50МГц. Создаем процесс process(clk). Условие if rising_edge(clk) then ждет очередного «тика» с кварца, и если он произошел выполняет прибавление на единичку счетчика count, проверяя не досчитал ли он до максимального значения. Далее, вне процесса пишем строку
led1 <= '1when count < porog else '0';

То есть на светодиоде висит логическая единица тогда, когда наш счетчик меньше какого-то порогового значения porog (у меня это 1/48 от всего периода, смотри объявление констант), остальную часть периода на светодиоде висит логический ноль. Это наглядно можно показать рисунком:


Часть 4. Прошивка.


Сохраняем все изменения, выбираем в иерархии файл shim_habr.vhd и снизу под иерархией ищем процесс Configure Target Device и запускаем его. Ждем, пока проект оттранслируется в файл прошивки, после чего откроется окно программы iMPACT, с помощью которой мы будем зашивать его в ПЛИС.
Двойной щелчек на Boundary Scan, и если у вас плата подключена к компьютеру по USB, то вы увидите примерно следующее:



Если вам не предложили выбрать файл прошивки для xc3s500e, то кликайте правой кнопкой по соответствующей микросхемке и выбирайте пункт меню Assign Configuration File. В окне выбора файла выбираем недавно созданный shim_habr.bit. Далее опять правой кнопкой на xc3s500e, затем Program. Запустится процесс зашивки, после чего появится надпись Program Successful. Если все прошло именно так, то можно смотреть на платку =)

Часть 5. Программирование плавного изменения яркости светодиода.


Итак, видим что у нас горят два светодиода – один ярко, другой тускло. Теперь попробуем сделать плавное изменение яркости. Для этого нам нужно porog сделать не константой, а переменной, и плавно ее изменять от минимума к максимуму.
signal porog: integer range 0 to max_count := 0;

Для того, чтобы задать скорость изменения порога, нам нужно опять поделить тактовую частоту и получить более маленькую. Например, нам хочется, чтобы порог увеличивался на 1 каждую 1/600 секунды. Создаем счетчик:
constant max_count_div: integer := clk_freq / 600;
signal count_div: integer range 0 to max_count_div := 0;

и дописываем в process(clk) в момент очередного «тика» if rising_edge(clk) then еще одно условие:
    if count_div = max_count_div then
      count_div <= 0;
      -- здесь частота поделена на 600.
    else
      count_div <= count_div + 1;
    end if;

Теперь нам нужно вписать в место, где частота поделена на 600, увеличение порога на 1 и сброс в 0 если значение достигло максимального:
      if porog = max_count then
        porog <= 0;
      else
        porog <= porog + 1;
      end if;

В результате общая картина будет выглядеть следующим образом:
architecture Behavioral of shim_habr is
 
constant clk_freq  : integer := 50_000_000; -- частота кварца
constant shim_freq : integer := 10_000;     -- частота ШИМ
constant max_count : integer := clk_freq / shim_freq; -- разрядность ШИМ
 
signal count: integer range 0 to max_count := 0; -- счетчик делителя частоты
signal porog: integer range 0 to max_count := 0; -- ширина импульса логической единицы
 
constant max_count_div: integer := clk_freq / 600;
signal count_div: integer range 0 to max_count_div := 0;
 
begin
 
process(clk)
begin
  if rising_edge(clk) then
    if count = max_count then
      count <= 0;
    else
      count <= count + 1;
    end if;
 
    if count_div = max_count_div then
      count_div <= 0;
      if porog = max_count then
        porog <= 0;
      else
        porog <= porog + 1;
      end if;
    else
      count_div <= count_div + 1;
    end if;
  end if;
end process;
 
led1 <= '1when count < porog else '0';
led2 <= '1';
 
end Behavioral;

Транслируем, зашиваем, радуемся успехам. =) Вам может показаться, что светодиод большую часть времени светится ярко, а тусклый он только в начале цикла. Я уже писал об этом эффекте в своей предыдущей статье про цветомузыку. Дело в том, что яркость зависит от порога не линейно, а логарифмически. То есть, например, чтобы яркость изменялась плавно от минимума к максимуму с использованием ШИМ разрядностью 1024 необходимо брать последовательно следующие значения переменной porog: 0, 16, 32, 64, 128, 256, 512, 1024.

Домашнее задание.


Как видите, светодиод плавно набирает яркость, а как только набрал – сразу сбрасывает в 0 (собственно, что написали то и получили). В качестве тренировки можно попробовать сделать плавный набор яркости, а по достижении максимальной начать ее плавно уменьшать до нуля. Тем, кто с легкостью справится с этим заданием, можно попытаться сделать «бегущие огни» из восьми имеющихся светодиодов: плавно загорается и гаснет первый диод, потом второй и т.д. Если не получается, но интересно, то спрашивайте — постараюсь ответить и объяснить.

Заключение.


Итак, мы освоили применение ШИМ к светодиоду и научились регулировать яркость светодиода с помощью ПЛИС. Этим же методом можно регулировать скорость вращения двигателя, магнитную силу катушек, и т.д.
Желаю всем успехов в освоении ПЛИС!

P.S.: Прошу прощения, забыл исходник проекта для этой статьи на работе… Выложу как только заберу его от туда (дома просто ничего не установлено). Впрочем он здесь и не сильно нужен, все можно (и желательно — нужно!) сделать самому, с нуля.

UPD: Обещанные исходники.
Tags:
Hubs:
+19
Comments 28
Comments Comments 28

Articles