Устраняем программно дребезг кнопок и переключателей

Здравствуйте! Кнопки и прочие переключатели — являются неотъемлемой частью, практически любого устройства. Однако, реализация этих устройств приводит к такому нежелательному явлению для микроконтроллерной техники, как дребезг контактов. Что такое дребезг? Давайте разбираться!

Рассмотрим дребезг на примере обычной кнопки, которая представляет из себя 2 контакта, замыкающиеся между собой при нажатии кнопки, т.е. в нажатом состоянии кнопка проводит электрический ток, а в не нажатом не проводит. Также следует иметь ввиду, что в данной статье мы будем рассматривать дребезг на примере подпружининной кнопки — которая сама возвращается в исходное положение и размыкает цепь, если её отпустить, но все высказанные мною мысли справедливы и для кнопок с фиксацией положения: нажал — кнопка нажалась и осталась нажатой, нажал ещё раз — кнопка вернулась в исходное положение.

При замыкании кнопки напряжение начинает скакать, что связано с тем, что до полного нажатия происходят многократные замыкания-размыкания кнопки. Таким образом, в момент нажатия кнопки её состояние некоторое время изменяется случайным образом и спустя некоторого время, обычно 10-20 мс, хотя зависит от конкретной кнопки, значение стабилизируется. Если кнопка подключена к контроллеру, то контроллер регистрирует этот дребезг как многократное нажатие. Если бы не было защиты от требезга, то нажимая на клавиатуре букву — вы бы получали, например, 7 букв на экране, затем при попытке стереть их нажатием кнопки backspace, вы бы стирали случайное число символов, напрмиер 5. Работать на такой клавиатуре было бы не возможно. Осцилограмма напряжения на такой кнопке выглядит следующим образом. На фото дребезг длиться ровно 10 мс.

Дребезг контактов

По хорошему же осциллограмма должна выглядеть следующим образом. Интересный факт, эта осциллограмма является идеальной: как видно из рисунка, переключение происходит мнгновенно, так что для этой кнопки можно не устранять дребезг. Для впечатления, вспомните сколько длится дребезг (я упомянул об этом ранее), и посмотрите на эту осциллограмму, где одна клетка — это 250 нс (1000 нс = 1 мкс). Фото кликабельна — жмите для увеличения картинки.

Идеальная кнопка

Для получения стабильного значения предлагаю следующий алгоритм: контроллер слушает порт (пин), к которому подключена кнопка. Если пин изменяет своё логическое знчение, по сравнению с прошлым состоянием, которое мы тоже запоминаем, то начинаем процедуру проверки: трижды проверяем через равные промежутки времени текущее значение пина и прошлое. Если значения трижды совпали, то можно считать, что значение стабилизировалось и функция возвращает логическое значение пина. Для реализации этого алгоритма предлагаю следующую программу на C. Время задержки между проверками подбирается экспериметально. В случае ошибок в определении — увеличивайте время задержки. Помните, что 1000 мкс = 1 мс. Это общий код, как переделать его под Arduino и язык processing я объясню ниже в этой статье. Код снабжён комментариями, но если что-либо останется для Вас непонятным — я всегда жду Ваши вопросы в комментариях к этому посту.

#define BUTTON PORTC.B2 // Порт, к которому подключена кнопка: в нашем случае PORTC.B2
#define DEBOUNCE_DELAY 5000 // Время между проверками в микросекундах.

int buttonDebounce(void);

void main() { 
	if(buttonDebounce() == 1) {
		// действие если кнопка нажата
	} else {
		// действие если кнопка не нажата
	}
}

int buttonDebounce(void) {
   int result, i, currState, lastState;
   result = lastState = 0;

   if(BUTTON != lastState) {
		lastState = BUTTON; // Запоминаем последнее состояние кнопки
		Delay_us(DEBOUNCE_DELAY); // Делаем задержку
         for(i = 3; i > 0; --i) { // Трижды сравниваем значение кнопки через равные промежутки времени. 
               currState = BUTTON;
               if (currState != lastState) { // Если текущее состояние не совпало с прошлым - завершаем все циклы.
                  break;
               }
               Delay_us(DEBOUNCE_DELAY);
               lastState = currState;
               if(i == 1) {
                    result = BUTTON; // Если трижды значение было одинаково, то возвращаем его в качестве значения функции
               }
         }

   }

   return result; 
}

Читать полностью

Arduino: работаем с SD картами

Здравствуйте, уважаемые! Снова я пишу пост про Arduino – это связано с тем, что на днях мне по почте пришёл, заказанный мною в начале июля, модуль для работы с SD-картами.  Поэтому, в этом посте я расскажу, как усовершенствовать, сделанный нами в прошлом, датчик для измерения температуры, а именно – мы избавим себя от надобности копирования данных из консоли вывода в Excel для дальнейшего анализа, заставив Arduino сохранять данные на SD-карту в виде csv-файла.  Данные будут записываться в файл в два столбца – в первом будут единицы времени (или просто номер строки), а во втором значение напряжения на термисторе или ином датчике, которое, как Вы помните, пропорционально температуре.

Необходимые компоненты

Собираем схему

Описывать схему сборки измерителя температуры я не буду, если Вы забыли или не знаете как его собирать – обратитесь к посту, ссылка на который указана в предыдущем разделе этого поста. (Картинка) Рассмотрим схему подключения SD модуля. Каждый модуль имеет следующие контакты: GND, 5V, 3V (3.3V), CS, MOSI, SCLK (SCK), MISO, GND. Подключим некоторые из этих контактов к следующим контактам Arduino:

  • GND – к земле с Arduino
  • 5V разъём к 5 вольтовому разъёму Arduino. (Аналогично 3.3V) Подключать оба разъёма одновременно – бессмысленно, я подключаю только 5V.
  • CS – к 4 разъёму (все разъёмы SD модуля подключаются ТОЛЬКО к цифровым выходам – никаких аналоговых здесь быть не должно!)
  • MOSI – к 11 разъёму
  • SCLK (SCK) – к 13 разъёму
  • MISO – к 12 разъёму
  • Ещё один GND – никуда не подключаем

