Урок 9. Создание библиотеки для Ардуино.

Библиотека Ардуино

Научимся создавать собственную библиотеку для программирования на Ардуино.

Предыдущий урок     Список уроков     Следующий урок

В прошлом уроке мы закончили создание класса Button – кнопка. Все проверили, отладили код, и собираемся использовать его в дальнейших проектах для Ардуино. Только как практически пользоваться созданным классом. Не очень удачно получилось.

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

 

Красивое и практичное решение – создать библиотеку для объекта типа Button.

Последовательность действий для создания библиотеки в программах для Ардуино.

Библиотека в Ардуино это не что иное, как дополнительный класс. Поэтому, прежде всего, необходимо определить функции для библиотеки как класс. Как это сделать, подробно описано в уроке 7.

Оформите свои функции как класс, прежде чем производить дальнейшие действия, а мы используем готовый класс Button из урока 8.

Библиотека должна иметь как минимум два файла:

  • заголовочный файл (расширение .h);
  • файл с исходным кодом (расширение .cpp).

В первом файле содержится описание самого класса, переменные, константы. Кода программы здесь нет.  А второй файл содержит программный код методов.

Назовем новую библиотеку Button и создадим заголовочный файл Button.h.

Arduino IDE не поддерживает редактирование текстовых файлов. Редактировать файлы библиотеки можно в любой среде разработки для C++ или в текстовом редакторе, желательно с подсветкой синтаксиса. Я использую Notepad.

 

Заголовочный файл Button.h

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

/*

информация о библиотеке

*/

Все остальное содержимое h-файла мы должны заключить в конструкцию:

// проверка, что библиотека еще не подключена
#ifndef Button_h  // если библиотека Button не подключена
#define Button_h  // тогда подключаем ее

// ............

#endif

Эти директивы исключают повторное подключение библиотеки.

Внутри конструкции следует написать:

#include "Arduino.h"

Директива #include предписывает компилятору включить в код программы текст из файла, имя которого следует после директивы. В данном случае будет включен файл Arduino.h, содержащий стандартные константы и переменные языка Ардуино. В обычных программах он добавляется автоматически, а для библиотеки должен быть указан явно.

Осталось добавить описание нашего класса Button. Полностью файл Button.h выглядит так.

/*
Button.h - библиотека для цифровой обработки сигналов контактов кнопок
 и сигналов других компонентов параллельным процессом
 
В параллельном процессе должен регулярно вызываться один из методов:
    void  scanState();    // метод проверки ожидание стабильного состояния сигнала
    void  filterAvarage(); // метод фильтрации сигнала по среднему значению
 
В результате формируются признаки:
 
 для метода scanState():
 - при нажатой кнопке flagPress= true
 - при отжатой кнопке flagPress= false
 - при нажатии на кнопку flagClick= true

 для метода filterAvarage() :
 - при сигнале низкого уровня flagPress= true
 - при сигнале высокого уровня flagPress= false
 - при изменении состояния с высокого на низкий flagClick= true

Объект типа Button при создании имеет параметры:
 - номер вывода, к которому подключена кнопка или сигнал
 - время обработки сигнала (умножается на период вызова метода scanState() или filterAvarage()

 Button button1(12, 15);  // создание объекта для кнопки, подключенной к 12 выводу
 с временем фильтрации 30 мс (при цикле 2 мс)

 Библиотека разработана Калининым Эдуардом
 http://mypractic.ru/urok-8-cifrovaya-filtraciya-signalov-v-programmax-dlya-arduino.html
 
*/

// проверка, что библиотека еще не подключена
#ifndef Button_h // если библиотека Button не подключена
#define Button_h // тогда подключаем ее

#include "Arduino.h"

// класс обработки сигналов
class Button {
  public:
    Button(byte pin, byte timeButton);  // конструктор
    boolean flagPress;    // признак кнопка нажата (сигнал в низком уровне)
    boolean flagClick;    // признак клика кнопки (фронт)
    void  scanState();    // метод проверки ожидание стабильного состояния сигнала
    void  filterAvarage(); // метод фильтрации по среднему значению
    void setPinTime(byte pin, byte timeButton); // установка номера вывода и времени фильтрации
 
 private:
    byte  _buttonCount;    // счетчик времени фильтрации  
    byte _timeButton;      // время фильтрации
    byte _pin;             // номер вывода
};

#endif

 

Исходный файл библиотеки Button.cpp.

В начале файла разместим ту же самую текстовую информацию, как и в Button.h. Неизвестно какой из файлов будет изучать пользователь.

Далее пишем директивы #include для включения стандартных функций Ардуино и заголовочного файла.

#include "Arduino.h"
#include "Button.h"

А затем коды методов нашего класса.

Полностью файл Button.cpp выглядит так:

/*
Button.h - библиотека для цифровой обработки сигналов контактов кнопок
 и сигналов других компонентов параллельным процессом
 
В параллельном процессе должен регулярно вызываться один из методов:
    void  scanState();    // метод проверки ожидание стабильного состояния сигнала
    void  filterAvarage(); // метод фильтрации сигнала по среднему значению
 
В результате формируются признаки:
 
 для метода scanState():
 - при нажатой кнопке flagPress= true
 - при отжатой кнопке flagPress= false
 - при нажатии на кнопку flagClick= true

 для метода filterAvarage() :
 - при сигнале низкого уровня flagPress= true
 - при сигнале высокого уровня flagPress= false
 - при изменении состояния с высокого на низкий flagClick= true

Объект типа Button при создании имеет параметры:
 - номер вывода, к которому подключена кнопка или сигнал
 - время обработки сигнала (умножается на период вызова метода scanState() или filterAvarage()

 Button button1(12, 15);  // создание объекта для кнопки, подключенной к 12 выводу
 с временем фильтрации 30 мс (при цикле 2 мс)

 Библиотека разработана Калининым Эдуардом
 http://mypractic.ru/urok-8-cifrovaya-filtraciya-signalov-v-programmax-dlya-arduino.html
*/

