простой калькулятор на Arduino
- Войдите на сайт для отправки комментариев
Вс, 12/03/2017 - 15:22
Тема о том куда применить клавиатуру и дисплей от кассовых аппаратов.
Код взят с сайта http://radioprog.ru/post/133
Клавиатура с кассового аппарата ЭКР 2102 - верхняя часть кнопки снимается - в прозрачных можно самому рисовать символы.
#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] = { {'7','8','9','C6'}, {'4','5','6','-'}, {'1','2','3','+'}, {'*','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); }
Разьём клавиатуры: Столбцы: 7, 8, 9, 10, 11
Строки: 4, 3, 2, 1, 5, 6
Тема о том куда применить клавиатуру и дисплей от кассовых аппаратов.
Видимо, в ближайшем будущем следует ожидать темы о том, куда применить «простой калькулятор на Arduino»? :))))
Aleks_neofit,
а если серьёзно, то попробуйте операцию и второй операнд оставлять в одной строке. Тогда у Вас получится не
123 (уже исчезло с экрана)
+123
=246.00
а
123+123
=246.00
(всё на экране)
По-моему, проект заметно выиграет.
У меня так просто два старых кассовых аппарата валяется целых без ЭКЛЗ, может кому надо?
Калькулятор - отстой. делайте программируемый. Программу в EEPROM хранить.
Я только приступил к изучению Arduino. Разбирался со сктчем. Оператор while() { } В круглых скобках согласно синтаксиса должно писаться условие выполнения инструкции, а в скетче проставлена цифра while(1). Подскажите пожалуйста , что означает эта цифра, и где же условие. Если в {} , то это же инструкция. Часа 2 лазил по инету так и ни чего не нашел. Заранее спасибо, если кто ответит.
Я только приступил к изучению Arduino. Разбирался со сктчем. Оператор while() { } В круглых скобках согласно синтаксиса должно писаться условие выполнения инструкции, а в скетче проставлена цифра while(1). Подскажите пожалуйста , что означает эта цифра, и где же условие. Если в {} , то это же инструкция. Часа 2 лазил по инету так и ни чего не нашел. Заранее спасибо, если кто ответит.
Число 1 и есть условие.
Вы знаете, что является результатом проверки любого условия? 1 - если истинно и 0 - если ложно
1 эквивалентно true, в данном случае условие будет выполняться всегда. Лишняя команда в данном случае, скорее всего в оригинале при разработке что- то было, но автор упростил
Спасибо за ответ. Просто такой записи ни где не нашел. Во всех описаниях оператора примеры совершенно другие, там явно прослеживается условие. Еще раз спасибо за разьяснение
1 эквивалентно true, в данном случае условие будет выполняться всегда. Лишняя команда в данном случае, скорее всего в оригинале при разработке что- то было, но автор упростил
Что значит лишняя? Ничего другого там никогда не было и никто ничего не упрощал. while(1) {} это бесконечный цикл. Автор в нём ожидает нажатие определённых клавиш. Если нажата нужная клавиша, то выход из цикла по команде break. Насколько это красивое решение - мы не обсуждаем, но в самом этом цикле ничего криминального и ничего удивительного.
Все стало понятно. Я то же не мог понять, зачем команда break нужна. Долго эксперементировал, подставляя эту команду в другие места, и за скобками и перед скбками. Теперь до конца все стало ясно. Просто я для для изучения Arduino поставил сам себе задачу сделать счетчик , что бы он считал общее количество и подавал сигнал на предварительно устанавливаемые циклы. Допустим цикл 150, на счете 148 подается сигнал, на 150 он снимается, счетчик обнуляется и цикл начинается сначала. а общий счетчик не сбрасывается а продолжает суммарный подсчет. Вот эту программу я использовал для ввода данных с клавиатуры. Такое устройство я делал в железе без контроллеров, а теперь пробую на современной базе. Большое спасибо за консультацию
1 эквивалентно true, в данном случае условие будет выполняться всегда. Лишняя команда в данном случае, скорее всего в оригинале при разработке что- то было, но автор упростил
Что значит лишняя? Ничего другого там никогда не было и никто ничего не упрощал. while(1) {} это бесконечный цикл. Автор в нём ожидает нажатие определённых клавиш. Если нажата нужная клавиша, то выход из цикла по команде break. Насколько это красивое решение - мы не обсуждаем, но в самом этом цикле ничего криминального и ничего удивительного.
Конечно ничего криминального в этом нет - каждый пишет как ему удобно, главное чтобы работало
Спасибо за ответ. Просто такой записи ни где не нашел. Во всех описаниях оператора примеры совершенно другие, там явно прослеживается условие. Еще раз спасибо за разьяснение
Не должно там быть условия. Про условие обычно пишут люди либо не очень грамотные, либо считающие, что у них будут не очень грамотные читатели.
На самом деле там должно быть логическое выражение. Условие - лишь частный случай выражения. Но в школе булеву алгебру не проходят, поэтому в расчете на школьника нередко вместо "логическое выражение" пишут "условие", считая, что так понятнее. Но как Вы сами только что убедиись, как раз понятность от этого и страдает.
Я только взялся за освоение arduino, языка С++ не знаю, пользуюсь доступной литературой которая есть в интернете, ну и статьями на форумах. Я приведу цитаты из двух книг, по которыми я пользуюсь как справочниками.
Обшая форма цикла while имеет следующий вид:
while (условие) оператор;
Здесь оператор (тело цикла) может быть пустым оператором, единственным оператором или блоком. Условие (управляющее выражение) может быть любым допустимым в языке выражением. Условие считается истинным, если значение выражения не равно нулю, а оператор выполняется, если условие принимает значение ИСТИНА. Если условие принимает значение ЛОЖЬ, программа выходит из цикла и выполняется следующий за циклом оператор. ( это из книги Г. Шильд)
Цикл while — это цикл for, у которого удалены инициализирующая и обновляющая части; в нем имеется только проверочное условие и тело:
while (проверочное_условие), тело ( Это из книги Прата С.)
Вот из этих книг, я понял, что обязательно должно быть условие. Возможно это перевод некомпетентный, а за разьяснение спасибо, я еще покопаюсь в интернете, для более глубокого понимания.
valera1950, так всё правильно написано. Читайте внимательноее. Смотрите:
Условие (управляющее выражение) может быть любым допустимым в языке выражением. Условие считается истинным, если значение выражения не равно нулю, а оператор выполняется, если условие принимает значение ИСТИНА. Если условие принимает значение ЛОЖЬ, программа выходит из цикла и выполняется следующий за циклом оператор. ( это из книги Г. Шильд)
Все чётко. 1 - допустимое в языке выражение? Да. Оно равно 0? Нет. Значит это истина.
Для последнего нужно, чтобы a было ранее описано.
Допустим
while (t * sin(x)-1.05e4)/((2 * к + 2) * (2 * к + 3))
если я правильно вас понял должно выполниться арифметическое выражение в скобках, и затем будет выполняться вычисление или преобразование до тех пор пока выражение в скобках не станет логическим значением ЛОЖНО. Только тогда этот оператор заканчивает работу, и переходит к следующему оператору. ???
Допустим
while (t * sin(x)-1.05e4)/((2 * к + 2) * (2 * к + 3))
если я правильно вас понял должно выполниться арифметическое выражение в скобках, и затем будет выполняться вычисление или преобразование до тех пор пока выражение в скобках не станет логическим значением ЛОЖНО. Только тогда этот оператор заканчивает работу, и переходит к следующему оператору. ???
Нет, конечно. Баланс скобок-то соблюдать надо. После while должно идти выражение в скобках, а после оператор или блок. У Вас же после while выражение без скобок.
Вообще, перестаньет придумывать частные случаи. Общий вид оперетора while таков
while (a) b;
Где а - любое допустимое в языке выражение, которое имеет результат и допускает преобразование этого результата к логическому типу. b - просто любая допустимая операция или блок.
Сначала вычисляется a, и если оно истина, выичсляется b, и процесс повторяется до тех пор, пока а не станет ложным.
А в Вашем выражении, что является а, и что - b?
-------------
А вообще, заведите полезную привычку, если есть вопрос, то прежде, чем лезть на форум, запустите и попробуйте сами. До тех пор, пока Вы не научитесь задавать вопросы компьютеру, Вы не научитесь ничему.
извините, я просто не так написал, вот сейчас в соответствии с синтаксисом white.
(a) (b)
while (t * sin(x)-1.05e4)/((2 * к + 2) * (2 * к + 3)) { здесь инструкция, операция, блок...}
в круглых скобках это то же арифметическое выражение, которое используется в языке. В результате вычисления здесь получается какой то результат, и чем оно отличается от
while
(1+1)...?.
Вы извините,я просто хочу все до конца понять. пусть выражение в круглых скобках будет (2 * к + 3), или это должно быть только логическим выражением.
(a) (b)
while (t * sin(x)-1.05e4)/((2 * к + 2) * (2 * к + 3)) { здесь инструкция, операция, блок...}
в круглых скобках это то же арифметическое выражение, которое используется в языке. В результате вычисления здесь получается какой то результат, и чем оно отличается от
while
(1+1)...?.
Ну, я же Вам писал, что баланс скобок надо соблюдать. Выражение "а" должно быть в скобках.
Посчитайте скобки в Вашем выражении и убедитесь, что "а" у Вас это "t * sin(x)-1.05e4". Оно в скобках и это правильно. Далее должно идти выражение b, а у Вас почему-то идёт знак / - а это синтиксически неверно.
Вы пробовали это запустить? Почему не пробовали?
пусть выражение в круглых скобках будет (2 * к + 3),
Ну, пусть будет, кто ему мешает? Совершенно нормальное выражение, если k предварительно было описано. Выражение будет вычислено. Если результат 0, то будет считаться ложью, если не 0, то истиной.
или это должно быть только логическим выражением.
Оно не должно быть логическим, одно должно предполагать возможность преобразования к логическому. Т.е. попросту его должно быть возможно сравнить с 0. Если равно - ложь, не равно - истина. Вот и всё. Там даже переменные описывать можно. Например:
вполне законная конструкция (если n раньше была описана, конечно). Область видимости переменной k - до конца цикла.
видимо так правильно?
while ((t * sin(x)-1.05e4)/((2 * к + 2) * (2 * к + 3))) { здесь инструкция, операция, блок...}
видимо так правильно?
while ((t * sin(x)-1.05e4)/((2 * к + 2) * (2 * к + 3))) { здесь инструкция, операция, блок...}
Формально правильно, но реально тут есть засада. Такая конструкция предполагает сравнение выражения
((t * sin(x)-1.05e4)/((2 * к + 2) * (2 * к + 3)))
с нулём (если равно - ложь, не равно - иститна).
Но это выражение даёт резальтат типа double. А точное сравнение чисел с плавающей точкой на равенство - игра в рулетку. Он может быть не равно 0 в каком-нибудь 10-ом знаке после запятой (а ещё лучше - в таком знаке, который присутствует, но никак не гарантируется точностью вычислений). Поэтому для плавающих чисел лучше сравнивать не на точное равенство, а с некоторой "допустимой погрешностью". Например, так будет более адекватно
while (fabs((t * sin(x)-1.05e4)/((2 * к + 2) * (2 * к + 3))) < 1e-6)
например.
Большое спасибо, прктически понял все. Скобки я пропустил. Сейчас в уме разложу все по полочкам, тогда приду к окончательному решению, понял или остались еще непонятные вопросы. Хорошо, что откликнулись на вопросы, так практически можно изучить все. Еще раз спасибо.!!!
Вот из этих книг, я понял, что обязательно должно быть условие.
Из приведенной Вами цитаты видно, что:
1. Под "условием" автор подразумевает именно выражение.
2. Что это "условие" может принимать значения либо ИСТИНА либо ЛОЖЬ. "1" - это ИСТИНА, следовательно, "1" с точки зрения автора - это именно "условие".
Т.е. понятно, что под "условием" автор понимает нечто отличающееся от бытового представлении о том, что такое условие. И пояснил, что именно.
Вы извините,я просто хочу все до конца понять. пусть выражение в круглых скобках будет (2 * к + 3), или это должно быть только логическим выражением.
Особенностью языка Си является то, что в нем нет логического типа. Вместо него используется обычный целочисленный. При этом 0 трактуется как ЛОЖЬ, а любое другое значение как ИСТИНА.
видимо так правильно?
while ((t * sin(x)-1.05e4)/((2 * к + 2) * (2 * к + 3))) { здесь инструкция, операция, блок...}
Формально правильно, но реально тут есть засада. Такая конструкция предполагает сравнение выражения
((t * sin(x)-1.05e4)/((2 * к + 2) * (2 * к + 3)))
с нулём (если равно - ложь, не равно - иститна).
Но это выражение даёт резальтат типа double. А точное сравнение чисел с плавающей точкой на равенство - игра в рулетку. Он может быть не равно 0 в каком-нибудь 10-ом знаке после запятой (а ещё лучше - в таком знаке, который присутствует, но никак не гарантируется точностью вычислений). Поэтому для плавающих чисел лучше сравнивать не на точное равенство, а с некоторой "допустимой погрешностью". Например, так будет более адекватно
while (fabs((t * sin(x)-1.05e4)/((2 * к + 2) * (2 * к + 3))) < 1e-6)
например.
а так?
while (unsigned int a = (t * sin(x)-1.05e4)/((2 * к + 2) * (2 * к + 3))) { здесь инструкция, операция, блок...}
Да, теперь все понятно. Как только в теле инструкции, или оператора, что то меняется, то это приводит к изменению условия. ИСТИНА меняется на ЛОЖЬ. В прведенной программе калькулятора в первом цикле изменения могут быть вызваны только изменениями состояния кнопок, и ни чем другим. Это проверяется оператором if Если условие меняется на ЛОЖЬ, то цикл не выполняется. Если это делается так как я написал, значит вопросов у меня по этому поводу уже не возникнет. Если не, то буду разбираться еще. А с выражениями мне все ясно. Просто не все внимательно прочитал.
Особенностью языка Си является то, что в нем нет логического типа.
Беда только в том, в Ардуино используется не С, а С++ (в .ino файлах).
а так?
while (unsigned int a = (t * sin(x)-1.05e4)/((2 * к + 2) * (2 * к + 3))) { здесь инструкция, операция, блок...}
Ну, скобки мне влом считать, а так идейно - пожалуйста, если Вам не жалко, что дробная часть будет отброшена. Вы же знаете, что если присваиваете целому числу плавающее, дробная часть отбрасывается даже если она .9999.
valera1950, смотрите, как изящно можно строки копировать
while (*dst++=*src++) ;
и без единого оператора в цикле