Avr кнопки на управление индикатором. Подключение кнопки к AVR. Устранение дребезга контактов


В предыдущих уроках я рассказывал, как с мк вывести информацию: и . А в этом уроке мы будем вводить информацию при помощи кнопок. Кнопки бывают нескольких видов: фиксирующие и тактовые.Из названия кнопки понятен принцип ее работы: тактовая - нажал, контакты замкнулись, разжал - разомкнулись; фиксирующие фиксируют своё состояние: нажал - замкнулись контакты, еще раз нажал - разомкнулись.

Стандартная схема подключения кнопок очень простая, выглядит так


Идея работы такова: на ножку через резистор 10к подается напряжение 5 вольт, на ножке логическая единица. Но когда мы нажимаем кнопку, мы ножку замыкаем на землю, а ток-то через резистор потечет маленький, и он будет не в состоянии удержать 5 вольт, и на ножке напряжение просядет до 0 вольт, а это логический 0.Эти моменты мы и будем отлавливать в программе. Напишем программу, которая будет при нажатии кнопки включать светодиод, при отжатой - выключать

#include #include void main(void) { // инициализация порта D PORTD=0b00000000; DDRD=0b10000000; while (1) { if (PIND & 0b00000100) /*проверяем, какой логический уровень у нас на ножке знак & - означает побитовое "И" например в PIND в нас находится 0b00000100, тогда 0b00000100 & 0b00000100 = 0b00000100, то есть true, а если в PIND у нас 0b00000000, то 0b00000000 & 0b00000100 = 0b00000000 а это false */ PORTD=0b00000000; // записываем ноль в седьмой бит порта D else PORTD=0b10000000; // записываем единицу в седьмой бит порта D }; delay_ms(100); // делаем задержку в 100 милисекунд для защиты от дребезга контактов }

В большинстве современных микроконтроллеров есть встроенный подтягивающий резистор R1, поэтому внешний можно и не ставить
Чтобы включить внутренний подтягивающий резистор нужно при инициализации порта в регистре PORTD выставить соответствующий бит, на котором висит кнопка, в единицу: PORTD=0b00000100;
А что же произойдет, если вывод будет сконфигурирован как выход:

  • Если на выводе логический ноль, ничего страшного не случится
  • если на выводе вдруг окажется логическая единица, то при нажатии кнопки мы попросту закоротим вывод на землю, и через него потечет ток, который ножка не выдержит (ток через ножку не должен превышать 40 милиампер), и вероятнее всего, она перегорит
Поэтому для защити желательно поставить между выводом микроконтроллера и кнопкой резистор ом на 300
Есть еще много способов как подключить кнопки к микроконтроллеру, например с помощью диодов или с помощью ацп, но я их описывать не буду, так как курс рассчитан для начинающих. Если надо, то найдете сами.

Обновлено 14.05.15. Здравствуйте дорогие друзья. В этой статье пойдет речь о кнопках и методе борьбы с дребезгом. В прошлой статье я рассказывал о первом своем устройстве — анализаторе, где были применены кнопки, поэтому пора рассмотреть тонкости их работы. Материала в интернете куча, но каждый индивидуальный взгляд на решение вопроса увеличивает скорость понимания данного направления. Поэтому я и решил написать. Ну что ж перейдем к кнопкам. Зачем нужны кнопки? К примеру вы создаете тот же , но для гибкости устройства необходимо менять граничные уровни напряжения, т.к. сеть у каждого очень индивидуальна, к сожалению. Вот тут и приходят на помощь кнопки.

Кнопка -это механическое устройство для передачи сигнала/ввода информации путём замыкания или размыкания двух или более контактов. По сути своей является датчиком внешнего физического воздействия. Не будем внедряться в курс электротехники самое главное нам необходимо знать что они бывают фиксирующие и не фиксирующиеся, а контакты кнопок бывают нормально замкнутые и разомкнутые, все дальше в лес не пойдем.