#include "Arduino.h"
#include "Button.h"
// метод фильтрации сигнала по среднему значению
// при сигнале низкого уровня flagPress= true
// при сигнале высокого уровня flagPress= false
// при изменении состояния с высокого на низкий flagClick= true
void Button::filterAvarage() {

 if ( flagPress != digitalRead(_pin) ) {
     //  состояние кнопки осталось прежним
     if ( _buttonCount != 0 ) _buttonCount--; // счетчик подтверждений - 1 с ограничением на 0
  }
  else {
     // состояние кнопки изменилось
     _buttonCount++;   // +1 к счетчику подтверждений

     if ( _buttonCount >= _timeButton ) {
      // состояние сигнала достигло порога _timeButton
      flagPress= ! flagPress; // инверсия признака состояния
     _buttonCount= 0;  // сброс счетчика подтверждений

      if ( flagPress == true ) flagClick= true; // признак клика кнопки      
     }   
  }
}
// метод проверки ожидание стабильного состояния сигнала
// при нажатой кнопке flagPress= true
// при отжатой кнопке flagPress= false
// при нажатии на кнопку flagClick= true
void Button::scanState() {

 if ( flagPress != digitalRead(_pin) ) {
     // признак flagPress = текущему состоянию кнопки
     // (инверсия т.к. активное состояние кнопки LOW)
     // т.е. состояние кнопки осталось прежним
     _buttonCount= 0;  // сброс счетчика подтверждений состояния кнопки
  }
  else {
     // признак flagPress не = текущему состоянию кнопки
     // состояние кнопки изменилось
     _buttonCount++;   // +1 к счетчику состояния кнопки

     if ( _buttonCount >= _timeButton ) {
      // состояние кнопки не мянялось в течение заданного времени
      // состояние кнопки стало устойчивым
      flagPress= ! flagPress; // инверсия признака состояния
     _buttonCount= 0;  // сброс счетчика подтверждений состояния кнопки

      if ( flagPress == true ) flagClick= true; // признак фронта кнопки на нажатие     
     }   
  }
}
// метод установки номера вывода и времени подтверждения
void Button::setPinTime(byte pin, byte timeButton)  {

  _pin= pin;
  _timeButton= timeButton;
  pinMode(_pin, INPUT_PULLUP);  // определяем вывод как вход
}

// описание конструктора класса Button
Button::Button(byte pin, byte timeButton) {

  _pin= pin;
  _timeButton= timeButton;
  pinMode(_pin, INPUT_PULLUP);  // определяем вывод как вход
}

 

Для того чтобы Arduino IDE выделяла цветом новые типы и методы из нашей библиотеки можно создать файл keywords.txt.

Button KEYWORD1

scanState KEYWORD2
filterAvarage KEYWORD2
setPinTime KEYWORD2

Каждая строка содержит ключевое слово, табуляцию (не пробелы) и тип ключевого слова. KEYWORD1 определяет классы, KEYWORD2 – методы.

Загрузить zip-архив с тремя файлами библиотеки Button можно по этой ссылке:

Зарегистрируйтесь и оплатитеВсего 60 руб. в месяц за доступ ко всем ресурсам сайта!

 

Теперь нужно правильно разместить файлы библиотеки.

Я сделал так:

  • Запустил Arduino IDE.
  • Файл -> Настройки -> Размещение папки скетчей  задал D:\Arduino Projects.  Это я  указал папку моих проектов Ардуино (D:\Arduino Projects).
  • В этой папке я создал папку libraries (D:\Arduino Projects\libraries).
  • В папке libraries создал папку новой библиотеки Button (D:\Arduino Projects\libraries\Button).
  • И уже в эту папку скопировал файлы Button.h, Button.cpp и keywords.txt.

Для проверки надо закрыть и заново запустить Arduino IDE. Открыть Скетч -> Подключть библиотеку и посмотреть, что в списке библиотек присутствует новая библиотека Button.

 

Как пользоваться библиотекой.

Очень просто. В начале программы включить заголовочный файл директивой

#include <Button.h>

Теперь можно пользоваться всеми открытыми методами и переменными класса Button абсолютно так же, как в предыдущем уроке.

Перепишем программу управления светодиодами из предыдущего урока. Естественно с использованием библиотеки Button.

/*  Программа sketch_9_1 урока 9
 *  К плате Ардуино подключены 2 кнопки и светодиод
 *  Каждое нажатие кнопки 1 инвертирует состояние светодиода на плате Ардуино
 *  Каждое нажатие кнопки 2 инвертирует состояние светодиода на макетной плате */

#include <Button.h>

#define LED_1_PIN 13     // светодиод 1 подключен к выводу 13
#define BUTTON_1_PIN 12  // кнопка 1 подключена к выводу 12
#define BUTTON_2_PIN 11  // кнопка 2 подключена к выводу 11
#define LED_2_PIN 10     // светодиод 2 подключен к выводу 10

