AVR. Работа с пьезоизлучателем. Проигрывание мелодии. Аудио проигрыватель на Atmega8 и WTV020 Atmega8 мелодии

Сидел я на днях думал, чего бы такого к своему скутеру "присобачить": музыка есть, подсветка есть, но чего то не хватает, и тут я вспомнил про сигнализацию, точно! Ведь как раз ее то у меня и нету! Предлагаю и вам тоже собрать сигнализацию для своего двухколесного – например велосипеда, а может быть и четырехколесного друга. Сигнализация собрана на микроконтроллере AVR ATmega8, проект так же повторен на микроконтроллере Attiny2313. Для варианты схемы на Atmega8 я написал три варианта прошивок, одна прошивка воспроизводит звук напоминающий сигнализацию автомобиля, а другой похож на сирену охранной сигнализации расположенной в здании (более быстрая и резкая мелодия). Все прошивки подписаны и лежат ниже в архиве, думаю вы в них разберетесь. Кроме того, в архиве содержится симуляция схем в протеусе, так что вы сможете прослушать звуки и подобрать свой вариант, который вам больше по душе.

Схема на Atmega8:

Как видите, ничего особенного, микроконтроллер, три резистора и два светодиод с динамиком. Вместо кнопки на схеме можно использовать например геркон, или другой контакт. Схема работает следующим образом, если подать питание то загорится (или замигает – в зависимости от варианта схемы) светодиод D3, если датчик не тронут, то сирена будет молчать. Как только сработает датчик сработает сигнализация и одновременно с этим будет мигать светодиод D2. Лично я вывод 24 PС1 через транзисторный ключ подключил к релюшке, а реле последовательно передней фаре скутера, так чтобы когда сработает сигнализация мигала фара скутера. Для того чтобы остановить сирену нужно выключить и включить схему или снова нажать на кнопку. Хочу заметить, что сигнал с контроллера можно усилить несколькими транзисторами собрав небольшой усилитель – что я в принципе и сделал, правда на схеме эту цепь не изобразил. Микроконтроллер работает от внутреннего генератора 8 МГц, фьюзы выставляем соответствующие.

Печатная плата для Atmega8 выглядит следующим образом:

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

Схема на Attiny2313:

Для этого варианта схемы я написал всего одну прошивку, с одним вариантом сигнала, схему на всякий случай собрал навесным монтажом и проверил работоспособность. Микроконтроллер работает от внутреннего генератора 4 МГц (можно прошить на 1 МГц), фьюзы при программировании выставляем следующие:

Так как под рукой не было живого контроллера Atmega8, я собрал схему на Attiny2313, схема заработала сразу, собирал схему навесным монтажом, ниже фото:

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

Скачать проекты в , прошивки и файлы печатных плат вы можете ниже

Список радиоэлементов

Обозначение Тип Номинал Количество Примечание Магазин Мой блокнот
U1 МК AVR 8-бит

ATmega8-16PU

1 В блокнот
R1 Резистор

47 Ом

1 В блокнот
R2, R3 Резистор

270 Ом

2 В блокнот
Схема на Attiny2313
U1 МК AVR 8-бит

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

Подготовка к работе

В программе объявляются два массива. Массив с нотами notes содержит простое перечисление нот. Этим нотам сопоставляется длительность звучания в массиве beats . Длительность в музыке определяется делителем ноты по отношению к целой ноте. За целую ноту принимается значение 255 . Половинки, четверти, восьмые получаются путем деления этого числа.
Обратите внимание, что длительность первой же ноты не получается путем деления 255 на степень двойки. Тут придется переключиться на теорию музыки. Ноты исходной мелодии можно посмотреть . Эти ноты объединены в триоли. При таком объединении три ноты по одной восьмой звучат также, как одна четвертая. Поэтому их относительная длительность 21.
Также пользователю необходимо явно указать количество нот в последовательности директивой:

# define SEQU_SIZE 19

В основной программе в первую очередь происходит пересчет массивов частот и длительность в периоды сигналов и длительность нот.
С периодами сигналов (массив signal_period ) все просто. Чтобы получить длительность периода в микросекундах достаточно разделить 1000000 на частоту сигнала.
Для расчета абсолютной длительности звучания нот необходимо, чтобы был указан темп музыкального произведения. Делается это директивой

# define TEMPO 108

Темп в музыке, это количество четвертей за минуту. В строке

# define WHOLE_NOTE_DUR 240000 / TEMPO

