Programming microcontrollers
2 December 2011

Программирование ПЛИС. Изучение явления «дребезг контактов» и метод избавления от него

Мы продолжаем изучение ПЛИС и языка VHDL. В данной статье, ориентированной на новичков, мы изучим явление «дребезг контактов» и рассмотрим способ избавления от него.

Итак, цель работы: Изучить явление «дребезг контактов», создать проект в Xilinx ISE Project Navigator: При нажатии на кнопку значение регистра увеличивается на 1.


Часть 1. Что такое «дребезг контактов»?


«Дре́безг — явление, возникающее в электрических и электронных переключателях, при котором они вместо некоторого стабильного сигнала выдают на выходе случайные высокочастотные колебания» (с) Википедия.
Говоря проще, при нажатии и отпускании кнопки она переходит в нужное состояние не сразу. Какое-то время контакты кнопки «дребезжат» между собой, что будет воспринято микроконтроллером как многократные импульсы. Количество этих импульсов может превышать тысячи. Наглядно дребезг можно увидеть на осциллограмме, на которой показан момент отпускания кнопки:



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


В моей предыдущей статье было подробно описано создание нового проекта для Spartan-3E Starter Kit в Xilinx ISE Project Navigator v12.3. Создадим проект еще раз, назовем его, например, drebezg_habr и внесем в него некоторые изменения:

1. Нам потребуется одна кнопка и восемь светодиодов. Добавим входной сигнал btn и 8 выходных сигналов led в порты:
entity drebezg_habr is
    Port ( clk : in  STD_LOGIC;
           btn : in STD_LOGIC;
           led : out  STD_LOGIC_VECTOR(7 downto 0));
end drebezg_habr;

2. В файле pin.ucf напишем имена ножек, соответствующие кнопке и каждому светодиоду:
NET "clk" LOC = "C9";
NET "led<0>" LOC = "F12";
NET "led<1>" LOC = "E12";
NET "led<2>" LOC = "E11";
NET "led<3>" LOC = "F11";
NET "led<4>" LOC = "C11";
NET "led<5>" LOC = "D11";
NET "led<6>" LOC = "E9";
NET "led<7>" LOC = "F9";
NET "btn" LOC = "K17";
NET "btn" PULLUP;

Ножка K17 соответствует нижней кнопке из имеющихся:

Слово PULLUP подключает кнопку по следующей схеме (прямо внутри ПЛИС):


Часть 3. Программирование.


Переходим в файл drebezg_habr.vhd. Создадим 8-битный регистр-счетчик, который мы будем пытаться прибавлять на единицу при однократном нажатии кнопки: между architecture и begin пишем
signal count_led: std_logic_vector(7 downto 0);

И сразу же, после слова begin, направляем этот счетчик на наши светодиоды:
led <= count_led;

Теперь наша задача прибавить единицу к счетчику count_led при нажатии кнопки. Сразу же напрашивается решение сделать переменную, которая бы хранила предыдущее состояние кнопки и сравнивалась с ее текущим состоянием. Давайте так и сделаем:
architecture Behavioral of drebezg_habr is
 
signal count_led: std_logic_vector(7 downto 0);
signal old_btn: std_logic;
 
begin
 
process(clk)
begin
  if rising_edge(clk) then
    old_btn <= btn;
    if old_btn = '0and btn = '1then
      count_led <= count_led + 1;
    end if;
  end if;
end process;
 
led <= count_led;
 
end Behavioral;

Транслируем, зашиваем, тестируем. Уверен, что результат вас совершенно не устроит, ибо диоды будут просто совершенно неупорядоченно мигать. Это связано с тем, что событий if old_btn = '0' and btn = '1' then во время нажатия и отпускания кнопки очень много как раз из-за дребезга контактов. Чтобы избавиться от этого явления нам необходимо дождаться четко установившегося значения логической единицы. Для этого мы заведем счетчик, который увеличивается на 1, пока кнопка имеет значение логической единицы. Счетчик обнуляется, если кнопка приняла значение логического нуля. Таким образом, сколько бы не было импульсов во время нажатия кнопки из-за дребезга, настанет момент, когда значение btn четко установится в логическую единицу, счетчик достигнет определенного значения, и мы сможем судить о том, что действительно было совершено нажатие кнопки. Теперь нам не потребуется переменная old_btn.
architecture Behavioral of drebezg_habr is
 
signal count_led: std_logic_vector(7 downto 0);
constant clk_freq : integer := 50_000_000; -- частота кварца
constant btn_wait : integer := clk_freq/4; -- будем ждать 0.25 секунды установления единицы
signal count : integer range 0 to btn_wait := 0;
 
begin
 
process(clk)
begin
  if rising_edge(clk) then
    if btn = '1then
      count <= count + 1;
      if count = btn_wait then
        count_led <= count_led + 1;
        count <= 0;
      end if;
    else
      count <= 0;
    end if;    
  end if;
end process;
 
led <= count_led;
 
end Behavioral;

Значение btn_wait было выбрано 0.25 секунды для того, чтобы значение count_led не прибавлялось слишком часто, пока кнопка находится в зажатом состоянии.
Еще один вариант антидребезга (даже более надежный) — прибавление count на 1 когда btn является логической единицей, и вычитание из count 1 когда btn является нулем. При этом если значение count опускается до 0 значит кнопка не нажата, либо был дребезг. Ну а если count досчитал до заветного btn_wait значит произошло нажатие =)
В качестве домашнего задания могу посоветовать дописать проект: сделайте прибавление count_led после того, как кнопка была нажата и отпущена.

Итак, мы ознакомились на практике с явлением «дребезг контактов» и научились избавляться от него. Это явление можно наблюдать не только в кнопках, тумблерах и прочих подобных вещах, но даже иногда и в различных протоколах, например RS-232.
Исходники проекта здесь. Желаю всем успехов в освоении ПЛИС!

+18
28.2k 55
Comments 31