Калькулятор на Arduino UNO R3

Quincklyen
Offline
Зарегистрирован: 11.06.2022

      Здравствуйте, имеется скетч программы в который написано код для имитации калькулятора, проблема состоит в том что вывод чисел на экран бывает не тот который хочется, например я нажал на матричной клавиатуре семь раз на кнопку "1", вывелось 1111111, нажимаю ещё раз, а вместо ожидаемой единицы у меня уже стоит 2, это видно очень заметно при десятичной дроби, которую я ввожу, уже после нажатого символа происходит тоже самое, совсем другие числа. Весь ввод происходит в функции fillNumber(float &num).

      Скетч прогонял в proteus и на реальном аппарате, все фукнции работают исправно, кроме ввода чисел, выше описал проблему. Вот сама схема в proteus:


// Соответствие кнопок калькулятора по отношений к кнопкам матричной клавиатуры
// Кнопка [-=] -> [A]
// Кнопка [+=] -> [B]
// Кнопка [*]  -> [*]
// Кнопка [/]  -> [#]
// Кнопка [C]  -> [C]
// Кнопка [K]  -> [D]
// Кнопка [.]  -> отдельная кнопка отвечающая за десятичную дробь

#include <Keypad.h>   // Подключение библиотеки для работы с матричной клавиатурой
const byte ROWS = 4;  // Определение константы, указывающей на количество рядов клавиатуры 
const byte COLS = 4;  // Определение константы, указывающей на количество столбцов клавиатуры
const int decimal = 8;// Определение константы, указывающей на отдельную кнопку

// Инициализация двухмерного массива, определяющего раскладку матричной клавиатуры
char keys[ROWS][COLS] = {
  {'1', '2', '3', 'A'},
  {'4', '5', '6', 'B'},
  {'7', '8', '9', 'C'},
  {'*', '0', '#', 'D'}
};

byte rowPins[ROWS] = {A0, A1, A2, A3};  // Номера выводов Arduino, к которым подключены линии управления рядами клавиатуры
byte colPins[COLS] = {5, 4, 3, 2};      // Номера выводов Arduino, к которым подключены линии управления столбцами клавиатуры

Keypad keypad = Keypad(makeKeymap(keys), rowPins, colPins, ROWS, COLS); // Создание объекта класса Keypad, инициализация его конструктора согласно вышеуказанным параметрам

#include <Wire.h>                       // Подключение библиотеки для работы с шиной I2C
#include <LiquidCrystal_I2C.h>          // Подключение библиотеки для работы с дисплеем по шине I2C
LiquidCrystal_I2C lcd(0x27, 16, 2);     // Создание объекта класса LiquidCrystal_I2C, инициализация его конструктора (адрес на шине 0x27, размер 16 симполов, 2 строки)

// ---------- ГЛОБАЛЬНЫЕ ПЕРЕМЕННЫЕ ----------
int globalState = 0;          // Переменная для разделения основного цикла на фазы
int globalStateNumber = 0;    // Переменная смены режима в введение десятичной дроби
int digitCursor = 0;          // Переменная хранит текущее положение цифры на дисплее
int z = 1;
float number = 0.0;               // Переменная для хранения вводимого числа
float tempNumber = 0.0;           // Вспомогательная переменная для режима умножения и деления на постоянное число
float tempNumberCounter = 0.0;    // Вспомогательная переменная для режима умножения и деления на постоянное число
float answer = 0.0;               // Переменная для хранения ответа
char key;                         // Переменная для хранения кода нажатой клавиши
long a;                           
bool flagMultiplication = false;  // Вспомогательный флаг для фиксации операции умножения
bool flagDivision = false;        // Вспомогательный флаг для фиксации операции деления
bool flagPlus = false;            // Вспомогательный флаг для фиксации операции сложения
bool flagFirstNumber = true;      // Вспомогательный флаг для фиксации ввода первого числа после сброса
bool flagEnterNumber = false;     // Вспомогательный флаг для фиксации момента ввода числа с клавиатуры
bool flagDecimal = false;         // Вспомогательный флаг для фиксации нажатия на отдельную кнопку
uint32_t btnTimer = 0;            // Таймер для предотвращения дребезга контактов чтобы алгоритм лишний раз не прогонялся

