Кнопки
Итак мы добрались до неотъемлемой части большинства проектов на микроконтроллерах — до кнопок. Кнопка достаточно простое устройство, имеющее, как правило, всего два состояния, если говорить языком программирования это состояние логической 1 (контакты замкнуты) и логического 0 (контакты разомкнуты). Рассмотрим схему.
Имеем все туже схему с семисегментными индикаторами, но добавлены 4 кнопки. При помощи кнопок группы A будем увеличивать или уменьшать выводимое значение на первых трех индикаторах, а кнопками группы B – изменять значение на последних двух индикаторах.
Для начала кратко о кнопках. Кнопки применим с нормально разомкнутыми контактами без фиксации. Одним контактом подключим к земле, а другим к отдельным выводам микроконтроллера. Подтягивающий к плюсу резистор устанавливать не будем, так как таковой предусмотрен в самом микроконтроллере. Осталось только написать программу для опроса кнопок (состояния выводов микроконтроллера) и вывода результата на индикаторы. В связи с простотой схемы и затруднениями читателей в понимании программы на C, основной упор в этом разделе направим именно на разбор программы.
Итак программа.
#include <avr/io.h> //подключаем библиотеки #include <util/delay.h> #define SPI_SS PB2 //выход SS #define SPI_MOSI PB3 //выход MOSI #define SPI_MISO PB4 //выход MISO #define SPI_SCK PB5 //выход SCK #define BUTTON_AP PD4 //выход кнопки A+ #define BUTTON_AM PD5 //выход кнопки A- #define BUTTON_BP PD6 //выход кнопки B+ #define BUTTON_BM PD7 //выход кнопки B- char di[5]; void spi(char cmd,char data) //Функция передачи двух пакетов по 8 бит по протоколу SPI { PORTB &= ~(1<<SPI_SS); //сбрасываем SS в 0 SPDR = cmd; //отправляем данные по SPI адрес while(!(SPSR&(1<<SPIF))); //ждем окончания отправки SPDR = data; //отправляем данные по SPI данные while(!(SPSR&(1<<SPIF))); //ждем окончания отправки PORTB |= (1<<SPI_SS); //устанавливаем SS в 1 } int main() { DDRB |= (1<<SPI_MOSI)|(1<<SPI_SCK)|(1<<SPI_SS); //настраиваем MOSI, SCK, SS как выходы PORTD |= (1<<BUTTON_AP)|(1<<BUTTON_AM)|(1<<BUTTON_BP)|(1<<BUTTON_BM); //подключение к кнопкам A+, A-, B+, B- подтягивающего резистора PORTB |=(1<<SPI_SS); //устанавливаем SS в 1 SPCR |= (1<<SPE)|(1<<MSTR)|(1<<SPR1)|(1<<SPR0); //через регистр SPCR настраиваеи аппаратное SPI //Инициализация MAX7221 spi(0x0C,0x00); //Отключение индикаторов spi(0x09,0xFF); //Отключение декодирования spi(0x0A,0x0A); //Интенсивность свечения индикаторов spi(0x0B,0x04); //Количество индикаторов начиная с 0 spi(0x0F,0x00); //Отключение теста индикаторов spi(0x0C,0x01); //Включение индикаторов int a=0; //значение на первых трех индикаторах int b=0; //значение на последних двух индикаторах unsigned char flag_button =0; //переменная для флагов нажатия кнопок while(1){ //Бесконечный цикл di[2]=a%10; //поразрядное рабиение значения if(a<10)di[1]=15;else di[1]=((a-di[2])/10)%10; //переменной a и занесение в if(a<100)di[0]=15;else di[0]=((a-di[1]*10-di[2])/100)%10; //массив с гашением не значащих нулей spi(1,di[0]); //вывод значения spi(2,di[1]); //переменной a на spi(3,di[2]); //первых трех индикаторах di[4]=b%10; //поразрядгое разбиение значения if(b<10)di[3]=15;else di[3]=((b-di[4])/10)%10; //переменной a и занесение в массив spi(4,di[3]); //вывод значения переменной b на spi(5,di[4]); //последних двух индикаторах if(!(PIND & (1<<BUTTON_AP)))flag_button|=(1<<3); //проверка состояния кнопок if(!(PIND & (1<<BUTTON_AM)))flag_button|=(1<<2); //нажатие заносится в переменную if(!(PIND & (1<<BUTTON_BP)))flag_button|=(1<<1); //флагов flag_button if(!(PIND & (1<<BUTTON_BM)))flag_button|=(1<<0); _delay_ms(100); //задержка для избежания дребезга контактов кнопок if(!(PIND & (1<<BUTTON_AP))&&(flag_button&(1<<3)))a++; //проверка нажатия кнопок if(!(PIND & (1<<BUTTON_AM))&&(flag_button&(1<<2)))a--; //с подтверждением нажатия по if(!(PIND & (1<<BUTTON_BP))&&(flag_button&(1<<1)))b++; //флагу flag_button if(!(PIND & (1<<BUTTON_BM))&&(flag_button&(1<<0)))b--; flag_button=0; //сброс флагов нажатия кнопок if(a>999)a=999; //проверка достижения максимального значения переменной a if(a<0)a=0; //проверка достижения минимального значения переменной a if(b>99)b=99; //проверка достижения максимального значения переменной b if(b<0)b=0; //проверка достижения минимального значения переменной b } return 0; }
Приступим. Программу на C обычно начинают с подключения внешних библиотек. За это в программе отвечают две строки:
#include <avr/io.h> #include <util/delay.h>
avr/io.h это библиотека ввода/вывода которая объяснит компилятору какие порты ввода/вывода есть у микроконтроллера, как они обозначены и на что они способны. И самое интересное что эта библиотека сама выбирает из настроек проекта для какого микроконтроллера нужно применить описания, то позволяет использовать эту библиотеку для разных микроконтроллеров. Эту библиотеку нужно подключать в первую очередь.
Вторая часто используемая библиотека util/delay.h помогает создавать задержки в выполнении программы, что достаточно удобно.
Далее в программе выполним макроопределение для резервирования выводов микроконтроллера под свои нужды. Например запись
#define BUTTON_AP PD4
указывает что на выводе PD4 (регистр 4 порта B) мы будем подключать кнопку A+ (смотрите схему). Это нужно для того чтобы если мы вдруг надумаем кнопку переключить на другой вывод то нам не понадобится искать по всей программе, просто изменить в define название вывода PD4 на нужный, при этом в программе так и останется BUTTON_AP. Но в случае с выводами для SPI ничего изменить не получится потому что поддержка SPI аппаратная и жестко привязана к выводам микроконтроллера производителем.
Следующая интересная часть программы это описание портов.
DDRB |= (1<<SPI_MOSI)|(1<<SPI_SCK)|(1<<SPI_SS);
Так были переключены на вывод перечисленные разряды порта B (по умолчанию все разряды всех портов настроены на ввод). Эту строку можно записать следующим образом без использования макроопределения в define
DDRB |= (1<<BP3)|(1<<PB5)|(1<<PB2);
Эти записи равноценны и предназначены для подстановки 1 в регистр DDRB в соответствующий разряд BR3 (разряд 3), BR5 (разряд 5) и BR2 (разряд 2). Остальные разряды регистра DDRB остаются без изменения. Также можно записать эту строку вот так
DDRB |= (1<<3)|(1<<5)|(1<<2);
что конечно более запутанно.
Запись 1<<3 означает что двоичную единицу нужно сдвинуть влево на 3 позиции, позиции справа заполняются нулями.
1<<3 будет означать 1000 (один ноль ноль ноль) в двоичной системе. И при выполнении операции (1<<3)|(1<<5) мы получим 101000 в двоичной системе.
В следующей строке программы подключаем подтягивающие резисторы для кнопок. Запишем единицы в соответствующие разряды регистра PORTD
PORTD |= (1<<BUTTON_AP)|(1<<BUTTON_AM)|(1<<BUTTON_BP)|(1<<BUTTON_BM);
Эти резисторы встроены в микроконтроллер. Их можно подключить на разряды порта с условием что эти разряды определены как ввод. Резисторы подтягивают нужный вывод микроконтроллера к логической 1.
Остальные настройки производятся аналогично с использованием соответствующих регистров микроконтроллера.
Теперь рассмотрим основную часть программы которую микроконтроллер после основных настроек выполняет бесконечно. Эта часть программы заключена в бесконечный цыкл.
while(1) { };
Все заключенное между фигурными скобками строки программы будут выполнятся по кругу. Внутри этого бесконечного цикла происходит поразрядное разбитие десятичного числа для вывода на отдельные разряды семисегментного индикатора. После разбиения отправляем поразрядно данные по SPI для каждого отдельного индикатора.
Далее происходит первый опрос кнопок на нажатие. Если кнопка нажата то устанавливается флаг о нажатии. Далее установлена задержка выполнения программы на 100 мс
_delay_ms(100);
для предотвращения ложного срабатывания от «дребезга» контактов кнопки. И второй раз опрашиваем кнопки о их нажатии и сверяем выставленным ранее флагам о нажатии. Если условия соблюдены изменяем значения выводимые на индикаторы. Затем сбрасываем флаги нажатия кнопок в 0. После чего цикл повторяется.
В следующей - заключительной части будет рассмотрен АЦП (аналого-цифровой преобразователь) и применение всего ранее изученного на практике.
Прикрепленные файлы:
- button_CB.zip (8 Кб)
- button_proteus.zip (18 Кб)
Комментарии (6) | Я собрал (0) | Подписаться
Для добавления Вашей сборки необходима регистрация
[Автор]
1. Самостоятельное написания кода со своими решениями и ошибками.
2. Перекапывание чужого кода и в последствии написание кода со своими решениями и ошибками.
[Автор]
if(a<100)di[0]=15;else di[0]=((a-di[1]*10-di[2])/100)%10;
[Автор]
char di[5];
Число сначала разбивается на разряды для порязрядного вывода на индикаторы по одной цифре.
15 это максимальное значение в десятичном исчислении шестнадцатиричного числа F при достижении которого дальше значение не увиличивается.
Это такой своеобразный алгоритм разбиения шестнадцатиричного числа на разряды для вывода на индикаторы.