В основном применяют не фиксирующиеся нормально разомкнутые, но это решение строго личное, что и как использовать. Я дальше буду писать про нормально разомкнутые. Подключить кнопку к микроконтроллеру AVR , очень просто на рисунке ниже представлена схема подключения. В данном случае, просто подсоединяем один конец к ножке МК, а второй вывод к земле либо к питанию. Не забываем задействовать внутренний подтягивающий резистор для исключения помех от наводок, даже тем же пальцем. Принцип следующий настраиваем порт как вход т.е. регистр DDRx выставляем в 0, далее подтягиваем внутренние резисторы, т.е. выставляет регистр PORTx в1.

А далее алгоритм опроса, вот здесь он может быть очень индивидуален, ну во первых если нам необходимо просто ввести данные вначале программы и далее кнопки недолжны реагировать, то тут можно поместить опрос в циклическое условие, например ниже представлен кусочек кода моей программы на СИ для двух кнопок.

while ((e == 0)&&(e1 == 0)) // Начало цикла, берем любые переменные, к примеру равные нулю
// Далее начинается обработка кода, пока не поменяется, хотя бы одно значение переменной на истинно
{
if ((PIND & 0×20) == 0) // здесь мы нажали на кнопку, и в данном случае на пине 5 порта установился 0. Ниже небольшой код по обработке дребезга контактов, о нем написал чуть ниже.
{
_delay_ms (50); // Устранение дребезга клавиш
if ((PIND & 0×20) == 0) // Опять проверяем нажатие
e++; //Увеличиваем на 1
…… ; // Здесь выполняется необходимая нам команда
while ((PIND & 0×20) == 0) //Все ждем отпускание кнопки
{}
} // Выходим из условия
else {}; //можно и неиспользовать, пустая комманда, даже занимающая сокото там тактов
if ((PIND & 0×40) == 0) // а здесь мы нажал на кнопку с2, ну и т.д.
{
_delay_ms (50);
if ((PIND & 0×40) == 0)
e1++;
……;
while ((PIND & 0×40) == 0)
{}
}
else {};
……;
}; // выходим из циклического условия если мы нажали хотя бы одну кнопку

Данный код представляет примерный подход опроса двух кнопок. Алгоритм может меняться как пожелает фантазия. Но самое главное что в коде опроса должен присутствовать антидребезговый код. Что это? Дребезг это явление возникает в переключателях, которое представляет собой в момент переключения случайные многократные неконтролируемые замыкания и размыкания контактов и длятся они от десятков до сотен миллисекунд. На рисунке ниже у нас пример дребезга. Т.к. в протеусе все идеально, то пришлось дорисовать реальность – дребезг красным. Что у нас получается, МК посчитает, перед переходом на другую команду в момент переключения кнопки любое состояние из полосы неопределенности 0 или 1 которая возникает при колебаниях за счет дребезга контактов. Т.е. выходит угадал не угадал… Техника требует точности. Борются двумя методами, первый для компенсации дребезга применяют переключатели с обратной связью, с характеристикой в виде прямоугольной петли гистерезиса и т. д., второй программный метод. Этот метод мы и рассмотрим. В коде выше есть комментарий //Устранение дребезга клавиш . к строке _delay_ms (50); я взял 50 мс, но для пущей уверенности можно и сто а вообще надо подстраиваться под свою разработку. В коде после задержки, когда колебания затухли, опять сравнивается состояние. Вот таким нехитрым методом происходит опрос кнопок.

Давайте рассмотрим случай когда кнопка должна использоваться на протяжении всего времени работы устройства. Здесь нам на помощь приходят прерывания, например их можно использовать внешнее прерывание. К примеру подключаем кнопку к выводу INT0, другой вывод к земле, здесь зависит от определения условий генераций внешних прерывания и от типа МК. Читайте даташит. Порт также настраиваем на вход, подтягиваем резистор. В нормальном состоянии, когда кнопка разомкнута на выводе присутствует 1. В момент замыкания на вывод приходит 0, где по спадающему фронту (настройка) запускается обработчик прерываний. Для запуска внешнего прерывания необходимо флаг I регистра SREG установить в 1. При возникновения прерывания этот флаг аппаратно сбрасывается. Если мы хотим вызывать вложенные прерывания то необходимо программно установить этот флаг. При выходе из обработчика необходимо выполнить комманду reti; , которая установит этот флаг в 1. Например в был описан способ считывания кнопок в прерывании, только уже используя АЦП.

