Попередні статті:
STM32: Перша програмаПередмова
В нас вже достатньо знань щоб створити просту гру. В процесі творення ознайомимось як підключати і працювати з цифровим сегментним індикатором. Розглянемо що таке динамічна індикація. Познайомимось з таймерами мікроконтролера. Повторимо, як реагувати на натискання кнопок та керувати світлодіодами.
Необхідні компоненти і деталі
Компоненти і деталі гри "Хто швидший" |
- Плата розробника STM32VLDISCOVERY - 1 шт
- Контактна макетна плата "BreadBoard"
- З'єднувальні дроти
- USB шнур - 1 шт.
- Зумер "Buzzer" - 1 шт.
- Тактильні мікрокнопки - 2 шт.
- Будь які N-P-N транзистори малої потужності - 5 шт.
- Семи сегментний індикатор на чотири розряди з загальним катодом E40361 - 1 шт.
- Резистор 10 кОм - 2 шт.
- Резистор 1 кОм - 6 шт.
- Резистор 330 Ом - 7 шт.
Контактна макетна плата. Як видно на світлині, макетна дошка не зовсім фабрична. З-за того, що в платі розробника STM32VLDISCOVERY ніжки 10-15 порту "B" розташовані поперек плати, то прийшлось придбати два окремі модулі контактних макетних плат (breadboard) і наклеїти їх до основи-дошки, за допомоги двошарової клейкої наліпки (скоч). Основа-дошка може бути фанера, гетинакс, текстоліт, або оргскло. Наклеювати треба з встановленою платою розробника в макетні плати, щоб точно витримати відстань між модулями макетних плат. Потім, як виявилось, для зручності монтажу схем, потрібні шини живлення. На світлині це такі вузенькі макетки по краях дошки з червоною і синьою смужками. Але за браком досвіду придбав шини живлення, які по кріпленню не підходили до моїх макетних плат. Тому я просто приліпив їх поруч. Звертайте на це увагу коли будете купувати контактні макетні плати. Ще можна до низу дошки прикрутити гумові ніжки, щоб дошка не ковзала по столу і додати клеми живлення.
Транзистори. Транзистори будь які N-P-N структури малої потужності. В мене валялись без діла багато старих транзисторів КТ315, то я їх і застосував. Можна щось типу 2N2222 чи аналогічні.
Семи-сегментний індикатор. Теж можна будь який такого типу з загальним катодом.
Зумер (buzzer). Такий собі випромінювач звуку. Є такі що пищать коли на них просто подати живлення, а є такі які треба живити певною частотою - подавати почергово високий рівень і низький. Зазвичай гучніше звучать на конкретній звуковій частоті. Зазначено в документації на зумер. Підійдуть і ті, і ті. Треба дотримуватись полярності при підключенні.
Електрична схема гри
Електрична схема гри "Хто швидший" |
На схемі не показана плата розробника STM32VLDiscovery, а вказано з якими однойменними контактами плати розробника треба з'єднати елементи схеми.
Ось таке приблизне розташування елементів схеми має бути на контактній макетній платі.
Розташування елементів на макетній платі |
А так буде виглядати вже зібраний макет відповідно до схеми.
Макет з елементами схеми і монтажем |
Всі з'єднання елементів уважно перевірте перед подачею напруги, щоб не вивести з ладу порт USB комп'ютера, або мікроконтролер.
Алгоритм гри
Алгоритм, або правила гри: Подаємо живлення. Гра очікує старту. Тиснемо кнопку старт. На цифровому індикаторі випадковий час відтворюється ігрова ситуація (хаотично світяться сегменти індикатора). Як тільки ігрова ситуація припиняється, показуємо рахунок гравців, і тоді гравці тиснуть, кожен на свою кнопку. Хто швидше натиснув, тому зараховується бал. Гра йде до 5 балів.
Алгоритм гри "Хто швидший" |
Алгоритм гри якихось додаткових пояснень не потребує. Все зрозуміло з блок-схеми алгоритму.
Програмна реалізація гри
Запускаємо CooCox IDE. Створюємо новий проект і називаємо його "Who Is Faster". Обираємо свій чип STM32F100RB. Обираємо з репозиторію бібліотеку GPIO. Видаляємо шаблон з 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 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 | //Гра "Хто швидший" автор Гончаренко А.В. //Вкладаємо до проекту потрібні файли #include "stm32f10x.h" #include "stm32f10x_gpio.h" #include "stm32f10x_rcc.h" #include "stdlib.h" #include "stdbool.h" //Макроси #define IND_PORT GPIOB //Порт до якого під'єднаний індікатор E40361 #define LED_PORT GPIOC //Порт до якого під'єднані світлодіоди #define BUTTON_PORT GPIOA //Порт до якого під'єднані кнопки #define BUZZER_PORT GPIOC //Порт до якого під'єднаний зумер //Загальні ніжки індикатора - розряди індикатора #define D0 GPIO_Pin_7 #define D1 GPIO_Pin_8 #define D2 GPIO_Pin_9 #define D3 GPIO_Pin_10 //До якої ноги який сегмент під'єднаний #define SEG_A GPIO_Pin_0 #define SEG_B GPIO_Pin_1 #define SEG_C GPIO_Pin_2 #define SEG_D GPIO_Pin_3 #define SEG_E GPIO_Pin_4 #define SEG_F GPIO_Pin_5 #define SEG_G GPIO_Pin_6 //Збираємо цифри з сегментів #define DIG0 (SEG_A | SEG_B | SEG_C | SEG_D | SEG_E | SEG_F) #define DIG1 ( SEG_B | SEG_C ) #define DIG2 ( SEG_A | SEG_B | SEG_G | SEG_E | SEG_D ) #define DIG3 ( SEG_A | SEG_B | SEG_G | SEG_C | SEG_D ) #define DIG4 ( SEG_F | SEG_G | SEG_B | SEG_C) #define DIG5 ( SEG_A | SEG_F | SEG_G | SEG_C | SEG_D ) #define DIG6 ( SEG_A | SEG_C | SEG_D | SEG_E | SEG_F | SEG_G ) #define DIG7 ( SEG_A | SEG_B | SEG_C ) #define DIG8 ( SEG_A | SEG_B | SEG_C | SEG_D | SEG_E | SEG_F | SEG_G) #define DIG9 ( SEG_A | SEG_B | SEG_C | SEG_D | SEG_F | SEG_G) #define DIGP (SEG_F | SEG_E | SEG_A | SEG_B | SEG_G) #define ALL_PINS (DIG8 | D0 | D1 | D2 | D3 ) //Назначаємо на яких ногах, які кнопки #define BUTTON_0 GPIO_Pin_0 #define BUTTON_1 GPIO_Pin_6 #define BUTTON_2 GPIO_Pin_7 #define Match_Drawn (BUTTON_1 | BUTTON_2) //Натисното дві кнопки одночасно //Назначаємо на яких ногах, які свілодіоди #define LED_BLUE GPIO_Pin_8 #define LED_GREEN GPIO_Pin_9 #define LED_ALL (LED_BLUE | LED_GREEN) //До якої ноги під'єднаний зумер #define BUZZER GPIO_Pin_2 //Для зручності надаємо порядковим номерам масиву зрозумілі позначення #define TIRE 10 #define NOP 12 #define P 11 //Частота таймера #define F_APB1 24000000 //Оголошуємо прототипи функцій що є в нашій програмі void gameplay(); void delay_ms(); void delay_us(); void start(); void tablo(); void welcome(); void BEEP(uint16_t tone, uint16_t time); //Головна програма int main(void){ uint8_t counter1=0; //Лічильник натискань першого гравця uint8_t counter2=0; //Лічильник натискань другого гравця uint8_t score=5; //до якого рахунку граємо bool button_flag = false; //прапорець натискання кнопки. false - ще нічого не натиснули, true - якусь кнопку вже натиснуто GPIO_InitTypeDef GPIO_InitStruct; //Оголошуємо структуру яка містить налаштування порту //вимикаємо JTAG (він займає ноги PB3,PB4 - вони потрібні нам) RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE); GPIO_PinRemapConfig(GPIO_Remap_SWJ_JTAGDisable, ENABLE); //налаштовуємо на вихід всі ноги підключенні до індикатору RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE); GPIO_InitStruct.GPIO_Pin = ALL_PINS; //Вказуємо які ноги потрібно налаштувати GPIO_InitStruct.GPIO_Mode = GPIO_Mode_Out_PP; //Налаштовуємо як вихід push-pull GPIO_InitStruct.GPIO_Speed = GPIO_Speed_2MHz; //частота 2МГц GPIO_Init(IND_PORT, &GPIO_InitStruct); //викликаємо функцію налаштування порту //налаштовуємо ноги (PA0, PA7, PA9) з кнопками на вхід RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IN_FLOATING; GPIO_InitStruct.GPIO_Pin = (BUTTON_0 | BUTTON_1 | BUTTON_2); GPIO_Init(BUTTON_PORT, &GPIO_InitStruct);//викликаємо функцію налаштування порту //налаштовуємо ноги з світлодіодами RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC, ENABLE); GPIO_InitStruct.GPIO_Pin = (LED_ALL | BUZZER); GPIO_InitStruct.GPIO_Mode = GPIO_Mode_Out_PP; GPIO_InitStruct.GPIO_Speed = GPIO_Speed_2MHz; GPIO_Init(LED_PORT, &GPIO_InitStruct);//викликаємо функцію налаштування порту //Вмикаємо тактування базового таймера 7 RCC->APB1ENR |= RCC_APB1ENR_TIM7EN; start(); //Запускаємо функцію start - почергове засвічування всіх вогників welcome(); //Запускаємо функцію welcome - запрошення до гри gameplay(); //Запускаємо функцію gameplay - ігрова ситуація while(1) { if ((BUTTON_PORT->IDR & Match_Drawn)==Match_Drawn) { //Натиснули одночасно кнопки? if (!button_flag) { //Як прапорець скинуто то... IND_PORT -> BRR = LED_ALL; //Гасимо всі вогники на індикаторі counter1++; //Додаємо першому гравцю бал counter2++; //Додаємо другому гравцю бал BEEP(600,300); //Робимо біп if ((counter1 == counter2)&(counter1 == score)) { //Якщо перший і другий гравець має однаково балів і досягнули кінця рахунку... while(1){ //Входимо до безкінечного циклу де поблимаємо написом P1P2 - що значить нічья Player1 і Player2 uint8_t j; for (j = 0; j < 200; ++j) { tablo(NOP, NOP, NOP, NOP); } for (j = 0; j < 200; ++j) { tablo(2, P, 1, P); } } } if (counter1 == score) { //Як гравець 1 досягнув переможних балів... goto player1; //Перейдемо до мітки player1 } if (counter2 == score) { //Як гравець 2 досягнув переможних балів... goto player2; //Перейдемо до мітки player2 } LED_PORT -> BSRR = LED_ALL; //світимо світлодіоди обох гравців uint8_t i; for (i = 0; i < 5; ++i) { //Поблимаємо рахунком гравця 1 і гравця 2 uint_fast8_t j; for (j = 0; j < 200; ++j) { tablo(NOP, NOP, NOP, NOP); } for (j = 0; j < 200; ++j) { tablo(counter1, TIRE, TIRE, counter2); } } IND_PORT -> BRR = ALL_PINS; //Гасимо всі вогники на індикаторі gameplay(); //Запускаємо функцію gameplay - ігрова ситуація } button_flag = true; //Встановлюємо прапорець } else if ((BUTTON_PORT -> IDR & BUTTON_1)==BUTTON_1) { //Якщо кнопку 1 натиснули? if (!button_flag) { //і прапорець скинуто counter1++; //Додаємо гравцю 1 один бал BEEP(500,100); //Робимо біп LED_PORT -> BSRR = LED_GREEN; //світимо світлодіод гравця 1 LED_PORT -> BRR = LED_BLUE; //гасимо світлодіод гравця 2 if (counter1 == score) { //Перевіряємо чи не досягнули переможних балів? player1: while(1){ //Якщо так, то до безкінечного циклу де блимаємо рахунком переможця uint8_t j; for (j = 0; j < 200; ++j) { tablo(NOP, NOP, NOP, NOP); } for (j = 0; j < 200; ++j) { tablo(counter1, TIRE, 1, P); } } } uint8_t i; for (i = 0; i < 5; ++i) { //Якщо ні, то блимаємо рахунком гравця 1 п'ять разів uint8_t j; for (j = 0; j < 200; ++j) { tablo(counter2, TIRE, NOP, NOP); } for (j = 0; j < 200; ++j) { tablo(counter2, TIRE, TIRE, counter1); } } IND_PORT -> BRR = ALL_PINS; //Гасимо всі вогники на індикаторі gameplay(); //Запускаємо функцію gameplay - ігрова ситуація } button_flag = true; //Встановлюємо прапорець } else if ((BUTTON_PORT -> IDR & BUTTON_2)==BUTTON_2) { //Якщо кнопку 2 натиснули? if (!button_flag) { //і прапорець скинуто counter2++; //Додаємо гравцю 2 один бал BEEP(500,100); //Робимо біп LED_PORT -> BSRR = LED_BLUE; //світимо світлодіод гравця 2 LED_PORT -> BRR = LED_GREEN; //гасимо світлодіод гравця 1 if (counter2 == score) { //Перевіряємо чи не досягнули переможних балів? player2: while(1){ //Якщо так, то до безкінечного циклу де блимаємо рахунком переможця uint8_t j; for (j = 0; j < 200; ++j) { tablo(NOP, NOP, NOP, NOP); } for (j = 0; j < 200; ++j) { tablo(counter2, TIRE, 2, P); } } } uint_fast8_t i; for (i = 0; i < 5; ++i) { //Якщо ні, то блимаємо рахунком гравця 2 п'ять разів uint_fast8_t j; for (j = 0; j < 200; ++j) { tablo(NOP, NOP, TIRE, counter1); } for (j = 0; j < 200; ++j) { tablo(counter2, TIRE, TIRE, counter1); } } IND_PORT -> BRR = ALL_PINS; //Гасимо всі вогники на індикаторі gameplay(); //Запускаємо функцію gameplay - ігрова ситуація } button_flag = true; //Встановлюємо прапорець } else { //інакше як нічого не натиснуто... button_flag = false; //скидаємо прапорець } tablo(counter2, TIRE, TIRE, counter1); //світиться поточний рахунок в очікуванні натискання кнопки } } //Функція формування затримки в мілісекундах 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 welcome() { uint16_t start_rand; //Оголошуємо змінну для вихідного числа послідовності, що генерується функцією rand () //Гасимо всі вогники світлодіоди та сегменти індикатора LED_PORT ->BRR = LED_ALL; IND_PORT ->BRR = ALL_PINS; LED_PORT ->BSRR = (LED_BLUE); //Засвічуємо потрібні вогники while ((BUTTON_PORT -> IDR & BUTTON_0)!=BUTTON_0){ //Поки не натиснули кнопку 0 (Start) - виконуємо цикл delay_ms(500); //Затримка на 500 мілісекунд (пів секунди) LED_PORT->ODR^=LED_ALL; //Інвертуємо вогники гравців start_rand++; //Додаємо одиницю до змінної start_rand } //Якщо кнопку start натиснуто LED_PORT ->BRR = LED_ALL; //вимикаємо всі світлодіоди IND_PORT ->BRR = ALL_PINS; //вимикаємо індикатор BEEP(400,200); //Робимо біп srand(start_rand); //функція srand () використовується, щоб при різних запусках програма могла використовувати різні послідовності псевдовипадкових чисел } //Функция початкової перевірки всіх світлодиодів void start() { uint_fast8_t segment[]={SEG_A,SEG_B,SEG_C,SEG_D,SEG_E,SEG_F,SEG_G}; //Оголошуємо масив з усіма сегментами індикатора uint_fast8_t digit[]={D0,D1,D2,D3}; //Оголошуємо масив з усіма розрядами індикатора uint_fast8_t splash[]={LED_BLUE,LED_GREEN}; //Оголошуємо масив з усіма світлодіодами uint8_t i; //змінна для циклу LED_PORT ->BRR = LED_ALL; //вимикаємо всі світлодіоди IND_PORT ->BRR = ALL_PINS; //вимикаємо індикатор for (i=0; i<=(sizeof(splash)/sizeof(int))-1; i++) { //цикл який послідовно запалює і гасить світлодіоди LED_PORT ->BSRR = splash[i]; delay_ms(50); LED_PORT ->BRR = splash[i]; delay_ms(50); } uint8_t d; //змінна для циклу uint8_t s; //змінна для циклу for (s=0; s<=(sizeof(segment)/sizeof(int))-1; s++){ //цикл який послідовно запалює і гасить сегменти індикатора по всіх розрядах for (d=0; d<=(sizeof(digit)/sizeof(int))-1; d++){ IND_PORT ->BSRR = (digit[d] | segment[s]); delay_ms(50); IND_PORT ->BRR = (digit[d] | segment[s]); } } BEEP(300,150); //Робимо біп } //Функція випадкового засвічування сегментів випадковий час void gameplay() { uint_fast8_t segment[]={SEG_A,SEG_B,SEG_C,SEG_D,SEG_E,SEG_F,SEG_G}; uint_fast8_t digit[]={D0,D1,D2,D3}; uint16_t i; uint16_t play = rand()%500+10; for (i = 0; i < play; ++i) { IND_PORT ->BSRR = (digit[rand()%(sizeof(digit)/sizeof(int))] | segment[rand()%(sizeof(segment)/sizeof(int))]); delay_ms(50); IND_PORT ->BRR = ALL_PINS; } } //Функція біп void BEEP(uint16_t tone, uint16_t time){ //Функція приймає значення тону звука і тривалість звуку int j; for (j = 0; j < time; ++j) { BUZZER_PORT ->BSRR = BUZZER; delay_us(tone); BUZZER_PORT ->BRR = BUZZER; delay_us(tone); } } //Функция виставляє в порт потрібну цифру void digit_to_port (uint8_t digit) { uint8_t digitsp[]={DIG0,DIG1,DIG2,DIG3,DIG4,DIG5,DIG6,DIG7,DIG8,DIG9,SEG_G,DIGP,~DIG8}; //оголошуємо масив з можливими варіантами символами на індикатор IND_PORT -> ODR &= ~DIG8; //Вимикаємо всі сегменти IND_PORT -> ODR |= digitsp[digit]; //Запалюємо потрібні } //Функція відображення інформації на індикатор void tablo(int_fast8_t SEG_1, int_fast8_t SEG_2, int_fast8_t SEG_3, int_fast8_t SEG_4){ IND_PORT -> BRR = (D0|D1|D2|D3);//Вимикаємо всі розряди IND_PORT-> BSRR = D0;//Вмикаємо нульовий розряд індикатора digit_to_port(SEG_1);//Виводимо цифру у нульовий розряд delay_us(500);//Невеличка затримка. Хай цифра світиться якийсь час IND_PORT -> BRR = (D0|D1|D2|D3);//Вимикаємо всі розряди IND_PORT-> BSRR = D1;//Вмикаємо перший розряд індикатора digit_to_port(SEG_2);//Виводимо цифру у перший розряд delay_us(500);//Невеличка затримка. Хай цифра світиться якийсь час IND_PORT -> BRR = (D0|D1|D2|D3);//Вимикаємо всі розряди IND_PORT-> BSRR = D2;//Вмикаємо другий розряд індикатора digit_to_port(SEG_3);//Виводимо цифру у другий розряд delay_us(500);//Невеличка затримка. Хай цифра світиться якийсь час IND_PORT -> BRR = (D0|D1|D2|D3);//Вимикаємо всі розряди IND_PORT-> BSRR = D3;//Вмикаємо третій розряд індикатора digit_to_port(SEG_4);//Виводимо цифру у третій розряд delay_us(500);//Невеличка затримка. Хай цифра світиться якийсь час } |
- Як працює динамічна індикація та які види бувають можна ознайомитись тут.
- Принцип роботи і приклад програмної реалізації сегментного індикатора взяв з цієї статті: "Простой счётчик на STM32".
- Паузи можна реалізовувати пустим циклом, але тоді напевно ми не можемо знати скільки саме часу триває пауза. За допомоги таймера ми можемо задавати паузи з високою точністю. Реалізацію пауз за допомоги базових таймерів запозичив звідси.
Як це працює
Щоб не нагромаджувати багато тексту, програму максимально доповнив коментарями. З них має бути зрозумілою робота програми. Розглянемо тільки ті моменти які ми не розглядали в попередній статті "Перша програма".
#include "stdlib.h"
Семи-сегментний індикатор, який підключений до порту "B" мікроконтролера, займає з 0 по 10 ніжку порту. За замовчуванням при подачі напруги на мікроконтролер ніжки PA15, PB3 і PB4 зайняті інтерфейсом JTAG. Але ми його не використовуємо, він навіть не запаяний на платі STM32VLDiscovery, а використовуємо для прошивання і налагодження інтерфейс SWD. Тому потрібно звільнити ці виводи під наші потреби. Для цього додали до ініціалізації і налаштування периферії мікроконтролера ці два рядки:
//вимикаємо JTAG (він займає ноги PB3,PB4 - вони потрібні нам)
RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE);
GPIO_PinRemapConfig(GPIO_Remap_SWJ_JTAGDisable, ENABLE);
Ще нам потрібен один з базових таймерів мікроконтролера. На його основі організуємо паузи в мікросекундах і мілісекундах. Дивимось на малюнок 6, блок-схеми STM32F100RB сторінка 8 документації на плату STM32VLDISCOVERY:
Блок схема STM32F100RB (малюнок 6, сторінка 8 STM32VLDISCOVERY) |
//Вмикаємо тактування базового таймера 7
RCC->APB1ENR |= RCC_APB1ENR_TIM7EN;
//Функція формування затримки в мілісекундах 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)); //Виконуємо цикл поки рахує таймер до нуля }
Можна застосувати для роботи з таймерами бібліотеку stm32f10x_tim.h, але в мене так і не вийшло запустити таймер за допомоги бібліотеки. Мабуть не дуже й старався розібратись, бо через регістри все просто і зрозуміло.
Як хтось поділиться робочим варіантом функції delay_ms з використанням бібліотеки TIM в коментарях, то доповню статтю з посиланням на автора.
Все про базові таймери і їх регістри можна переглянути в довіднику на наш чип сторінка 440. Розширений довідник на наш чип можна знайти по пошуку, або завантажити за ланкою.
Для звуку оформили окрему функцію BEEP, яка приймає тон звуку і тривалість звуку в мікросекундах. Звук створюється просто. Як на цифровий вихід швидко (в межах звукової частоти) подавати почергово 0 і 1 то на виході утвориться генерація сигналу. Який ми посилили транзисторним ключем, що живиться від 5В, а цифровий вихід логічної одиниці мікроконтролера має 3.3В. Навантаженням транзисторного ключа є наш "Зуммер" (Buzzer). Дивись схему гри. Спробуйте різні значеннями тону і тривалості звуку.Далі все за алгоритмом гри.
В програмі зустрічається оператор безумовного переходу goto. Є така "традиція" в програмерських колах, що використовувати цей оператор нізащо не можна, дурний тон і таке інше. Я не сноб, по потребі використовую. Не зловживаю. В випадку нашої програми це нормальний хід. Докладніше, про використовувати оператор goto чи ні, в хорошій статті з цього приводу: "Запретный плод GOTO сладок (версия для микроконтроллеров)!"
Ось так гра має працювати:
Гру ще можна вдосконалити. Ввести зняття балів за фальш-старт. Додати мелодію для старту і перемоги.
Як буде потреба щось пояснити детальніше повідомте мене в коментарях до статті.В наступній статті порт гри "Саймон каже" з платформи ARDUINO на STM32.
Немає коментарів:
Дописати коментар