Якось випадково, коли шукав щось по мікроконтролерам, натрапив на просту і цікаву гру "Simon Says" на платформі ARDUINO. Дуже сподобалась гра. Мінімум деталей, а ігровий процес затягує. Почав шукати в мережі, може є така гра на платформі STM32? Не знайшов абсолютно нічого. Ну що ж, значить робота по портуванню цієї цікавої гри за мною. За основу портування взяв програмний код звідси. З ARDUINO ніколи не мав справу, то ж прийшлось трішки розібратись що до чого, але на загал витратив на порт гри не багато часу. Гра проста. Головне алгоритм! Все інше деталі.
Якось випадково, коли шукав щось по мікроконтролерам, натрапив на просту і цікаву гру "Simon Says" на платформі ARDUINO. Дуже сподобалась гра. Мінімум деталей, а ігровий процес затягує. Почав шукати в мережі, може є така гра на платформі STM32? Не знайшов абсолютно нічого. Ну що ж, значить робота по портуванню цієї цікавої гри за мною. За основу портування взяв програмний код звідси. З ARDUINO ніколи не мав справу, то ж прийшлось трішки розібратись що до чого, але на загал витратив на порт гри не багато часу. Гра проста. Головне алгоритм, все інше деталі.
Будь який N-P-N транзистор малої потужності - 1 шт.
Світлодіоди, бажано різного кольору - 4 шт.
Резистор 10 кОм - 4 шт.
Резистор 330 Ом - 4 шт.
Електрична схема гри
Електрична схема гри "Саймон каже"
На схемі немає зображення плати розробника STM32VLDiscovery (мікроконтролеру), а вказані назви виводів мікроконтролера з якими потрібно з'єднати елементи схеми.
Зібрана гра на макетній платі:
Алгоритм гри
При подачі живлення, на макет гри, почергово миготять світлодіоди - запрошення до гри. Натиснувши кнопку старт, кнопка USER на самій платі STM32VLDISCOVERY, гра почергово миготить два рази ігровими світлодіодами зі звуком - сигнал до старту. А потім починається гра. Випадково запалюються світлодіоди зі звуком, з кожним раундом послідовність вогників збільшується. Гра очікує відтворення тієї ж послідовності кнопками, які гравець тисне. Як правильно - рівень збільшується, як ні - гра спочатку.
Програмна реалізація гри
Створюємо новий проект в CooCox IDE. Називаємо його "simon says". Обираємо чип STM32F100RB. Позначаємо в репозиторії бібліотеку GPIO. Переходимо до "main.c". Видаляємо обов'язковий шаблон. Копіюємо до main.c цей текст програми, або завантажуємо файл.
//Гра "Саймон каже" для плати розробника STM32VLDiscovery на чіпові STM32F100RB (порт з платформи ARDUINO)//Додаємо потрібні файли до проекту#include "stm32f10x.h"#include "stm32f10x_gpio.h"#include "stm32f10x_rcc.h"#include "stdbool.h"#include "stdlib.h"//Макроси #define щоб нам було зручно#define BUZZER_PORT GPIOB //Порт до якого під'єднаний зумер#define BUZZER GPIO_Pin_9 //До якої ноги під'єднаний зумер#define BUTTON_PORT GPIOA //Порт до якого підключені кнопки#define BUTTON_START GPIO_Pin_0 //Кнопка USER на платі STM32VLDiscovery#define BUTTON_1 GPIO_Pin_1 //Кнопка 1#define BUTTON_2 GPIO_Pin_2 //Кнопка 2#define BUTTON_3 GPIO_Pin_3 //Кнопка 3#define BUTTON_4 GPIO_Pin_4 //Кнопка 4#define LED_PORT GPIOC //Порт до якого під'єднані світлодіоди#define LED_4 GPIO_Pin_6 //Світлодіод 4#define LED_3 GPIO_Pin_7 //Світлодіод 3#define LED_BLUE GPIO_Pin_8 //Світлодіод голубий на платі STM32VLDiscovery#define LED_GREEN GPIO_Pin_9 //Світлодіод зелений на платі STM32VLDiscovery#define LED_2 GPIO_Pin_10 //Світлодіод 2#define LED_1 GPIO_Pin_11 //Світлодіод 1#define LED_ALL (LED_BLUE | LED_GREEN | LED_1 | LED_2 | LED_3 | LED_4) //Світлодіоди всі разом//Позначимо тональність звуку#define TON1 600#define TON2 500#define TON3 400#define TON4 300#define F_APB1 24000000 //Частота таймера в герцах#define MAX_LEVEL 100 //Максимальний рівень гри//Оголошення глобальних зміннихint sequence[MAX_LEVEL]; //масив з послідовністю номерів світлодіодів для ігрової ситуаціїint your_sequence[MAX_LEVEL]; //Масив з послідовністю натиснутих клавішint level =1; //Початковий рівень складностіint velocity =1000; //швидкість//Оголошення всих прототипів функцій самі функції розташовані за основною функцією mainvoidGPIO_Init_Game(void); //Функція увімкнення і налаштування периферії яку задіялиvoiddelay_ms(unsignedint delay); //Функція паузи в мілісекундахvoiddelay_us(unsignedint delay); //Функція паузи в мікросекундахvoidBEEP(uint16_t tone, uint16_t time); //Функція відтворення звуку "біп"voidstart(); //Функція запрошення до гри. Блимаємо світлодіодами очікуємо на старт. Генеруємо унікальне число для srandvoidgenerate_sequence(void); //Генеруємо псевдовипадкову послідовність і заповнюємо масив sequence номерами світлодіодів 0-3voidshow_sequence(void); //Показуємо ігрову ситуацію. Почергово засвічуємо світлодіодиvoidget_sequence(void); //Приймаємо послідовність натискання кнопок і перевірка на правильністьvoidright_sequence(void); //Як послідовність вірна, то блимаємо всіма світлодіодами і робимо біпvoidwrong_sequence(void); //Як послідовність не вірна, то декілька раз блимаємо світлодіодами з сиреною вертаємо рівень на 1 і швидкість на 1000
//Головна функція
intmain(void)
{
GPIO_Init_Game(); //ініціалізація всієї периферіїwhile(1)
{
if (level==1) {
generate_sequence(); //генеруємо послідовність і заповнюємо масив номерами світлодіодів 0-3
}
show_sequence(); //відтворюємо послідовність вогниками
get_sequence(); //приймаємо послідовність від кнопок
}
}
//Показуємо ігрову ситуацію. Почергово засвічуємо світлодіодиvoidshow_sequence(){
uint_fast8_t led_lights[]={LED_1,LED_2,LED_3,LED_4}; //Оголошуємо масив з усіма ігровими світлодіодамиuint16_t beep_tone[]={TON1,TON2,TON3,TON4}; //Оголошуємо масив з переліком тону звуку відповідно до кожного світлодіода
GPIO_ResetBits(LED_PORT,LED_ALL); //Скидаємо всі вогникиint i; //Оголошуємо змінну для циклуfor (i=0; i<level; i++){ //Поки не досягнули рівня гри показуємо послідовність вогників
GPIO_SetBits(LED_PORT,led_lights[sequence[i]]); //Берем з масиву номер вогника і засвічуємо його
BEEP(beep_tone[sequence[i]],100); //Створюємо звук який відповідає за конкретний вогник
delay_ms(velocity); //Час який буде світити вогник. Чим більший рівень тим менше часу світитиме
GPIO_ResetBits(LED_PORT,led_lights[sequence[i]]); //Гасимо вогник
delay_ms(200); //Невеличка затримка
}
}
//Приймаємо послідовність натискання кнопок і перевірка на правильністьvoidget_sequence(){
bool flag; //Цей прапор вказує, якщо кнопку натиснулиint i; //Оголошуємо змінну для циклуfor (i =0; i < level; i++) { //Поки не досягнули рівня гри приймаємо послідовність натискань на кнопки
flag=false; //Скидаємо прапорець натискання кнопкиwhile(flag==false){ //Поки прапорець скинуто, йде опитування кнопокif (GPIO_ReadInputDataBit(BUTTON_PORT,BUTTON_1)==SET) { //Якщо натиснули першу кнопку то...
GPIO_SetBits(LED_PORT,LED_1); //Світимо перший вогник
BEEP(TON1,100); //Створюємо звук відповідний до вогника
your_sequence[i]=0; //Записуємо до масиву номер кнопки-1
flag=true; //Встановлюємо прапорець - кнопку вже натиснуто
delay_ms(200); //Невеличка затримкаif (your_sequence[i] != sequence[i]){ //Перевіряємо чи співпадає натиснута кнопка з заданою послідовністю
wrong_sequence(); //Як ні то виходимо з функціїreturn;
}
GPIO_ResetBits(LED_PORT,LED_1); //Як так, то продовжуємо функцію поки цикл
}
//Перевірка натискання другої кнопки, повністю аналогічна першоїif (GPIO_ReadInputDataBit(BUTTON_PORT,BUTTON_2)==SET){
GPIO_SetBits(LED_PORT,LED_2);
BEEP(TON2,100);
your_sequence[i]=1;
flag=true;
delay_ms(200);
if (your_sequence[i] != sequence[i]){
wrong_sequence();
return;
}
GPIO_ResetBits(LED_PORT,LED_2);
}
//Перевірка натискання третьої кнопки, повністю аналогічна першійif (GPIO_ReadInputDataBit(BUTTON_PORT,BUTTON_3)==SET){
GPIO_SetBits(LED_PORT,LED_3);
BEEP(TON3,100);
your_sequence[i]=2;
flag=true;
delay_ms(200);
if (your_sequence[i] != sequence[i]){
wrong_sequence();
return;
}
GPIO_ResetBits(LED_PORT,LED_3);
}
//Перевірка натискання четвертої кнопки, повністю аналогічна першоїif (GPIO_ReadInputDataBit(BUTTON_PORT,BUTTON_4)==SET){
GPIO_SetBits(LED_PORT,LED_4);
BEEP(TON4,100);
your_sequence[i]=3;
flag=true;
delay_ms(200);
if (your_sequence[i] != sequence[i]){
wrong_sequence();
return;
}
GPIO_ResetBits(LED_PORT,LED_4);
}
}
}
right_sequence(); //Як послідовність вірна, викличемо функцію right_sequence
}
//Генеруємо псевдовипадкову послідовність і заповнюємо масив sequence номерами світлодіодів 0-3voidgenerate_sequence(){
start(); //запрошення до гри. генерується число для srand()int i; //Оголошуємо змінну для циклуfor (i =0; i < MAX_LEVEL; i++) { //Заповнюємо масив від 0 до MAX_LEVEL-1
sequence[i]=rand()%4; //числами від 0 до 3
}
}
//Як послідовність не вірна, то декілька раз блимаємо світлодіодами з сиреною, вертаємо рівень на 1 і швидкість на 1000voidwrong_sequence(){
int i; //Оголошуємо змінну для циклуfor (i =0; i <3; i++) { //Блимаємо три рази
GPIO_SetBits(LED_PORT,LED_ALL); //Запалюємо всі вогники що є
BEEP(800,100); //Звук
delay_ms(50); //Невеличка затримка
GPIO_ResetBits(LED_PORT,LED_ALL); //Гасимо всі вогники що є
BEEP(1000,200); //Звук
delay_ms(50); //Невеличка затримка
}
level=1; //Після циклу присвоюємо рівень 1
velocity=1000; //І швидкість 1000
}
//Як послідовність вірна, то "радісно" блимаємо всіма світлодіодами і робимо короткий біпvoidright_sequence(){
GPIO_ResetBits(LED_PORT,LED_ALL);
delay_ms(250);
BEEP(100,200);
GPIO_SetBits(LED_PORT,LED_ALL);
delay_ms(500);
GPIO_ResetBits(LED_PORT,LED_ALL);
delay_ms(500);
if (level<MAX_LEVEL) { //Поки не досягнули максимального рівня
level++; //збільшуємо рівень гри на одиницю
}
velocity -=50; //Швидкість збільшуємо
}
//Функция запрошення до гри і генерація вихідного числа для для функції srand()voidstart() {
uint16_t start_rand=0; //Оголошуємо змінну для вихідного числа послідовності, що генерується функцією rand ()uint_fast8_t splash[]={LED_BLUE,LED_GREEN,LED_1,LED_2,LED_3,LED_4}; //Оголошуємо масив з усіма світлодіодамиuint8_t i=0; //Оголошуємо тимчасову зміннуwhile (GPIO_ReadInputDataBit(BUTTON_PORT,BUTTON_START)==Bit_RESET){ //Поки не натиснуто кнопки Старт виконуємо цикл
LED_PORT ->BSRR = splash[i]; //По черзі запалюємо вогники
delay_ms(70); //Хай вогник посвітить якийсь час
LED_PORT ->BRR = splash[i]; //По черзі гасимо вогники
start_rand++; //Генеруємо унікальне число для генерації унікальної псевдовипадкової послідовностіif ((i<(sizeof(splash)/sizeof(int))-1)) { //Поки не досягнули кінця масиву
i++; //додаємо 1 до i
} else { //інакше
i=0; // i обнуляємо
}
}
//Як кнопку старт натиснули
srand(start_rand); //ініціалізуємо унікальним числом функцію rand//Далі послідовність запалювання вогників і звук для початку гри
GPIO_SetBits(LED_PORT,LED_1);
BEEP(TON1,100);
GPIO_ResetBits(LED_PORT,LED_1);
GPIO_SetBits(LED_PORT,LED_2);
BEEP(TON2,100);
GPIO_ResetBits(LED_PORT,LED_2);
GPIO_SetBits(LED_PORT,LED_3);
BEEP(TON3,100);
GPIO_ResetBits(LED_PORT,LED_3);
GPIO_SetBits(LED_PORT,LED_4);
BEEP(TON4,100);
GPIO_ResetBits(LED_PORT,LED_4);
delay_ms(500);
GPIO_SetBits(LED_PORT,LED_4);
BEEP(TON4,100);
GPIO_ResetBits(LED_PORT,LED_4);
GPIO_SetBits(LED_PORT,LED_3);
BEEP(TON3,100);
GPIO_ResetBits(LED_PORT,LED_3);
GPIO_SetBits(LED_PORT,LED_2);
BEEP(TON2,100);
GPIO_ResetBits(LED_PORT,LED_2);
GPIO_SetBits(LED_PORT,LED_1);
BEEP(TON1,100);
GPIO_ResetBits(LED_PORT,LED_1);
delay_ms(1000);
}
//Функція формування затримки в мілісекундахvoiddelay_ms(unsignedint delay)
{
TIM7->PSC = F_APB1/1000+1; //Встановлюємо подрібнювач
TIM7->ARR = delay; //встановлюємо значення переповнювання таймеру, а також і значення при якому генеруеться подія оновлення
TIM7->EGR |= TIM_EGR_UG; //Генерируемо Подію оновлення для запису даних в регістри PSC і ARR
TIM7->CR1 |= TIM_CR1_CEN|TIM_CR1_OPM; //Запускаемо таймер записом биту CEN і встановлюємо режим Одного проходу встановленням біту OPMwhile ((TIM7->CR1) & (TIM_CR1_CEN!=0)); //Виконуємо цикл поки рахує таймер до нуля
}
//Функція формування затримки в мікросекундахvoiddelay_us(unsignedint delay)
{
TIM7->PSC = F_APB1/1000000+1; ///Встановлюємо подрібнювач
TIM7->ARR = delay; //встановлюємо значення переповнювання таймеру, а також і значення при якому генеруеться подія оновлення
TIM7->EGR |= TIM_EGR_UG; //Генерируемо Подію оновлення для запису даних в регістри PSC і ARR
TIM7->CR1 |= TIM_CR1_CEN|TIM_CR1_OPM; //Запускаемо таймер записом биту CEN і встановлюємо режим Одного проходу встановленням біту OPMwhile ((TIM7->CR1) & (TIM_CR1_CEN!=0)); //Виконуємо цикл поки рахує таймер до нуля
}
//Функція біпvoidBEEP(uint16_t tone, uint16_t time){ //Функція приймає значення тону звука і тривалість звукуuint16_t j;
for (j =0; j < time; ++j) {
BUZZER_PORT ->BSRR = BUZZER;
delay_us(tone);
BUZZER_PORT ->BRR = BUZZER;
delay_us(tone);
}
}
//Функція увімкнення і налаштування периферії яку задіялиvoidGPIO_Init_Game(void)
{
GPIO_InitTypeDef GPIO_InitStruct; //Оголошуємо назву об'єкта структури/*Вмикаємо потрібну периферію і тактуємо її */
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA|RCC_APB2Periph_GPIOC|RCC_APB2Periph_GPIOB, ENABLE);
/*Налаштування порту де кнопки GPIO pin : PA */
GPIO_InitStruct.GPIO_Pin = BUTTON_START|BUTTON_1|BUTTON_2|BUTTON_3|BUTTON_4;
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IN_FLOATING;
GPIO_Init(BUTTON_PORT, &GPIO_InitStruct);
/*Налаштування порту де свілодіоди GPIO pin : PC */
GPIO_InitStruct.GPIO_Pin = LED_ALL;
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_2MHz;
GPIO_Init(LED_PORT, &GPIO_InitStruct);
/*Налаштування порту де зумер GPIO pin : PB */
GPIO_InitStruct.GPIO_Pin = BUZZER;
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_2MHz;
GPIO_Init(BUZZER_PORT, &GPIO_InitStruct);
//Вмикаємо тактування базового таймера 7
RCC->APB1ENR |= RCC_APB1ENR_TIM7EN;
GPIO_ResetBits(LED_PORT,LED_ALL); //гасимо всі вогники світлодіодів
}
Як це працює
Гра дуже проста і в тексті програми по максимуму додав коментарі. Має бути все зрозумілим. В порівнянні з попередньою грою "Хто швидший" нічого нового немає. Як бачимо з програми, тут застосовані ті ж самі функціїdelay_ms(us) і beep. Використовуємо ті самі бібліотеки. А ініціалізацію і налаштування периферії оформили окремою функцією.
В якості вдосконалення гри можна додати семи-сегментний індикатор на два розряди, або індикатор на рідких кристалах LCD, для відтворення на ньому рівня складності гри. Та додати мелодій для правильної комбінації і програшу. З нотами в мене все дуже погано, а ось як будуть побажання підключити індикатор до гри - пишіть в коментарях.
У цій статті ознайомимось з трьома реальними історіями дії надлюдського розуму, розглянемо наш досвід його застосування і сформулюємо три найближчі завдання нашого проекту.
STM32: Саймон каже
Світ:
Якось випадково, коли шукав щось по мікроконтролерам, натрапив на просту і цікаву гру "Simon Says" на платформі ARDUINO. Дуже сподобалась гра. Мінімум деталей, а ігровий процес затягує. Почав шукати в мережі, може є така гра на платформі STM32? Не знайшов абсолютно нічого. Ну що ж, значить робота по портуванню цієї цікавої гри за мною. За основу портування взяв програмний код звідси. З ARDUINO ніколи не мав справу, то ж прийшлось трішки розібратись що до чого, але на загал витратив на порт гри не багато часу. Гра проста. Головне алгоритм! Все інше деталі.
Table of Contents
Попередні статті:
STM32: Саймон каже
Передмова
Необхідні компоненти і деталі
Електрична схема гри
На схемі немає зображення плати розробника STM32VLDiscovery (мікроконтролеру), а вказані назви виводів мікроконтролера з якими потрібно з'єднати елементи схеми.
Зібрана гра на макетній платі:
Алгоритм гри
Програмна реалізація гри
Як це працює
Зверніть увагу
Шукачі перлів: надлюдський розум «Народного Оглядача», його практичні прояви та перспективи