Урок 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 можно здесь.

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

Я сделал так:

  • Запустил 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 мс
}

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

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

 

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

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

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

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

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

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

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

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

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

  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

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

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

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

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

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

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

      Test test1;

      void setup() {
      }

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

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

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

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