Как сделать простой калькулятор на Arduino

Добавлено 30 октября 2016 в 18:30

В данной статье мы рассмотрим, как использовать LCD дисплей совместно с клавиатурой и отладочной платой Arduino, чтобы создать простой калькулятор.

Хотя калькуляторы существуют уже очень давно, их электронные версии доминировали в мире в течение десятков лет. Калькуляторы, от простых до научных, бывают всех форм и размеров. Но в современном мире, где практически у каждого есть смартфон, от них нет никакой пользы. Это не значит, что они нам не нравятся. Так что давайте отдадим им дань уважения, собрав свой собственный простой калькулятор на Arduino.

Что нам понадобится, чтобы завершить этот проект:

Перед тем, как начать

Чтобы иметь возможность скомпилировать программу, необходимо, чтобы в Arduino IDE были установлены библиотеки LiquidCrystal.h и Keypad.h. Это можно сделать одним из двух способов. Если у вас установлена Arduino IDE версии 1.6.2 и выше, просто используйте менеджер библиотек. Если вы используете более раннюю версию, то вам необходимо скопировать эти библиотеки в каталог библиотек в месте установки Arduino IDE. Ссылка на руководство, как устанавливать библиотеки.

Arduino Uno
Arduino Uno

Принцип действия

Это простое устройство стартует с очистки экрана LCD и ждет нажатий кнопок на клавиатуре. В зависимости от нажатых пользователем кнопок оно формирует числа. Как только пользователь нажимает на кнопку операции, оно запоминает первое число и операцию, которую необходимо выполнить, и продолжает обрабатывать ввод второго числа. Когда пользователь закончит ввод второго числа и нажмет кнопку «равно» на клавиатуре, программа выполнит требуемую операцию и выведет на экран результат. После чего она ожидает нажатия кнопки «очистить» ('C'), чтобы начать всё с начала (пользователь также может в любой момент сбросить программу).

LCD дисплей 1602

Arduino IDE обладает встроенной библиотекой (LiquidCrystal.h), которая поддерживает LCD дисплеи на базе чипсета Hitachi HD44780 (и на базе совместимых аналогов). Кроме отображения текста на LCD, эта библиотека также может обрабатывать печать чисел с плавающей запятой с заданным количеством цифр после запятой, что делает работу разработчика проще. Например:

double Pi = 3.1415926535;
Lcd.print (Pi,4);

Этот код напечатает на LCD дисплее 3.1415, так как число 4 в вызове функции означачает вывод только 4 цифр после запятой.

LCD дисплей в данном примере использует 4-битный режим передачи данных через выводы D4-D7 (11-14 на плате). Потенциометр действует, как делитель напряжения, и управляет контрастностью отображаемого текста. Его средний вывод подключен к выводу V0 (вывод 3) дисплея.

LCD дисплей HD44780 1602
LCD дисплей 1602

Давайте подключим наш LCD к плате Arduino. Сначала подключите выводы +5V и GND от Arduino к линиям питания на макетной плате. Подключите свой LCD к макетной плате и соедините вывод 1 с шиной корпуса, а вывод 2 с шиной +5V.

Далее установите на макетную плату потенциометр и подключите две его крайние ноги: первую к +5V, вторую к GND (какая из них будет первой или второй, значения не имеет). Теперь подключите средний вывод потенциометра к выводу 3 LCD дисплея.

Нам понадобится подать питание на подсветку дисплея. Выводы 15 и 16 LCD дисплея – это анод и катод встроенного светодиода, который служит в качестве подсветки LCD. Мы подключаем их к шинам питания, как и любой другой светодиод к источнику напряжения 5 вольт: анод к положительному выводу напряжения, а катод к GND, с последовательно включенным токоограничивающим резистором. Вы можете использовать резистор номиналом 100–220 Ом. Я использовал 1 кОм, так как с резистором 220 Ом подсветка была слишком яркой для моей камеры, чтобы снять видео. Если хотите, то можете заменить резистор потенциометром и сделать подсветку регулируемой.

Продолжаем. Выполните следующие соединения: вывод 4 LCD дисплея к выводу 7 платы Arduino, вывод 5 LCD дисплея к GND, вывод 6 LCD дисплея к выводу 8 платы Arduino, и последние выводы 11, 12, 13, 14 LCD дисплея к выводам 9, 10, 11, 12 платы Arduno, соответственно. Если вы хотите убедиться, что выполнили все соединения правильно, я добавил простой код для проверки дисплея. Вы можете просто записать его в свою плату Arduno и посмотреть, работает ли ваш LCD дисплей правильно.

