Хотите чем-либо занять ребёнка? Машинки и куклы надоели, хочется что-нибудь по-веселее? Предлагаю сделать простую электронную игрушку своими руками. В магазинах есть интересная игра под названием: "Саймон сказал":
Суть игры заключается в том, чтобы игрок повторил код, который показал компьютер. Нечто подобное мы и будем собирать сегодня сами.
Для начала разберёмся с основными частями проекта:
- 4 кнопки и 4 светодиода - это основа. Посредством светодиодов компьютер будет показывать код, а с помощью кнопок игрок будет его повторять.
- Динамик будет озвучивать нажатие на кнопки и включение лампочек.
- Семисегментный индикатор с драйвером 74HC595. Будет отображать текущий уровень сложности.
- Фоторезистор. Для генератора случайных чисел. (Позже объясню).
Подключаем к Arduino по такой схеме:
- Светодиоды подключены к 8, 6, 4, 2 выходам Arduino через резисторы по 220 Ом (номинал можно изменить от 150 до 330 Ом);
- Кнопки к 9, 7, 5, 3 выходам со стягивающими резисторами 10 кОм (номинал не важен, важно его наличие);
- Динамик(Я взял пассивный) подключен напрямую к 10 пину (поддерживает ШИМ);
- Контакты семисегмента (через резистор) идут к выходам регистра.
- Сам регистр подключен к 13-му, 12-му и 11-му контакту (такт, данные и защёлка); Подробнее в этой статье.
- Ну, и фоторезистор к аналоговому входу 1;
Я всё сделал на Breadboard. Получилось так:
В дальнейшем планирую сделать печатную плату, добавить питание от USB и поместить в какой-либо корпус. Плату уже нарисовал (скачать можно внизу). Там 2 платы. 1-я - это верхняя(лицевая) часть с кнопками, светодиодами, индикатором и. т. д.. А 2-я (нижняя) - это микроконтроллер с обвязкой (вместо неё можно использовать Arduino);
Теперь о коде: (Скачать код полностью можно внизу по ссылке)
В первую очередь пишем глобальные переменные:
#define CLOCK 13 #define DATA 12 #define LATCH 11
- это выходы регистра;
#define buzzer 10 const int leds[4] = {8, 6, 4, 2}; const int buttons[4] = {9, 7, 5, 3};
- выходы динамика (пищалки), светодиодов и входы кнопок.
const int notes[4] = {262, 330, 392, 523};
- а в это массиве хранятся ноты, которые будут проигрываться при нажатии кнопок. При желании их можно сделать одинаковыми. Я их брал из файла pitches.h. Откройте пример Digital > toneMelody. К нему прикреплён это файл:
В setup-е назначаем пины регистра, динамика и светодиодов выходами, а кнопки входами; ставим высокий уровень на защёлку.
pinMode(CLOCK, OUTPUT); pinMode(DATA, OUTPUT); pinMode(LATCH, OUTPUT); pinMode(buzzer, OUTPUT); for(int i=0; i<4; i++) pinMode(leds[i], OUTPUT), pinMode(buttons[i], INPUT); digitalWrite(LATCH, HIGH);
Теперь разберём функции:
- для семисегмента я сначала написал 2 функции. 1-я будет выводить на него цифры (от 0 до 9. 10 - это 9 с точкой):
void segWrite(int number){ byte numbers[11] = {0b11111100,0b01100000,0b11011010, 0b11110010,0b01100110,0b10110110,0b10111110, 0b11100000,0b11111110,0b11110110,0b11110111}; digitalWrite(LATCH, LOW); shiftOut(DATA, CLOCK, LSBFIRST, numbers[number]); digitalWrite(LATCH, HIGH); }
А 2-я будет выключать индикатор:
void segOff(){ digitalWrite(LATCH, LOW); shiftOut(DATA, CLOCK, LSBFIRST, 0); digitalWrite(LATCH, HIGH); }
- Потом решил добавить 3-ю, которая будет показывать небольшую анимацию перед началом игры:
void animation(){ byte add = 1; byte value = 0; for(int i=0; i<6; i++){ value+=add; digitalWrite(LATCH, LOW); shiftOut(DATA, CLOCK, MSBFIRST, value); digitalWrite(LATCH, HIGH); add*=2; delay(100); } }
- Следующая называется showled. Она будет показывать код игроку:
void showled(int count){ tone(buzzer, notes[count]); digitalWrite(leds[count], HIGH); delay(300); digitalWrite(leds[count], LOW); noTone(buzzer); delay(300); }
- Следующие 2 функции противоположны друг-другу. Одна будет исполнятся, когда игрок выиграл:
void soundwin(){ //262, 294, 330, 392; 330, 392; tone(buzzer, 262); digitalWrite(leds[0], HIGH); delay(100); tone(buzzer, 294); digitalWrite(leds[1], HIGH); delay(100); tone(buzzer, 330); digitalWrite(leds[2], HIGH); delay(100); tone(buzzer, 392); digitalWrite(leds[3], HIGH); delay(100); noTone(buzzer); for(int i=0; i<4; i++) digitalWrite(leds[i], LOW); delay(100); tone(buzzer, 330); delay(100); tone(buzzer, 392); for(int i=0; i<4; i++) digitalWrite(leds[i], HIGH); delay(500); noTone(buzzer); for(int i=0; i<4; i++) digitalWrite(leds[i], LOW); }
(ноты брал из того же файла);
Другая - когда проиграл:
void soundlose(){ tone(buzzer, 1480); for(int i=0; i<4; i++) digitalWrite(leds[i], HIGH); delay(350); for(int i=0; i<4; i++) digitalWrite(leds[i], LOW); tone(buzzer, 1047); delay(350); tone(buzzer, 1480); for(int i=0; i<4; i++) digitalWrite(leds[i], HIGH); delay(500); noTone(buzzer); for(int i=0; i<4; i++) digitalWrite(leds[i], LOW); }
- Функции для работы с кнопками: button() - будет ожидать нажатия любой кнопки и возвращать её номер.
int button(){ int pressed = -1; do{ for(int i=0; i<4; i++){ if(digitalRead(buttons[i])==HIGH){ pressed = i; break; } } } while(pressed<0); return pressed; }
readButton() - а эта будет проверять, правильная ли кнопка нажата и, соответственно, возвращать: да или нет.
boolean readbutton(int count){ int pressed = button(); delay(5); tone(buzzer, notes[pressed]); digitalWrite(leds[pressed], HIGH); delay(100); while(digitalRead(buttons[pressed])!=LOW); noTone(buzzer); digitalWrite(leds[pressed], LOW); delay(100); if(pressed==count) return true; else return false; }
(И, кстати, данная функция не будет ничего возвращать, пока человек не отпустит кнопку. Таким образом, последнее состояние кнопки будет всегда LOW, потому для борьбы с дребезгом мы ограничимся только задержкой на 5 мс).
- И последняя функция (самая интересная) будет делать случайные числа максимально случайными :)
void setRandom(){ int analog = analogRead(0); int light = analogRead(1); unsigned long time = millis(); unsigned long results = analog * light * time; do results = results / 10; while(results>=1000); results+=random(0, 300); randomSeed(results); }
Всё что она делает - это формирует число, зависящее от освещённости (для этого и нужен фоторезистор), показаний с пустого аналог. входа и времени. А потом отправляет его в randomSeed();
Теперь сам цикл. (Для меня это было самым сложным, т. к. пришлось использовать метки)
- Вначале стоят 4 переменные, отвечающие за кол-во жизней, длину кода, текущий уровень и сам код.
int lives = 2; int codelength = 10; int level = 0; int code[codelength];
(обращу ваше внимание, что Arduino будет показывать игроку сначала одну цифру кода, потом первую + вторую, + 3-ю, 4-ю и. т. д. Поэтому весь код надо разбивать на части, а лучше сразу сделать массив переменных, как это сделал я);
- Затем показываем анимацию и ожидаем нажатия любой кнопки для начала игры. Ну, и чуть-чуть ждём:
animation(); button(); delay(1000);
- Кода игра началась, сразу заполняем ячейки кода random-ом:
for(int i=0; i<codelength; i++){ setRandom(); code[i] = random(4); }
- Вся игра будет крутиться в цикле, пока игрок не пройдёт все уровни, либо пока жизни не закончатся. Поэтому заводим цикл for():
for(level=0; level < codelength; level++){
- В цикле ставим метку show, отображаем на индикаторе текущий уровень и проверяем, не закончились ли жизни:
show: segWrite(level+1); if(lives==0) break;
- Потом крутим ячейки нашего кода и показываем их игроку (кол-во таких ячеек должно быть равно текущему уровню):
//показываем код(одно за другим) for(int count=0; count<=level; count++) showled(code[count]);
- Затем заводим ещё один цикл for(), в котором выводим на сегмент количество кнопок, которое ещё необходимо нажать, и проверяем нажатие кнопок. Если нажата не та кнопка, то играем печальную мелодию о проигрыше и переходим в начало первого цикла (заново показываем код):
//проверяем код(одно за другим) for(int count=0; count<=level; count++){ segWrite(level+1-count); // если правильно - идём дальше // если неправильно, уменьшаем жизни if(!readbutton(code[count])){ segOff(); soundlose(); delay(1000); lives--; goto show; } }
- И, если данный цикл пройден, значит игрок нажал всё правильно. Поздравляем его и переходим на следующий уровень:
segWrite(0); delay(100); soundwin(); delay(1000);
Надеюсь, я понятно объяснил. Если есть вопросы - пишите в комментариях. Буду рад ответить.
Список радиоэлементов
Обозначение | Тип | Номинал | Количество | Примечание | Магазин | Мой блокнот | |
---|---|---|---|---|---|---|---|
Arduino или ATmega8 с обвязкой | |||||||
МК AVR 8-бит | ATmega8 | 1 | Поиск в магазине Отрон | ||||
Кварцевый резонатор | 16 МГц | 1 | Поиск в магазине Отрон | ||||
Конденсатор | 22 пФ | 2 | Поиск в магазине Отрон | ||||
Конденсатор | 100 мФ | 1 | Поиск в магазине Отрон | ||||
Светодиод | 1 | ON | Поиск в магазине Отрон | ||||
Резистор | 220 Ом | 1 | Поиск в магазине Отрон | ||||
Тактовая кнопка | 1 | RESET | Поиск в магазине Отрон | ||||
Резистор | 1 - 10 КОм | 1 | Поиск в магазине Отрон | ||||
Разъём | USB | 1 | Поиск в магазине Отрон | ||||
Периферия | |||||||
IC1 | Сдвиговый регистр | CD74HC595 | 1 | Поиск в магазине Отрон | |||
SEG1 | Семисегмент | 1 | Поиск в магазине Отрон | ||||
LED1-LED4 | Светодиод | 4 | У меня 2 красных и 2 жёлтых | Поиск в магазине Отрон | |||
R1-8, R13-16 | Резистор | 220 Ом | 12 | Поиск в магазине Отрон | |||
R9-12 | Резистор | 1 - 10 КОм | 4 | Поиск в магазине Отрон | |||
S1-S4 | Тактовая кнопка | 4 | Поиск в магазине Отрон | ||||
SP1 | Динамик (пищалка) | активный/пассивный | 1 | Поиск в магазине Отрон | |||
PH1 | Фоторезистор | 1 | Поиск в магазине Отрон | ||||
Скачать список элементов (PDF)
Прикрепленные файлы:
- SymonSays_game(1).ino (4 Кб)
- Simon_плата.lay6 (117 Кб)
Комментарии (3) | Я собрал (0) | Подписаться
Для добавления Вашей сборки необходима регистрация
[Автор]
Для начинающих могу выложить подробную версию схемы на бредборде.