10.1: Вступ
- Page ID
- 30591
У цій вправі ми об'єднаємо обробку цифрового входу та виходу разом із функціями синхронізації та генерацією випадкових чисел, щоб створити просту гру. Суть гри полягатиме в тому, щоб перевірити час реакції гравця (технічно ми будемо вимірювати час відгуку, який є сумою часу реакції плюс отриманий час руху). Замість того, щоб просто дивитися на код, ця вправа також дозволить нам зробити крок назад і подивитися на дизайн системи та прості проблеми користувальницького інтерфейсу.
Гра дасть гравцеві п'ять спроб досягти найкращого часу реакції. Кожна спроба буде складатися з активації червоного попереджувального світла як засобу сказати гравцеві підготуватися. Потім друге зелене світло (тобто «йти») буде активовано між однією і десятьма секундами пізніше, точний час є випадковим між спробами. Як тільки загориться зелене світло, гравцеві потрібно натиснути перемикач. Проміжок часу між увімкненням зеленого світла та активацією перемикача відповіді - це час реакції гравця. Гравцеві повідомлять це значення і в кінці п'яти спроб відобразиться найкращий результат. Гра також повинна оберігатися від обману. Тобто він повинен заборонити попадання, яке просто передбачає стрільбу зеленого світла, іншими словами, «стрибки пістолета».
Перш ніж розглядати апаратне та програмне забезпечення, нам потрібно мати деяке уявлення про те, що ми вимірюємо. Загалом, наскільки швидким є час реакції людини? Виявляється, це частково функція подразника (чутні сигнали швидше обробляються людським мозком, ніж зорові сигнали). Типовий час відгуку становить близько 160 мілісекунд для слухового стимулу та 190 мілісекунд для візуального стимулу, хоча професійні спортсмени, такі як олімпійські спринтери, можуть іноді досягти значення лише 2/3rds цього 1. Очевидно, що тоді наше обладнання та програмне забезпечення повинні бути набагато швидшими, якщо ми хочемо досягти гідного рівня точності.
Інтерфейс користувача складається з чотирьох речей: червоне світло, зелене світло, перемикач відповіді гравця і деякі засоби виведення тексту. Для простоти і зручності створення прототипів ми будемо використовувати послідовний монітор для виведення тексту. Якби це стало виробничим елементом, монітор можна було б замінити спеціальним РК-дисплеєм. Що стосується вогнів, світлодіоди здаються очевидним вибором, можливо, через їх знайомство та особисту безпосередність гри один на один (якщо кілька людей відповіли синхронно, нам може знадобитися набагато більше або яскравіше джерело світла, ніж звичайний світлодіод). Існує ще один пункт, який слід враховувати, і це швидкість включення світла. Світлодіоди включаються за частку мілісекунди, тоді як лампи розжарювання можуть не досягати повної яскравості протягом десятків або навіть сотень мілісекунд. Оскільки ми могли б очікувати часу відгуку в області 200 мілісекунд, лампа розжарювання може бути недостатньо швидкою. Тобто, тимчасова затримка, необхідна для досягнення повної яскравості, може перекосити результати, сповільнюючи очевидний (виміряний) час відгуку гравця. Отже, здається, що прості світлодіоди були б хорошим вибором тут.
Заключний пункт - перемикач гравця. Гравець може вдарити перемикач досить сильно, тому що вони захочуть перемістити руку якомога швидше, щоб мінімізувати час руху. Отже, вимикач повинен бути досить міцним. Він також повинен представляти пристойний розмір цілі для гравця, тобто той, який є достатньо великим, щоб гравцеві також не довелося турбуватися про те, щоб зробити дуже точний рух. Хоча проста миттєва кнопка контакту може здатися гідним вибором, FSR може виявитися краще. FSR не містить рухомих частин і забезпечує велику цільову площу. Він також реагує майже миттєво і не страждає від перемикання відмов. Взаємозв'язок теж простий: ми можемо просто підключити його між вхідним штифтом і землею, і включити внутрішнє підтягування. Зазвичай FSR є дуже високим опором, але при натисканні значення падає до, можливо, 100 або близько того Ом. Це було б послідовно з підтягуванням, який підключений до джерела живлення. З FSR недоторканим, правило подільника напруги вказує на те, що напруга штифта буде плавати високо. При натисканні FSR напруга буде занурюватися до логічного низького рівня. Єдиним мінусом FSR є відсутність фізичного зворотного зв'язку. Багато перемикачі видають звук клацання при включенні. Це забезпечує слуховий зворотний зв'язок з гравцем, щоб вони знали, що їх страйк зареєстрований. Ми можемо виконати деякі відгуки гравців, запалюючи один або обидва світлодіоди в момент відчуття страйку.
Тепер ми маємо уявлення про потенційні вимоги до обладнання. А як щодо програмного забезпечення? Очевидно, що раніше розглянуті методи цифрового введення і виведення можуть бути використані для зчитування і управління плеєром перемикача і світлодіодів. Функція delay () може використовуватися для синхронізації світлодіодних спалахів і основної затримки «йти». Однак потрібні два нові елементи: якийсь спосіб генерації випадкових значень (у нашому випадку від 1000 мілісекунд до 10 000 мілісекунд) та якийсь метод вимірювання часу між двома подіями. Розглянемо спочатку випадкове число.
Як правило, цифрові обчислювальні пристрої не добре генерують справді випадкові значення, оскільки вони є детермінованими машинами. Насправді, ми намагаємося «спроектувати» всі потенційні випадковості, тому що нам потрібна дуже передбачувана робота. Воістину випадкові значення жодним чином не співвідносяться між собою. Тобто немає нічого про рядок випадкових значень, яка дозволить визначити наступне значення в послідовності. Виявляється, однак, що ми можемо створювати псевдовипадкові послідовності без особливих зусиль. Псевдовипадкова послідовність видається випадковою, але якщо ви виконуєте послідовність досить довго, вона повториться. Псевдовипадкові послідовності можуть бути недостатньо випадковими для належних наукових досліджень, але їх достатньо для чогось подібного до нашої гри. Хоча процес виведення псевдовипадкових чисел виходить за рамки цієї вправи, ми можемо сказати, що процес передбачає деяку досить рудиментарну обробку, таку як зсув бітів, на початкове значення, яке називається насінням. Результат цього обчислення потім використовується як вхідні дані для обчислення наступного значення тощо.
Бібліотека Arduino включає функцію random (), яка поверне псевдовипадкове довге ціле значення. Функція також може бути викликана з верхньою та нижньою межами для обмеження повернених значень, як у випадкових (30,200). Сама послідовність ініціюється за допомогою функції randomSeed (), яка приймає довгий цілий аргумент. Іноді корисним ефектом цього є те, що якщо значення насіння однакове для наступних запусків програми, отримана псевдовипадкова послідовність буде повторюватися точно. Це дозволяє дещо систематично налагоджувати нібито випадкову функцію. Але це викликає проблему, а саме, як отримати випадкове число для насіння для початку? Хоча можна запитати у користувача значення насіння, це відкриває двері для можливого обману. Кращим способом є використання шумової напруги. За своєю природою шум буває випадковим. Якщо ми подивимося на аналоговий вхідний штифт, який ні до чого не підключений, ми побачимо невелике напруга шуму. Ми можемо оцифрувати це справді випадкове значення за допомогою функції analoGread () і використовувати її для насіння. Ця напруга не зміниться в дуже великому діапазоні, але воно змінить достатню кількість, щоб наша псевдовипадкова послідовність дійсно виглядала дуже випадковою.
Наступний код показує, як працює система випадкових чисел. Введіть код, скомпілюйте і перенесіть його, а потім запустіть його. Спочатку він роздрукує напругу шуму, а потім роздрукує випадкові значення між 50 і 1000. Після того, як ви побачите достатню кількість значень, натисніть кнопку скидання на платі, щоб перезапустити код. Виконайте це кілька разів. Ці перезавантаження повинні давати різні послідовності. Час від часу ви можете отримати однакову напругу шуму. Якщо ви бачите це, зверніть увагу, що псевдовипадкова послідовність чисел буде повторюватися точно.
void setup()
{
long i;
Serial.begin(9600);
i=analogRead(0); // any analog channel will do
randomSeed(i);
Serial.print(“Noise voltage: ”);
Serial.println(i);
}
void loop()
{
long a;
a = random(50,1000);
Serial.println(a);
delay(2000);
}
Ця техніка повинна ідеально працювати для нашого застосування. Все, що нам потрібно зробити, це обмежити випадкову функцію до 1000 до 10,000, тому ми можемо використовувати значення безпосередньо як мілісекундний аргумент для виклику delay (), який використовується для рандомізації освітлення зеленого світлодіода «йти».
Друга частина, яка нам потрібна, передбачає вимірювання затримки часу між освітленням світлодіода «йти» і плеєром, що вражає FSR. Зазвичай, якщо ви робили це з нуля, вам доведеться ініціалізувати один з таймерів ATMega 328P і перевірити його вміст, коли це необхідно. Події синхронізації є загальною потребою у вбудованому світі, тому процедура ініціалізації Arduino встановлює таймер для вас. У той момент, коли ви вмикаєте або скидаєте плату, цей внутрішній таймер починає збільшувати регістр, який ініціалізується на нулі. Регістр збільшується на одиницю кожну мілісекунду. Цей таймер продовжує працювати незалежно від основного коду, який працює (звичайно, якщо основний код не перепрограмує таймер). Отже, значення регістра вказує, скільки мілісекунд пройшло з моменту включення або скидання плати. Вміст цього реєстру можна прочитати за допомогою функції millis (). Тобто millis () повертає кількість мілісекунд, що відбулися з моменту останнього скидання/включення живлення. Приблизно через 50 днів цей реєстр переповнеться і підрахунок почнеться знову. Сумнівно, що будь-яка людина захоче грати в цю гру, що довго безперервно, так що це не повинно бути проблемою. Наступний фрагмент коду зробить те, що нам потрібно:
starttime = millis(); // Flash “go” LED // wait for response from player pressing FSR, looking at input port pin finishtime = millis(); responsetime = finishtime - starttime;
Отже, тепер ми можемо придумати псевдокод для циклу частини гри:
1. Initialize best response time as a very high value (long int)
2. Tell user to start game by hitting FSR pad
3. Wait for the player’s start response of pressing the FSR
4. Start a loop for five tries, for each try:
a. Generate a random number between 1000 and 10,000
b. Wait a second or two to give the user a moment to get ready
c. Flash red LED one or more times quickly to indicate the start
of this try
d. Delay the random amount of milliseconds from 4.a
e. Record start time millis
f. Flash green “go” LED quickly
g. Wait for player’s response of pressing FSR
h. Record finish time and calculate response time millis
j. Flash both red and green LEDs for visual feedback
k. If response time is less than 100 msec, declare a cheat and
reset response time to a high penalty value
l. Print out the response time
m. Compare response time to best time so far, resetting best to
this value if this value is smaller (i.e., better)
(Loop complete)
5. Print out the best value
6. Wait a moment before starting next game
Деякі з цих кроків можуть потребувати подальшого уточнення або коригування. У кроці 4.k оголошується «чит», якщо час відгуку менше 100 мілісекунд. Це, ймовірно, щедре, враховуючи статистику, зазначену раніше, і може бути скоригована на більш високе значення. Значення штрафу тут може бути таким же, як і значення ініціалізації, можливо, 10 000 або більше мілісекунд. Зрештою, якщо нормальній людині потрібно більше 10 секунд, щоб відповісти на стимул, можна припустити, що вони нетривалі, легко плутаються або повинні припинити текстові повідомлення так багато.
Ще одним важливим пунктом є код для кроків 3 і 4.g. По суті, код потрібно «дочекатися» удару гравця по FSR. Якщо FSR підключений до вхідного штифта, використовуючи внутрішній підтягуючий резистор, натискання FSR призведе до того, що цифровий вхід на цьому контакті перейде від високого до низького. (Пам'ятайте, натискання FSR створює низький опір, і FSR в основному послідовно з підтягуючим резистором. Тому правило дільника напруги показує, що напруга між двома елементами падає до дуже низького значення, або логічного низького.) Отже, код може «зайнято очікування» на цьому контакті, вириваючись, коли штифт йде низьким. Ми могли б підключити FSR до Arduino контактний 8, який є бітом 0 порту B. Крім того, ми можемо уникнути жорсткого кодування це за допомогою #define:
#define FSRMASK 0x01
Отже, необхідний бітовий тестовий код виглядає так:
while( PINB & FSRMASK ); // DON’T use PORTB!!
Пам'ятайте, для введення ми дивимося на PinX, а для виведення використовуємо PortX. Таким чином, цей цикл продовжує працювати до тих пір, поки вхідний біт FSR високий. Як тільки гравець штовхає на FSR з розумною силою, штифт йде низько і петля закінчується.
Ми можемо впорядкувати наш код трохи більше щодо кроків 4.c, 4.f та 4.h, які спалахують світлодіод. Ми можемо підключити зелені та червоні світлодіоди до штифтів Arduino 6 та 7 (порт D.6 та D.7) та використовувати #define s разом із функцією, яка швидко вмикає та вимикає світлодіод:
// At D.6
#define GREENLED 0x40
// At D.7
#define REDLED 0x80
// on/off time in msec
#define LEDTIME 30
void FlashLED( int mask )
{
PORTD |= mask; // turn on LED
delay(LEDTIME);
PORTD &= (~mask); // turn off LED
delay(LEDTIME);
}
Ця функція буде викликана так для одного швидкого спалаху:
FlashLED(GREENLED);
Він також може бути поміщений в цикл для серії швидких спалахів, і ви можете побітове АБО значення маски, щоб спалахнути два світлодіоди одночасно.
Останній пункт, який викликає занепокоєння, - крок 6, «Зачекайте хвилинку перед початком наступної гри». Чому б ми включали це? Причина не очевидна. Пам'ятайте, що мікроконтролер буде обробляти все в дуже короткому порядку, можливо, лише кілька мілісекунд пройде між часом, коли гравець потрапляє на FSR, друкуються фінальні значення і функція loop () завершується, тільки щоб знову викликати невидимий main (). Після повторного входу в цикл () код повідомляє гравцеві натиснути FSR, щоб розпочати наступну гру, а потім чекає на біт FSR. За цей короткий проміжок часу гравець не мав шансу підняти палець. Іншими словами, біт FSR все ще низький, тому цикл припиняється і пробний виконується негайно! Щоб запобігти цьому, ми можемо або ввести невелику затримку, оскільки середня людина не буде продовжувати натискати FSR після запису часу відгуку, або ми можемо використовувати окрему FSR для початку гри. Перше здається набагато більш прямолінійним.
На основі попередніх фрагментів коду код установки може виглядати приблизно так:
// Green LED at D.6, red at D.7, FSR at B.0, flash time in msec
#define GREENLED 0x40
#define REDLED 0x80
#define FSRMASK 0x01
#define LEDTIME 30
void setup()
{
Serial.begin(9600);
// set Arduino pins 6 & 7 for output to LEDs
DDRD |= (GREENLED | REDLED);
// Arduino pin 8 for input from FSR
DDRB &= (~FSRMASK); // initialize the pin as an input.
PORTB |= FSRMASK; // enable pull-up
// get seed value for random, noise voltage at pin A0
randomSeed(analogRead( 0 ));
}
