Это нужно знать

Общий перечень знаний –
на этой странице



Подключение кнопки к AVR

Управляющая программа обработки нажатия кнопки.
Подключаем кнопку к микроконтроллеру Atmega8, устраняем дребезг контактов, зажигаем и гасим светодиод

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

Как справиться с этим вредным эффектом аппаратными средствами, мы подробно рассмотрели на странице – Ссылка на страницу. Сегодня же мы проделаем всё то же самое, но только программными методами, т. е. без использования каких-либо внешних элементов.

По традиции начнём с конца, т. е. со схемы уже готового проекта, собранного на Atmega8 в Протеусе (Рис.1). Далее нам предстоит написать программу прошивки микроконтроллера в Atmel Studio, а потом проверить всё это хозяйство в симуляторе.

Кнопка управляет Atmega8 – зажигаем и гасим светодиод

Рис.1 Кнопка управляет Atmega8 – зажигаем и гасим светодиод

Используем традиционный способ подключения, подсоединив нормально разомкнутую кнопку без фиксатора между выводом PB1 и землёй. Таким образом, эта кнопка при нажатии будет замыкать вывод PB1 на низкий (нулевой) уровень напряжения.
А для того, чтобы при разомкнутой кнопке данный вход микроконтроллера не болтался в воздухе, а был притянут к питанию, мы применим подтягивающий резистор в несколько десятков килоом, который можно не паять физически, а подключить его внутри МК специально предназначенной для этого командой.

Светодиод, как и ранее, подключим через резистор к выводу Atmega8 PC0.

Ну что ж, пришло время Atmel Studio и программы прошивки, которую будем писать всё на том же языке Си. Для начала – всё по аналогии с предыдущим проектом:

#include <avr/io.h>
#define F_CPU 1000000UL   // Выбираем частоту МК
#include <util/delay.h>   // Включаем функцию задержек

int main(void)
{   // Начало основной программы

Теперь нам надо настроить порт PC0 – как выход и установить на нём низкий уровень, чтобы при включении питания светодиод был погашен:

DDRC |= ( 1 << 0 );   // Конфигурируем вывод порта PC0 - как выход,
PORTC &= ~(1 << PC0);   // Устанавливаем 0 на его выходе

PB1 настраиваем как вход, причём не простой вход, мотающийся абы как в воздухе, а притянутый внутренним резистором к шине питания:

DDRB &= ~( 1 << 1 );   // Конфигурируем вывод порта PB1 - как вход
PORTB |= ( 1 << 1 );   // Подключаем к PB1 подтягивающий резистор на плюс питания

Переключение светодиода, т. е. изменение выходного уровня на PC0 должно происходить в момент замыкания копки (по отрицательному фронту сигнала на PB1). Всё остальное время, причём не сильно важно: замкнута ли кнопка или разомкнута, МК не должен совершать никаких действий с PC0, мало того, не должен останавливать выполнение циклов основной программы.

И поскольку нам надо отслеживать перепад уровня сигнала на входе PB1, а не сам уровень, то нам потребуется некая дополнительная переменная, назовём её "in". Эта переменная будет запоминать уровень входного сигнала в конце предыдущего цикла, а программа будет сравнивать её значение с текущим состоянием входа PB1. Поскольку начальное напряжение на PB1 (при ненажатой кнопке) равно напряжению питания, то и начальное значение этой переменной объявим равным единице.

char in = 1;   // Объявляем переменную in и записываем в неё число 1

Теперь, когда все подготовительные работы у нас проведены, можно запускать основную программу:

while
     {    // начало цикла

и в каждом цикле ожидать перепада напряжения на PB1, соответствующего моменту замыкания кнопки.
Такое у нас произойдёт при PB1 = 0 и in = 1. А условие такого ожидания будет иметь вид:

if (!(PINB & (1<<PINB1)) && in == 1 )    /* если PB1 стал равен нулю и значение in при этом = 1, то мы поймали замыкание кнопки на входе, т. е. перепад из 1 в 0 */
         {

Что нам теперь нужно сделать, после того как мы определили момент нажатия кнопки?
1. Присвоить переменной in = 0, потому как уровень входного сигнала на PB1 у нас стал нулевым.
2. Подождать 10...100 мс (в зависимости от качества кнопки), пока не закончится дребезг контактов.
3. Переключить выход PC0 в противоположное состояние, т. е. если на выходе была единица – скинуть её в ноль и наоборот.

Сделаем мы это посредством следующих команд:

in = 0;    // 1. Стало быть уровень входного сигнала стал равен нулю

_delay_ms(100);    // 2. Задержка 100мс. Выжидаем окончания дребезга контактов

PORTC ^= (1 << 0);    // 3. Переключаем PC0 в противоположное состояние
          }

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

Такое произойдёт при условии PB1 = 1 и in = 0, то есть:

if (PINB & (1 << PINB1) && in == 0)    /* если PB1 стал равен единице и in = 0,
то мы поймали на входе размыкание кнопки, т. е. перепад из 0 в 1 */

{

Что делать, после того, как мы определили момент отпускания кнопки?
1. Присвоить переменной in = 1, потому как уровень входного сигнала на PB1 у нас вернулся к единице.
2. Опять подождать 10...100 мс, пока не закончится дребезг контактов.
А больше ничего делать и не надо, т. к. выход у нас должен переключаться только при замыкании кнопки.
Опишем эти процедуры командами:

in = 1;    // В этот момент уровень входного сигнала стал равен нулю

_delay_ms(100);    /* Задержка 100мс - опять ждём окончания дребезга,
больше ничего в момент размыкания кнопки делать не будем */

}

Всё, что теперь остаётся – это опять вернуться к началу цикла и продолжить выполнение программы

   }      // конец цикла
}    // конец программы