boolean ledState1;         // переменная светодиода 1
boolean ledState2;         // переменная светодиода 2

Button button1(BUTTON_1_PIN, 15);  // создание объекта для кнопки 1
Button button2(BUTTON_2_PIN, 15);  // создание объекта для кнопки 2
  
void setup() {
  pinMode(LED_1_PIN, OUTPUT);           // определяем выводы светодиодов как выходы
  pinMode(LED_2_PIN, OUTPUT);          
}

// цикл с периодом 2 мс
void loop() {

  button1.filterAvarage();  // вызов метода фильтрации по среднему для кнопки 1
  button2.scanState();  // вызов метода ожидания стабильного состояния для кнопки 2

 
  // блок управления светодиодом 1
  if ( button1.flagClick == true ) {
    // был клик кнопки
    button1.flagClick= false;         // сброс признака
    ledState1= ! ledState1;             // инверсия состояние светодиода
    digitalWrite(LED_1_PIN, ledState1);  // вывод состояния светодиода   
  }

  // блок управления светодиодом 2
  if ( button2.flagClick == true ) {
    // был клик кнопки
    button2.flagClick= false;         // сброс признака
    ledState2= ! ledState2;             // инверсия состояние светодиода
    digitalWrite(LED_2_PIN, ledState2);  // вывод состояния светодиода   
  }

  delay(2);  // задержка 2 мс
}

Ничего лишнего. Только объекты, с которыми мы работаем.

Хороший стиль добавить в файлы библиотеки примеры. Но у нас в последующих уроках примеров будет достаточно.

 

В следующем уроке мы научимся работать с прерываниями по аппаратному таймеру.

Предыдущий урок     Список уроков     Следующий урок

5

Автор публикации

не в сети 4 дня

Эдуард

285
Комментарии: 1946Публикации: 200Регистрация: 13-12-2015