// ---------- ФУНКЦИЯ ВЫВОДА РЕЗУЛЬТАТОВ ВЫЧИСЛЕНИЙ ----------
void showResult(float num, bool point = false) {
  digitCursor = 0; 
  lcd.setCursor(digitCursor, 0); 
  lcd.clear(); 
  delay(300);
  if((num - int(num)) > 0 || point) 
    lcd.print(num, 7);
  else { 
    lcd.print(num, 0); 
    lcd.print("."); 
  }
}

// ---------- ФУНКЦИЯ ПРЕДВАРИТЕЛЬНЫХ УСТАНОВОК ----------
void setup() { 
  pinMode(decimal,INPUT_PULLUP);
  lcd.init();             // Инициализация дисплея
  lcd.backlight();        // Включение подсветки дисплея
  lcd.clear();            // Очистка дисплея
}

// ---------- БЕСКОНЕЧНЫЙ ЦИКЛ -----------
void loop() {
  key = keypad.getKey();        // Чтение состояния клавиатуры
  bool btnState = !digitalRead(8);
  // ----------[ C ]----------
  if(globalState == 0) {        
    if(key != NO_KEY) {   // Если зафиксировано нажатие на какую-либо кнопку...
      if(key == 'C') {    // Если нажата клавиша [С]...
        showResult(0);    // Очистка буфера, подготовка к вводу первого числа
        globalState = 1;  // Переход к следующей программной фазе
      }
    }
  }
  else if(globalState == 1) {
    if(key != NO_KEY) {                     // Если зафиксировано нажатие на какую-либо кнопку...
      fillNumber(number);                   // Если пользователь нажимает цифры - вводим и запоминаем это число

      // ----------[ += ]----------
      if(key == 'B') {                      // Если нажата кнопка [ += ]...
        if(flagFirstNumber) {               // Если вводимое число является первым по счёту...
          answer = number;                  // Приравниваем результату вычислений вводимое число
          flagFirstNumber = false;          // Деактивируем флаг фиксации ввода первого числа после сброса
          flagPlus = true;                  // Устанавливаем флаг фиксации операции сложения
          showResult(answer);               // Выводим результат на дисплей
        }
        else {                              // Если вводимое число не является первым...
          if(flagPlus) {                    // Если флаг фиксации операции сложения активен...
            answer += number;               // Выполняем операцию сложения
            showResult(answer);             // Выводим результат на дисплей
          }
          if(flagMultiplication) {          // Если до операции сложения была операция умножения...
            answer *= number;               // Выполняем операцию умножения
            flagPlus = true;                // Устанавливаем флаг фиксации операции сложения
            flagMultiplication = false;     // Деактивируем флаг фиксации операции умножения
            showResult(answer);             // Выводим результат на дисплей
          }
          if(flagDivision) {                // Если до операции сложения была операция деления...
            answer /= number;               // Выполняем операцию деления
            flagPlus = true;                // Устанавливаем флаг фиксации операции сложения
            flagDivision = false;           // Деактивируем флаг фиксации операции деления
            showResult(answer);             // Выводим результат на дисплей
          }
        }
        if(flagDecimal) {
          flagDecimal = false;              // Деактивируем флаг фиксации ввода десятичной дроби, если она была нажата
        }
        flagEnterNumber = false;            // Деактивируем флаг фиксации момента ввода числа с клавиатуры
        globalStateNumber = 0;
        z = 1;
      }
      
      // ----------[ * ]----------
      if(key == '*') {                      // Если нажата кнопка [ * ]...
        if(flagPlus && !flagEnterNumber) {  // Если до операции умножения была операция сложения, но не было введено второе число...
          flagPlus = false;                 // Деактивируем флаг фиксации операции сложения
          flagMultiplication = true;        // Устанавливаем флаг фиксации операции умножения
          number = 1.0;                     // Присваеваем значение по умолчанию вместо вводимого числа
        }
        if(flagFirstNumber) {               // Если вводимое число является первым по счёту...
          answer = number;                  // Приравниваем результату вычислений вводимое число
          flagFirstNumber = false;          // Деактивируем флаг фиксации ввода первого числа после сброса
          flagMultiplication = true;        // Устанавливаем флаг фиксации операции умножения
          showResult(answer);               // Выводим результат на дисплей
        }
        else {                              // Если вводимое число не является первым...
          if(flagMultiplication) {          // Если флаг фиксации операции умножения активен...
            answer *= number;               // Выполняем операцию умножения
            showResult(answer);             // Выводим результат на дисплей
          }
          if(flagDivision) {                // Если до операции умножения была операция деления...
            answer /= number;               // Выполняем операцию деления
            flagDivision = false;           // Деактивируем флаг фиксации операции деления
            showResult(answer);             // Выводим результат на дисплей
            flagMultiplication = true;      // Устанавливаем флаг фиксации операции умножения
          }
          if(flagPlus) {                    // Если до операции умножения была операция сложения...
            answer += number;               // Выполняем операцию сложения
            flagPlus = false;               // Деактивируем флаг фиксации операции сложения
            showResult(answer);             // Выводим результат на дисплей
            flagMultiplication = true;      // Устанавливаем флаг фиксации операции умножения
          }
        }
        if(flagDecimal){
          flagDecimal = false;              // Деактивируем флаг фиксации ввода десятичной дроби, если она была нажата
          }
        flagEnterNumber = false;            // Деактивируем флаг фиксации момента ввода числа с клавиатуры
      }

      // ----------[ # ]----------
      if(key == '#') {                      // Если нажата кнопка [ # ]...
        if(flagPlus && !flagEnterNumber) {  // Если до операции деления была операция сложения, но не было введено второе число...
          flagPlus = false;                 // Деактивируем флаг фиксации операции сложения
          flagDivision = true;              // Устанавливаем флаг фиксации операции деления
          number = 1.0;                     // Присваеваем значение по умолчанию вместо вводимого числа
        }
        if(flagFirstNumber) {               // Если вводимое число является первым по счёту...
          answer = number;                  // Приравниваем результату вычислений вводимое число
          flagFirstNumber = false;          // Деактивируем флаг фиксации ввода первого числа после сброса
          flagDivision = true;              // Устанавливаем флаг фиксации операции деления
          showResult(answer);               // Выводим результат на дисплей
        }
        else {                              // Если вводимое число не является первым...
          if(flagDivision) {                // Если флаг фиксации операции деления активен...
            answer /= number;               // Выполняем операцию деления
            showResult(answer);             // Выводим результат на дисплей
          }
          if(flagMultiplication) {          // Если до операции деления была операция умножения...
            answer *= number;               // Выполняем операцию умножения
            flagMultiplication = false;     // Деактивируем флаг фиксации операции умножения
            showResult(answer);             // Выводим результат на дисплей
            flagDivision = true;            // Устанавливаем флаг фиксации операции деления
          }
          if(flagPlus) {                    // Если до операции деления была операция сложения...
            answer += number;               // Выполняем операцию сложения
            flagPlus = false;               // Деактивируем флаг фиксации операции умножения
            showResult(answer);             // Выводим результат на дисплей
            flagDivision = true;            // Устанавливаем флаг фиксации операции деления
          }
        }
        if(flagDecimal) {
          flagDecimal = false;              // Деактивируем флаг фиксации ввода десятичной дроби, если она была нажата
        }
        flagEnterNumber = false;            // Деактивируем флаг фиксации момента ввода числа с клавиатуры
        globalStateNumber = 0;
        z = 1;
      }
      // ----------[ -= ]----------
      if(key == 'A') {                      // Если нажата кнопка [ А ]...
        answer -= number;                   // Выполняем операцию вычитания
        showResult(answer);                 // Выводим результат на дисплей
        if(flagDecimal) { 
          flagDecimal = false;              // Деактивируем флаг фиксации ввода десятичной дроби, если она была нажата
        }
        flagEnterNumber = false;            // Деактивируем флаг фиксации момента ввода числа с клавиатуры
        globalStateNumber = 0;
        z = 1;
      }
      
      // ----------[ C ]----------
      else if(key == 'C') {                 // Если нажата кнопка [ С ] -> сбрасываем все параметры
        number = 0.0;
        answer = 0.0;
        tempNumber = 0.0;
        tempNumberCounter = 0.0;
        flagMultiplication = false;
        flagDivision = false;
        flagPlus = false;
        flagFirstNumber = true;
        flagDecimal = false;
        showResult(0);
        globalState = 1;
        globalStateNumber = 0;
        z = 1;
      }
      
      // Если нажата кнопка [К] сразу после кнопки [C] - переходим в режим умножения или деления на постоянное число
      else if(key == 'D' && !flagMultiplication && !flagDivision && !flagPlus && number == 0.0 && answer == 0.0) {
        globalState = 2;
      }
    }
      // ----------[ . ]----------
      if(btnState && !flagDecimal && millis() - btnTimer > 100) {
        flagDecimal = true;
        btnTimer = millis();
        globalStateNumber++;
      }
  } // globalState = 1

  else if(globalState == 2) {
    fillNumber(number);                   // Если пользователь вводит число - записываем его в переменную Number
    // Если нажата кнопка [+=]
    if(key == 'B') {                      
      if(flagMultiplication) {            // Если мы работаем с умножением
        answer = tempNumber * number++;   // Вычисляем результат умножения и автоматически увеличиваем число на единицу
        showResult(answer);               // Выводим результат на дисплей
      }
      else if(flagDivision) {             // Если мы работаем с делением
        answer = tempNumber / number;     // Вычисляем результат деления
        showResult(answer);            // Выводим результат на дисплей
        //tempNumber += tempNumberCounter;  // Автоматически увеличиваем число на величину первоначального значения
      }
    }
    // Если нажата кнопка [*]
    else if(key == '*') {  
      z = 1;
      if(answer == 0.0) {     
        tempNumber = number;              // Запоминаем число, с которым будем в дальнейшем работать
        answer = number;          
        showResult(answer);               // Выводим результат на дисплей
      }
      flagMultiplication = true;
    }
    // Если нажата кнопка [#]
    else if(key == '#') { 
      z = 1;
      if(answer == 0.0) {
        tempNumber = number;              // Запоминаем число, с которым будем в дальнейшем работать
        answer = number;
        //tempNumberCounter = number;
        showResult(answer);               // Выводим результат на дисплей
      }
      flagDivision = true;
    }
    // ----------[ . ]----------
    else if(btnState && !flagDecimal && millis() - btnTimer > 100) {
      flagDecimal = true;
      btnTimer = millis();
      globalStateNumber++;
    }
    // Если нажата кнопка [ С ] -> сбрасываем все параметры
    else if(key == 'C') {         
        number = 0.0;           
        answer = 0.0;          
        tempNumber = 0.0;         
        tempNumberCounter = 0.0;     
        flagMultiplication = false;    
        flagDivision = false;      
        flagPlus = false;        
        flagFirstNumber = true;
        flagDecimal = false;
        showResult(0);            
        globalState = 1;
        globalStateNumber = 0;
        z = 1;          
    }
    
  } // globalState = 2
}

