Калькулятор на Arduino UNO R3
- Войдите на сайт для отправки комментариев
Сб, 11/06/2022 - 13:25
Здравствуйте, имеется скетч программы в который написано код для имитации калькулятора, проблема состоит в том что вывод чисел на экран бывает не тот который хочется, например я нажал на матричной клавиатуре семь раз на кнопку "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++;
}
}
Конечно же писал не сам? "Надергал" откуда-то из интернета?
Потому что ты пытаешься оперировать с числами с плавающей точкой как с десятичными.
Вводи текст, в числа переводи уже в момент операции, будет нормально выглядеть. (работать будет всё так же).
Спасибо за совет, исправил, стало получше всё выглядить.
нажимаю ещё раз, а вместо ожидаемой единицы у меня уже стоит 2, это видно очень заметно при десятичной дроби
Дребезг контактов клавиатуры? Стандартная задача студентам.
Не, не дребезг контактов, если ранее вводилось всё точно.