62 комментария на «Урок 9. Создание библиотеки для Ардуино.»

    • keywords.txt не обязателен. Он нужен для того, чтобы Arduino IDE (там, где вы пишете скетч для платы) по разному разукрашивал названия классов и методов. Как, например, он делает с analogRead или digitalWrite.

      0
  1. Подскажите пожалуйста! В Вашей библиотеке создан только класс Button для встроенного «подтягивающего» pullup резистора. Есть ли практический смысл в том, что бы добавить классы для внешнего стягивающего и подтягивающего резисторов?
    Так как логика if в этом случае будет иной, инверсия будет не нужна и следовательно кнопка работать не будет?

    0
    • Сигнал находящийся в высоком уровне меньше подвержен действию помех. Хороший стиль — выбирать активным низкое состояние сигнала. Поэтому лучше использовать схему с резисторами подтягивающими к + 5 В.

      0
      • Добрый день!

        Не могу осознать ваше сообщение. Помогите пожалуйста, чтобы понять.
        Почему сигнал, находящийся в высоком (+5В), меньше подвержен помехам? Из чего это следует? Ведь если будут помехи, они могут быть как внизу (около 0В), так и наверху (около 5В).
        При этом, следующей фразой вы сообщаете: «хороший стиль — выбирать активным низкое состояние сигнала». Как так? Раз меньше помех вверху, то может лучше активным сделать высокое состояние?
        И как фаталили финальная фраза: «поэтому лучше использовать схему с резисторами подтягивающими к + 5 В». Т.е. мы вроде как бы договорились за активное считать низкое состояние. Может, тогда к 0 лучше подтягивать?
        Я наверняка что-то путаю, направьте, пожалуйста, что и где :).

        0
        • Здравствуйте!
          Это известный принцип схемотехники. Не я его придумал. Считается, что у сигналов активный уровень должен быть низким.
          Активный уровень для кнопки — нажатая кнопка. Порог срабатывания цифровых входов обычно ниже половины напряжения питания. Поэтому низкий уровень больше подвержен влиянию помех. И желательно, чтобы кнопка в не нажатом состоянии выдавала высокий уровень, что уменьшает вероятность ложных срабатываний. Мы подтягиваем резистором сигнал к 5 В, и получается высокий уровень, когда кнопка не нажата.

          0
          • Эдуард, спасибо!

            Просветление почти наступило :).
            Буквально один нюанс: коли активное состояние — низкое, то получается, что при отсутствии сигнала (кнопка не нажата) у нас постоянное напряжение на пине? А если речь идет о каком-нибудь датчике, то следуя этому принципу, мы также должны постоянно держать под напряжением сигнальный контакт? Насколько это эффективно, если допустим, датчик будет раз в сутки срабатывать? Получается, что все время мы просто греем провод?

            0
  2. А почему тогда вообще возникла схема со стягивающим резистором? Его установку «диктует» железо или есть иные причины когда его необходимо ставить?

    0
    • Бывает необходимость обрабатывать сигнал с активным высоким уровнем.
      Иногда так удобнее из соображений схемотехники. Например, в уроке 21 я подключаю матрицу кнопок и светодиодные индикаторы к плате используя общие выводы. Для индикаторов сигналы выбора имеют высокий активный уровень. Эти же сигналы я использую для сканирования матрицы кнопок.

      0
      • Добрый вечер. Еще один вопрос? При создании библиотек файлы набирать в блокноте, а потом переименовывать с расширением h и cpp?
        Или их можно набрать в редакторе arduino?
        Если нет, как тогда можно проверить правильность написания и работы библиотеки если пишешь ее сам?

        0
        • Я создаю полностью рабочий класс в Arduino IDE и проверяю его. Затем выделяю файлы h и cpp. Опять проверяю работу программы уже с библиотекой. Все как в уроке 9.

          0
      • Добрый день! Как можно сделать так, чтобы при долгом нажатии кнопки не было реакции? Я подключил вашу библиотеку для работы с 7-сегментными индикаторами и надо сделать, чтобы 1 раз нажали на кнопку и счетчик увеличивается, а на долгое нажатие не реагировало

        0
        • Здравствуйте!
          Наверное, много вариантов. Например, считать время нажатой кнопки и, если оно больше заданного, то возвращать счетчик в предыдущее состояние.

          0
  3. Добрый день. Вылетает такая ошибка при компиляции:
    C:\Documents and Settings\Мои документы\Arduino\libraries\Button/Button.cpp:41: multiple definition of `Button::Button(unsigned char, unsigned char)’

    sketch\Button.cpp.o:sketch/Button.cpp:8: first defined here

    collect2.exe: error: ld returned 1 exit status

    0
    • Здравствуйте!
      У вас дважды определяется библиотека Button. Проверьте в папках:
      C:\Documents and Settings\Мои документы\Arduino\libraries\Button/Button.cpp

      и sketch\Button.cpp.
      Одну библиотеку надо удалить. У меня вылетала ошибка этой библиотеки при обновлении Arduino IDE. Дело в том, что появилась еще одна библиотека с тем же именем Button.h. Проверьте внимательно и лишнюю удалите.

      0
  4. Должен сказать спасибо ещё раз.
    Сергей, г. Владимир.

    Вопрос про массив — можно ли его объявить как публичную переменную?
    Информация из массива выводится в дисплей с помощью библиотеки,
    А записывается в массив в основной программе.
    Объявил массив в public: строчкой
    byte ozu32disp[33];
    но компиляция основной программы выдаёт, что массив в этой области не объявлен.
    Что я сделал неправильно, и как его объявить?
    Заранее спасибо ещё раз.
    Сделал программу для дисплея 32х8 мах7219.
    одновременный вывод нескольких данных русскими символами в разных режимах, если Вам интересно — могу поделиться. Позже будет видео с демонстрацией возможностей.
    Дисплей не сравнить с 16х2 — большой, яркий, просвечивает сквозь пластмассу — не требует окна в корпусе.
    Дисплей i2c

    0
    • Здравствуйте!
      Никаких ограничений на объявление массивов в классах нет. Вот пример, который компилируется без ошибок.

      class Test{
      public:
      byte ozu32disp[33];
      };

      Test test1;

      void setup() {
      }

      void loop() {
      test1.ozu32disp[0]= 1;
      }

      0
      • Ну конечно, как всегда сижу и ломаю голову над элементарной ошибкой)))
        Не написал название объекта с точкой перед обращением к массиву.
        Первая библиотека в моей жизни), и сразу из пары сотен строчек.
        Спасибо за подсказку, ломал бы голову до завтрашнего вечера

        0
  5. Правильно ли я понял что можно создать два объекта
    Button1(12,15); //краткое нажатие
    Button2(12,500);// нажатие более 1сек.
    и т.д

    0
  6. Button button1(BUTTON_1_PIN, 15); // создание объекта для кнопки 1
    Button button2(BUTTON_2_PIN, 15); // создание объекта для кнопки 2

    Здравствуйте!
    что значит: » PIN, 15″ ?

    0
    • Button button1(BUTTON_1_PIN, 15); — создается объект типа Button с именем button1 и с двумя параметрами:
      BUTTON_1_PIN — номер вывода, к которому подключена кнопка;
      15 — число подтверждений состояния кнопки.

      1
  7. Здравствуйте Эдуард! Очередной раз передаю Вам слова благодарности за Ваши уроки и Вашу помощь! У меня такой вопросик — можно ли в библиотеки включать другие библиотеки? Никак не могу разобраться с такой проблемой — хочу подключить кнопку к I2C расширителю портов на PCF8575 и преминить Вашу библиотеку. Но никак не могу сообразить — как правильно в библиотеке Button объявить expander и через него подключать кнопки. То есть как сделать так, что бы созданный в библиотеке expander не конфликтовал с созданными (возможно) экспандерами в основной программе. Понятно, что адреса I2C устройств нужно будет сконфигурировать — но ещё же и объявить корректно? Типа expander01, expander02… и т.д.

    0
  8. Или, может правильней будет не включать в библиотеку Button библиотеку расширителя портов, а включить её в основную программу? Но тогда я не могу сообразить, как правильно назначать пины для объекта Button, используя Вашу библиотеку

    0
  9. Добрый день Эдуард! Есть ли у вас готовое решение или библиотека фильтрации входного сигнала с активным высоким уровнем? Предполагается применять для фильтрации сигнала от различных модулей ардуино в которых активный высокий уровень. Спасибо

    0
    • Здравствуйте!
      Для этого достаточно в файле Button.cpp в 2 местах сделать изменения изменения.

      if ( flagPress != digitalRead(_pin) ) {
      заменить на
      if ( flagPress == digitalRead(_pin) ) {

      По сути инвертируется чтение вывода.

      0
  10. Здравствуйте!
    Очень нужна библиотека для RTC FM30C256. Можете ли вы ее создать?
    Готов обсудить коммерческую сторону вопроса.

    0
    • Здравствуйте!
      У меня нет этой микросхемы, не на чем проверять. Обращайтесь к ней через библиотеку для I2C интерфейса. Если не ошибаюсь, библиотека Wire. Это несложно.

      0
    • Здравствуйте!
      Может библиотека Button.h не подключена или подключена другая библиотека с таким же именем. Загрузите библиотеку с сайта, как написано в уроке 9.
      Бывают в Ардуино проблемы, если путь к файлам содержит имена с кириллицей.

      0
  11. Здравствуйте.
    Спасибо за ваши уроки.
    У вас файл Arduino.h подключен сначала в Button.h, а потом в Button.cpp. Я правильно понимаю, что если Arduino.h подключен в Button.h, то уже в Button.cpp его можно не подключать?

    0
  12. Недавно почитал книжицу по с++. там про какие то конструкторы и деструкторы написано. мол для избежания утечки памяти. А у вас таким и не пахнет. Это как то связано с особенностями процессоров АРМ архитектуры?

    0
    • Здравствуйте!
      Про конструкторы было в уроке 8. О динамических переменных немного есть в уроке 15.
      У меня нет цели преподать все тонкости языка C++. Я все это можно найти в книжках, в интернете. Я стараюсь научить практическому программированию микроконтроллеров.

      0
  13. Здравствуйте Эдуард.

    Библиотеку переписал. Загрузил. При загрузке скетча выходит ошибка следущего содержания
    …../Button.h:56:2: error: #endif without #if

    Спасибо.

    0
  14. Эдуард,доб.вечер,зашел на ArduinoPlus.ru,воспроизвел простейший вариант библиотеки с программой BlinkLED,в списке
    библиотека появилась как INSTALLED и в Contributed LIbraries тоже, но при нажатии в программе появляются вместо include…
    пустые строки,в настройках указываю то же,что и у вас,что не так?спасибо за совет

    0
  15. Доб.день,Эдуард,обращался уже к вам по поводу подключения своей библиотеки с пустыми строками вместо include…Не могли бы вы проиллюстрировать на простом примере BlinkLED из ArduinoPlus.ru,где затык?Спасибо

    0
    • Здравствуйте!
      Все там правильно написано. Выполнил описанную там последовательность действий. Компилируется без ошибок.

      0
  16. Эдуард, здравствуйте. Можно ли сделать так чтобы признак нажатия срабатывал при отпускании кнопки, а не при нажатии? Я задаю двумя кнопками нижний порог температуры, а при нажатии одновременно двух кнопок перехожу в другой режим для задания верхнего порога температуры, Так вот при нажатии двух кнопок нижний порог успевает измениться прежде чем произойдет переход в другой режим.

    else if ( mode == 1 ) {

    disp.digit[2] = 0x38; // отображается L в 3м раряде
    disp.print(Ltemperature, 2, 1);
    disp.digit[1] &= 0x7f; // погасить точку второго разряда

    if ( (button1.flagPress == true) || (button2.flagPress == true) ) {
    commonTimer = 0; // сброс счетчика времени
    //buttonHoldCount = 0;
    //buttonHold = false;
    // кнопку нажали
    // если нажали кнопку 1 уменьшаем значение нижнего порога температуры
    if ( button1.flagPress == true ) {
    if ( buttonHold == true) {
    buttonHoldCount = 0;
    buttonHold = false;
    Ltemperature—;
    if (Ltemperature MAX_TEMP) Ltemperature = MAX_TEMP;
    if (Ltemperature >= Htemperature) Ltemperature = Htemperature — 1;
    }
    }
    }

    // переход на режим установки верхнего порога температуры (короткое удержание двух кнопок)
    // при нажатии двух кнопок одновремено вырабатывается признак flagTwoButtons
    // переход на режим 2 происходит, если были нажаты обе кнопки, а затем они обе отжаты
    if (timer > DELAY) {
    if ( (button1.flagPress == true) && (button2.flagPress == true) ) flagTwoButtons = true;
    if ( (flagTwoButtons == true) && (button1.flagPress == false) && (button2.flagPress == false) ) {
    // переход на установку верхней температуры
    commonTimer = 0;
    EEPROM.update(L_TEMP_ADR, Ltemperature);
    flagTwoButtons = false;
    button1.flagClick = false;
    button2.flagClick = false;
    timer = 0;
    mode = 2;
    }
    }

    // проверка времени бездействия и переход на режим 0
    if (commonTimer >= TEMP_SET_DELAY) {
    EEPROM.update(L_TEMP_ADR, Ltemperature);
    // переход на режим 0
    commonTimer = 0;
    showDisplayCount = 0;
    showDisplay = true;
    button1.flagClick = false;
    button2.flagClick = false;
    flagTwoButtons = false;
    timer = 0;
    mode = 0;
    }
    }

    0
    • Здравствуйте!
      Да, конечно. Достаточно изменить строку
      if ( flagPress == true ) flagClick= true; // признак фронта кнопки на нажатие
      Надо устанавливать признак при flagPress == false.
      Можно добавить такую строку, оставив прежнюю. Тогда будут 2 признака на нажатие и отжатие.

      0
      • А изменять строку if ( flagPress == true ) flagClick= true; нужно в файле библиотеки или в скетче?

        Если оставить оба варианта признака нажатия, то как их различить? Ведь flagClick= true будет устанавливаться как при нажатии так и при отпускании кнопки.

        Я так понял в Вашей библиотеке flagPress == true и flagClick= true устанавливаются одновременно при нажатии кнопки, только flagClick автоматически не сбрасывается в false?

        0
  17. ……boolean flagClick; // признак клика кнопки (фронт)…..
    ……boolean flagPress; ……………

    Позвольте вопрос —
    следует ли перед этими строчками поставить volatile ?
    Допустим, в основной программе мы используем
    цикл while(flagClick == 0) или while (flagPress == 0),
    а в теле цикла никаких операций с этими переменными не производим. Компилятор может упростить и выкинуть это условие из программы? Если не указана volatile?
    Заранее спасибо за ответ.

    0
    • Здравствуйте!
      Если программа каждый цикл loop() проходит до конца, не зависает в операторе вроде while(flagClick == false) {}, то можно обойтись без volatile.

      0
  18. Эдуард, создаю свою библиотеку для кнопок,
    вопрос по созданной в Вашем уроке библиотеке:
    зачем нужна функция setPinTime(byte pin, byte timeButton),
    если мы ей совсем не пользуемся, а параметры мы задаём в конструкторе объекта?
    Может, логичней будет эту функцию выкинуть из библиотеки?
    Ещё раз спасибо)

    0
    • Здравствуйте!
      Вдруг захочется динамически, в ходе программы изменять число подтверждений состояния кнопки. Но, в принципе, я этой функцией никогда не пользовался.

      0
  19. Вопрос про библиотеку.
    Библиотека — это всегда класс?
    История вопроса — нужно упростить код.
    В моём устройстве кнопок четыре.
    ESC,DEC,INC,ENTER — это общий случай, минимум, который нужен человеку для общения с компьютером. Блуждать по меню, задавать параметры, вызывать программы.
    Неудобно каждый раз писать четыре одинаковых строки:
    button_Esc.readButton1(); // опрос кнопок
    button_Dec.readButton1();
    button_Inc.readButton1();
    button_Enter.readButton1();
    Хочется заменить их одной строчкой:
    readButton4(); // опрос кнопок
    И оформить код функции в новой библиотеке.
    Вопрос — обязательно создавать новый класс, к которому будет принадлежать функция void readButton4()?
    Нельзя просто код функции без создания класса сохранить?
    И второй вопрос — если я этот класс создаю…
    Допустим..
    class Button4 {
    public: ……………
    Я должен создать новый объект, состоящий из четырёх объектов класса Button (у меня ButtonSerge):

    ButtonSerge button_Esc(ESC_PIN0,20); // объект из 4 кнопок
    ButtonSerge button_Dec(DEC_PIN,20);
    ButtonSerge button_Inc(INC_PIN,20);
    ButtonSerge button_Enter(ENTER_PIN,20);

    В каком месте программы я должен это делать?
    В конструкторе Button4::Button4(){,
    в функции ли void Button4::button4init(
    компиллятор везде ругается.
    Без класса скетч работает. В класс «заворачиваться не хочет.
    Вот код скетча, возможно кому-нибудь пригодится:
    /*
    * скетч создан как основа для библиотеки
    * Button4.h — библиотека для цифровой обработки сигналов контактов четырёх кнопок параллельным процессом
    * Сделана на основе (использует) библиотеку ButtonSerge.h
    * Необходимо её указать в шапке скетча ( #include )
    *
    * Инициализация кнопок производится в void setup() функцией
    *void button4init(byte esc_pin,byte dec_pin,byte inc_pin,byte enter_pin,byte period);
    *
    *Пример: button4init(12,11,10,9,20); // прописываем объектам — четырём кнопкам ESC,DEC,INC,ENTER
    *(соответственно!!!) номера портов 12,11,10,9, время отклика (20х5мс) = 0.1сек
    *
    * В параллельном процессе должна регулярно (5мс) вызываться функция
    * void readButton4(); — считывание сигнала с четырёх портов и ожидание стабильного состояния сигнала
    * по среднему значению. Функция так же запрещает одновременное нажатие двух кнопок,
    * в результате которого блокируются и все одиночные нажатия на 0.75сек
    *
    *В результате обработки в функции void readButton4(); формируются данные:
    *
    *public:
    *
    * boolean pressEsc = false; // true — если кнопка нажата
    * boolean pressDec = false;
    * boolean pressInc = false;
    * boolean pressEnter = false;
    *
    * boolean buttsClick=0; // true — если одну из четырёх кнопок нажали. Сбрасывается верхней программой
    * byte butStat=0; // 1,2,3,4 если нажата соотв. Esc,Dec,Inc,Enter.
    * // «0» если ничего не нажато, либо нажаты две одновременно
    * byte butStatKeep=0; // Сохраняет номер нажатой кнопки,когда она уже отпущена.
    * //Сбрасывается верхней программой, или устанавливается следующим нажатием
    * byte butPrev[4] = {0,0,0,0}; // память ненулевых butStat (нажатых кнопок)
    * // butPrev[0]-текущая/последняя, butPrev[1]-предыдущая, и т.д.
    * unsigned long button_time=0; // время неизменного ненулевого состояния butStat, мс (удержания кнопки)
    *
    *Библиотека разработана Карабановым Сергеем
    *
    */

    #include
    #include // библиотека прерываний по таймеру2

    byte chek1 = 0;

    ButtonSerge button_Esc(0,0); // описание объекта для кнопок
    ButtonSerge button_Dec(0,0);
    ButtonSerge button_Inc(0,0);
    ButtonSerge button_Enter(0,0);

    boolean buttsClick=0; // true — если одну из четырёх кнопок нажали. Сбрасывается верхней программой
    byte butStat=0;// 1,2,3,4 если нажата соотв. Esc,Dec,Inc,Enter.»0″ если ничего не нажато, либо нажаты две одновременно
    byte butStatKeep=0;// Сохраняет номер нажатой кнопки,когда она уже отпущена. Сбрасывается верхней программой.
    byte butPrev[4] = {0,0,0,0};
    unsigned long button_time=0; // время неизменного состояния butStat, если butStat > 0, мс
    unsigned long button_fix=0; // точка отсчёта времени, мс

    boolean pressEsc = false; // true — если кнопка нажата
    boolean pressDec = false;
    boolean pressInc = false;
    boolean pressEnter = false;

    unsigned long time_current;
    unsigned long time_fix = 0;
    unsigned long time_Delta;

    void setup() {

    button4init(12,11,10,9,20); // создаём объект из четырёх кнопок ESC,DEC,INC,ENTER, время отклика (20х5мс)

    MsTimer2::set(5, timerInterupt); // задаем период прерывания по таймеру 5 мс
    MsTimer2::start(); // разрешаем прерывание по таймеру, вызываем функцию timerInterupt каждые 5мс

    Serial.begin(9600);
    }

    void loop() {

    time_current = millis();
    time_Delta = time_current — time_fix;
    if ( time_Delta > 100)
    {
    time_fix = time_current;

    chek1 = chek1 + 1;
    Serial.print(chek1); Serial.print(«__»);
    Serial.print(buttsClick); Serial.print(«__»);
    Serial.print(butStatKeep); Serial.print(«__»);
    Serial.print(butStat); Serial.print(«__»);
    Serial.print(butPrev[0]);Serial.print(butPrev[1]);
    Serial.print(butPrev[2]);Serial.print(butPrev[3]); Serial.print(«__»);
    Serial.println(button_time);

    if(chek1 == 50) {
    chek1 = 0;
    buttsClick=0;
    butStatKeep=0;
    }
    }
    }

    void timerInterupt() { //программа прерывания по таймеру, вызывается каждые 5 мс
    readButton4();
    }
    void button4init(byte esc_pin,byte dec_pin,byte inc_pin,byte enter_pin,byte period) {

    button_Esc.setMode(esc_pin, period); // настройка объекта и режима ввода (инициализация кнопки)
    button_Dec.setMode(dec_pin, period);
    button_Inc.setMode(inc_pin, period);
    button_Enter.setMode(enter_pin, period);
    }
    void readButton4() { // опрос кнопок и формирование данных(каждые 5 мс)

    static byte buttonStop; // время запрета активного состояния кнопок, х5мс, «0»-разрешено, >0 — запрещено
    if (buttonStop > 0) buttonStop = buttonStop — 1; // уменьшаем каждые 5мс, считаем время

    button_Esc.readButton1(); // опрос кнопок
    button_Dec.readButton1();
    button_Inc.readButton1();
    button_Enter.readButton1();
    pressEsc = button_Esc.press1;
    pressDec = button_Dec.press1;
    pressInc = button_Inc.press1;
    pressEnter = button_Enter.press1;
    // если нажато более одной кнопки:
    byte i=0;
    if(pressEsc) i=i+1;
    if(pressDec) i=i+1;
    if(pressInc) i=i+1;
    if(pressEnter) i=i+1;
    if (i>1){butStat=0;
    buttonStop = 150; // 150х5мс = 0.75 сек блокировка кнопок при одновременном нажатии более 1 кнопки
    return;
    }
    // если ничего не нажато,или время запрета активного состояния кнопок ещё не окончилось
    if ((i==0)||(buttonStop > 0)) {
    button_time = 0;
    butStat=0;
    return;
    }
    // если нажата одна кнопка:
    if(butStat==0) {
    buttsClick=1;
    button_fix = millis();
    butPrev[3] = butPrev[2];
    butPrev[2] = butPrev[1];
    butPrev[1] = butPrev[0];
    }
    else{
    button_time = millis()- button_fix; // время удержания нажатой кнопки
    }
    if(pressEsc) butStat=1;
    if(pressDec) butStat=2;
    if(pressInc) butStat=3;
    if(pressEnter) butStat=4;
    butStatKeep = butStat;
    butPrev[0]= butStat;
    }

    0
  20. поясню: в результате всё это «общение» контроллера с четырьмя кнопками панели оператора в верхней программе должно свестись к одной строчке
    readButton4();
    И всё.

    0
    • Здравствуйте!
      Библиотека не обязательно класс. Если вам не требуется создание однотипных объектов, то проще создать функцию. И не стоит ее оформлять библиотекой.
      Если все же хотите использовать библиотеку, то посмотрите в уроке 24 STM32, как создана библиотека DelayDWT. Она очень простая.
      Еще в качестве примера библиотеки без класса могу привести MsTimer2. В ней используется разрешение области видимости.

      0
  21. Здравствуйте, Эдуард. Разъясните пожалуйста такую ситуацию. Я изменил Вашу библиотеку так, чтобы считывать не нажатия кнопки, а состояния битов в байте. Для работы со сдвиговым регистром 165 или с регистрами портов напрямую без digitalRead. Так вот, при создании объекта типа ButtonReg but1(PIND, BUT1_PIN, 15), считывания методом but1.filterAvarage() не происходит. Считывание происходит только если перед методом but1.filterAvarage() добавить метод but1.setPinTime(PIND, BUT1_PIN, 25). Можно ли уйти от использования метода but1.setPinTime(PIND, BUT1_PIN, 25) или в данном случае это не возможно? Я хочу уменьшить итоговый объем скетча.

    ButtonReg.h:
    // проверка, что библиотека еще не подключена
    #ifndef ButtonReg_h // если библиотека Button не подключена
    #define ButtonReg_h // тогда подключаем ее

    #include «Arduino.h»

    // класс обработки сигналов
    class ButtonReg {
    public:
    ButtonReg(byte reg, byte pin, byte timeButton); // конструктор
    boolean flagPress; // признак кнопка в нажатом состоянии (сигнал в низком)
    boolean flagClick; // признак клика кнопки (фронт)
    void scanState(); // метод проверки ожидание стабильного состояния сигнала
    void filterAvarage(); // метод фильтрации сигнала по среднему значению
    void setPinTime(byte reg, byte pin, byte timeButton); // метод установки номера вывода и времени (числа) подтверждения

    private:
    byte _buttonCount; // счетчик времени фильтрации
    byte _timeButton; // время фильтрации
    byte _reg; // регистр
    byte _pin; // номер бита в регистре
    };

    #endif

    ButtonReg.cpp:
    #include «Arduino.h»
    #include «ButtonReg.h»
    // метод фильтрации сигнала по среднему значению
    // при сигнале низкого уровня flagPress= true
    // при сигнале высокого уровня flagPress= false
    // при изменении состояния с высокого на низкий flagClick= true
    void ButtonReg::filterAvarage() {
    if ( flagPress != bitRead(_reg, _pin) ) {
    // состояние кнопки осталось прежним
    if ( _buttonCount != 0 ) _buttonCount—; // счетчик подтверждений — 1 с ограничением на 0
    }
    else {
    // состояние кнопки изменилось
    _buttonCount++; // +1 к счетчику подтверждений

    if ( _buttonCount >= _timeButton ) {
    // состояние сигнала достигло порога _timeButton
    flagPress = ! flagPress; // инверсия признака состояния
    _buttonCount = 0; // сброс счетчика подтверждений

    if ( flagPress == false ) flagClick = true; // признак клика кнопки
    }
    }
    }

    // метод проверки ожидание стабильного состояния сигнала
    // при нажатой кнопке flagPress= true
    // при отжатой кнопке flagPress= false
    // при нажатии на кнопку flagClick= true
    void ButtonReg::scanState() {

    if ( flagPress != bitRead(_reg, _pin) ) {
    // признак flagPress = текущему состоянию кнопки
    // (инверсия т.к. активное состояние кнопки LOW)
    // т.е. состояние кнопки осталось прежним
    _buttonCount = 0; // сброс счетчика подтверждений состояния кнопки
    }
    else {
    // признак flagPress не = текущему состоянию кнопки
    // состояние кнопки изменилось
    _buttonCount++; // +1 к счетчику состояния кнопки

    if ( _buttonCount >= _timeButton ) {
    // состояние кнопки не мянялось в течение заданного времени
    // состояние кнопки стало устойчивым
    flagPress = ! flagPress; // инверсия признака состояния
    _buttonCount = 0; // сброс счетчика подтверждений состояния кнопки

    if ( flagPress == false ) flagClick = true; // признак фронта кнопки на нажатие
    }
    }
    }

    // метод установки номера вывода и времени подтверждения
    void ButtonReg::setPinTime(byte reg, byte pin, byte timeButton) {

    _reg = reg;
    _pin = pin;
    _timeButton = timeButton;
    //pinMode(_pin, INPUT_PULLUP); // определяем вывод как вход
    }

    // описание конструктора класса Button
    ButtonReg::ButtonReg(byte reg, byte pin, byte timeButton) {

    _reg = reg;
    _pin = pin;
    _timeButton = timeButton;
    //pinMode(_pin, INPUT_PULLUP); // определяем вывод как вход
    }

    0
    • Здравствуйте!
      Я не вижу разницы между операциями конструктора и метода setPinTime. А где у вас происходит разворот вывода на вход и подключение подтягивающего резистора? Или вы используете внешний?

      0
      • Я тоже не вижу разницы между конструктором и методом setPinTime. Но конструктор создается, как я понимаю при компиляции, и в процессе работы программы состояние регистра в конструкторе не обновляется. Поэтому приходиться использовать setPinTime в прерывании вместе с filterAvarage, чтобы прочитать значения битов из регистра. Или может быть перед чтением бита в методе filterAvarage добавить несколько строк из метода setPinTime. Хотя в итоге получится тоже самое.

        Параметры портов я задаю в setup через регистры DDR и PORT.

        0

Добавить комментарий

Ваш e-mail не будет опубликован. Обязательные поля помечены *

Нажимая кнопку "Отправить" Вы даёте свое согласие на обработку введенной персональной информации в соответствии с Федеральным Законом №152-ФЗ от 27.07.2006 "О персональных данных".