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); } } }
Индивидуальные задания
- Сделайте так, чтобы кнопка, подключенная к выводу PD2 меняла яркость свечения зеленого светодиода между значениями "50" и "250"
- Напишите программу, которая будет передавать состояния кнопок компьютеру. Например, при отправке плате "1" будет в ответ присылать текущее состояние кнопки BUTTON1, а при отправке "0" — BUTTON2
Остальные статьи цикла можно найти здесь.
Мы будем очень рады, если вы поддержите наш ресурс и посетите магазин наших товаров shop.customelectronics.ru.
Метки: AVR, PIN, вход, программирование Просмотров: 12514