КОРЗИНА
магазина
8 (499) 500-14-56 | ПН. - ПТ. 12:00-18:00
ЛЕСНОРЯДСКИЙ ПЕРЕУЛОК, 18С2, БЦ "ДМ-ПРЕСС"

Программирование комплекта для пайки "Тучка"

Общие сведения

В микроконтроллере Тучки уже находится прошивка, обеспечивающая её работу, поэтому выполнение действий, описанных в данной статье не является обязательным.

Тем не менее, данный материал поможет вам, если вы хотите запрограммировать Тучку на выполнение другого алгоритма работы. Кроме того, вы научитесь программировать микроконтроллеры, если никогда ранее этого не делали.

Если вы увлекаетесь электроникой и программированием, то, вероятно, у вас уже есть контроллер Arduino: Arduino Nano, Arduino UNO или любой другой. В этой статье мы будем использовать Arduino для того, чтобы запрограммировать микроконтроллер Attiny2313A, установленный в Тучке. Разумеется, сделать это можно и другими способами, например, используя USBasp программатор, однако, данный способ в этой статье не рассматривается. 

Если вы впервые работаете со средой Arduino IDE, сначала прочитайте нашу инструкцию.

Схема устройства

Схема Тучки приведена на рисунке ниже. Она необходима в том числе для того, чтобы вы могли определить, какие светодиоды к какому выводу подключены. 

Обратите внимание, что физические выводы контроллера на схеме обозначены снаружи, а вывода GPIO, которые указываются при программировании - внутри. Так, например, мы видим, что физически светодиод HL1 подключен ко 2 выводу, но для того, чтобы зажечь его, в коде мы будем обращаться к D0.

(картинка кликабельна)

На рисунке печатной платы вы можете видеть места, на которых находятся светодиоды.

(картинка кликабельна)

Пояснения к схеме и особенности устройства

Обратите внимание, что катоды светодиодов подключены также к контроллеру (а не к GND, как принято в классическом варианте). Такая реализация позволяет увеличить число светодиодов, подключаемых к контроллеру, сохранив при этом возможность отдельного управлять ими. Это влечёт за собой некоторую особенность управления: для включения светодиода на его анод нужно подать высокий логический сигнал, а на катод - низкий.

Рассмотрим на примере включения светодиода HL1. Для его включения необходимо подать логическую единицу на 2 вывод и логический ноль на 15 вывод.

Также для обеспечения стабильной работы от маломощного источника питания рекомендуем на всей схеме единовременно зажигать только один светодиод. Разумеется, программно реализована функция "одновременного" зажигания нескольких (или всех) светодиодов. С функцией управления светодиодами вы можете ознакомиться самостоятельно в скетче ниже.

Включение поддержки ATtiny2313A в Arduino IDE

Для того, чтобы в Arduino IDE можно было работать с микроконтроллером ATtiny2313A необходимо добавить его поддержку. Для этого:

1) Закройте программу Arduino IDE, если она открыта.

2) Скачайте архив с ядром ATtiny с GitHub.

3) Разархивируйте скаченный архив и поместите папку ATTinyCore-master в С://Program Files (x86) / Arduino / hardware или другое место, где у вас установлена Arduino IDE. Если папка hardware отсутствует, создайте её. 

Для того, чтобы загрузить прошивку в контроллер, необходим программатор. Можно использовать либо специально предназначенный для этого программатор, например USBasp, либо использовать любую плату Arduino в качестве программатора. Ниже мы рассмотрим второй способ.

Используем Arduino в качестве программатора

Вы можете использовать плату Arduino из своего любого другого проекта. При этом, после выполнения всех действий данной статьи, вы снова сможете использовать контроллер (разумеется, загрузив туда свой скетч).

Итак, для того, чтобы использовать Arduino в качестве программатора, необходимо загрузить в неё код загрузчика.  

1) Перейдите в Файл -> Примеры -> 11.ArduinoISP -> ArduinoISP