// ---------- ФУНКЦИЯ ВЫЧИСЛЯЕТ ЧИСЛО, ВВОДИМОЕ ПОЛЬЗОВАТЕЛЕМ И СОХРАНЯЕТ ЕГО В ПЕРЕМЕННУЮ ПО ССЫЛКЕ С ПОСЛЕДУЮЩИМ ВЫВОДОМ НА ЭКРАН ----------
void fillNumber(float &num) {
  if((key == '0' || key == '1' || key == '2' || key == '3' || key == '4' || key == '5' || key == '6' || key == '7' || key == '8' || key == '9') && (digitCursor < 8)) {
    flagEnterNumber = true;
    if(digitCursor == 0) num = (key - 48);
    else if(globalStateNumber == 0){
      a = pow(10, z);
      num = num  * (float)a + key - 48;
    }
    else if(globalStateNumber == 1){
      a = pow(10, z);
      num = (num * (float)a + (key - 48))/(float)a;
      z++;
      }
    
    lcd.clear();
    lcd.setCursor(0, 0);
    lcd.print(num, z-1);
    if (globalStateNumber == 0)
       lcd.print(".");
    digitCursor++;
  }
}
BOOM
BOOM аватар
Offline
Зарегистрирован: 14.11.2018

Конечно же писал не сам? "Надергал" откуда-то из интернета?

rkit
Offline
Зарегистрирован: 23.11.2016

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

Вводи текст, в числа переводи уже в момент операции, будет нормально выглядеть. (работать будет всё так же).

Quincklyen
Offline
Зарегистрирован: 11.06.2022

Спасибо за совет, исправил, стало получше всё выглядить.

Iv_
Iv_ аватар
Offline
Зарегистрирован: 06.04.2022

нажимаю ещё раз, а вместо ожидаемой единицы у меня уже стоит 2, это видно очень заметно при десятичной дроби

Дребезг контактов клавиатуры? Стандартная задача студентам.

Iv_
Iv_ аватар
Offline
Зарегистрирован: 06.04.2022

Не, не дребезг контактов, если ранее вводилось всё точно.