Ардуинщик-лентяй, техноманьяк
16,0
рейтинг
27 августа 2014 в 21:58

Музыкальный дверной звонок в стиле Star Wars на Arduino

Всем привет, не так давно я рассказал, да и показал на видео, как можно прошить Attiny13 при помощи Arduino, а теперь покажу практическое применение этому.

Скажу сразу, фьюзы, прошивку в виде hex-файла, код на Си, скетч для Arduino IDE, файлы для Proteus 7 можно скачать в конце статьи.

Ну, что уж тянуть, покажу как работает:



Давно хотел себе такой музыкальный дверной звонок, чтобы при нажатии на кнопку играл марш империи(Imperial March or Darth Vader's Theme) из «Звёздных войн», очень нравится эта мелодия.


Как видно из названия видео выше, главный компонент устройства — микроконтроллер Attiny13, его применение сделало возможным сделать размеры платы в несколько раз меньше по размерах чем спичечной коробок:

Размеры платы


А вот уже всё подключённое:




Диапазон питания примерно от 2 В до 6 В, то есть, от двух до четырех батареек формата AA, хотя в идеале бы обеспечить напряжение 3-4 В, то есть для этих целей идеально подходит литий ионный аккумулятор от мобильника или же аккумулятор формата 18650, правда желательно чтобы он был с защитой, так как я пока не реализовал никакой защиты от глубокого разряда.

Ну что же, скажу пару слов по схемотехнике:

Схема выглядит вот так(файлы Proteus прилагаются в конце статьи, можно даже ничего не собирать на макетке):

Нарисована в Proteus'е
image


Так как микроконтроллер не может самостоятельно вытянуть достаточную громкость, для того чтобы было слышно звонок по всему доме, я добавил NPN транзистор 2N3904, довольно таки распространенный транзистор, в принципе может подойти любой транзистор который потянет динамик, в моём случае транзистор рассчитан на 100 мА.
При использовании 3-х батареек формата AA транзистор немножко грелся, при использовании двух — громкость осталась на приличном уровне но уже транзистор был слегка тёплым.
Резистор R2 — стандартная обвязка для микроконтроллера, служит защитой от случайных перезагрузок микроконтроллера, в принципе работать должно и без него, резистор R1 служит для ограничения тока на базе транзистора.
На фото видно ещё защитный диод, защищает он от невнимательности, а именно от переполюсовки, в моём случае, переполюсовка очень быстро выведет микроконтроллер из строя, причём вполне возможны как пиротехнические так и звуковые эффекты. Кстати, на схеме ниже я забыл указать его, подойдёт любой диод который рассчитан на напряжение от 10 В и ток от 200 мА, ставиться последовательно входу платы, или по минусу или по плюсу, у меня по минусу.
Динамик от старого Dial up модема Zyxel, кстати, про Dial up модемы этой фирмы есть один известный анекдот:

Анекдот
Сидят два хакера, и в комнату заходит кот. Один хакер спрашивает:
— Твой кот?
— Да, мой. Зухель зовут!
— Почему Зухель?
— Вот смотри. Берет веник, тычет им в кота и говорит: «Зухель, коннект!!!» Кот:
— Пшшшшшшшшшшшшшш!



Код, скажу сразу, нагуглил, точней нашёл на просторах ютуба, вот собственно само видео:



Под видео есть ссылка на код, вот он:

Код из описания к видео
#include <avr/pgmspace.h>


// by @tartakynov
// programmed for ATtiny13 in Arduino IDE using core13 http://sourceforge.net/projects/ard-core13/

#define PIN_LED 1
#define PIN_BUZZER 0
#define COUNT_NOTES 39

word frequences[COUNT_NOTES] PROGMEM = { 
                                392, 392, 392, 311, 466, 392, 311, 466, 392, 
                                587, 587, 587, 622, 466, 369, 311, 466, 392, 
                                784, 392, 392, 784, 739, 698, 659, 622, 659, 
                                415, 554, 523, 493, 466, 440, 466, 
                                311, 369, 311, 466, 392 };
word durations[COUNT_NOTES] PROGMEM = {  
                                350, 350, 350, 250, 100, 350, 250, 100, 700, 
                                350, 350, 350, 250, 100, 350, 250, 100, 700, 
                                350, 250, 100, 350, 250, 100, 100, 100, 200, 
                                100, 350, 250, 100, 100, 100, 200, 
                                100, 350, 250, 100, 750 };

void setup()
{
    pinMode(PIN_LED, OUTPUT);
    pinMode(PIN_BUZZER, OUTPUT);
}

void loop()
{
    for (byte i = 0; i < COUNT_NOTES; i++)
    {
        buzz(PIN_BUZZER, pgm_read_word(&(frequences[i])), 2 * pgm_read_word(&(durations[i])));
        delay(100);
    }
    delay(3000);
}

void buzz(unsigned char pin, word frequencyInHertz, word timeInMilliseconds)
{ 
    long delayAmount = (long)(long(1000000) / (long)frequencyInHertz);
    long loopTime = (long)(((long)timeInMilliseconds * 500) / delayAmount);
    for (long i = 0; i < loopTime; i++)  
    {  
        digitalWrite(pin, HIGH);
        delayMicroseconds(delayAmount);
        digitalWrite(pin, LOW);
        delayMicroseconds(delayAmount);
    }
}

Размер скетча в двоичном коде: 946 байт (из 1 024 байт максимум)


Как Вы можете видеть, есть два массива frequences — в переводе из английского частота и durations — длительность, всё данные типа word, PROGMEM — данные хранятся во флеш-памяти микроконтроллера(без использования библиотеки pgmspace.h работать не будет) ну и есть генератор частоты buzz() который принимает три параметра — пин, на котором будет генерироваться частота, второй — частота в герцах, третий — длительность в миллисекундах.
Данный код должен быть Arduino совместимым и работать даже на Arduino Uno, Arduino Nano или же Arduino Pro Mini ну и и других дуинах.

Как Вы могли слышать на видео, звучание моего видео немного отличается от второго видео, дело в том, что я немного изменил длительность нот и в три раза популярным методом «научного тыка», поднял частоту, так как такой маленький динамик как у меня плохо воспроизводит те частоты что были изначально, и добавил кнопку, какой же дверной звонок без кнопки?

Немного подправил под себя и закомментировал

// by @tartakynov:
// http://youtu.be/5R7NeQkVS_8
// and me - vk.com/razniepodelki

#define F_CPU 1200000L  // Частота МК в герцах
#include <avr/pgmspace.h> // нужно для PROGMEM

#define PIN_BUZZER 2 // PB2 Динамик
#define BUTTON 4 // PB4 Кнопка

#define COUNT_NOTES 39  // количество нот

word frequences[COUNT_NOTES] PROGMEM = { // тут лежат частоты
  392, 392, 392, 311, 466, 392, 311, 466, 392,
  587, 587, 587, 622, 466, 369, 311, 466, 392,
  784, 392, 392, 784, 739, 698, 659, 622, 659,
  415, 554, 523, 493, 466, 440, 466,
  311, 369, 311, 466, 392 };

word durations[COUNT_NOTES] PROGMEM = { // тут их длительность 
  350, 350, 350, 250, 100, 350, 250, 100, 700,
  350, 350, 350, 250, 100, 350, 250, 100, 700,
  350, 250, 100, 350, 250, 100, 100, 100, 450,
  150, 350, 250, 100, 100, 100, 450,
  150, 350, 250, 100, 750 };

//void setup()
//{
  
int main( void ) // это аналог void setup(), для экономии места
{
  pinMode(PIN_BUZZER, OUTPUT); // инициализация пинов
  pinMode(BUTTON, INPUT); // подключаем подтягивающий резистор
  digitalWrite(BUTTON, HIGH); // чтобы кнопка возвращала 
  //LOW при нажатии

  //}
  //void loop()
  //{
    
  while(1){ // аналог void loop()(вечный цикл)
    if (digitalRead(BUTTON) == LOW) {   // когда кнопка нажата
      for (byte i = 0; i < COUNT_NOTES; i++) // собственно проигрываем мелодию
      {
        buzz(PIN_BUZZER, pgm_read_word(&(frequences[i])) * 3, 2 * pgm_read_word(&(durations[i])));
        // изначально было:
        // buzz(PIN_BUZZER, pgm_read_word(&(frequences[i])), 2 * pgm_read_word(&(durations[i])));
        // но я умножил частоту на 3
        //(pgm_read_word(&(frequences[i])) * 3) 
        //чтобы было громче слышно на небольшом динамике
        //delay(100); // этого не нужно
      }
    }
    //delay(3000); // и этого
    
  }         // эти строчки нужны 
  return 0; // int main( void ) 
}           // и while(1)

void buzz(unsigned char pin, word frequencyInHertz, word timeInMilliseconds) // по сути это 
{                                                                            // генератор частоты            
  long delayAmount = (long)(long(1000000) / (long)frequencyInHertz);         // имеет 3 параметра
  long loopTime = (long)(((long)timeInMilliseconds * 500) / delayAmount);    // 1 - пин
  for (long i = 0; i < loopTime; i++)                                      // 2 - частота
  {                                                                          // 3 - длительность
    digitalWrite(pin, HIGH);        // генерируем импульсы нужной частоты  
    delayMicroseconds(delayAmount);
    digitalWrite(pin, LOW);
    delayMicroseconds(delayAmount);
  }
}

Размер скетча в двоичном коде: 976 байт (из 1 024 байт максимум)


Как видите, добавления срабатывания при нажатии на кнопку превысило 1024 байта и пришлось вставлять кусочки кода Си чтобы уместиться в attiny13.
Частоту поднял строках buzz(PIN_BUZZER, pgm_read_word(&(frequences[i])) * 3, 2 * pgm_read_word(&(durations[i]))); тем самим умножив генерируемые частоты, как Вы поняли на три, это чисто мой каприз, можете ничего не умножать вообще.

Если Вы опытный ардуинщик, то думаю заметите в коде что я сделал программную подтяжку PULLUP резистора к порту PB4.
Для тех кто не знает делается это так:
Выставляем порт на вход и подаём на него логическую единицу. Теперь на порту будет напряжение, примерно равно напряжению питания, если этот порт закоротить на землю то он изменит своё состояние с логической единицы на логический ноль, причём микроконтроллер при этом не будет страдать, так как используется внутренний резистор, номинал которого 10-100 кОм.
Сделал я это чисто для экономии размеров платы, можно было просто подпаять резистор на 10 кОм к плюсу питанию и нужному порту, при нажатии на кнопку мы «притягиваем порт к земле» и на нём будет логической ноль.

Добавляю код на чистом AVR-С для тру AVR'щиков:

Код на AVR-С

// by @tartakynov:
// http://youtu.be/5R7NeQkVS_8
// and me - vk.com/razniepodelki


#define F_CPU 1200000UL  // Частота МК в герцах
#include <avr/io.h>  // библиотека ввода вывода
#include <util/delay.h> // библиотека для работы с задержками
#include <avr/pgmspace.h> // нужно для PROGMEM

#define PIN_BUZZER 2 // PB2 Динамик
#define BUTTON 4 // PB4 Кнопка

#define COUNT_NOTES 39 // количество нот

word frequences[COUNT_NOTES] PROGMEM = { // тут лежат частоты
  392, 392, 392, 311, 466, 392, 311, 466, 392,
  587, 587, 587, 622, 466, 369, 311, 466, 392,
  784, 392, 392, 784, 739, 698, 659, 622, 659,
  415, 554, 523, 493, 466, 440, 466,
  311, 369, 311, 466, 392 };

word durations[COUNT_NOTES] PROGMEM = { // тут их длительность 
  350, 350, 350, 250, 100, 350, 250, 100, 700,
  350, 350, 350, 250, 100, 350, 250, 100, 700,
  350, 250, 100, 350, 250, 100, 100, 100, 450,
  150, 350, 250, 100, 100, 100, 450,
  150, 350, 250, 100, 750 };

//void setup()
//{

int main( void ) // это аналог void setup(), для экономии места
{
  DDRB |= (1<<PIN_BUZZER); // иницыализация портов
  DDRB &= ~(1<<BUTTON); // подключаем подтягивающий резистор
  PORTB |= (1<<BUTTON);  // чтобы кнопка возвращала 
  //0 при нажатии(замыкинии)

  //}
  //void loop()
  //{

  while(1){ // аналог void loop()(вечный цикл)
    if (!(PINB & (1<<BUTTON))) {   // когда кнопка нажата
      for (byte i = 0; i < COUNT_NOTES; i++) // собственно проигрываем мелодию
      {
        buzz(pgm_read_word(&(frequences[i])) * 3, 2 * pgm_read_word(&(durations[i])));
        // изначально было:
        // buzz(PIN_BUZZER, pgm_read_word(&(frequences[i])), 2 * pgm_read_word(&(durations[i])));
        // но я умножил частоту на 3
        //(pgm_read_word(&(frequences[i])) * 3) 
        //чтобы было громче слышно на небольшом динамике
      }
      //_delay_ms(100); // для отладки
    }

    //_delay_ms(3000); // для отладки

  }         // эти строчки нужны 
  return 0; // int main( void ) 
}           // и while(1)

//void buzz(unsigned char pin, word frequencyInHertz, word timeInMilliseconds) 
void buzz(word frequencyInHertz, word timeInMilliseconds)  // по сути это 
{                                                                            // генератор частоты            
  long delayAmount = (long)(long(1000000) / (long)frequencyInHertz);         // имеет 3 параметра
  long loopTime = (long)(((long)timeInMilliseconds * 500) / delayAmount);    // 1 - пин
  for (long i = 0; i < loopTime; i++)                                      // 2 - частота
  {                                                                          // 3 - длительность
    //digitalWrite(pin, HIGH);        // генерируем импульсы нужной частоты  
    PORTB |= (1<<PIN_BUZZER);  // для экономии вставлю строчки на Си
    //delayMicroseconds(delayAmount); // мкс задержка на ардуино
    _delay_us(delayAmount); // на Си

    //digitalWrite(pin, LOW);
    PORTB &= ~(1<<PIN_BUZZER);
    //delayMicroseconds(delayAmount); // мкс задержка на ардуино
    _delay_us(delayAmount); // на Си
  }
}



Вроде как должен компилироваться без проблем, а вот при использовании этого кода в Arduino IDE почему-то размер hex файла как минимум удваивается «Размер скетча в двоичном коде: 1 986 байт (из 1 024 байт максимум)».
Как мне удалось выяснить всё это из за использования _delay_us(*); хотя ардуиновская функция delayMicroseconds(*); по сути делает то же самое, думаю это недочёт файлов ядра для тини13, вот ветка на буржуйском форуме ардуино откуда я и взял файлы ядра, правда самую малость доработал под себя, ссылка на доработанные мною файлы ядра есть в статье про прошивку Attiny13 при помощи Arduino.
Если кто-то поможет разобраться с этим багом буду только рад, ведь чисто теоретически Arduino IDE должна без проблем проглатывать С-шний код.

Ну и пару слов по прошивке — на первом видео запускал тиньку на частоте 1.2 мГц, как по мне, так 1.2 миллиона операций вполне достаточно чтобы играть любимую мелодию и при этом кушать совсем не много, кстати hex-файл скомпилирован именно под эту частоту.
Вот фьюзы из калькулятора фьюзов:

Скрытый текст
image


Теперь, каждый кто имеет AVR программатор сможет повторить это устройство.

Как было обещано в начале статьи вот все нужные файлы.

Все мои публикации.

И напоследок — «Да прибудет с Вами сила».

UPD 05.01.2015

Хаброюзер SparF сделал то, что я ленился сделать всё это время, а именно довести до ума энергопотребление и сделать проигрывание при нажатии на кнопку PB1.
Теперь после проигрывания мелодии МК не «молотит» дальше в ожидании нажатия кнопки а засыпает и при каждом нажатии на кнопку он просыпается, проигрывает любимый нами Имперский марш и засыпает дальше, потребляя при этом практически ничего, батареек хватит на десяток лет в таком режиме потребления.

Схема выглядит вот так:
image



Вот код:
/*
Attiny13 Star Wars melody
*/

#define F_CPU 1200000UL
#include <avr/io.h>  
#include <avr/pgmspace.h> 
#include <util/delay.h> 
#include <avr/interrupt.h>
#include <avr/sleep.h>

#define COUNT_NOTES 39

//частоты ноты
const uint16_t frequences[COUNT_NOTES] PROGMEM = {
    392, 392, 392, 311, 466, 392, 311, 466, 392,
    587, 587, 587, 622, 466, 369, 311, 466, 392,
    784, 392, 392, 784, 739, 698, 659, 622, 659,
    415, 554, 523, 493, 466, 440, 466,
    311, 369, 311, 466, 392 };

//длительность нот
const uint16_t durations[COUNT_NOTES] PROGMEM = {
    350, 350, 350, 250, 100, 350, 250, 100, 700,
    350, 350, 350, 250, 100, 350, 250, 100, 700,
    350, 250, 100, 350, 250, 100, 100, 100, 450,
    150, 350, 250, 100, 100, 100, 450,
    150, 350, 250, 100, 750 };

int main( void )
{
    /*
    вход от кнопки (фиксирован) = PB1 , т.к. от этого порта 
    срабатывает прерывание INT0 - единственное внешнее прерывание,
    которое может пробудить контроллер из режима максимального 
    энергосбережения Power-down
    
    выход на пищалку (фиксирован) = PB0, т.к. таймер может управлять
    только пинами PB0 и PB1, последний из которых уже занят кнопкой
    */
    
    DDRB = (1 << DDB0);		//переключить PB0 на выход
    
    /*
    по-хорошему, даже от вывода мелодии по прерыванию INT0 можно отказаться,
    если подключить кнопку на RESET: после сброса контроллер будет выполнять инициализацию,
    затем тут же проигрывать мелодию и ...уходить в режим максимального
    энергосбережения до следующего сброса
    */
    
    PORTB = (1 << PORTB1);	//включить подтяжку на PB1
    GIMSK = (1 << INT0);	//разрешить прерывание INT0 (PB1)
    
    set_sleep_mode(SLEEP_MODE_PWR_DOWN);	//настроить энергосберегающий режим Power-down
    
    sei();				//включить прерывания
    
    while(1)
        sleep_mode();	//войти в режим энергосбережения
} 

// вывод мелодии
static inline void doSound(void)
{
    //включить и настроить таймер
    TCCR0A = (1 << COM0A0) | (1 << WGM01);	//Toggle OC0A (PB0) on Compare Match ; mode = CTC
    TCCR0B = (1 << CS01) | (1 << CS00);		//Clock Select = F_CPU/64
    
    //поочередно проигрываем ноты
    for (uint8_t i = 0; i < COUNT_NOTES; i++)
    {
        //получить данные из памяти
        int16_t freq = pgm_read_word(&(frequences[i]));
        int16_t dur = pgm_read_word(&(durations[i]));
        
        //увеличиваем длительность в 1.5 раза
        dur *= 3;
        dur /= 2;
        
        //задаем частоту на выходе PB0
        OCR0A = (F_CPU / (2UL * 64UL * freq)) - 1;
        
        //ожидаем, пока играет нота
        while (dur > 0)
        {
            _delay_ms(10);
            dur -= 10;
        }
    }
    
    //отключить таймер
    TCCR0A = 0;
    TCCR0B = 0;
}

// обработчик прерывания INT0
ISR(INT0_vect)
{
    doSound();
}



Код без проблем компилируется в Arduino IDE.

Файлы.

Проверил, всё работает:

Сергей ПоделкинЦ @HWman
карма
39,0
рейтинг 16,0
Ардуинщик-лентяй, техноманьяк
Реклама помогает поддерживать и развивать наши сервисы

Подробнее
Реклама

Самое читаемое

Комментарии (27)

  • –5
    Голосуем.
    • +1
      Это прикольно, но полезно или нет каждый решит еще до хабраката, голосование лучше убрать, мне кажется ;)
      • +1
        Ну да… убрал.
  • –3
    Я зашел под кат только для того что-бы посмотреть где-же тут Arduino :)
    • +3
      Основной код тут под Arduino.
    • 0
      И правильно что автор ушел от бояна-ардуино и осилил отдельный контроллер.
      • 0
        Осилил это слишком громко сказано, начал изучать только.
        • 0
          Да я же не против, я только поддерживаю.
          Просто моё мнение в том что «Основной код тут под Arduino» != Arduino — в общем название не соответствует содержимому.
          • 0
            Почему? Предпоследний код у меня без особых проблем работает как на тини так и на ардуино.
  • 0
    На приход тёщи хорошо такую мелодию ставить.
    • –6
      А мне и так нравиться.
  • +1
    Быстро надоест
    • –1
      Мне — нет.
      • 0
        надоест, надоест.
        Но можно неплохо украсить, если полезть немного дальше в степь звукоизвлечения — я о вейвтейблах.
        Вот неплохая маленькая статейка (и исходники): elm-chan.org/works/mxb/report.html
  • +4
    Да чего накинулись на человека, вас помоему никто не заставляет себе такой звонок делать, а кому интересно — тот попробует. И как я понимаю, мелодию можно любую, так что кому не нравится эта — делайте свою (Лично я все пытаюсь Slayer — Raining Blood заставить ардуину пропищать).
    • +8
      Arduino может гораздо более интересный звук выдавать, а не просто «пропищать». Вот к примеру что у меня получилось
      • +1
        Афигеть! Респект и уважуха!
        Прослушал и вспомнил Scream Tracker и коллекцию .stm файлов (на полторы 5-дюймовых дискеты), попавшую ко мне через флоппинет в начале 90-х. Я до сих пор помню свое удивление и восхищение, когда обыкновенная пищалка компьютера начала играть МУЗЫКУ… Это было лучшим подтверждением прочитанного чуть ранее в «Серой книге» (к сожалению, уже не помню ни автора, ни название, только размер (очень толстая, формата А4) и содержание — подробнейшее по-компонентное описание IBM XT) факта, что пищалка в ПК подключена к собственному ЦАП и может играть довольно сложные звуки (но в то время этим редко кто пользовался).
        З.Ы. А коллекция у меня до сих пор сохранилась, изредка слушаю Winamp-ом.
        • 0
          Не было там ЦАП. Было управление динамиком через один бит уже не помню какого порта.
          В Scream Tracker и соответствующих DOS проигрывателях звук на внутренний динамик выводился с помощью ШИМ.
      • 0
        music from ZX Spectrum
        Insult megademo part «Ballmania — part 3»
        composer — KLAV/CodeBusters
        www.youtube.com/watch?v=LD8eXnib8to#t=115
      • 0
        Очень напомнило чипофон — тоже вроде бы на атмеле сделан.
  • НЛО прилетело и опубликовало эту надпись здесь
  • 0
    Возможная реакция жильцов на такой звонок:
    www.youtube.com/watch?v=ZdTOy4v8Yog#t=209
  • 0
    Караул! Идею украли :-)

    www.youtube.com/watch?v=xZllPVIHUFo
    • 0
      Почему украли?
      Я ведь в статье написал что основной код взят из видеоролика на ютюбе, точней из ссылки в описании.
      Но я решил немного доработать его под свои нужды.

      Хотя сама идея у меня уже жила давно, ещё до знакомства с с миром микроконтроллеров.
  • +1
    Извиняюсь за небольшой почти оффтоп.
    Совпало
    дарт
  • +1
    Тот же самый лунапарк, но с использованием аппаратного таймера, прерываний и режима энергосбережения
    /*
    Attiny13 Star Wars melody
    */
    
    #define F_CPU 1200000UL
    #include <avr/io.h>  
    #include <avr/pgmspace.h> 
    #include <util/delay.h> 
    #include <avr/interrupt.h>
    #include <avr/sleep.h>
    
    #define COUNT_NOTES 39
    
    //частоты ноты
    const uint16_t frequences[COUNT_NOTES] PROGMEM = {
    	392, 392, 392, 311, 466, 392, 311, 466, 392,
    	587, 587, 587, 622, 466, 369, 311, 466, 392,
    	784, 392, 392, 784, 739, 698, 659, 622, 659,
    	415, 554, 523, 493, 466, 440, 466,
    	311, 369, 311, 466, 392 };
    
    //длительность нот
    const uint16_t durations[COUNT_NOTES] PROGMEM = {
    	350, 350, 350, 250, 100, 350, 250, 100, 700,
    	350, 350, 350, 250, 100, 350, 250, 100, 700,
    	350, 250, 100, 350, 250, 100, 100, 100, 450,
    	150, 350, 250, 100, 100, 100, 450,
    	150, 350, 250, 100, 750 };
    
    int main( void )
    {
    	/*
    	вход от кнопки (фиксирован) = PB1 , т.к. от этого порта 
    	срабатывает прерывание INT0 - единственное внешнее прерывание,
    	которое может пробудить контроллер из режима максимального 
    	энергосбережения Power-down
    	
    	выход на пищалку (фиксирован) = PB0, т.к. таймер может управлять
    	только пинами PB0 и PB1, последний из которых уже занят кнопкой
    	*/
    	
    	DDRB = (1 << DDB0);		//переключить PB0 на выход
    	
    	/*
    	по-хорошему, даже от вывода мелодии по прерыванию INT0 можно отказаться,
    	если подключить кнопку на RESET: после сброса контроллер будет выполнять инициализацию,
    	затем тут же проигрывать мелодию и ...уходить в режим максимального
    	энергосбережения до следующего сброса
    	*/
    	
    	PORTB = (1 << PORTB1);	//включить подтяжку на PB1
    	GIMSK = (1 << INT0);	//разрешить прерывание INT0 (PB1)
    	
    	set_sleep_mode(SLEEP_MODE_PWR_DOWN);	//настроить энергосберегающий режим Power-down
    	
    	sei();				//включить прерывания
    	
    	while(1)
    		sleep_mode();	//войти в режим энергосбережения
    } 
    
    // вывод мелодии
    static inline void doSound(void)
    {
    	//включить и настроить таймер
    	TCCR0A = (1 << COM0A0) | (1 << WGM01);	//Toggle OC0A (PB0) on Compare Match ; mode = CTC
    	TCCR0B = (1 << CS01) | (1 << CS00);		//Clock Select = F_CPU/64
    	
    	//поочередно проигрываем ноты
    	for (uint8_t i = 0; i < COUNT_NOTES; i++)
    	{
    		//получить данные из памяти
    		int16_t freq = pgm_read_word(&(frequences[i]));
    		int16_t dur = pgm_read_word(&(durations[i]));
    		
    		//увеличиваем длительность в 1.5 раза
    		dur *= 3;
    		dur /= 2;
    		
    		//задаем частоту на выходе PB0
    		OCR0A = (F_CPU / (2UL * 64UL * freq)) - 1;
    		
    		//ожидаем, пока играет нота
    		while (dur > 0)
    		{
    			_delay_ms(10);
    			dur -= 10;
    		}
    	}
    	
    	//отключить таймер
    	TCCR0A = 0;
    	TCCR0B = 0;
    }
    
    // обработчик прерывания INT0
    ISR(INT0_vect)
    {
    	doSound();
    }
    
    • 0
      Спасибо, добавил.
      Всё заработало с первого раза:

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