Урок 8
|
Предыдущий урок | Ардуино - главная | Следуюший урок |
Узнаем о другом способе обработки сигнала кнопки для фильтрации помех и устранении дребезга контактов.
В уроке 6 мы рассмотрели один из способов устранения дребезга контактов кнопки и написали программу для его реализации.
Назовем этот способ - ожидание стабильного состояния контактов. Напомню его алгоритм. Решение о состоянии контактов кнопки принимается после того, как состояние сигнала стало стабильным (перестало изменяться) в течение заданного времени. Очень надежный метод для устранения дребезга контактов.
Но кроме дребезга контактов в сигнале бывают еще и электромагнитные помехи. Мы сказали, что вместе с дребезгом, метод ожидания стабильного состояния контактов устраняет и помехи. Давайте посмотрим, как этот метод обработает сигнал с регулярными импульсными помехами.
Первая диаграмма это сигнал контактов кнопки с дребезгом и импульсными помехами. Вторая диаграмма – код счетчика, который считает время устойчивого состояния сигнала. Видно, что короткие импульсы помех сбрасывают счетчик. Значит, его значение никогда не достигнет порога переключения, и признак нажатия кнопки сформирован не будет. Т.е. алгоритм ожидания стабильного состояния никогда не выделит коммутацию контактов в таком сигнале, хотя явно видно, что в середине диаграммы контакты замкнулись.
А ведь такая ситуация вполне реальная.
Удачный пример – охранная сигнализация.
И так:
Во всех этих случаях следует применять цифровую фильтрацию сигналов, которая значительно повышает устойчивость к электромагнитным помехам.
Существуют очень сложные алгоритмы цифровой обработки сигналов. В нашем случае достаточно простого усреднения уровня сигнала. По диаграмме, показанной выше, мы легко определяем состояние сигнала. Мы просто видим, что в первой половине диаграммы средний уровень явно высокий, а во второй половине – низкий. И короткие помехи не мешают нам визуально определить состояние сигнала.
Алгоритм усреднения достаточно прост и выглядит так. Нам необходим счетчик среднего значения сигнала и константа AVERAGE_TIME – время усреднения.
Вот диаграммы, показывающие работу алгоритма усреднения сигнала.
Такой алгоритм легко определит состояние контактов даже при регулярных помехах. Главное, чтобы средний уровень помех был меньше среднего уровня сигнала.
Надо помнить, что цифровая фильтрация по такому методу вносит временную задержку в определение состояния сигнала. Т.е. она может быть использована только для достаточно медленных сигналов – с реакцией десятки мс. Все компоненты с механическими контактами быстрее и не переключаются.
Реализация алгоритма цифровой фильтрации сигнала в программе для Ардуино.
Предлагаю не создавать отдельный класс, а добавить метод для класса Button, созданного в предыдущем уроке. Как я уже сказал, под кнопкой подразумевается любой электронный элемент, требующей программной обработки сигнала. Поэтому давайте делать цифровую фильтрацию в том же классе, понимая, что этот класс можно использовать не только для кнопки.
Назовем метод filterAvarage (фильтрация по среднему значению).
Считаем, что в классе может быть использован только один из двух методов обработки сигнала:
В таком случае нам не надо создавать новые переменные. Мы можем использовать те же признаки, те же переменные. Ограничение – можно вызывать только один из методов для каждого объекта. С точки зрения объектно-ориентированного программирования это не совсем хорошо, но помним, что ресурсы нашего контроллера ограничены. Проблем такая универсальность нам не создаст.
Добавим метод filterAvarage() в описании класса Button.
class Button { public: void filterAvarage(); // метод фильтрации сигнала по среднему значению . . . . . . . . . . //- другие члены класса };
Напишем код для нового метода.
// метод фильтрации сигнала по среднему значению // при сигнале низкого уровня 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; // признак клика кнопки } } } }
Комментировать не буду.
Теперь давайте в программе из предыдущего урока создадим 2 объекта типа кнопка. Первый будем обрабатывать методом фильтрации по среднему значению, а второй методом ожидания стабильного состояния. По прежнему будем инвертировать состояние светодиода на плате Ардуино первой кнопкой, а по нажатию второй кнопки – инвертировать состояние светодиода на макетной плате. Вот полный код программы.
/* Программа sketch_8_1 урока 8 * К плате подключены 2 кнопки и светодиод * Каждое нажатие кнопки 1 меняет состояние светодиода на плате Ардуино * Каждое нажатие кнопки 2 меняет состояние светодиода на макетной плате */ #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 // Описание класса обработки сигналов кнопок 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; // номер вывода кнопки }; // Описание глобальных переменных 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); // определяем вывод светодиода1 как выход pinMode(LED_2_PIN, OUTPUT); //- определяем вывод светодиода2 как выход } // бесконечный цикл с периодом 2 мс void loop() { button1.filterAvarage(); // вызов метода фильтрации по среднему сигнала кнопки 1 button2.scanState(); // вызов метода сканирования состояния сигнала кнопки 2 // блок управления светодиодом1 if ( button1.flagClick == true ) { // кнопка была нажата button1.flagClick= false; // сброс признака клика кнопки ledState1= ! ledState1; // изменение состояние светодиода digitalWrite(LED_1_PIN, ledState1); // вывод состояния светодиода1 } // блок управления светодиодом2 if ( button2.flagClick == true ) { // кнопка была нажата button2.flagClick= false; // сброс признака клика кнопки ledState2= ! ledState2; // изменение состояние светодиода digitalWrite(LED_2_PIN, ledState2); // вывод состояния светодиода2 } /* //- проверка признака кнопка нажата digitalWrite(LED_1_PIN, button1.flagPress); digitalWrite(LED_2_PIN, button2.flagPress); */ delay(2); // задержка на 2 мс } // метод фильтрации сигнала по среднему значению // при сигнале низкого уровня 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) ) { // состояние кнопки осталось прежним _buttonCount= 0; // сброс счетчика подтверждений } else { // состояние кнопки изменилось _buttonCount++; // +1 к счетчику подтверждений if ( _buttonCount >= _timeButton ) { // состояние кнопки не мянялось в течение времени _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); // определяем вывод кнопки как вход }
Загрузите программу в контроллер. Проверьте. Вы увидите, что на обе кнопки соответствующие светодиоды реагируют совершенно одинаково. Просто Вы не в состоянии руками имитировать сигналы разной скважности с периодом 30 мс.
Наиболее дотошливые читатели могут проверить работу алгоритма. Для этого надо искусственно сделать время усреднения очень большим. Время усреднения будет равным 5 секунд, если Вы:
- при создании объектов-кнопок второй параметр зададите максимальным
Button button1(BUTTON_1_PIN, 250); // создание объекта для кнопки 1 Button button2(BUTTON_2_PIN, 250); // создание объекта для кнопки 2
- время цикла зададите 20 мс
delay(20); // задержка на 20 мс.
Теперь удалите блоки управления светодиодами от признаков-кликов кнопок и сделайте управление светодиодами от признаков кнопка нажата.
digitalWrite(LED_1_PIN, button1.flagPress); digitalWrite(LED_2_PIN, button2.flagPress);
Теперь можете проверить, что при нажатии на кнопки, оба светодиода зажигаются с задержкой примерно 5 секунд. Опять кнопки работают одинаково.
Но если Вы будете циклически удерживать обе кнопки 3 секунды нажатыми и одну секунду отжатыми, то увидите, что второй светодиод останется выключенным. А первый светодиод с большой задержкой, но загорится.
Мы закончили создание класса Button – кнопка. Кнопка оказалась не таким уж и простым элементом. В следующем уроке оформим этот класс библиотекой.