2) Подключите Arduino к компьютеру с помощью провода USB. Ещё раз повторимся: плата Arduino может быть любой, главное указывайте далее в инструкции свою модель. Мы будем использовать Arduino Nano.

3) Выберите используемую вами плату в меню Инструменты -> Плата, а также укажите порт, к которому она подключена. 

4) Загрузите код. Теперь Arduino выполняет роль программатора.

5) Теперь необходимо указать, что в качестве программатора мы используем Arduino. Выберите в Инструменты -> Программатор -> Arduino as ISP.

6) Подключите Тучку к Arduino. Предварительно убедитесь, что выключатель её питания находится в положении OFF

(картинка кликабельна)

ArduinoКолодка ICSP Тучки
120: MISO
+3.3V1: +3V
132: SCK
113: MOSI
104: RES
GND5: GND

7) Откройте скетч, который вы хотите загрузить. В качестве примера мы загрузим стандартный код Тучки (он приведён ниже). 

Для того, чтобы убедиться, что вы успешно загрузили новый код, можете изменить настройки, например изменить длительность грозы (28 строка) или включить по умолчанию звук (34 строка).

8) Теперь необходимо указать контроллер, в который мы будем загружать код. Выбираем Инструменты -> Плата -> ATtiny2313(a).

9) Указываем контроллеру следующие параметры (таблица ниже).

1) ПлатаATTiny2313(a)/4313 (No bootloader)
2) ChipATtiny2313/ATtiny2313A
3) Clock Source (Only set on bootload)1 MHz (internal)
4) ПортПорт, к которому подключен контроллер Arduino
5) ПрограмматорArduino as ISP

Пояснения: в пункте 3 мы установили частоту 1МГц. Такая низкая частота обеспечивает более низкое энергопотребление, это важно, учитывая что Тучка питается от батарейки.

10) Нажимаем кнопку "Загрузить".  Скетч загрузится в Контроллер Тучки.

Теперь вы можете написать свою программу и загрузить её, а Тучка будет её выполнять.

Стандартный код Тучки

Мы приводим его здесь стандартный код Тучки для того, чтобы после ваших экспериментов вы могли загрузить его и вернуть стандартный алгоритм работы устройства. 

В коде программы представлены подробные комментарии. Они помогут вам разобраться в приницпе его работы.

/* Плата:         ATtiny2313(a)
 * Чип:           ATtiny2313/ATtiny2313A
 * Тактирование:  1МГц (internal)
*/
                                                                        //
#define  STATE_RES_EN    1                                              // Состояние устройства - подключение фоторезистора.
#define  STATE_CHECK_RES 2                                              // Состояние устройства - проверка состояния фоторезистора.
#define  STATE_STORM     3                                              // Состояние устройства - Анимация грозы.
#define  STATE_CHECK_KEY 4                                              // Состояние устройства - проверка состояния фоторезистора.
#define  STATE_GAME      5                                              // Состояние устройства - Игра.
#define  STATE_GAME_KEY  6                                              // Состояние устройства - Ожидание отпускания кнопки в игре.
                                                                        //
#include <avr/sleep.h>                                                  //
#include <avr/wdt.h>                                                    //
                                                                        //
//       Определяем номера выводов:                                     //
uint8_t  pin_LED_A[7] = {0,1,4,9,6,7,8};                                // Аноды  светодиодов.
uint8_t  pin_LED_C[3] = {13,12,2};                                      // Катоды светодиодов.
uint8_t  pin_RES_EN   = 3;                                              // Вывод включения фоторезистора.
uint8_t  pin_BEP      = 11;                                             // Вывод зуммера.
uint8_t  pin_RES      = 10;                                             // Вывод фоторезистора.
uint8_t  pin_KEY      = 5;                                              // Вывод кнопки DEMO.
                                                                        //