Ниже приведен код для работы с внешним прерыванием.

GICR =0×40; //Управляющий регистр для разрешения/запрещения прерываний. Разрешаем внешние прерывание INT0.
MCUCR = 0×02; //Конфигурационный регистр для выбора условий генераций внешнего прерывания. По спадающему фронту.
sei (); //установка флага I регистра SREG в 1. Разрешение общего прерывания. Должен быть обязательно установлен.
SIGNAL (SIG_INTERRUPT0 ) //Обработчик прерывания по вектору внешнего прерывания. Данные векторы хорошо описаны в Шпаке для среды WinAVR.
{ //Здесь пишем необходимый нам код при нажатии на кнопку }
//Далее обработчик прерывания заканчивает свою работу, и программа возвращается в основной цикл.

Также наверное стоит упомянуть о регистре GIFR, флаги которого используются для индикации прерываний. Кому необходимо читаем даташит.Способов использования прерываний для кнопок куча, поэтому читаем, подбираем… На этом все. В следующей статье поговорим об использовании памяти EEPROM. Всем пока.

Сегодня мы расширим свой кругозор по изучению работы портов микроконтроллера и изучим второе назначение порта — работу на вход. И для изучения работы на вход мы применим обычную тактовую кнопку.

Как всегда, создадим проект в Atmel Studio, выберем Atmega8A, назовем проект Test04 и код также в main.c, как обычно, скопируем с проекта предыдущего урока.

В качестве подопытного порта давайте возьмём порт B. Можно с успехом использовать любой порт. И в качестве ножки возьмем нулевую ножку. Итак у нас ножка B0.

Также опять мы соберём проект, скопируем и переименуем файл протеуса, откроем его и в свойствах контроллера покажем путь к новому проекту. Запустим на выполнение и убедимся, что всё работает.

Добавим кнопку в протеусе, для этого в поиске компонентов найдём Button

Затем подключим нашу кнопку вот таким вот образом к ножке B0 контроллера

Судя по данной схеме, когда кнопка будет в нажатом состоянии, то Ножка PB0 у нас будет иметь низкий логический уровень, а при отжатом — непонятный уровень, поэтому мы применим подтягивающий к питанию резистор, который можно не паять физически, а подключить опционально определенной командой.

Для этого мы, во-первых настроим порт B. Мы можем объявить все ножки порта B на вход, так как нам не важны настройки остальных ножке, ибо мы их не используем

DDRD = 0xFF;

DDRB = 0x00;

В случае, когда мы работали с портом D на выход, биты регистра PORTD отвечали за уровень на соответствующих ножках. А в случае, когда порт инициализирован на вход, как наш порт B, то биты регистра PORTB будут уже отвечать за подтягивание к соответствующим ножкам порта резисторов на шину питания. Если будет логическая единица, то регистр будет подтягиваться, а если логический ноль — то не будет. Поэтому мы в 0 бите регистра установим 1

PORTD = 0b00000001;

PORTB = 0b00000001;

Соберём код и запустим его в протеусе. Мы видим, что на ножке B0 у нас установилась логическая 1 , а если мы нажмём кнопку, то увидим, что на ней будет логический 0 , о чём свидетельствует синий цвет квадратиков на ножке и на кнопке.

В бесконечном цикле закомментируем весь код. В видеоверсии урока показано, как данную операцию можно выполнить одним движением (выделяем весь текст, который мы хотим закомментировать, затем нажимаем одновременно Shift и обычный слэш (не обратный))

// for(i=0;i<=7;i++)

// {

// PORTD = (1<

// _delay_ms(500);

// }

В данном цикле мы и будем отслеживать состояние ножки PB0. Делается это с помощью определения состояния соответствующего бита в регистре PINB, который собственно за это и отвечает.

Чтобы нам следить за каким-либо действием или состоянием, нам необходимо будет обработать условие.

Условие в языке C добавляется с помощью команды if .

И в качестве условия мы возьмём состояние ножки 0 порта B или состояние бита 0 регистра PINB .

Как же можно получить состояние одного бита, ведь в языке C в отличие от ассемблера нет битовых операций?

Можно пойти на хитрость и применить вот такую конструкцию PINB &0b00000001 .

Данная конструкция нам и проверит нулевой бит. То есть если в регистре PINB также будет 1 в нулевом его бите, то независимо от состояния остальных битов в данном регистре мы получим ненулевой результат, что также является истиной. То есть если ни с чем не сравнивать в условии результат, то условие эквивалентно сравниванием с нулём, только наоборот. Для истинности результат должен быть ненулевым — (результат!=0 ).

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

while (1)

if (!( PINB &0b00000001))

{

}

else

{

}

Теперь нам необходимо добавить тело условия. При выполнении условия, что кнопка нажата, мы будем зажигать светодиод на ножке D0. А если условие не будет выполняться (кнопка будет отжата), то мы будем его гасить. Также мы погасим данный светодиод и в начале программы. Поэтому получим следующий код

DDRB =0x00;

PORTD =0b00000000 ;

PORTB =0b00000001;

while (1)

If (!(PINB &0b00000001))

PORTD =0b00000001;

Else

PORTD =0b00000000;

Теперь давайте пересоберём проект и пойдём в протеус смотреть, удалось ли нам что-то.

Чтобы у нас при сборке не было даже предупреждений, уберём объявление переменной i, так как она в коде не используется

int main ( void )

// unsigned char i;

Unsigned char butcount =0;

Запустим проект в протеусе и увидим, что при нажатии на кнопку у нас начинает светиться самый верхний светодиод

Казалось бы, что мы своей цели уже добились. Но чтобы сделать наш код более ответственным и совершенным, мы просто обязаны провести борьбу с дребезгом контактов, так как такое явление может иметь место, это только в протеусе всё идеально, на практике такое бывает не всегда.

И чтобы это как-то отследить и определить, что это было именно нажатие, а не дребезг, то мы будим отслеживать нажатие некоторое время, ну или некоторое количество тактов или циклов. Для этого в начале функции main() до бесконечного цикла мы добавим другую переменную (i нам ещё пригодится и мы её портить не будем). Назовём мы переменную butcount , так как имя переменной должно как-то само за себя говорить и тем самым достигается ещё большая читабельность кода

// unsigned char i;

unsigned char butcount =0;

И чтобы воспользоваться данной переменной, мы применим ещё одно условие. И у нас будет условие в условии. Это всё допустимо и очень широко используется. И в зависимости от этого условия мы данную переменную будем наращивать (инкрементировать). Условием будет у нас достижение данной переменной определённой величины. То есть попробуем сделать так, чтобы значение переменной не достигало 5

if (!( PINB &0b00000001))

if ( butcount < 5)

{

butcount ++;

}

А когда значение данной переменной достигнет значения 5, то мы уже в данный цикл не попадём, а попадём мы в тело оператора else , который мы сейчас и добавим и в его теле напишем следующий код

Butcount ++;

else

PORTD =0b00000001;

То есть мы как раз после достижения пятёрки и будем обрабатывать нажатие кнопки и включать на нулевой ножке порта D высокое состояние.

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

else

if (butcount >0)

{

butcount —;

}

else

{

PORTD =0b00000000;

}

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

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

Смотреть ВИДЕОУРОК

Post Views: 13 084

Почти ни одно изделие с микроконтроллером не обходится без кнопок. Тема эта уже избитая и во многом известная. Написанием этой статьи я не пытаюсь изобрести велосипед. Просто решил собрать всю инфу по схемотехнике воедино. Думаю, что материал будет полезен начинающим.Чтобы не сбивать вас с толку, на приведенных ниже рисунках не показаны схемы питания, сброса и тактирования микроконтроллеров.

Способ первый - традиционный

рис1а рис1б

Если кнопок немного и дефицита выводов мк не наблюдается, используем традиционный способ подключения.

Когда кнопка отпущена – вывод мк через резистор соединен с “плюсом” питания (рис. 1а). Когда кнопка нажата – вывод мк соединен с землей. Подтягивающий резистор R1 ограничивает силу тока в цепи переключателя. Если бы его не было, то при нажатии кнопки мы бы просто закоротили наш источник питания.

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

Что произойдет, если вывод микроконтроллера окажется в режиме выхода? Это будет зависеть от состояния этого вывода. Если на выводе “логический ноль” – ничего страшного не случиться, потому что - в первом случае (рис1а) величина втекающего тока ограничена резистором R1, а во втором случае (рис1б) никакой ток вообще не потечет. При нажатии кнопки тоже ничего не случиться, поскольку разность потенциалов между выводом и “землей” в этом случае будет равна нулю.

Если же на выводе будет ”логическая единица” и кнопка окажется нажатой, то через вывод микроконтроллера на землю потечет ток величиной в несколько десятков миллиампер и вывод порта может “погореть”. Предельно допустимый ток для вывода микроконтролера AVR согласно документации равен 40 мА. Поэтому иногда нелишним бывает поставить между выводом мк и кнопкой резистор номиналом в несколько сотен ом, например 330 (рис 1с). Так, например, подключены кнопки на отладочной плате STK500. Это сделано для подстраховки, чтобы пользователь нечаянно не спалил микроконтроллер в ходе своих эксперементов.

Для своих макетов впрочем можно обойтись и без этого резистора.

Второй способ - с использованием диодов

Используется когда кнопок больше двух, а выводы мк хочется сэкономить. Каждой кнопке в данном случае соответствует свой цифровой код, а количество кнопок, которые можно таким способом повесить на N выводов мк = 2 N - 1. То есть на три вывода можно повесить 7 кнопок, на четыре – 15 и так далее... но я бы больше 7-ми вешать не стал. Увеличивается количество дополнительных внешних компонентов, усложняется схема и программа мк. Кроме того, для большого количества кнопок есть и другие схемы включения. Подтягивающие резисторы на схеме не показаны, подразумевается, что используются внутренние.

Кстати, через диоды еще можно завести сигналы от кнопок на вывод внешнего прерывания контроллера (рис. 3). При нажатии любой кнопки вывод внешнего прерывания через диод будет замыкаться на землю и вызывать прерывание (естественно при условии, что оно настроено и разрешено). Таким образом контроллеру не нужно будет постоянно опрашивать кнопки, эта процедура будет запускаться только по событию внешнего прерывания.

Данная схема актуальна не для всех микроконтроллеров AVR, потому что в некоторых моделях микроконтроллеров внешнее прерывание может возникать по любому изменению на любом выводе. (например в ATmega164P)

Третий способ – для матричной клавиатуры

Такой вариант подключения обычно используется для блоков из нескольких кнопок, которые объединены конструктивно и соединены электрически по матричной схеме. Но никто не запрещает использовать эту схему и для включения обычных кнопок, однако реальную экономию она дает при количестве кнопок? 9.

Выводы PС0, PС1, PС2, PC3 – это строки матрицы, выводы PB0, PB1, PB2 – это столбцы матрицы. Кнопки можно опрашивать либо по строкам, либо по столбцам. Допустим, мы опрашиваем их по столбцам. Процедура опроса будет выглядеть следующим образом. Начальное состояние всех выводов – вход с включенным подтягивающим резистором. Устанавливаем вывод PB0 в режим выхода и выставляем ноль. Теперь нажатие кнопок S1, S2, S3, S4 будет замыкать выводы PС0, PС1, PС2, PC3 на 0 питания. Опрашиваем эти выводы и определям нажата ли какая-нибудь кнопка в данный момент. Устанавливаем вывод PB0 в режим выхода и включаем подтягивающий резистор. Устанавливаем вывод PB1 в режим выхода и выставляем ноль. Снова опрашиваем выводы PС0, PС1, PС2, PC3. Теперь нажатие кнопок S5, S6, S7, S8 будет замыкать выводы PС0, PС1, PС2, PC3. Последний столбец кнопок опрашиваем аналогично.

Строки матрицы можно завести через диоды на вывод внещнего прерывания. Тогда логику программы можно было бы построить так. Если клавиатура не используется в течении нескольких минут, микроконтроллер переходит в режим пониженного энергопотребления. При этом выводы PB0, PB1, PB2 – конфигурируются как выходы с нулевым логическим уровнем. Когда одна из кнопок нажимается, вывод прерывания через диод замыкается на ноль. Это вызывает внешнее прерывание, микроконтроллер просыпается и запускает таймер по сигналам которого происходит сканирование клавиатуры. Параллельно запускается счетчик времени, который сбрасывается при нажатии любой из кнопок. Как только он переполняется, микроконтроллер опять переходит в режим пониженного энергопотребления.

На первом уроке мы научились подавать напряжение ножкой микроконтроллера. Теперь нужно научиться управлять микроконтроллером без перепрошивки.

Зачем это нужно? Например, вы сделали часы на микроконтроллере, нужно выставить время, но очень не удобно каждый раз перепрошивать, когда собьется время. Намного удобнее пользоваться кнопками, например, одной менять часы, другой минуты.

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

Пример: создаем проект при помощи мастера проектов. Первую ногу настраиваем как выход, вторую как вход. При создании мастером проектов указываем, что ножка PB1 будет входом, на ней же включаем подтягивающий резистор, реализуется это так:

После создания проекта приведем код к такому виду:

#include #include void main(void ) { PORTB= 0x02 ; DDRB= 0x01 ; while (1 ) { if (PINB.1== 0 ) { PORTB.0= 1 ; delay_ms(100 ) ; PORTB.0= 0 ; delay_ms(100 ) ; } } ; }

#include #include void main(void) { PORTB=0x02; DDRB=0x01; while (1) { if(PINB.1==0) { PORTB.0=1; delay_ms(100); PORTB.0=0; delay_ms(100); } }; }

Как мы видим, по сравнению с первым уроком изменилась настройка порта

if(PINB.1==0) {}

данную строчку нужно читать так — если на ножке 1 порта В подключили землю (0 потенциал), то выполнить код в фигурных скобках. В нашем примере это код из первого урока. Если кнопка не замкнута, то ничего не делать. Промоделировать можно в Proteuse.

Вместо кнопки можно поставить датчик, реле и т.п., вместо светодиода — пищалку, получится сигнализация.

Архив с прошивкой и файлом протеуса доступен

Update1: Зачем нужна подтяжка порта?
У входа мк большое сопротивление, если будут течь даже микротоки вызванные помехами, то по закону Ома U=R*I это может привести к тому, что на входе появится лог 1. Чтобы не было таких проблем в AVR микроконтроллерах можно подключить ножку к плюсу питания, через подтягивающий резистор. В этом случае даже, логика работы меняется наоборот — но если появится помеха, нам это не важно, ведь у нас на входе уже логическая единица.

Почему подключение через резистор? Допустим мы подключили вход к плюсу напрямую без резистора. Когда кнопка сработает, она притянет вход к земле, поэтому на входе будет короткое замыкание между + и землей. Если же стоит резистор, то при замыкании кнопки с одной стороны он так и останется подключен к +, а со второй стороны на нем появится земля от кнопки. Через резистор потечет ток, но его величина будет не такой большой.

Update2: Добавлен тест, в котором вы можете проверить на сколько хорошо вы усвоили материал урока

This movie requires Flash Player 9