На этом наше подключение окончено. Для подключения можете использовать макетную плату – просто втыкаете туда SD модуль и в правильном порядке протягиваете провода от соответствующих дорожек к нужным разъёмам.

Подготавливаем SD-карту

SD модуль работает только с SD картами, отформатированными под файловую систему FAT16 или FAT32. Карту необходимо заранее отформатировать на компьютере. БУДЬТЕ ВНИМАТЕЛЬНЫ! При форматировании все данные, находящиеся на носителе, будут уничтожены! Не забудьте переписать их в другое место перед форматированием!

Программируем Arduino

// Подключаем библиотеку для работы с SD-картами
#include <SD.h>

File myFile;
// Задаём начальное время (точку отсчёта)
int time = 0;
// Указываем номер аналогового разъёма для считывания данны
int tempPin = 0;

void setup()
{
 // Открываем serial порт
  Serial.begin(9600);
  // Выдаём сообщение о том, что начинается инициализация SD-карты
  Serial.print("Initializing SD card...");
   pinMode(10, OUTPUT);
   // Проверяем готовность SD модуля. Если модуль не готов - выдаём сообщение
  if (!SD.begin(4)) {
    Serial.println("initialization failed!");
    return;
  }
  // Проверяем, существует ли на карте файл data.csv, если существует, то удаляем его.
  if(SD.exists("data.csv")) {
    SD.remove("data.csv");
  }
  // Выводим сообщение о том, что инициализация прошла успешно
  Serial.println("initialization done.");
}

void loop()
{
    // Считываем данные (напряжение) с нулевого аналогового разъёма.
    int temp = analogRead(tempPin);
    // Увеличиваем значение времени (номер строки) на единицу. Если вы будете записывать данные раз в минуту, но хотите чтобы время записывалось в секундах - прибавляйте не единицу, а 60.
    time = time + 1;
  // Открываем файл data.csv для записи
  myFile = SD.open("data.csv", FILE_WRITE);
  // Если удалось открыть файл для записи, то записываем данные
  if (myFile) {
    // Выводим данные на экран
    Serial.print(time);
    Serial.print("; ");
    Serial.println(temp);
    // Записываем время
    myFile.print(time);
    // Добавляем точку с запятой
    myFile.print(";");
    // Добавляем температуру и добавляем перенос строки
    myFile.println(temp);
    // закрываем файл
    myFile.close();
  } else {
    // Выводим сооб щение о том, что открыть файл не удалось
    Serial.println("error opening data.csv");
  }
  // Повторяем считывание данных с датчика и запись на флэш-карту через одну секунду
  delay(1000);
}

Читать полностью

Измеритель температуры с Arduino

Измеритель температуры с Arduino Приветствую! В прошлом посте мы узнали что такое делитель напряжения, научились его рассчитывать и узнали где его используют. Сегодня мы применим наши знания на деле, а именно — сделаем измеритель температуры, который будет выводить на экран монитора текущее значение температуры в условных единицах, а также, в определённых пределах — с ростом температуры мы заставим гореть светодиод ярче, а при понижении температуры — тусклее и тусклее. Поехали!

Для этого нам понадобятся следующие компоненты нашего Arduino

Необходимые компоненты

  • Резистор 10 [кОм]
  • Резистор 220 [Ом]
  • Термистор
  • Светодиод

Собираем схему

Схема подключение будет такая же, как и в предыдущем посте, контакт A подключаем к 5 вольтовому разъёму Arduino, контакт B к земле, а контакт b к первому аналоговому разъёму Arduino (A0). Вместо первого резистора подключаем термистор, а второй так и оставляем — резистор на 10 [кОм]. Кроме того, нам необходимо ещё подключить светодиод — запитываем его из 11 порта и соединяем через резистор 220 [Ом] с землёй.

Программируем Arduino

// Указываем номер порта для датчика температуры (А0)
int tempPin = 0;
// Указываем номер порта для светодиода
int ledPin = 11;

//  Описываем настройки программы
void setup() {
  // Устанавливаем порт ledPin как выход
  pinMode(ledPin, OUTPUT);

  // Запускаем чтение данных с сериал порта со скоростью 9600 бод\сек (стандартная скорость)
  Serial.begin(9600);
}

//  Описываем цикл программы
void loop() {
  // Считываем значение с аналогового порта
  int temp = analogRead(tempPin);
  // Выводим на монитор считанное значение (ctrl+shift+M для просмотра консоли)
  Serial.println(temp);
  // Делаем задержку на 1 секунду
  delay(1000);

  // "обрезаем" значения температуры в пределах от 316 до 336. Всё что меньше нижнего предела станет равно 316, а всё что больше - 336
  temp = constrain(temp, 316, 336);
  // Соотносим шкалы: при значении переменной temp 316 - значение ledLevel будет равно 0, ну а при 336 - 255.
  int ledLevel = map(temp, 316, 336, 0, 255);

  // Передаём значение на светодиод
  analogWrite(ledPin, ledLevel);
}

Читать полностью

Всё о делителях напряжения

Всё о делителях напряжения

Здравствуйте, мы продолжаем изучать Arduino! Напомню, что в прошлый раз мы сделали простейший светофор на Arduino, а в этот раз я расскажу о делителях напряжения — о том, как они работают, как их рассчитывать и как их можно использоваться для создания измерительного прибора или какого-либо датчика.

Читать полностью