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

    Мы продолжаем изучение ПЛИС и языка 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.
    Исходники проекта здесь. Желаю всем успехов в освоении ПЛИС!
    AdBlock похитил этот баннер, но баннеры не зубы — отрастут

    Подробнее
    Реклама

    Комментарии 31

      +7
      Я бы не стал смешивать логигу антидребезга и счетчика, а разнес их на 2 отдельных модуля.


      Модуль «Антидребезг» «врезается» в дребезжащую линию, давая на выходе чистый сигнал. А все остальные модули продолжают ловить фронт, как делали это раньше:
      if (old_D = '0') and (D = '1') then
        +1
        А я бы вообще не стал смешивать логику и антидребезг. Есть специальные буферные микросхемы для аппаратного решения проблемы.
          0
          А если у нас уже готовая стандартная плата, такая как Spartan-3E Starter Kit, то выкручиваться лучше не аппаратно, чтобы не было уже лишних делатей.
            0
            тем более не будете же вы вешать «специальные буферные микросхемы» на каждый используемый вывод ПЛИС
              +3
              такие микросхемы обычно ставятся на кнопки reset. Ни один производитель серьезных девкитов не пожалеет 10 центов на такой девайс.
              ключ «reset circuit».

              PS Хорошо, что пишете стать, спасибо.
                0
                Зачем на все выводы? только на выводы подключенные к кнопкам и это специальная микросхема не нужна, хватит старинного дедовского способа, КОНДЕНСАТОРА.

                я просто не занимаю зачем использовать какие то сложные компоненты в место простых? тем более что конденсатор явно надежнее чем любая микросхема.
                  0
                  Да, конденсатор выполнит роль антидребезга, но при нажатии кнопки конденсатор разряжается через нее, что приводит к появлению мгновенных значений тока в десятки — сотни ампер, что приведет к преждевременному выходу кнопки из строя.
                    0
                    Причем не важно какая именно будет ёмкость — маленькая или большая. Емкость это, грубо говоря, время существования тока. А мы имеем дело с мгновенными токами. И вот при сопротивлении в микроомы мгновенное значение тока будет огромным.

                    Особенно плохо это будет влиять на герконовые контакты: при таком раскладе они просто приварятся друг к другу.
                      +2
                      Но ведь можно впаять резистор! Там будет достаточно буквально единиц ом.
                        0
                        Мы с вами говорим о тактовых кнопках, обычных тактовых кнопках, если вы мне покажите случай где тактовая кнопка сломалась не от механического воздействия я от электрического при АДЕКВАТНЫХ значениях конденсатора то я съем свою шляпу.

                        В случае с герконовыми контактами, то вы наверное говорите о реле, там применяются совсем другие методы. Если покажите тактовую кнопку с герконовыми контактами то я буду благодарен.

                        и я полностью согласен с AlexanderG
                          0
                          На старых древних компьютерах были клавиатуры с такими кнопками.

                          И, хорошо, конденсаторы-резисторы решают проблему. И если Вам доставляет удовольствие, или же просто не лень сидеть с паяльником и заморачиваться с железом и детальками, нежели просто написать аккуратно код и так же аккуратно решить эту проблему, то я ничего против не имею.

                          В статье я описал как решить данную проблему на ПЛИС программно. Пользоваться этим способом или припаивать RC к каждой ножке — выбор лично Ваш.
                      –1
                      Защита от антидребезга с помощью конденсатора это миф, который идёт от не понимания физических процессов происходящих при коммутации.
                      Люди покупаются на триггер Шмидта с конденсатором на входе, но по сути это расширитель импульсов. Самый примитивный и без гарантий, потому что параллельно с конденсатором всегда есть паразитная индуктивность и при первом же прерывании происходят автоколебания (хотя бы пара периодов) на частоте резонанса и такой формирователь на 5-10 переключений даёт один сдвоенный или строенный импульс.
                      Самый надёжный и правильный способ борьбы с антидребезгом это переключающий контакт с RS тригерром на входе.
                        0
                        Размеры конденсатора и Индуктивности вы как теоретик явно не учитывали? да колебания будут но их заметит не всякий АЦП, что уж говорить про 2 статических уровня 0 и 1? или вы считаете что колебания буду с размахом в Uпитания/2?
                          0
                          Размеры конденсатора и Индуктивности вы как теоретик явно не учитывали?
                          При чём тут размер? На колебания впервую очередь влияет добротность паразитного контура. Дребезг контактов это задача возбуждения колебаний в длинной линии с распределёнными параметрами где, L и С имеют погонные значения.

                          да колебания будут но их заметит не всякий АЦП
                          По каким критериям вы различаете «всякий» и «не всякий АЦП»? Ни на одном КБ такой антидребезг с конденсатором не примут.

                          или вы считаете что колебания буду с размахом в Uпитания/2
                          Я вас опять удивлю. Амплитуда зависит от добротности паразитного контура и может превысить питание в несколько раз.
                        0
                        с конденсатором замечена следующая вещь была — при нажатии кнопки дребезга нет — все ровно, а при отжатии — вот что получается — конденсатор заряжен до напряжения ПИТАНИЯ U(это следует из того что в момент замыкания кнопки через подтяжечный резистор и конденсатор тока нет, следовательно напряжения на концах резистора равны), а напряжение подтяжки у нас меньше напряжения питания — поэтому при отпускании кнопки у нас будет импульс от напряжения подтяжки до напряжения питания, пока конденсатор разряжается через входное сопротивление пина — при частоте опроса 50МгЦ ПЛИС Altera Cyclone III это интерпретировала как еще одно нажатие кнопки — устранил путем понижения частоты опроса кнопки до 380 Гц
                          0
                          сейчас только что пришла идея — можно просто подобрать частоту опроса так, чтобы период ее был больше времени дребезга — допустим 50 мс, то есть частота опроса 20Гц и дребезг просто не будем видеть, а нажатие кнопки все равно минимум 120-130 мс длится(человеческая реакция) — поэтому за время удержания у нас гарантированно 2 опроса состояния — допустим нам не повезло и первый опрос попал на тот момент, когда кнопка дребезжала и был высокий уровень — не беда — второй опрос гарантированно будет низкий уровень
                    +3
                    Один раз мой друг, электронщик, попросил меня помочь ему написать маленькую программку на атмел, которая считала количество нажатий кнопки и в зависимости от этого выдавала нужный сигнал на выходе. Тогда столкнулся в первый раз с дребезгом контактов и героически начал его решать программно. На это мой друг просто впаял конденсатор к кнопке и проблема решилась сама собой. Не знаю, на сколько это было правильное решение, скорее всего оно может глючить в некоторых ситуациях, но тогда на практике оно помогло, что сэкономило пару десятков байт драгоценной памяти attiny13. Тогда это было оправдано.
                      +2
                      Вполне себе нормальное решение. Главное емкость подобрать.
                      А с учетом наличия SMD-кондеров это сэкономит память и такты внутри контроллера и не займет много места на плате.
                      +2
                      А еще можно просто считывать значение кнопки раз в 0.25 секунды. Криво, но работает. Возможно, когданибудь глюкнет, но вероятность мала.
                        0
                        ну ту в принципе есть шанс, что мы считаем именно в момент иголки на графике и получим ложный результат, хотя, наверняка, лучше чем ничего.
                          +1
                          Это лучше, чем искать фронт по 50-80Мгц сигналу (для спартанов-виртексов). Но хуже честного антидребезга, который приведен в посте.
                    +2
                    также рекомендуется еще вход с кнопки пропускать через два триггера предварительно, для избавления от возможной метастабильности (существует несущественная, но вероятность попадания нажатия кнопки в момент setup\hold у триггера даже после логического элемента).

                    а здесь пожалуй лучшая реализация, из тех что я видел.

                      +1
                      Верно.
                      Не
                      old_btn <= btn;
                      if (old_btn = '0') and (btn = '1') then

                      А
                      old_btn2 <= old_btn;
                      old_btn <= btn;
                      if (old2_btn = '0') and (old_btn = '1') then

                      Приведенный в посте вариант будет работать только тогда, когда btn переключается по синхроимпульсу.
                      Если btn асинхронный сигнал, то нужно использовать конструкцию, которую я привел.
                      0
                      Я когда-то делал уход от дребезга просто таким образом, что при нажатии кнопки ставил флаг и не читал клавиатуру 300 мс. В среднем длительность нажатия кнопки — 100 мс, период нажатия — 500 мс, но в отличие от метода с ожиданием отпускания, тут возможен вариант с тем, что можно плавно двигаться по меню просто держа кнопку.
                        0
                        Господа и дамы, будьте любезны, напишите кто-нибудь статью о микропроцессорах на VHDL/Verilog для ПЛИС. Есть несколько вопросов и хотелось бы задать их в комментариях соответствующей статье. По-жа-луй-ста!

                          +1
                          А не проще зарегистрироваться на electronix.ru/forum/ и там задать свой вопрос?
                          Там как раз обитаются ПЛИСоводы, которые пишут свои процессоры.
                            +1
                            ждем статью от whiteTigr про написание процессора на VHDL =) (выдаю все его секреты хе-хе)
                            0
                            А на сдвиговом регистре не проще?
                              0
                              А что есть простота? Мне вот проще сделать так как я написал, ибо я (и не только я) так делаю уже давно и это работает. Для Вас, возможно, Ваш метод будет проще.
                              0
                              Почти тоже самое, но на verilog: Debouncer на Verilog

                              Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

                              Самое читаемое