//       Определяем настраиваемые переменные:                           //
uint16_t tmr_Sleep    = WDTO_2S;                                        // Длительность сна (интервалы до пробуждения): WDTO_15MS / WDTO_30MS / WDTO_60MS / WDTO_120MS / WDTO_250MS / WDTO_500MS / WDTO_1S / WDTO_2S / WDTO_4S / WDTO_8S.
uint16_t tmr_KeyGame  = 5000;                                           // Длительность удержания кнопки до входа в игру.   tmr_KeyBeep > tmr_KeyGame. Игра запустится если кнопка удерживается пока значение счётчика cnt_State не достигнет значения tmr_KeyGame. Чем выше tmr_KeyGame, тем дольше нужно удерживать кнопку.
uint16_t tmr_KeyBeep  = 10000;                                          // Длительность удержания кнопки до вкл/выкл звука. tmr_KeyBeep > tmr_KeyGame. Если при удержании кнопки счётчик cnt_State достигнет значения tmr_KeyBeep, то игра не начнётся, а звук молнии будет включён или отключён.
uint16_t tmr_Storm    = 10000;                                          // Длительность грозы. Гроза будет идти пока значение счётчика cnt_State не достигнет значения tmr_Storm. Чем выше tmr_Storm, тем дольше будет идти гроза.
uint16_t spd_Storm    = 200;                                            // Скорость появления капель дождя. Новые капли появляются когда значение счётчика cnt_State кратно значению spd_Storm. Чем выше spd_Storm, тем медлееннее появляются новые капли.
uint16_t spd_Light    = 50;                                             // Скорость переключения светодиодов молнии. Очередной ряд молнии включается когда значение счётчика cnt_State кратно значению spd_Light. Чем выше spd_Light, тем дольше светится молния.
                                                                        //
//       Определяем переменные не требующие настроек:                   //
bool     arr_LED        [3][7];                                         // Массив состояния всех светодиодов [катод][анод].
bool     flg_BeepEn   = 0;                                              // Флаг разрешающий звук.
uint8_t  val_State    = 0;                                              // Текущее состояние устройства.
uint16_t cnt_State    = 0;                                              // Счетчик длительности текущего состояния.
                                                                        //
//       Объявляем дополнительные функции:                              //
uint16_t fnc_Random     (uint8_t);                                      // Функция генератор псевдослучайных чисел от 0 до указанного аргумента (не включительно).
void     fnc_Beep       (bool);                                         // Функция вывода звукового сигнала (вкл/выкл).
void     fnc_SetLED     (void);                                         // Функция включения светодиодов по состоянию флагов массива arr_LED.
void     fnc_InitSleep  (uint16_t);                                     // Функция конфигурирования режима сна и настройки пробуждения по времени указанному в качестве аргумента функции.
void     fnc_Sleep      (void);                                         // Функция перехода в сон.
                                                                        //
void setup(){                                                           //
//   Отключаем неиспользуемую периферию:                                //
     ACSR |= (1 << ACD);                                                // Отключаем аналоговый компаратор.
                                                                        //
//   Конфигурируем выводы анодов светодиодов:                           //
     for(uint8_t i=0; i<7; i++){                                        //
         pinMode        (pin_LED_A[i], OUTPUT);                         // Конфигурируем вывод анода pin_LED_A[i] как выход.
         digitalWrite   (pin_LED_A[i], LOW   );                         // На аноде должен быть +, а мы ставим 0 (отключаем анод).
     }                                                                  //
                                                                        //
//   Конфигурируем выводы катодов светодиодов:                          //
     for(uint8_t i=0; i<3; i++){                                        //
         pinMode        (pin_LED_C[i], OUTPUT);                         // Конфигурируем вывод катода pin_LED_C[i] как выход.
         digitalWrite   (pin_LED_C[i], HIGH  );                         // На катоде должен быть -, а мы ставим 1 (отключаем катод).
     }                                                                  //
                                                                        //
//   Конфигурируем вывод зуммера:                                       //
     pinMode            (pin_BEP,      OUTPUT);                         // Конфигурируем вывод pin_BEP как выход.
     digitalWrite       (pin_BEP,      LOW   );                         // Устанавливаем на выводе низкий логический уровень.
                                                                        //
//   Конфигурируем вывод включения фоторезистора:                       //
     pinMode            (pin_RES_EN,   OUTPUT);                         // Конфигурируем вывод pin_RES_EN как выход.
     digitalWrite       (pin_RES_EN,   LOW   );                         // Устанавливаем на выводе низкий логический уровень.
                                                                        //
//   Конфигурируем вывод фоторезистора и кнопки:                        //
     pinMode            (pin_RES,      INPUT );                         // Конфигурируем вывод pin_RES как вход.
     pinMode            (pin_KEY,      INPUT_PULLUP );                  // Конфигурируем вывод pin_KEY как вход с подтяжкой.
                                                                        //
//   Уходим в сон для экономии батарейки:                               //
     fnc_InitSleep(tmr_Sleep);                                          // Конфигурируем режим сна, настроив пробуждение от WDT таймера с периодом tmr_Sleep и пробуждение по нажатию кнопки.
     fnc_Sleep();                                                       // Уходим в сон.
}                                                                       //
                                                                        //
