Урок 2
Обработка нажатий кнопок
На прошлом уроке мы научились использовать светодиоды как устройства вывода информации.
На этом уроке попробуем освоить устройства ввода, а именно – пять кнопок расположенных на
плате EP3C10 DevBoard.
В прошлый раз, при подготовке проекта, мы уже сконфигурировали выводы, подключенные к
кнопкам. Используем проект LED_Blinking, дополнив его модулем обработки кнопок.
Выводы ПЛИС , к которым подключены кнопки, подтянуты резисторами к «плюсу» питания, сами
кнопки, при срабатывании, замыкают положительный потенциал вывода на землю. Таким
образом, для фиксирования нажатия, достаточно «увидеть» уровень логического ноля на
соответствующем выводе.
Но не все так просто, известен такой эффект как «дребезг контактов» , когда в момент
срабатывания кнопки ( замыкания контактов ), происходит очень быстрое, хаотическое изменение
напряжения на ней. В этом случае, если бы мы хотели например запускать какую-то функцию по
нажатии кнопки, вместо одного запуска на нажатие мы можем получить с десяток. Особенно,
если учесть что быстродействие входной логики нашей ПЛИС порядка 200МГц, борьба с
«дребезгом» является необходимым условием для работы с кнопками.
Алгоритмы подавления «дребезга» достаточно просты. Например, самый популярный, после
фиксации нажатия, подождать 100мсек и проверить состояние кнопки еще раз. 100мсек –
достаточное время, чтобы процесс «дребезга» прекратился и можно было уверенно фиксировать
нажатия.
На прошлом уроке мы уже назначили выводы ПЛИС подключенные к кнопкам.
Оформим код обработчика кнопок в виде модуля, который пока разместим внутри основного
модуля проекта. Сигнал CLK_10kHz будет поступать в модуль для формирования необходимых
временных интервалов , пять линий, подключенных к кнопкам Buttons[4:0] и переменная Btn
шириной 3 бита, которая будет выдавать номер нажатой кнопки .
module buttons (clk_10kHz, Buttons[4:0], Btn[2:0]);
input clk_10kHz;
input Buttons[4:0];
output Btn[2:0];
// Код модуля
Endmodule
Для того, чтобы иметь возможность выполнять задачи через определенные промежутки времени,
необходимо предусмотреть тактирование схемы. Самый простой способ – использовать
конструкцию always @(clk) и применить счетчик.
//Код модуля
reg [15:0] cnt;
always @(posedge clk_10kHz)
begin
if (Buttons[4:0] != 5'b11111 ) begin if (cnt <= 1000) cnt <= cnt + 1; end
else begin if (cnt > 1)cnt <= cnt - 1; end
//……….
Согласно этому коду, каждый раз при положительном фронте тактового сигнала, с частотой 10кГц,
если любая из кнопок нажата ( уровень отличный от 1 ) и счетчик имеет значение меньше 1000,
значение счетчика увеличивается на 1 . Если кнопка не нажата, счетчик уменьшается на 1.
Таким образом, при нажатии кнопки, счетчик достигнет максимального значения в 1000 за
100мсек и за это же время уменьшится до 0 при отпускании. Далее…
if (cnt > 1000)
begin
case (Buttons[4:0])
5'b11110: Btn <= 1;
5'b11101: Btn <= 2;
5'b11011: Btn <= 3;
5'b10111: Btn <= 4;
5'b01111: Btn <= 5;
endcase
end
else Btn <= 0;
Если значение счетчика достигает 1000 (прошло 100мсек с момента нажатия), то в соответствии с
нажатой кнопкой записывается ее номер в выходной регистр. Если значение счетчика
уменьшается, в выходной регистр записывается 0.
Преобразование состояния линий к которым подключены кнопки, в двоичный код, выполнено с
помощью оператора case. В случае, если проверяемая величина может принимать относительно
немного значений, либо нас интересует небольшое количество ее значений, удобно применять
case. Запись очень наглядна, 0 в одном из разрядов шины Buttons означает нажатую кнопку. Если
бы в нашем проекте было бы не 5 кнопок а , например 55, пришлось бы отказаться от такой
формы записи.
Код модуля обработки кнопок полностью :
module buttons (clk_10kHz, Buttons[4:0], Btn[2:0]);
input clk_10kHz;
input [4:0] Buttons;
output reg [2:0] Btn;
//
reg [15:0] cnt;
always @(posedge clk_10kHz)
begin
if (Buttons[4:0] != 5'b11111 ) begin if(cnt <= 1000) cnt <= cnt + 1; end
else begin if (cnt > 1)cnt <= cnt - 1; end
//
if (cnt > 1000)
begin
case (Buttons[4:0])
5'b11110: Btn <= 1;
5'b11101: Btn <= 2;
5'b11011: Btn <= 3;
5'b10111: Btn <= 4;
5'b01111: Btn <= 5;
endcase
end
else Btn <= 0;
//
end
endmodule
Для того, чтобы использовать наш новый модуль, в коде основного модуля напишем
wire [2:0] Btn;
buttons (clk_10kHz, Buttons, Btn);
//
assign Port_LED = Btn;
Линии clk_10kHz и Buttons у нас уже прописаны, это выход модуля PLL1 и вход в ПЛИС
соответственно, осталось обозначить только выход результат работы модуля, Btn , содержащий
номер нажатой кнопки. Единственная строчка кода в основном модуле подключает эту
переменную к светодиодам на плате.
После компиляции и прошивки, можно проверить работоспособность проекта, при нажатии на
любую из кнопок, светодиоды платы будут показывать номер кнопки в двоичном коде.
Зная только общий принцип работы модуля, его входные и выходные сигналы, можно
представить его как «черный ящик», а код вообще убрать из файла основного проекта.
FileNewVerilog HDL File , скопируем в новый файл код нового модуля , из основного файла его
можно удалить, FileSave As сохраним файл в папку проекта под названием buttons. Теперь
нужно добавить этот файл в проект ProjectAdd Current file to Project.
Можно убедиться, что проект все также успешно компилируется и работает, хотя в коде
основного модуля осталась всего одна строчка
buttons (clk_10kHz, Buttons, Btn);
которая подключает внешний модуль к основной схеме.
Теперь можно применить новые возможности на практике. На прошлом уроке мы научились
получать различные эффекты с помощью светодиодов. Было бы неплохо объединить все эффекты
в одной прошивке, переключаясь между ними с помощью кнопок.
Для начала, преобразуем номер нажатой кнопки Btn в удобный для дальнейшей работы вид
reg [15:0]cnt;
reg [3:0] index;
reg dir;
reg [2:0] blink;
always @(posedge clk_10kHz)
begin
if (Btn != 0) blink <= Btn;
//…………….
Новый регистр blink так же содержит номер нажатой кнопки, только в отличие от Btn, при
отпускании кнопки, значение 0 в него не записывается. Теперь мы всегда можем знать, какая
кнопка была нажатой последней
case (blink)
1: begin
//Код первого эффекта
if (cnt <= 1000) cnt <= cnt + 1;
else begin
cnt <= 0;
Port_LED[12:0] <= 0;
index <= index + 1;
if (index > 12) index <= 0;
Port_LED[index] <= 1;
end
end
2: begin
//Код второго эффекта
end
3: begin
//Код третьего эффекта
end
endcase
end
Цель достигнута, кнопки переключают световые эффекты. На этом урок окончен.
Скачать

статью полностью в формате docx