Главная » Микроконтроллеры
Призовой фонд
на октябрь 2020 г.
1. 1500 руб
Сайт Паяльник
2. Тестер компонентов MG328
Сайт Паяльник
3. 150 руб.
От пользователей


Блок питания 12В для светодиодных лент, 18-100 Вт

AVR на C - просто? Часть 4

Кнопки

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

Кнопки ATmega328 семисегментные индикаторы

Имеем все туже схему с семисегментными индикаторами, но добавлены 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. После чего цикл повторяется.

В следующей - заключительной части будет рассмотрен АЦП (аналого-цифровой преобразователь) и применение всего ранее изученного на практике.

Прикрепленные файлы:

Теги:

Опубликована: 0 0
Я собрал 0 2
x

Оценить статью

  • Техническая грамотность
  • Актуальность материала
  • Изложение материала
  • Полезность устройства
  • Повторяемость устройства
  • Орфография
0

Средний балл статьи: 5 Проголосовало: 2 чел.

Комментарии (6) | Я собрал (0) | Подписаться

0
андрей #
В стиле смотрите как я делаю, делайте так же, а вот почему побитовая конъюнкция, почему не дизъюнкция, почему побитовая, а не логическая, функции логического сдвига не рассмотрены и что из себя вцелом представляют, почему это все делается именно так в сравнении с другими режимами портов ввода-вывода и так далее. Просто - это когда суть ясна и не нужно примеры смотреть как делается, а понимаешь сам что делать нужно, тут, к сожалению, не так мне кажется
Ответить
+1

[Автор]
vavaav #
Вот вы пишите "В стиле смотрите как я делаю" а как вы делаете? Предложите свой вариант. Конъюкция потому что происходит подтверждение нажатия, а также продолжение действий при удержании кнопки. У портов ввода/вывода всего два режима - соответственно ввод и вывод. А в целом в программировании два подхода:
1. Самостоятельное написания кода со своими решениями и ошибками.
2. Перекапывание чужого кода и в последствии написание кода со своими решениями и ошибками.
Ответить
0
Yarikkasl #
Здравствуйте, а как реализовать изменение значения не нажатие, ведь если нажать и держать значение будет расти, как сделать так что бы, для последующего увеличения необходимо было отпустить кнопку
Ответить
0

[Автор]
vavaav #
Не совсем понятен вопрос. Повтор срабатывает не сразу. Если отпустить останавливается. И если нажимать кратковременно, срабатывает по единице.
Ответить
0
Сергей #
а почему в условии значение di =15, как вообще работает данный алгоритм?
if(a<10)di[1]=15;else di[1]=((a-di[2])/10)%10;
if(a<100)di[0]=15;else di[0]=((a-di[1]*10-di[2])/100)%10;
Ответить
0

[Автор]
vavaav #
Это массив объявлен в строке
char di[5];
Число сначала разбивается на разряды для порязрядного вывода на индикаторы по одной цифре.
15 это максимальное значение в десятичном исчислении шестнадцатиричного числа F при достижении которого дальше значение не увиличивается.
Это такой своеобразный алгоритм разбиения шестнадцатиричного числа на разряды для вывода на индикаторы.
Отредактирован 17.11.2019 21:43
Ответить
Добавить комментарий
Имя:
E-mail:
не публикуется
Текст:
Защита от спама:
В чем измеряется электрическая мощность?
Файлы:
 
Для выбора нескольких файлов использйте CTRL

AVR-программатор USB ASP
AVR-программатор USB ASP
USB осциллограф DSO-2090 Arduino UNO
вверх