void loop(){                                                            //
//   Выполняем действия при каждом проходе loop:                        //
     wdt_reset();                                                       // Сбрасываем сторожевой таймер, который был включён функцией fnc_InitSleep(время).
     cnt_State++;                                                       // Увеличиваем счётчик нахождения в состоянии val_State.
     fnc_SetLED();                                                      // Включаем светодиоды в соответствии со значениями массива arr_LED.
                                                                        //
//   Включаем фоторезистор:                                             //
     if( val_State == STATE_RES_EN ){                                   //
         digitalWrite(pin_RES_EN, 1);                                   // Включаем фоторезистор.
         if( cnt_State>=20 ){                                           // Ждём...
         //  Переходим в состояние проверки фоторезистора:              //
             val_State = STATE_CHECK_RES;                               // Переходим в состояние проверки фоторезистора STATE_CHECK_RES.
             cnt_State = 0;                                             // Сбрасываем счётчик текущего состояния cnt_State.
             return;                                                    // Переходим в начало функции loop.
         }                                                              //
     }                                                                  //
                                                                        //
//   Проверяем состояние фоторезистора:                                 //
     if( val_State == STATE_CHECK_RES ){                                //
         static int  cnt_RES   = 0;                                     // Определяем счётчик состояний фоторезистора. Он будет считать в плюс на свету, в минус в темноте.
         static bool flg_Night = false;                                 // Определяем флаг фиксации ночи.
         if( cnt_State==1 ){ cnt_RES=0; }                               // Сбрасываем счётчик состояний фоторезистора.
         cnt_RES += digitalRead(pin_RES)? 1:-1;                         // Увеличиваем значение счётчика cnt_RES на свету и уменьшаем его в темноте.
         if( cnt_State>=100 ){                                          // Ждём...
             digitalWrite(pin_RES_EN, 0);                               // Отключаем фоторезистор.
         //  Выполняем действия в соответствии с флагом flg_Night:      //
             if( flg_Night ){                                           //
             //  В предыдущий раз была ночь.                            //
                 if( cnt_RES > 95 ){                                    // Если счётчик cnt_RES досчитал от 0 до более 95...
                 //  В этот раз фоторезистор находился на свету.        //
                     flg_Night = false;                                 // Сбрасываем флаг ночи.
                 //  При переходе от ночи к дню, ничего не делаем.      //
                 }                                                      //
             }else{                                                     //
             //  В предыдущий раз был день.                             //
                 if( cnt_RES <-95 ){                                    // Если счётчик cnt_RES досчитал от 0 до менее -95...
                 //  В этот раз фоторезистор находился в темноте.       //
                     flg_Night = true;                                  // Устанавливаем флаг ночи.
                 //  Переходим в состояние грозы:                       //
                     val_State = STATE_STORM;                           // Переходим в состояние грозы STATE_STORM.
                     cnt_State = 0;                                     // Сбрасываем счётчик текущего состояния cnt_State.
                     return;                                            // Переходим в начало функции loop.
                 }                                                      //
             }                                                          //
             fnc_Sleep();                                               // Уходим в сон.
         }                                                              //
     }                                                                  //
                                                                        //
//   Анимируем грозу:                                                   //
     if( val_State == STATE_STORM ){                                    //
     //  Управляем дождём:                                              //
         if( cnt_State%spd_Storm == 0 ){                                // Если счетчик длительности текущего состояния cnt_State кратен скорости появления новых капель дождя spd_Storm...
         //  Гасим все светодиоды дождя:                                //
             memset(arr_LED, 0, 14);                                    // Отключаем 14 светодиодов (на катодах 0...1).
         //  Включаем до 3 светодиодов дождя:                           //
             for(uint8_t i=0; i<3; i++){                                //
                 arr_LED[fnc_Random(2)][fnc_Random(7)]=1;               // Включаем один светодиод на одном из 2 катодов (0...1) и одном из 7 анодов (0...6).
             }                                                          //
         }                                                              //
     //  Управляем молнией:                                             //
         static uint8_t cnt_Light = 0;                                  // Определяем уровень отрисовки молнии.
         if( cnt_State%spd_Light == 0 ){                                // Если счетчик длительности текущего состояния cnt_State кратен скорости переключения светодиодов молнии spd_Light...
         //  Генерируем молнию:                                         //
             if( !fnc_Random(20) && !cnt_Light ){ cnt_Light=1; }        // Запускаем молнию если случайное число от 0 до 20 равно 0 и уровень отрисовки молнии cnt_Light тоже равен 0.
         //  Анимируем молнию:                                          //
             switch( cnt_Light ){                                       // Выполняем действия в соответствии с уровнем отрисовки молнии cnt_Light.
                 case 1:                  arr_LED[2][0]=1; break;       // Включаем  блок светодиодов на 0 аноде.
                 case 3: arr_LED[2][0]=0; arr_LED[2][1]=1; break;       // Отключаем блок светодиодов на 0 аноде и включаем на 1 аноде.
                 case 5: arr_LED[2][1]=0; arr_LED[2][2]=1; break;       // Отключаем блок светодиодов на 1 аноде и включаем на 2 аноде.
                 case 7: arr_LED[2][2]=0; cnt_Light=0;     break;       // Отключаем блок светодиодов на 2 аноде и сбрасываем уровень отрисовки молнии cnt_Light.
             }                                                          //
         //  Выводим гром:                                              //
             fnc_Beep(cnt_Light%2);                                     // Включаем звук при нечетном уровне cnt_Light и отключаем при четном.
         //  Увеличиваем уровень отрисовки молнии:                      //
             if( cnt_Light ){ cnt_Light++; }                            //
         }                                                              //
     //  Отключаем грозу:                                               //
         if( cnt_State>=tmr_Storm ){                                    // Если счетчик длительности текущего состояния cnt_State достиг значения длительности грозы tmr_Storm...
             fnc_Sleep();                                               // Уходим в сон.
         }                                                              //
     }                                                                  //
                                                                        //
//   Проверяем состояние кнопки DEMO:                                   //
     if( val_State == STATE_CHECK_KEY ){                                //
     //  Информируем о возможности начать игру:                         //
         if( cnt_State == tmr_KeyGame ){                                // Если за время удержания кнопки счётчик cnt_State досчитал до значения tmr_KeyGame.
             memset(arr_LED,1,14);                                      // Включаем 14 светодиодов (на катодах 0...1).
         }                                                              //
     //  Информируем о включении/отключении звука:                      //
         if( cnt_State == tmr_KeyBeep ){                                // Если за время удержания кнопки счётчик cnt_State досчитал до значения tmr_KeyBeep.
             memset(arr_LED,0,14);                                      // Отключаем 14 светодиодов (на катодах 0...1).
             flg_BeepEn = !flg_BeepEn;                                  // Вкл/выкл звук.
             fnc_Beep(flg_BeepEn);                                      // Подаём звуковой сигнал.
         }                                                              //
     //  Избавляемся от переполнения счётчика:                          //
         if( cnt_State >= 65000 ){                                      // Если за время удержания кнопки счётчик cnt_State досчитал до значения 65000.
             cnt_State  = 65000;                                        // Запрещаем дальнейшее увеличение счётчика cnt_State.
         }                                                              //
     //  Ждём отпускания кнопки:                                        //
         if( digitalRead(pin_KEY) ){                                    // Если кнопка отпущена...
         //  Переходим в следующее состояние:                           //
             if( cnt_State < tmr_KeyGame ){                             // Если за время удержания кнопки счётчик cnt_State не досчитал до значения tmr_KeyGame.
             //  Включаем грозу STATE_STORM:                            //
                 val_State = STATE_STORM;                               // Включаем грозу STATE_STORM.
                 cnt_State = 0; return;                                 // Сбрасываем счётчик текущего состояния cnt_State и переходим в начало функции loop.
             }                                                          //
             if( cnt_State < tmr_KeyBeep ){                             // Если за время удержания кнопки счётчик cnt_State не досчитал до значения tmr_KeyBeep.
             //  Включаем игру STATE_GAME:                              //
                 val_State = STATE_GAME;                                // Включаем игру STATE_GAME.
                 cnt_State = 0; return;                                 // Сбрасываем счётчик текущего состояния cnt_State и переходим в начало функции loop.
             }                                                          //
             if( cnt_State >= tmr_KeyBeep ){                            // Если за время удержания кнопки счётчик cnt_State досчитал до значения tmr_KeyBeep.
                 fnc_Sleep();                                           // Уходим в сон.
                 cnt_State = 0; return;                                 // Сбрасываем счётчик текущего состояния cnt_State и переходим в начало функции loop.
             }                                                          //
         }                                                              //
     }                                                                  //
                                                                        //
//   Игра:                                                              //
     if( val_State == STATE_GAME ){                                     //
         static uint8_t num1 = 0;                                       // Определяем переменную для хранения 1 числа.
         static uint8_t num2 = 0;                                       // Определяем переменную для хранения 2 числа.
         static uint8_t num3 = 0;                                       // Определяем переменную для хранения предыдущего значения 1 числа.
         static uint8_t num4 = 0;                                       // Определяем переменную для хранения предыдущего значения 2 числа.
     //  Генерируем новые числа:                                        //
         if( cnt_State<=1 ){                                            // Если счетчик длительности досчитал до 3000.
             do{                                                        //
                 num1 = fnc_Random(3);                                  // Генерируем случайное число от 0 до 2.
                 num2 = fnc_Random(3);                                  // Генерируем случайное число от 0 до 2.
             }                                                          //
             while( num1==num3 && num2==num4 );                         // Повторяем генерацию если текущая комбинация (num1, num2) совпала с предыдущей (num3, num4).
             num3 = num1;                                               // Сохраняем значение num1 в num3.
             num4 = num2;                                               // Сохраняем значение num2 в num4.
         //  Включаем светодиоды и отключаем звук:                      //
             fnc_Beep(0);                                               // Отключаем зуммер.
             memset(arr_LED, 0, sizeof(arr_LED));                       // Отключаем все светодиоды.
             if( num1==0 ){arr_LED[0][1]=1;}                            // Включаем угловой светодиод: 0 0 1 0 0 0.
             if( num1==1 ){arr_LED[1][6]=1;}                            // Включаем угловой светодиод: 0 1 0 0 0 0.
             if( num1==2 ){arr_LED[1][4]=1;}                            // Включаем угловой светодиод: 1 0 0 0 0 0.
             if( num2==0 ){arr_LED[0][2]=1;}                            // Включаем угловой светодиод: 0 0 0 1 0 0.
             if( num2==1 ){arr_LED[0][4]=1;}                            // Включаем угловой светодиод: 0 0 0 0 1 0.
             if( num2==2 ){arr_LED[0][5]=1;}                            // Включаем угловой светодиод: 0 0 0 0 0 1.
         }                                                              //
     //  Ждём нажатия на кнопку:                                        //
         if( cnt_State<=2000 ){                                         // На кнопку можно нажать, пока счётчик не досчитает до 2000.
             if( !digitalRead(pin_KEY) ){                               // Если кнопка нажата ...
             //  На кнопку нажали, проверяем правильность нажатия:      //
                 if( num1!=num2 ){ fnc_Beep(1); }                       //    Числа не совпали, а кнопка нажата - это ошибка!.    Включаем звук.
                 else {arr_LED[2][0]=1; arr_LED[2][1]=1; arr_LED[2][2]=1;} // Числа    совпали  и кнопка нажата - это правильно!. Включаем молнию.
             //  Переходим в состояние ожидания отпускания кнопки:      //
                 val_State = STATE_GAME_KEY;                            // Переходим в состояние STATE_GAME_KEY.
                 cnt_State = 0;                                         // Сбрасываем счётчик текущего состояния cnt_State.
                 return;                                                // Переходим в начало функции loop.
             }                                                          //
         }                                                              //
     //  Вышло время отведённое для нажатия на кнопку:                  //
         if( cnt_State>2000 ){                                          // На кнопку можно нажать, пока счётчик не досчитает до 2000.
         //  Проверяем правильность не нажатия на кнопку:               //
             if( num1==num2 ){ fnc_Beep(1); }                           // Числа совпали, а кнопка не нажата - это ошибка!. Включаем звук.
         }                                                              //
     //  Переходим к началу игры:                                       //
         if( cnt_State>=3000 ){ cnt_State=0; }                          //
     }                                                                  //
                                                                        //
//   Ожидание отпускания кнопки в игре:                                 //
     if( val_State == STATE_GAME_KEY ){                                 //
     //  Уходим в сон при длительном удержании кнопки:                  //
         if( cnt_State>tmr_KeyGame ){fnc_Sleep(); cnt_State=0; return;} // Уходим в сон.
     //  Возвращаемся в игру при отпускании кнопки:                     //
         if( cnt_State>1000 ){                                          // Проверяем состояние кнопки не раньше чем счётчик cnt_State досчитает до 1000.
             fnc_Beep(0);                                               // Отключаем зуммер.
             arr_LED[2][0]=0; arr_LED[2][1]=0; arr_LED[2][2]=0;         // Отключаем молнию.
             if( digitalRead(pin_KEY) ){                                // Если кнопка отпущена.
                 val_State=STATE_GAME; cnt_State=0;                     // Переходим в состояние STATE_GAME.
             }                                                          //
         }                                                              //
     }                                                                  //
}                                                                       //
                                                                        //