Теперь сгруппируем всё воедино и получим искомый код на языке Си:

/*
* GccApplication2.c
*
* Created: 22.06.2021 11:37:14
* Author : Vpayaem.ru
*/

#include <avr/io.h>
#define F_CPU 1000000UL   // Выбираем частоту МК
#include <util/delay.h>   // Включаем функцию задержек

int main(void)
{   // Начало основной программы

DDRC |= ( 1 << 0 );   // Конфигурируем вывод порта PC0 - как выход
PORTC &= ~(1 << PC0);   // Устанавливаем 0 на его выходе
DDRB &= ~( 1 << 1 );   // Конфигурируем вывод порта PB1 - как вход
PORTB |= ( 1 << 1 );   // Подключаем к PB1 подтягивающий резистор на плюс питания
char in = 1;   // Объявляем переменную in и записываем в неё число 1

while (1)
     {    // начало цикла

if (!(PINB & (1<<PINB1)) && in == 1 )    /* если PB1 стал равен нулю и значение in при этом = 1, то мы поймали замыкание кнопки на входе, т. е. перепад из 1 в 0 */
         {
in = 0;    // 1. Стало быть уровень входного сигнала стал равен нулю

_delay_ms(100);    // 2. Задержка 100мс. Выжидаем окончания дребезга контактов

PORTC ^= (1 << 0);    // 3. Переключаем PC0 в противоположное состояние
          }

   // Теперь ждём момента, когда копка будет отпушена:

if (PINB & (1 << PINB1) && in == 0)    /* если PB1 стал равен единице и in = 0,
то мы поймали на входе размыкание кнопки, т. е. перепад из 0 в 1 */

{
in = 1;    // В этот момент уровень входного сигнала стал равен нулю

_delay_ms(100);    /* Задержка 100мс - опять ждём окончания дребезга,
больше ничего в момент размыкания кнопки делать не будем */

}

   }      // конец цикла
}    // конец программы

Теперь, так же как и в предыдущем примере, нужно перенести готовый код в Atmel Studio и скомпилировать его. Для этого необходимо кликнуть по кнопке Build и в выпавшем меню выбрать Build Solution.
Если ошибок нет, то файл успешно скомпилируется, а в нижней части экрана появится надпись:
==== Build: 1 succeeded or up-to-date, 0 failed, 0 skipped ====

Далее нам надо войти в папку, в которой мы сохранили наш проект, найти там ещё одну папку с названием Debug и убедиться в существовании файла с расширением HEX.
При помощи этого файла может производиться как прошивка микроконтроллера, так и проверка его работоспособности в программе для автоматизированного проектирования Proteus.

Скачать файл knopka.hex можно по ссылке – скачать файл




      Назад        Дальше      

 