рассчитывается длительность целой ноты в миллисекундах. Теперь достаточно по формуле пересчитать относительные значения из массива beats в абсолютные массива note_duration .
В основном цикле, переменная elapsed_time инкрементируется после каждого периода воспроизводимого сигнала на длительность этого периода до тех пор, пока не превысит длительность звучания ноты. Стоит обратить внимание на эту запись:

while (elapsed_time < 1000 * ((uint32_t) note_duration[ i] ) )

Переменная elapsed_time 32ух-битная, а элементы массива notes_duration 16ти-битная. Если 16ти битное число умножить на 1000, то гарантированного наступит переполнение и переменная elapsed_time будет сравниваться с мусором. Модификатор (uint32_t) преобразует элемент массива notes_duration[i] в 32ух-битное число и переполнение не наступает.
В цикле воспроизведения звука вы можете увидеть еще одну особенность. В нем не получится использовать функцию _delay_us() , так как ее аргументом не может быть переменная.
Для создания таких задержек используется функция VarDelay_us() . В ней цикл с задержкой в 1мкс прокручивается заданное количество раз.

void VarDelay_us(uint32_t takt) { while (takt- - ) { _delay_us(1 ) ; } }

При воспроизведении мелодии используется еще две задержки. Если ноты будут воспроизводиться без пауз, то они будут сливаться в одну. Для этого между ними вставлена задержка 1мс, заданная директивой:

# define NOTES_PAUSE 1

После каждого полного цикла проигрывания мелодии программа делает паузу в 1с и начинает воспроизведение заново.
В итоге мы получили код, в котором легко изменить темп, поправить длительности или полностью переписать мелодию. Для этого достаточно будет только преобразовывать только часть программы с директивами и объявлением переменных.