//       ДОПОЛНИТЕЛЬНЫЕ ФУНКЦИИ:                                        //
                                                                        //
//       Функция генератор псевдослучайных чисел:                       //
uint16_t fnc_Random(uint8_t i){                                         // Функция возвращает число от 0 до i (не включительно).
             static uint16_t val_Random=1;                              // Определяем переменную для хранения числа возвращаемого функцией fnc_Random().
             val_Random = val_Random*345+(millis()>>1);                 //
             return (val_Random%i);                                     // Можно воспользоваться обычной функцией random(), но она займёт более 10% памяти программ.
         }                                                              //
                                                                        //
//       Функция вывода звукового сигнала:                              //
void     fnc_Beep(bool i){                                              // Можно воспользоваться обычной функцией tone(), но она займёт более 10% памяти программ.
             if( i && flg_BeepEn ){analogWrite(pin_BEP, 127);}          // Выводим на вывод pin_BEP ШИМ с 50% заполнением.
             else                 {analogWrite(pin_BEP, 0  );}          // Выводим на вывод pin_BEP ШИМ с  0% заполнением.
         }                                                              //
                                                                        //
//       Функция включения светодиодов по массиву arr_LED:              //
void     fnc_SetLED(void){                                              // Функция не возвращает никаких значений.
             static uint8_t act_A=0;                                    // Номер активного анода  светодиодов.
             static uint8_t act_C=0;                                    // Номер активного катода светодиодов.
         //  Отключаем активный светодиод:                              //
             digitalWrite(pin_LED_A[act_A], 0);                         // На аноде  должен быть +, а мы ставим 0.
             digitalWrite(pin_LED_C[act_C], 1);                         // На катоде должен быть -, а мы ставим 1.
         //  Переходим к следующему светодиоду:                         //
             act_A++;                                                   // Переходим к следующему аноду act_LED_A.
             if( act_A>=7 ){ act_A=0; act_C++; }                        // Если достигнут последний  анод act_LED_A, то переходим к 0  аноду.
             if( act_C>=3 ){ act_C=0; }                                 // Если достигнут последний катод act_LED_C, то переходим к 0 катоду.
         //  Включаем активный светодиод:                               //
             digitalWrite(pin_LED_A[act_A], arr_LED[act_C][act_A]);     // Устанавливаем на аноде act_LED_A уровень из массива arr_LED.
             digitalWrite(pin_LED_C[act_C], 0);                         // На катоде должен быть -, его и ставим.
         }                                                              //
                                                                        //
