Хотите чем-либо занять ребёнка? Машинки и куклы надоели, хочется что-нибудь по-веселее? Предлагаю сделать простую электронную игрушку своими руками. В магазинах есть интересная игра под названием: «Саймон сказал»:
Суть игры заключается в том, чтобы игрок повторил код, который показал компьютер. Нечто подобное мы и будем собирать сегодня сами.
Для начала разберёмся с основными частями проекта:
- 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-битATmega81
Кварцевый резонатор16 МГц1
Конденсатор22 пФ2
Конденсатор100 мФ1
Светодиод1
ON
Резистор220 Ом1
Тактовая кнопка1
RESET
Резистор1 — 10 КОм1
РазъёмUSB1
ПериферияIC1
Сдвиговый регистрCD74HC5951
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 Кб)