Главная страница | Наши разработки | Полезные схемы | Это нужно знать | Вопросы-ответы | Весёлый перекур
© 2017 Vpayaem.ru   All Rights Reserved

     
     

Подключение кнопки к AVR

Управляющая программа обработки нажатия кнопки.
Подключаем кнопку к микроконтроллеру Atmega8, устраняем дребезг контактов, зажигаем и гасим светодиод

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

Как справиться с этим вредным эффектом аппаратными средствами, мы подробно рассмотрели на странице – Ссылка на страницу. Сегодня же мы проделаем всё то же самое, но только программными методами, т. е. без использования каких-либо внешних элементов.

По традиции начнём с конца, т. е. со схемы уже готового проекта, собранного на Atmega8 в Протеусе (Рис.1). Далее нам предстоит написать программу прошивки микроконтроллера в Atmel Studio, а потом проверить всё это хозяйство в симуляторе.

Кнопка управляет Atmega8 – зажигаем и гасим светодиод

Рис.1 Кнопка управляет Atmega8 – зажигаем и гасим светодиод

Используем традиционный способ подключения, подсоединив нормально разомкнутую кнопку без фиксатора между выводом PB1 и землёй. Таким образом, эта кнопка при нажатии будет замыкать вывод PB1 на низкий (нулевой) уровень напряжения.
А для того, чтобы при разомкнутой кнопке данный вход микроконтроллера не болтался в воздухе, а был притянут к питанию, мы применим подтягивающий резистор в несколько десятков килоом, который можно не паять физически, а подключить его внутри МК специально предназначенной для этого командой.

Светодиод, как и ранее, подключим через резистор к выводу Atmega8 PC0.

Ну что ж, пришло время Atmel Studio и программы прошивки, которую будем писать всё на том же языке Си. Для начала – всё по аналогии с предыдущим проектом:

#include <avr/io.h>
#define F_CPU 1000000UL   // Выбираем частоту МК
#include <util/delay.h>   // Включаем функцию задержек

int main(void)
{   // Начало основной программы

Теперь нам надо настроить порт PC0 – как выход и установить на нём низкий уровень, чтобы при включении питания светодиод был погашен:

DDRC |= ( 1 << 0 );   // Конфигурируем вывод порта PC0 - как выход,
PORTC &= ~(1 << PC0);   // Устанавливаем 0 на его выходе

PB1 настраиваем как вход, причём не простой вход, мотающийся абы как в воздухе, а притянутый внутренним резистором к шине питания:

DDRB &= ~( 1 << 1 );   // Конфигурируем вывод порта PB1 - как вход
PORTB |= ( 1 << 1 );   // Подключаем к PB1 подтягивающий резистор на плюс питания

Переключение светодиода, т. е. изменение выходного уровня на PC0 должно происходить в момент замыкания копки (по отрицательному фронту сигнала на PB1). Всё остальное время, причём не сильно важно: замкнута ли кнопка или разомкнута, МК не должен совершать никаких действий с PC0, мало того, не должен останавливать выполнение циклов основной программы.

И поскольку нам надо отслеживать перепад уровня сигнала на входе PB1, а не сам уровень, то нам потребуется некая дополнительная переменная, назовём её "in". Эта переменная будет запоминать уровень входного сигнала в конце предыдущего цикла, а программа будет сравнивать её значение с текущим состоянием входа PB1. Поскольку начальное напряжение на PB1 (при ненажатой кнопке) равно напряжению питания, то и начальное значение этой переменной объявим равным единице.

char in = 1;   // Объявляем переменную in и записываем в неё число 1

Теперь, когда все подготовительные работы у нас проведены, можно запускать основную программу:

while
     {    // начало цикла

и в каждом цикле ожидать перепада напряжения на PB1, соответствующего моменту замыкания кнопки.
Такое у нас произойдёт при PB1 = 0 и in = 1. А условие такого ожидания будет иметь вид:

if (!(PINB & (1<<PINB1)) && in == 1 )    /* если PB1 стал равен нулю и значение in при этом = 1, то мы поймали замыкание кнопки на входе, т. е. перепад из 1 в 0 */
         {

Что нам теперь нужно сделать, после того как мы определили момент нажатия кнопки?
1. Присвоить переменной in = 0, потому как уровень входного сигнала на PB1 у нас стал нулевым.
2. Подождать 10...100 мс (в зависимости от качества кнопки), пока не закончится дребезг контактов.
3. Переключить выход PC0 в противоположное состояние, т. е. если на выходе была единица – скинуть её в ноль и наоборот.

Сделаем мы это посредством следующих команд:

in = 0;    // 1. Стало быть уровень входного сигнала стал равен нулю

_delay_ms(100);    // 2. Задержка 100мс. Выжидаем окончания дребезга контактов

PORTC ^= (1 << 0);    // 3. Переключаем PC0 в противоположное состояние
          }

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

Такое произойдёт при условии PB1 = 1 и in = 0, то есть:

if (PINB & (1 << PINB1) && in == 0)    /* если PB1 стал равен единице и in = 0,
то мы поймали на входе размыкание кнопки, т. е. перепад из 0 в 1 */

{

Что делать, после того, как мы определили момент отпускания кнопки?
1. Присвоить переменной in = 1, потому как уровень входного сигнала на PB1 у нас вернулся к единице.
2. Опять подождать 10...100 мс, пока не закончится дребезг контактов.
А больше ничего делать и не надо, т. к. выход у нас должен переключаться только при замыкании кнопки.
Опишем эти процедуры командами:

in = 1;    // В этот момент уровень входного сигнала стал равен нулю

_delay_ms(100);    /* Задержка 100мс - опять ждём окончания дребезга,
больше ничего в момент размыкания кнопки делать не будем */

}

Всё, что теперь остаётся – это опять вернуться к началу цикла и продолжить выполнение программы

   }      // конец цикла
}    // конец программы


