Чарльз Платт – Электроника для начинающих (2-е издание) (страница 86)
Эксперимент 34. Точные игральные кости
В этом последнем эксперименте я собираюсь переделать устройство из эксперимента 24, в котором комбинации игральных костей формировались с помощью логических микросхем. Вместо микросхем теперь мы можем написать условные и логические операторы в программе для микроконтроллера. В результате компоненты схемы превратятся в несколько строк компьютерного кода и вместо таймера 555, счетчика и трех логических микросхем нам понадобится всего один микроконтроллер. Это отличный пример для демонстрации возможностей контроллеров. Но, безусловно, по-прежнему требуются светодиоды и токоограничительные резисторы.
Что вам понадобится
• Макетная плата, монтажный провод, кусачки, инструмент для зачистки проводов, тестовые провода, мультиметр
• Стандартный светодиод (7 шт.)
• Резистор 330 Ом (7 шт.)
• Плата Arduino Uno (1 шт.)
• Ноутбук или настольный компьютер со свободным USB-портом (1 шт.)
• USB-кабель с разъемами типа А и типа В на противоположных концах (1 шт.)
Эксперимент или программирование?
Обучение путем эксперимента хорошо работает, когда вам нужно изучить реальный электронный компонент. Вы можете установить его в макетную плату, подать питание и посмотреть, что получится. Даже когда вы разрабатываете схему, то можете действовать методом «проб и ошибок», внося изменения по ходу дела.
Создание программ – это занятие другого рода. Вы должны быть дисциплинированны и логичны, иначе будете писать программный код с ошибками, и он не станет работать надежно. Также здесь необходимо все планировать наперед, в противном случае вы потеряете много времени, переделывая выполненную ранее работу или полностью отказываясь от нее.
Я не люблю планировать, но еще больше я не люблю терять время. Поэтому, я все же составляю план, а в этом заключительном проекте опишу процесс планирования подробно. Прошу извинить меня за то, что вы не получите немедленного удовольствия от простого процесса сборки компонентов и возможности увидеть результат. Но если я не поясню процесс разработки программного обеспечения, то создам ошибочное впечатление о том, что программирование проще, чем оно есть на самом деле.
Случайность
Первый вопрос кажется очевидным: «Каких конкретных действий я жду от этой программы?» Вопрос необходим, потому что если цель не вполне ясна даже вам, то микроконтроллер и подавно не сможет ее реализовать. Формулировка цели напоминает описанный в эксперименте 15 процесс написания технического задания для системы охранной сигнализации, но в случае микроконтроллера детализация должна быть больше.
Основное требование очень простое. Мне нужна программа, которая будет выбирать случайное число и показывать его с помощью светодиодов, расположение которых напоминает точки на игральном кубике.
Поскольку выбор случайного числа – это основа данной программы, то вас следует познакомить с данной темой. Давайте заглянем на сайт Arduino, где находится справка о языке. Этот раздел сайта не настолько исчерпывающий, как мне хотелось бы, но для начала сгодится.
Чтобы найти его, перейдите на главную страницу Arduino[18], выберите вкладку Learning (Обучение) и отыщите раздел Reference, где вы найдете секцию Random Numbers. Там вы обнаружите специально созданную для контроллера Arduino функцию под названием random().
Вас не должно это удивлять, потому что практически все языки программирования высокого уровня имеют какую-либо встроенную функцию генерации случайных чисел, и она всегда основана на математических приемах для формирования последовательности чисел, которая продолжается очень долго, прежде чем начнет повторяться. Единственная проблема заключается в том, что поскольку эти числа создаются путем математических операций, то случайная последовательность будет начинаться с одного и того же места каждый раз, когда вы запускаете программу.
А если вы желаете, чтобы последовательность начиналась с другого числа? Для этого есть другая функция ПОД названием randomSeed(), которая запускает генератор чисел в зависимости от состояния вывода микроконтроллера, который ни к чему не подключен. Как я уже упоминал ранее, «плавающий» логический вывод улавливает окружающее электромагнитное излучение, и вы никогда не узнаете, что от него ожидать. Поэтому значение randomSeed() может оказаться в полном смысле слова случайным, и его использование даст хороший результат, но следует помнить, что «плавающий» контакт нельзя задействовать для чего-либо еще.
Отложим ненадолго вопрос о начальном значении для генератора случайных чисел. Давайте предположим, что случайное значение генерирует функция random() и затем из него формируется число в качестве выходного значения программы имитации игральных костей. Как это реализовать?
Я думаю, игрок будет нажимать кнопку, и в этот момент отобразится случайно выбранная конфигурация точек на кубике. Готово! Затем, если вам нужно «кинуть кости» повторно, вы просто нажимаете кнопку еще раз, и появляется другая, выбранная случайным образом, комбинация точек на кубике.
Это нам подходит, но выглядит не очень правдоподобно. Люди могут задаться вопросом, на самом ли деле это случайное число? Полагаю, проблема в том, что пользователь лишен возможности управлять процессом.
Вернемся к «аппаратной» версии этого устройства. Мне нравился вариант, когда после включения точки отображаются очень быстро и конфигурации нечеткие, а игрок может нажать кнопку, чтобы произвольно прервать последовательность.
Может быть, программа должна работать именно так, а не использовать функцию random()?
Она может вести отсчет от 1 до 6 снова и снова очень быстро – как микросхема счетчика в аппаратной версии игральных костей.
Но теперь возникают другие сложности. Когда программа считает от 1 до 6, а затем повторяет счет, микроконтроллеру, как я думаю, понадобится еще несколько микросекунд, чтобы вернуться к началу цикла. Поэтому число «6» всегда будет отображаться чуть дольше, чем другие числа.
Возможно, мне удастся скомбинировать две концепции. Я могу применить генератор случайных чисел для создания последовательности чисел, а затем буду показывать их очень быстро, пока игрок не нажмет кнопку в произвольный момент.
Мне нравится этот план. Но что потом? Не придется ли добавить еще одну кнопку, чтобы перезапустить быстрое отображение чисел? Хотя, нет, это излишне: одна и та же кнопка выполнит обе операции. Нажмите, чтобы остановить, снова нажмите, чтобы перезапустить.
Видите, я все более четко представляю, какие действия должна выполнять наша программа. Теперь можно сделать следующий шаг при определении инструкций для микроконтроллера.
Алгоритм
Мне нравится составлять
Основной цикл:
• Шаг 1. Выбрать случайное число.
• Шаг 2. Преобразовать его в конфигурацию точек на игральной кости и зажечь соответствующие светодиоды.
• Шаг 3. Проверить, нажата ли кнопка.
• Шаг 4. Если кнопка не нажата, вернуться к Шагу 1 и выбрать другое случайное число, чтобы быстро продолжить последовательность. Иначе…
• Шаг 5. Остановить индикацию на дисплее.
• Шаг 6. Подождать, пока игрок не нажмет кнопку повторно. После этого вернуться к Шагу 1 и повторить.
Есть ли в этой последовательности шагов какие-нибудь проблемы? Попробуйте представить ее с точки зрения микроконтроллера. Если бы вы получили инструкции из такой программы, у вас было бы все необходимое, чтобы выполнить это задание?
Нет, потому что некоторых инструкций не хватает. Шаг 2 говорит «зажечь соответствующие светодиоды», но нигде нет инструкции по их выключению!
Внимание!
Вы всегда должны помнить: компьютер делает только то, что вы ему приказываете.
Если вы хотите, чтобы зажженные светодиоды выключились, прежде чем появится новое число, то должны предусмотреть такую команду.
Где она должна быть? Необходимо гасить дисплей пред выбором и отображением каждого нового числа. Поэтому правильное место для сброса дисплея находится в начале основного цикла. Добавим его так:
• Шаг 0. Выключить все светодиоды.
Но погодите. В зависимости от того, какое число отображалось на предыдущем цикле, одни светодиоды будут включены, а другие выключены. Если мы выключаем все светодиоды, чтобы очистить дисплей, то эта команда затронет также те светодиоды, которые уже выключены. Микроконтроллеру это безразлично, однако, он потратит впустую некоторое время, выполняя эту инструкцию. Возможно, было бы гораздо эффективнее выключить светодиоды, которые перед этим были включены, и проигнорировать те, которые уже и так выключены.
Однако в результате программа усложнится и, возможно, так делать не следует. На заре вычислительной техники людям приходилось