Попередні статті:
Передмова
Якось випадково, коли шукав щось по мікроконтролерам, натрапив на просту і цікаву гру "Simon Says" на платформі ARDUINO. Дуже сподобалась гра. Мінімум деталей, а ігровий процес затягує. Почав шукати в мережі, може є така гра на платформі STM32? Не знайшов абсолютно нічого. Ну що ж, значить робота по портуванню цієї цікавої гри за мною. За основу портування взяв програмний код звідси. З ARDUINO ніколи не мав справу, то ж прийшлось трішки розібратись що до чого, але на загал витратив на порт гри не багато часу. Гра проста. Головне алгоритм, все інше деталі.
Необхідні компоненти і деталі
- Плата розробника STM32VLDISCOVERY - 1 шт
- Контактна макетна плата "BreadBoard"
- З'єднувальні дроти
- USB шнур - 1 шт.
- Зумер "Buzzer" - 1 шт.
- Тактильні мікрокнопки - 4 шт.
- Будь який N-P-N транзистор малої потужності - 1 шт.
- Світлодіоди, бажано різного кольору - 4 шт.
- Резистор 10 кОм - 4 шт.
- Резистор 330 Ом - 4 шт.
Електрична схема гри
Електрична схема гри "Саймон каже" |
Зібрана гра на макетній платі:
Алгоритм гри
При подачі живлення, на макет гри, почергово миготять світлодіоди - запрошення до гри. Натиснувши кнопку старт, кнопка USER на самій платі STM32VLDISCOVERY, гра почергово миготить два рази ігровими світлодіодами зі звуком - сигнал до старту. А потім починається гра. Випадково запалюються світлодіоди зі звуком, з кожним раундом послідовність вогників збільшується. Гра очікує відтворення тієї ж послідовності кнопками, які гравець тисне. Як правильно - рівень збільшується, як ні - гра спочатку.
Програмна реалізація гри
Створюємо новий проект в CooCox IDE. Називаємо його "simon says". Обираємо чип STM32F100RB. Позначаємо в репозиторії бібліотеку GPIO. Переходимо до "main.c". Видаляємо обов'язковий шаблон. Копіюємо до main.c цей текст програми, або завантажуємо файл.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 | //Гра "Саймон каже" для плати розробника 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; //швидкість //Оголошення всих прототипів функцій самі функції розташовані за основною функцією main void GPIO_Init_Game(void); //Функція увімкнення і налаштування периферії яку задіяли void delay_ms(unsigned int delay); //Функція паузи в мілісекундах void delay_us(unsigned int delay); //Функція паузи в мікросекундах void BEEP(uint16_t tone, uint16_t time); //Функція відтворення звуку "біп" void start(); //Функція запрошення до гри. Блимаємо світлодіодами очікуємо на старт. Генеруємо унікальне число для srand void generate_sequence(void); //Генеруємо псевдовипадкову послідовність і заповнюємо масив sequence номерами світлодіодів 0-3 void show_sequence(void); //Показуємо ігрову ситуацію. Почергово засвічуємо світлодіоди void get_sequence(void); //Приймаємо послідовність натискання кнопок і перевірка на правильність void right_sequence(void); //Як послідовність вірна, то блимаємо всіма світлодіодами і робимо біп void wrong_sequence(void); //Як послідовність не вірна, то декілька раз блимаємо світлодіодами з сиреною вертаємо рівень на 1 і швидкість на 1000 int main(void) { GPIO_Init_Game(); //ініціалізація всієї периферії while(1) { if (level==1) { generate_sequence(); //генеруємо послідовність і заповнюємо масив номерами світлодіодів 0-3 } show_sequence(); //відтворюємо послідовність вогниками get_sequence(); //приймаємо послідовність від кнопок } } //Показуємо ігрову ситуацію. Почергово засвічуємо світлодіоди void show_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); //Невеличка затримка } } //Приймаємо послідовність натискання кнопок і перевірка на правильність void get_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-3 void generate_sequence(){ start(); //запрошення до гри. генерується число для srand() int i; //Оголошуємо змінну для циклу for (i = 0; i < MAX_LEVEL; i++) { //Заповнюємо масив від 0 до MAX_LEVEL-1 sequence[i]=rand()%4; //числами від 0 до 3 } } //Як послідовність не вірна, то декілька раз блимаємо світлодіодами з сиреною, вертаємо рівень на 1 і швидкість на 1000 void wrong_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 } //Як послідовність вірна, то "радісно" блимаємо всіма світлодіодами і робимо короткий біп void right_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() void start() { 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); } //Функція формування затримки в мілісекундах void delay_ms(unsigned int 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 і встановлюємо режим Одного проходу встановленням біту OPM while ((TIM7->CR1) & (TIM_CR1_CEN!=0)); //Виконуємо цикл поки рахує таймер до нуля } //Функція формування затримки в мікросекундах void delay_us(unsigned int 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 і встановлюємо режим Одного проходу встановленням біту OPM while ((TIM7->CR1) & (TIM_CR1_CEN!=0)); //Виконуємо цикл поки рахує таймер до нуля } //Функція біп void BEEP(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); } } //Функція увімкнення і налаштування периферії яку задіяли void GPIO_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); //гасимо всі вогники світлодіодів } |
Немає коментарів:
Дописати коментар