#include <LiquidCrystal.h>
LiquidCrystal lcd(7,8,9,10,11,12); // Выделить выводы для LCD дисплея
// Убедитесь, что правильно объявили выводы

void setup() 
{
  lcd.begin(16,2); // инициализация LCD
  lcd.setCursor(0,0);
  lcd.print("This is row 1");
  lcd.setCursor(0,1);
  lcd.print("This is row 2");
}
 
void loop() 
{
  // Главный цикл
}
Простой калькулятор на Arduino – Подключение LCD дисплея
Простой калькулятор на Arduino – Подключение LCD дисплея

Клавиатура

Большинство проектов, которые чуть сложнее, чем просто мигание светодиодам (проекты, использующие только вывод), потребует от пользователя какого-либо ввода. В многих случаях для получения пользовательского ввода используются кнопки. Для Arduino существует библиотека Keypad.h (если у вас ее нет, проверьте ссылки в конце статьи), которая способна обрабатывать ввод с матричной клавиатуры и проста в использовании. Эта библиотека устраняет необходимость использования внешних подтягивающих резисторов, так как она использует встроенные в микросхему подтягивающие резисторы, а также обрабатывает/устанавливает высокое сопротивление на всех выводах неиспользуемого столбца. Она, по сути, сканирует столбец за столбцом. Выполняется это путем установки низкого уровня на выводе текущего столбца и чтения значений выводов строк для этого столбца, а затем перехода к следующему столбцу и так далее, пока не просканирует все подключенные/назначенные выводы. Кроме использования встроенных подтягивающих резисторов, библиотека также обрабатывает дребезг контактов. Библиотека не использует задержки; вместо этого, она периодически использует встроенную функцию millis() Arduino и определяет, как долго была нажата кнопка, и было изменение состояния определенной кнопки. Без задержек код выполняется более эффективно и не потребляет вычислительные ресурсы, устраняя необходимость обработки дребезга контактов с использованием программных задержек.

Я хотел использовать выводы на плате Arduino только с одной стороны с 0 по 13, так как это как раз необходимое количество выводов (и было бы проще подключать провода, расположенные на одной стороне, и не получить в итоге спутанную лапшу из проводов). Но после нескольких тестов светодиод на выводе 13 стал раздражать, поэтому я решил не использовать вывод 13. Я также не стал использовать выводы 0–1. Я подключил LCD к выводам 7–12, а клавиатуру к выводам 2–5 и аналоговым выводам A2–A5 для строк и столбцов. Таким образом, вы можете подключить клавиатуру, как вам удобно; просто убедитесь, что разделили строки и столбцы. Либо подключите строки к выводам A2–A5 или к выводам 2–5, а столбцы подключите к оставшимся выводам на противоположной стороне платы Arduino. Так, если вы подключаете строку 1 к выводу A2, то подключайте столбец 1 к выводу 2.

Простой калькулятор на Arduino – Подключение клавиатуры
Простой калькулятор на Arduino – Подключение клавиатуры

Если вы подключили что-то не так, не волнуйтесь! Просто откройте исходный код программы калькулятора и измените порядок выводов клавиатуры в соответствии с вашей схемой:

byte rowPins[ROWS] = {A2,A3,A4,A5}; // подключите выводы строк клавиатуры

byte colPins[COLS] = {2,3,4,5};     // подключите выводы столбцов клавиатуры

Если вы хотите проверить вашу клавиатуру, есть простой код, который поможет вам сделать это. Просто скомпилируйте и загрузите его в плату Arduino с подключенной клавиатурой:

#include <Keypad.h>
const byte ROWS = 4;
const byte COLS = 4;
char button;
char keys[ROWS][COLS] = {
  {'1','2','3','+'},
  {'4','5','6','-'},
  {'7','8','9','*'},
  {'C','0','=','/'}
};
byte rowPins[ROWS] = {A2,A3,A4,A5}; // подключение к строкам клавиатуры
byte colPins[COLS] = {2,3,4,5};     // подключение к столбцам клавиатуры
Keypad customKeypad = Keypad( makeKeymap(keys), rowPins, colPins, ROWS, COLS);

// Этот код выполняется только один раз: при включении платы
void setup() 
{
  Serial.begin(9600);
}

// Этот код выполняется постоянно
void loop() 
{
  button = customKeypad.getKey(); // Определение нажатой кнопки
  if (button)
    Serial.print(button); 
}
Матричная клавиатура
Матричная клавиатура