Теперь сгруппируем всё воедино и получим искомый код на языке Си:

/*
* GccApplication2.c
*
* Created: 22.06.2021 11:37:14
* Author : Vpayaem.ru
*/

#include <avr/io.h>
#define F_CPU 1000000UL   // Выбираем частоту МК
#include <util/delay.h>   // Включаем функцию задержек

int main(void)
{   // Начало основной программы

DDRC |= ( 1 << 0 );   // Конфигурируем вывод порта PC0 - как выход
PORTC &= ~(1 << PC0);   // Устанавливаем 0 на его выходе
DDRB &= ~( 1 << 1 );   // Конфигурируем вывод порта PB1 - как вход
PORTB |= ( 1 << 1 );   // Подключаем к PB1 подтягивающий резистор на плюс питания
char in = 1;   // Объявляем переменную in и записываем в неё число 1

while (1)
     {    // начало цикла

if (!(PINB & (1<<PINB1)) && in == 1 )    /* если PB1 стал равен нулю и значение in при этом = 1, то мы поймали замыкание кнопки на входе, т. е. перепад из 1 в 0 */
         {
in = 0;    // 1. Стало быть уровень входного сигнала стал равен нулю

_delay_ms(100);    // 2. Задержка 100мс. Выжидаем окончания дребезга контактов

PORTC ^= (1 << 0);    // 3. Переключаем PC0 в противоположное состояние
          }

   // Теперь ждём момента, когда копка будет отпушена:

if (PINB & (1 << PINB1) && in == 0)    /* если PB1 стал равен единице и in = 0,
то мы поймали на входе размыкание кнопки, т. е. перепад из 0 в 1 */

{
in = 1;    // В этот момент уровень входного сигнала стал равен нулю

_delay_ms(100);    /* Задержка 100мс - опять ждём окончания дребезга,
больше ничего в момент размыкания кнопки делать не будем */

}

   }      // конец цикла
}    // конец программы

Теперь, так же как и в предыдущем примере, нужно перенести готовый код в Atmel Studio и скомпилировать его. Для этого необходимо кликнуть по кнопке Build и в выпавшем меню выбрать Build Solution.
Если ошибок нет, то файл успешно скомпилируется, а в нижней части экрана появится надпись:
==== Build: 1 succeeded or up-to-date, 0 failed, 0 skipped ====

Далее нам надо войти в папку, в которой мы сохранили наш проект, найти там ещё одну папку с названием Debug и убедиться в существовании файла с расширением HEX.
При помощи этого файла может производиться как прошивка микроконтроллера, так и проверка его работоспособности в программе для автоматизированного проектирования Proteus.

Скачать файл knopka.hex можно по ссылке – скачать файл




      Назад        Дальше      

  ==================================================================