CUSTOMELECTRONICS.RU
Информационно-учебный блог о разработке электроники
Эл. почта: info@customelectronics.ru

AVR. Настройка выводов на вход

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

Эквивалентная схема вывода

Для начала предлагаем вспомнить эквивалентную схему вывода микроконтроллера.

Эквивалентная схема вывода микроконтроллера

Эквивалентная схема вывода микроконтроллера

Как вы помните, регистр DDR настраивает режим работы вывода. Если в нем единица, то переключатель замкнут и на ногу выводится состояние регистра PORT.
Регистр PIN непрерывно контролирует и хранит текущее состояние вывода. Обратившись к нему мы можем узнать, какое напряжение подано на вывод.
Если вывод настроен на выход, то в регистре PIN окажется то же самое, что и в регистре PORT. Но если мы запишем в регистр DDR логический ноль, то переключатель выключится и усилитель будет отвязан от входа. Это значит, что можно будет подать произвольный логический сигнал на вывод и контролировать по средствам регистра PIN. Очень просто! Осталось только разобраться резистором.
Резистор необходим для подтяжки состояния вывода на тот случай, если вывод ни к чему не подключен. Если записать в регистр PORT "1", то вывод будет подтянут к напряжению питания и из регистра PIN вы прочитаете логическую единицу, а не какой-то мусор. Теоретически. На самом деле этот резистор имеет сопротивление в сотни килоом и на него лучше не рассчитывать, а поставить в схему дополнительный резистор.
Также наша схема сознательно упрощена. Дело в том, что если вывод настроен на вход, а в регистр PORT записан ноль, то будет производиться не подтяжка к нулю, а вывод перейдет в высокоимпедансное состояние. Другими словами не будет подключен ни к чему ни через какое сопротивление. Если при этом вы не подадите ничего не вход, то в регистре PIN вы точно получите мусор (который даже иногда используют для генерирования случайных чисел). Это необходимо в некоторых случаях для исключения влияния микроконтроллера на схему, которая к нему подключена. И это сложно отразить на схеме.
Если коротко подвести итог, то при разработке очень важно контролировать правильность настройки управляющих регистров и следить за подтяжкой каждого входного вывода. Еще можно дополнительно перечислить варианты настройки вывода микроконтроллера:
DDR=1, PORT=x, PIN=PORT. Вывод настроен на выход. На выводе и в регистре PIN то, что записано в регистр PORT
DDR=0, PORT=1, PIN=x. Вывод настроен на вход с подтяжкой к единице. В регистре PIN то, что подано на вход, либо логическая единица, когда ничего не подано
DDR=0, PORT=0, PIN=x. Вывод настроен как высокоимпедансный вход. То есть в регистре PIN то, что на входе. Но давайте перейдем к практике!

Подготовка к работе

На нашей плате TutorShield установлены две кнопки, состояние которых можно контролировать микроконтроллером. Перемычками эти кнопки можно подключить к выводам PD2, PD3. Также установим оду перемычку для управления трехцветным светодиодом:

Установка перемычек

Установка перемычек

На нашем шилде выводы микроконтроллера подтянуты к высокому уровню напряжения, а при нажатии на кнопку вывод подключается к земле. То есть в нормальном состоянии в регистре PIN будет единица, а если нажата кнопка, то ноль. Вот так выглядит схема подключения кнопок:

Схема подключения кнопок

Схема подключения кнопок

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

Считывание состояния вывода

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

#include <avr/io.h>
#define F_CPU 16000000UL //16MHz
#include <util/delay.h>

#define BUTTON1_PD 3
#define LED_BLUE_PB 3

int main(void) {
    //input configuration
    DDRD &= ~(1<<BUTTON1_PD);
    PORTD |= 1<<BUTTON1_PD;
    //output configuration
    DDRB  |= 1<<LED_BLUE_PB;
    PORTB &= ~(1<<LED_BLUE_PB);
    while(1) {
        if( (PIND & 1<<BUTTON1_PD) == 0 ) {
        	PORTB &= ~(1<<LED_BLUE_PB);
        }
        else {
        	PORTB |= 1<<LED_BLUE_PB;
        }
    }
}

Если все сделано правильно, то вот так должна выглядеть работа программы:

Теперь разберемся, что именно этот код делает.
В первую очередь мы настраиваем вывод на вход. Обратите внимание, что это вход без подтяжки. Подтяжка к питанию уже есть в схеме.
Затем производится настройка на выход ноги, управляющей светодиодом.
В бесконечном цикле состояние регистра PIND умножается на битовую маску BUTTON1_PD (то есть на "0b00001000"). Если кнопка нажата, то в третьем бите регистра PIND будет quot;0quot;, и результат сравнения произведения этого регистра и маски с нулем будет истинным. Светодиод при этом погаснет.
В противном случае, светодиод будет включен. То есть всегда, когда кнопка будет не нажата, светодиод будет гореть.

Выполнение действия

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

#include <avr/io.h>
#define F_CPU 16000000UL //16MHz
#include <util/delay.h>

#define BUTTON1_PD 3
#define LED_BLUE_PB 3

int main(void) {
    //input configuration
    DDRD &= ~(1<<BUTTON1_PD);
    PORTD |= 1<<BUTTON1_PD;
    //output configuration
    DDRB  |= 1<<LED_BLUE_PB;
    PORTB &= ~(1<<LED_BLUE_PB);
    while(1) {
        if( (PIND & 1<<BUTTON1_PD) == 0 ) {
            PORTB |= 1<<LED_BLUE_PB;
            _delay_ms(100);
            PORTB &= ~(1<<LED_BLUE_PB);
            _delay_ms(100);
        }
        else {
            PORTB &= ~(1<<LED_BLUE_PB);
        }
    }
}

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

Работа с двумя кнопками

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

#include <avr/io.h>
#define F_CPU 16000000UL //16MHz
#include <util/delay.h>

#define BUTTON1_PD 3
#define BUTTON2_PD 2
#define LED_BLUE_PB 3
#define LED_ORANGE_PB 2

int main(void) {
    //input configuration
    DDRD &= ~((1<<BUTTON1_PD)|(1<<BUTTON2_PD));
    PORTD |= ((1<<BUTTON1_PD)|(1<<BUTTON2_PD));
    //output configuration
    DDRB  |= ((1<<LED_BLUE_PB)|(1<<LED_ORANGE_PB));
    PORTB &= ~((1<<LED_BLUE_PB)|(1<<LED_ORANGE_PB));
    while(1) {
        if( (PIND & 1<<BUTTON1_PD) == 0 ) {
            PORTB |= 1<<LED_BLUE_PB;
        }
        else {
            PORTB &= ~(1<<LED_BLUE_PB);
        }

        if( (PIND & 1<<BUTTON2_PD) == 0 ) {
            PORTB |= 1<<LED_ORANGE_PB;
        }
        else {
            PORTB &= ~(1<<LED_ORANGE_PB);
        }
    }
}

Индивидуальные задания

  1. Сделайте так, чтобы кнопка, подключенная к выводу PD2 меняла яркость свечения зеленого светодиода между значениями "50" и "250"
  2. Напишите программу, которая будет передавать состояния кнопок компьютеру. Например, при отправке плате "1" будет в ответ присылать текущее состояние кнопки BUTTON1, а при отправке "0" — BUTTON2

Остальные статьи цикла можно найти здесь.

Мы будем очень рады, если вы поддержите наш ресурс и посетите магазин наших товаров shop.customelectronics.ru.

Метки: , , , Просмотров: 5881