//       Функция конфигурирования режима сна и сторожевого таймера:     //
void     fnc_InitSleep(uint16_t i){                                     // Функция не возвращает никаких значений.
             cli();                                                     // Запрещаем любые прерывания.
             wdt_reset();                                               // Сбрасываем сторожевой таймер.
             wdt_enable(i);                                             // Активируем сторожевой таймер на срабатывание по времени i.
             WDTCSR |= (1<<WDIE);                                       // Включаем прерывание от WDT таймера.
             GIMSK   = (1<<INT1);                                       // Включаем прерывание INT1 от кнопки.
             MCUCR   = (0<<ISC11)|(0<<ISC10);                           // Определяем тип прерывания INT1: 00-LOW(низкий), 01-CHANGE(изменение), 10-falling(спад), 11-rising(рост).
             sei();                                                     // Разрешаем включенные прерывания.
             set_sleep_mode(SLEEP_MODE_PWR_DOWN);                       // Выбираем режим сна: SLEEP_MODE_IDLE / SLEEP_MODE_PWR_DOWN / SLEEP_MODE_STANDBY.
         }                                                              //
                                                                        //
//       Функция перехода в сон:                                        //
void     fnc_Sleep(void){                                               // Функция не возвращает никаких значений.
             memset(arr_LED, 0, sizeof(arr_LED)); fnc_SetLED();         // Отключаем все светодиоды.
             fnc_Beep(0);                                               // Отключаем звук.
             GIMSK = (1<<INT1);                                         // Включаем прерывание INT1, если оно было отключено в ISR(INT1_vect).
             sleep_mode();                                              // Уходим в спящий режим.
         }                                                              //
                                                                        //