У меня не было готовой клавиатуры, а ждать месяц доставки из Китая не хотелось, поэтому я сделал ее сам. Клавиатура показана на рисунке ниже. Если вы планируете сделать ее сами, то вам понадобятся:

  • 16 кнопок;
  • небольшая макетная печатная плата;
  • 8 проводов (4 для разводки строк и 4 для разводки столбцов);
  • паяльник и припой.
Матричная клавиатура на макетной плате
Матричная клавиатура на макетной плате

Программа/код

Код состоит из трех циклов. Первый цикл сканирует клавиатуру на наличие нажатий клавиш и выводит их на дисплей, одну клавишу за раз. В то же время, когда он сдвигает предыдущее число на один порядок вверх, он добавляет новую цифру на место единиц. Это продолжается, пока пользователь не нажмет на кнопку одного из операторов или не сбросит, нажав 'C'. Затем программа прерывает первый цикл и переходит ко второму.

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

В третьем цикле программа просто ждет нажатия кнопки 'C' на клавиатуре. При нажатии кнопки 'C', программа перезапускается.


#include <Keypad.h>
#include <LiquidCrystal.h>
LiquidCrystal lcd(7,8,9,10,11,12); // Выделить выводы для LCD дисплея

long num1,num2 ;
double total;
char operation,button;

const byte ROWS = 4;
const byte COLS = 4;

char keys[ROWS][COLS] = {
  {'1','2','3','+'},
  {'4','5','6','-'},
  {'7','8','9','*'},
  {'C','0','=','/'}
};
byte rowPins[ROWS] = {A2,A3,A4,A5}; // подключение к строкам клавиатуры
byte colPins[COLS] = {2,3,4,5};     // подключение к столбцам клавиатуры
Keypad customKeypad = Keypad( makeKeymap(keys), rowPins, colPins, ROWS, COLS);

// Этот код выполняется только один раз: при включении платы
void setup() 
{
  pinMode(13, OUTPUT);
  digitalWrite(13, LOW);
  lcd.begin(16,2);      // инициализация LCD
}

void loop()
{
  
  // Циклы удобны для чтения нажатых кнопок клавиатуры 
  while(1)
  {
    // Первый цикл. Здесь мы читаем клавиатуру и составляем наше первое число.
	// Он выполняется, пока мы не нажмем кнопку оператора, и цикл прервется,
	// или, если будет нажата кнопка 'C', всё начнется с начала. 
        
    button = customKeypad.getKey(); // Чтение кнопки
    if (button=='C') // Если пользователь хочет сбросить набор первого числа
    {
      num1=0;
      num2=0;
      total=0;
      operation=0;
      lcd.clear();
    }
        
    if (button >='0' && button <='9') // Если пользователь нажал на числовое значение, один символ за раз
    {
      num1 = num1*10 + (button -'0'); 
      // Наши числовые значения лежат в диапазоне от 0 до 9, что означает, что это единицы.
      // Когда мы умножаем на 10, мы, по сути, добавляем 0 после числа.
      // Затем мы добавляем новое введенное число на место нуля.
      // Что касается (button -'0'), это простой трюк с таблице ASCII. Цифры 0...9 в таблице ASCII
      // это 48...57 (в десятичном виде), поэтому, вычитание '0' из любого из этих символов дает
      // соответствующее значение в десятичном виде. Например, символ '5' = 53 в десятичной системе
      // минус 48 (символ нуля) дает нам значение 5.
      // Если наше предыдущее число было, например, 25, мы получили 250 умножением его на 10, 
      // а затем добавили 5, и получили 255, что и будет выведено на LCD.
      lcd.setCursor(0,0); // Выбор первой строки на LCD
      lcd.print(num1);    // Печать текущего значения числа num1
    }
    
	if (num1 !=0 && (button=='-' || button=='+' || button=='*' || button=='/')) 
    {
      // Если пользователь завершил ввод цифр
      operation = button;   // запоминаем, какое математическое действие пользователь хочет выполнить
      lcd.setCursor(0,1);   // установить курсор на строку 2
      lcd.print(operation); // напечатать наш оператор
      break;
    }

  }
    
  while(1) 
  {
    // Второй цикл, он выполняется пока пользователь не нажмет '=' или 'C'.
    // И тогда будет выведен результат или сброшена программа.
    if (button =='C')
      break; // Это обрабатывает случай, когда пользователь нажал кнопку оператора и захотел сбросить
    button = customKeypad.getKey();
    if (button=='C') // Еще раз проверяем, не хочет ли пользователь сбросить
    {
      num1=0;
      num2=0;
      total=0;
      operation=0;
      lcd.clear();
      break;
    }
    if (button >='0' && button <='9') // Получение символов от клавиатуры для числа 2
    {
      num2 = num2*10 + (button -'0');
      lcd.setCursor(1,1);
      lcd.print(num2);
    }
    if (button == '=' && num2 !=0)
    { // Если нажата кнопка '=', то это конец пути. Вызываем функцию domath(), выполняющую расчет
      // и выводим результат.      
      domath();             
      break;  
    }
  }
    
  while(1) 
  {
    // Когда всё выполнено, этот цикл ждет нажатия кнопки 'C', чтобы сбросить программу и начать с начала.
      
    // Это побочный эффект от предыдущего цикла, когда пользователь нажал 'C', предыдущий цикл прерывается
    // и переходит сюда. Поэтому мы должны также прервать и текущий цикл, иначе пользователю придется
    // нажимать 'C' дважды.      
    if (button =='C')
      break;

    button = customKeypad.getKey();
    if (button =='C') 
    {
      lcd.clear();
      lcd.setCursor(0,0);
      num1=0;
      num2=0;
      total=0;
      operation=0;
      break;
    }
  }

}