Индивидуальные задания

  1. В предложенной мелодии попробуйте изменить темп исполнения и сделайте паузу 5 секунд между повторами.
  2. Элементы массива beats принимают значения только от 0 до 255. Измените разрядность элементов массива и посмотрите в выводе компилятора, как это повлияет на объем памяти, занимаемой программой.
  3. Теперь попробуйте самостоятельно изменить мелодию. Например, вот “Имперский марш” из того же кинофильма: int notes = { A4, R, A4, R, A4, R, F4, R, C5, R, A4, R, F4, R, C5, R, A4, R, E5, R, E5, R, E5, R, F5, R, C5, R, G5, R, F5, R, C5, R, A4, R} ; int beats = { 50 , 20 , 50 , 20 , 50 , 20 , 40 , 5 , 20 , 5 , 60 , 10 , 40 , 5 , 20 , 5 , 60 , 80 , 50 , 20 , 50 , 20 , 50 , 20 , 40 , 5 , 20

    Написал программный модуль, позволяющий добавить функцию воспроизведения мелодий или последовательностей звуков практически в любой проект на микроконтроллере AVR.

    Особенности модуля:

    Простая интеграция с готовым проектом

    Задействован только 8-ми разрядный таймер т2, при этом остается возможность использовать его для опроса или формирования временных интервалов

    Модуль настраивается практически на любую частоту тактового генератора

    Высота нот задается в виде символических констант (С0, А2 и т.д) или в Герцах

    Длительности задаются в стандартном виде (четверти, восьмые и т.д.) или в миллисекундах

    Имеется возможность задавать темп воспроизведения мелодии и количество ее повторений

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


    Подключение звукового модуля

    1. Переписываем все файлы модуля (tone.h, sound.h, sound.c) в папку проекта.

    2. Подключаем файл sound.c к проекту.

    Для IAR `a – кликнуть правой кнопкой мышке в окне workspace и выбрать Add > Add Files…

    Для WINAVR примерно то же самое, только sound.c нужно еще прописать в make файл:

    SRC = $(TARGET).c sound.c

    3. Включаем заголовочный файл sound.h в соответствующий модуль. Например, в main.c

    #include "sound.h"

    4. Задаем настройки модуля в файле sound.h

    //если закомментировать - длительность нот будет

    //рассчитываться из BPM`а заданного в мелодии

    //если оставить, то из значения заданного ниже

    //#define SOUND_BPM 24

    //тактовая частота мк

    #define SOUND_F_CPU 16U

    //вывод микроконтроллера, на котором будет генерироваться звук

    #define PORT_SOUND PORTB

    #define PINX_SOUND 0

    //количество заданных мелодий.

    #define SOUND_AMOUNT_MELODY 4

    5. Добавляем в sound.c свои мелодии и прописываем названия мелодий в массив melody.

    Добавление мелодий

    Мелодия представляет собой массив 16-ти разрядных чисел и имеет следующую структуру

    BPM (количество четвертных нот в минуту) – это константа, используемая для расчета длительности нот и определяющая скорость воспроизведения мелодии.

    BPM может принимать значения от 1 до 24, что соответствует 10 и 240 четвертным нотам в минуту соответственно.

    Если длительность нот/звуков задается в миллисекундах, то BPM, прописанный в массиве, должен быть равен 1.

    Если в заголовочном файле sound.h константа SOUND_BPM закомментирована, то длительность нот рассчитывается в процессе выполнения программы по BPM `у заданному в массиве. Если SOUND_BPM не закомментирована – длительность нот рассчитывается еще на этапе компиляции, исходя из значения этой константы, при этом все мелодии будут воспроизводиться в одинаковом темпе. Это ограничивает функционал, но позволяет сэкономить несколько байт кода.

    Количество повторений. Может принимать значения 1 ... 254 и LOOP (255). LOOP - означает, что мелодия будет повторяться бесконечно, пока не будет подана команда SOUND_STOP или SOUND_PAUSE.

    Длительность ноты – время в течение, которого генерируется заданный тон звука или выдерживается пауза. Может задаваться в ms, с помощью макроса ms(x), или в виде стандартных длительностей нот – восьмых, шестнадцатых и т.д. Ниже приведен список поддерживаемых длительностей. Если возникнет нужда в каких-то экзотических длительностях, их всегда можно добавить в файле tone.h

    n1 - целая нота

    n2 - половинная нота

    n4 - четверть

    n8 - восьмая

    n3 - восьмая триоль

    n16 - шестнадцатая

    n6 - секстоль

    n32 - тридцать вторая

    Высота ноты задается с помощью символических констант описанных в файле tone.h, например C2, A1 и т.д. Также высота нот может задаваться в Герцах с помощью макроса f(x).

    В программе есть ограничения на минимальную и максимальную частоту звука!

    Маркер конца мелодии. Значение последнего элемента массива обязательно должно быть нулевым.

    Использование звукового модуля

    В начале main`a нужно обязательно вызывать функцию SOUND_Init(). Эта функция настраивает вывод микроконтроллера на выход, конфигурирует таймер Т2 и инициализирует переменные модуля.

    Затем нужно установить флаг разрешения прерываний - __enable_interrupt(), ведь в модуле используется прерывания таймера Т2 по переполнению и совпадению.

    После этого можно запускать воспроизведение мелодий.

    Например, так:

    SOUND_SetSong(2);

    SOUND_Com(SOUND_PLAY); //воспроизвести мелодию

    //установить указатель на 2-ю мелодию

    //и запустить воспроизведение

    SOUND_PlaySong(2);

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

    В принципе данный функционал не особо нужен (это уж я просто навернул) и при работе с модулем достаточно функции SOUND_PlaySong(unsigned char numSong);

    Файлы

    Примеры использования звукового модуля вы можете скачать по ссылкам ниже. Схему рисовать не стал, потому что там все просто. подключен к выводу PB0, кнопка запуска мелодий подключена к выводу PD3. В проектах определено 4 мелодии. Нажатие на кнопку запускает каждый раз новую мелодию. Используется микроконтроллер atmega8535. Изначально хотел заморочиться на проект с четырьмя кнопками - PLAY, STOP, PAUSE и NEXT, но потом подумал, что это лишнее.

    PS: Модуль не проходил расширенное тестирование и предоставляется “как есть“. Если есть какие-то рациональные предложения - давайте его доработаем.

    Если в вашем автомобиле не установлена звуковая сирена, и вы все еще никак не решайтесь какую купить и установить, то данная статья именно для Вас. Зачем купить дорогие сигнализации если можно довольно простым образом собрать все это своими руками?

    Представляю две такие простые схемы на микроконтроллерах AVR ATmega8 и Attiny2313, точнее схема одна, просто реализована для работы на этих двух микроконтроллерах. Кстати в архиве вы найдете два варианта прошивок для микроконтроллера Атмега8, из которых первый воспроизводит звук похожий на автомобильную сигнализацию, а второй звук похож на охранную сигнализацию здании(быстрый и резкий сигнал).

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

    Ниже схема сигнализации на Atmega8

    Список применяемых радиокомпонентов в схеме Атмега8

    U1- Микроконтроллер AVR 8-бит ATmega8-16PU, кол. 1,
    R1- Резистор с номиналом 47 Ом, кол. 1,
    R2, R3- Резистор с номиналом 270 Ом, кол. 2,
    D2,D3-светодиод, кол. 2,
    LS1-динамик, кол. 1,
    S1- датчик.

    А в схеме сигнализации на Attiny2313 изменен только мк.
    U1- Микроконтроллер AVR 8-бит ATtiny2313-20PU, кол. 1.

    Печатная плата для Atmega8 выглядит следующим образом:

    Как видим схема очень простая, там всего один микроконтроллер, 3резистора, 2светодиода и еще один динамик. Вместо кнопки можете применить геркон, или другой контакт.

    Принцип работы следующее. Как только мы подаем питание, сразу же загорается или начнет мигать(в зависимо от прошивки) светодиод(в схеме D3), и если датчика не будем трогать, то сигнализация будет молчать. Вот если сработал датчик то и сирена заработает, также будет мигать светодиод, но уже D2.

    Если хотите чтобы при работе сигнализации моргал также фары автомобиля, то для этого нужно вывод микроконтроллера 24 РС1 подключить к реле через транзистор, а сам реле уже к фарам. Чтобы отключить сирену необходима выключить и снова включить прибор, или просто нажать на кнопку. Для работы микроконтроллера нужен внутренний генератор на 8МГЦ,

    Если хотите как то усилить звук сигнализации, то с транзисторами можете собрать усилитель и подключить к схеме. Я именно так и сделал, только в данной схеме этого не изобразил.

    Перейдем к схеме на Attiny 2313, в нем как и сказал раньше все те же детали и тот же принцип работы, только изменен МК, в следствие и подключенные выводы.Такой микроконтроллер работает от внутреннего генератора 4МГц, хотя можно и на 1Мгц прошить.

    Ниже схема подключении уже на Attiny2313

    Для данной мк написал всего одну версию прошивки, собрал все на какетной плате, проверил, все нормально работает.
    А фьюзи нужны выставить ниже представленным образом:


    Продолжение урока затянулось, оно и понятно, пришлось освоить работу с картами памяти и файловой системой FAT. Но все таки, оно свершилось, урок готов — фактически новогоднее чудо.

    Дабы не перегружать статью информацией, я не буду описывать структуру формата wav файла, информации в поисковиках более чем предостаточно. Достаточно сказать, что если открыть файл, каким либо Hex редактором, то в первых 44 байтах содержится вся информация о типе файла, частоте дискретизации, количестве каналов и пр. Если нужно анализировать файл, читайте этот заголовок и будет вам счастье.

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

    Как физически заставить динамик дрыгаться? Нужно выводить эти уровни напряжения, при помощи ШИМ, либо использовать R2R. В любом случае, использовать очень просто, прочитал число, засунул его либо в OCR, либо в PORTx. Далее через определенное время, подставил следующее значение и так до конца файла.

    Пример, некий wav файл, данные идут с 44=0х2С байта, там записано число 0х80, воспроизводим звук например ШИМом первого таймера, пишем OCR1A=0х80; Допустим, частота дискретизации вавки 8кГц, соответственно прерывание должно быть настроено на эту же частоту. В прерывании, подставляем следующее значение 0x85 через 1/8000=125мкс.

    Как настроить прерывание на 8кГц? Вспоминаем, если таймер работает на частоте 250кГц, то регистр сравнения прерывания нужно подставить (250/8)-1=31-1 или 0x1E. С ШИМом тоже все просто, чем выше частота на которой он работает тем лучше.

    Чтобы прошивка работала, условимся, что флешка отформатирована в FAT32, используется либа PetitFat из урока 23.2. Файл в формате wav либо 8кГц, либо 22,050кГц, моно. Название файла 1.wav. Анализируем прошивку.

    #include #include "diskio.h" #include "pff.h" unsigned char buffer[ 512 ] ; /* буфер в который копируется инфа с флешки */ volatile unsigned int count; //счетчик скопированных данных interrupt [ TIM2_COMP] void timer2_comp_isr(void ) //прерывание в котором подставляются значения { OCR1A = buffer[ count] ; //выводим звук на динамик if (++ count >= 512 ) //увеличиваем счетчик count = 0 ; //если 512 обнуляем } void main(void ) { unsigned int br; /* счетчик чтения/записи файла */ unsigned char buf = 0 ; //переменная определяющая какая часть буфера читается FATFS fs; /* Рабочая область (file system object) для логических дисков */ PORTB= 0x00 ; DDRB= 0x02 ; //дрыгаем шимом ocr1a // Timer/Counter 1 initialization // Clock source: System Clock // Clock value: 8000,000 kHz // Mode: Fast PWM top=0x00FF // OC1A output: Non-Inv. TCCR1A= 0x81 ; TCCR1B= 0x09 ; TCNT1= 0x00 ; OCR1A= 0x00 ; // Timer/Counter 2 initialization // Clock source: System Clock // Clock value: 250,000 kHz // Mode: CTC top=OCR2 TCCR2= 0x0B ; TCNT2= 0x00 ; //OCR2=0x1E; //настройка регистра сравнения для 8кГц OCR2= 0xA ; //для 22кГц #asm("sei") // Timer(s)/Counter(s) Interrupt(s) initialization if (disk_initialize() == 0 ) //инициализируем флешку { pf_mount(& fs) ; //монтируем файловую систему pf_open("1.wav" ) ; //открываем вавку pf_lseek(44 ) ; //перемещаем указатель на 44 pf_read(buffer, 512 ,& br) ; //в первый раз заглатываем сразу 512байт TIMSK= 0x80 ; //врубаем музон while (1 ) { if (! buf && count> 255 ) //если воспроизвелось больше 255 байт, { pf_read(& buffer[ 0 ] , 256 ,& br) ; //то читаем в первую половину буфера инфу с флешки buf= 1 ; if (br < 256 ) //если буфер не содержит 256 значений значит конец файла break ; } if (buf && count< 256 ) { pf_read(& buffer[ 256 ] , 256 ,& br) ; // читаем во вторую часть буфера с флешки buf = 0 ; if (br < 256 ) break ; } } TIMSK = 0x00 ; //глушим все pf_mount(0x00 ) ; //демонтируем фат } while (1 ) { } }

    #include #include "diskio.h" #include "pff.h" unsigned char buffer; /* буфер в который копируется инфа с флешки */ volatile unsigned int count; //счетчик скопированных данных interrupt void timer2_comp_isr(void) //прерывание в котором подставляются значения { OCR1A = buffer; //выводим звук на динамик if (++count >= 512) //увеличиваем счетчик count = 0; //если 512 обнуляем } void main(void) { unsigned int br; /* счетчик чтения/записи файла */ unsigned char buf = 0; //переменная определяющая какая часть буфера читается FATFS fs; /* Рабочая область (file system object) для логических дисков */ PORTB=0x00; DDRB=0x02; //дрыгаем шимом ocr1a // Timer/Counter 1 initialization // Clock source: System Clock // Clock value: 8000,000 kHz // Mode: Fast PWM top=0x00FF // OC1A output: Non-Inv. TCCR1A=0x81; TCCR1B=0x09; TCNT1=0x00; OCR1A=0x00; // Timer/Counter 2 initialization // Clock source: System Clock // Clock value: 250,000 kHz // Mode: CTC top=OCR2 TCCR2=0x0B; TCNT2=0x00; //OCR2=0x1E; //настройка регистра сравнения для 8кГц OCR2=0xA; //для 22кГц #asm("sei") // Timer(s)/Counter(s) Interrupt(s) initialization if(disk_initialize()==0) //инициализируем флешку { pf_mount(&fs); //монтируем файловую систему pf_open("1.wav"); //открываем вавку pf_lseek(44); //перемещаем указатель на 44 pf_read(buffer, 512,&br); //в первый раз заглатываем сразу 512байт TIMSK=0x80; //врубаем музон while(1) { if(!buf && count>255) //если воспроизвелось больше 255 байт, { pf_read(&buffer, 256,&br);//то читаем в первую половину буфера инфу с флешки buf=1; if (br < 256) //если буфер не содержит 256 значений значит конец файла break; } if(buf && count<256) { pf_read(&buffer, 256,&br); // читаем во вторую часть буфера с флешки buf = 0; if (br < 256) break; } } TIMSK = 0x00; //глушим все pf_mount(0x00); //демонтируем фат } while (1) { } }

    Для проверки, на ножку OCR1A подключаем динамик через конденсатор 100мкФ, «+» на ножку микроконтроллера, «-» на динамик. «-» динамика на землю, «+» на конденсатор.

    Не ждите громкого сигнала на выходе, чтобы звучало громко, необходим усилитель. На видео это хорошо видно. Для теста залил петуха 8кГц и трек 22кГц.

    Желающие могут смело увеличить частоту таймера2, чтобы проигрывать файлы 44кГц, опыты показывают, что можно добиться вполне неплохого качества звучания. На видео звук слабый и качество плохое, но на самом деле это из-за того, что снимал на фотоаппарат.

    Также выкладываю материалы любезно предоставленные Аппаратчиком — исходник для GCC, с которого была написана прошивка под CAVR.

    И видео с воспроизведением 44кГц.

    Пользуясь случаем поздравляю Всех с Наступающим, желаю чтобы все прошивки и девайсы у вас работали 🙂

    Проект wav плеера на Atmega8