//       Функция обработки прерывания от сторожевого таймера:           //
         ISR(_VECTOR(18)){                                              // В некоторых версиях Arduino IDE нельзя писать: ISR(WDT_vect), ISR(WATCHDOG_vect) и даже ISR(_VECTOR(0x12)) работать не будет!
             WDTCSR |= (1<<WDIE);                                       // Устанавливаем бит WDIE (разрешить прерывания WDT) в регистре WDTCSR, так как он сбрасывается при обработке этого прерывания.
             val_State=STATE_RES_EN;                                    // Переходим в состояние включения резистора STATE_RES_EN.
             cnt_State=0;                                               // Сбрасываем счётчик нахождения устройства в состоянии val_State.
         }                                                              //
                                                                        //
//       Функция обработки прерывания INT1 от кнопки DEMO:              //
         ISR(INT1_vect){                                                //
             GIMSK = (0<<INT1);                                         // Отключаем прерывание INT1. Оно будет включено позже, при погружении в сон.
             val_State=STATE_CHECK_KEY;                                 // Переходим в состояние проверки кнопки STATE_CHECK_KEY.
             cnt_State=0;                                               // Сбрасываем счётчик нахождения устройства в состоянии val_State.
         }                 

Ссылки

  1. Инструкция по работе в среде Arduino;
  2. Программатор USBasp;
  3. Контроллеры Arduino.



Обсуждение

Гарантии и возврат Используя сайт Вы соглашаетесь с условями