void domath()
{
  switch(operation)
  {
    case '+': // Сложение
      total = num1+num2;
      break;
      
    case '-': // Вычитание 
      total = num1-num2;
      break;
      
    case '/': // Деление
      // Может добавить ошибку деления на ноль, или измените строку во втором цикле,
      // где тот ожидает символа '=' на if (button == '=' && num2 != 0), это остановит программу
      // от дальнейших действий, пока num2 не будет отличаться от нуля.
      total = (float)num1/(float)num2;
      break;
      
    case '*': // Умножение
      total = num1*num2;
      break;
          
  }
    
  lcd.setCursor(0,1);
  lcd.print('=');
  lcd.setCursor(1,1);
  lcd.print(total);   
} 

Код может быть оптимизирован, но я написал его таким образом, чтобы начинающий читатель мог быстро проследить за кодом и получить представление о ходе выполнения программы (и чтобы не застрять, анализируя упакованный код).

Теперь, когда мы закончили со сборкой макета и объяснением кода, пришло время прошить код.

Если вы не хотите подключать клавиатуру к этому проекту (если у вас ее нет, и вы не хотите ее собирать), то можете исользовать, вместо нее, монитор последовательного порта Arduino. Вы можете сделать это, просто изменив несколько строк кода:

// в void setup() добавьте
Serial.begin(9600);

// в циклах получения символов измените
button = customKeypad.getKey();
// на
button=Serial.read();

Продолжаем! Если у вас нет LCD дисплея, вы можете аналогичным способом изменить программу для передачи данных на ваш компьютер, вместо печати на LCD дисплее, и получать результаты через монитор последовательного порта.

Комплектующие простого калькулятора на Arduino
Комплектующие простого калькулятора на Arduino

Заключение и полезные ссылки

Хотя тема данного проекта – простой калькулятор на базе Arduino, его основная цель – объяснить, как использовать клавиатуру для получения символов, и как получить итоговое число из отдельно введенных символов.

Он также объясняет, как управлять LCD дисплеем, подключенным к Arduino, и как объединить эти два действия в работающий калькулятор. Данная программа ограничена переменными и математикой платформы Arduino, поэтому не стоит ожидать от нее слишком многого – Arduino имеет свои ограничения, когда речь идет о больших числах и числах с плавающей запятой. Например, когда речь идет о числах с плавающей запятой, у нас есть типы float и double. Тип double должен иметь большую точность по сравнению с float, но на Arduino это не так. Поэтому использование double, вместо float, не даст вам большей точности, если вы не используете Arduino Due.

Создание более сложного калькулятора, который будет обрабатывать большие числа и большие числа с плавающей запятой, возможно, но это выходит за рамки данной статьи. Для тех из вас, кому интересны большие числа, существует библиотека (BigNumber.h, на самом деле класс C++), которая может обрабатывать большие числа, доступна по ссылке. И когда я говорю большие числа, то имею в виду, что вы сможете выполнять вычисления, пока не кончится память. Так что, если вы любите большие числа и хотите проверить ограничения плат Arduino, вы обязательно должны пройти по этой ссылке.

Что касается библиотеки работы с клавиатурой, если вы хотите глубже узнать, как она работает, то должны проверить эту ссылку и посмотреть ее возможности.

Вот и всё! Надеюсь, статья оказалась полезной. Оставляйте комментарии!


На сайте работает сервис комментирования DISQUS, который позволяет вам оставлять комментарии на множестве сайтов, имея лишь один аккаунт на Disqus.com.


Сообщить об ошибке