Ранее экспериментировал с синтезом звука на AVR. По итогу сделал музыкальную шкатулку на ATTiny2313, где задействовал два таймера Т0 и Т1 в режиме СТС для вывода основной мелодии и бас-партии. Динамик, соответственно, смешивал все это хозяйство. Получилось неплохо. Продолжая эту историю, заинтересовался, как технически суммируются прямоугольные импульсы? И можно ли обойтись в принципе одной ногой таймера в коде для вывода двух (и более нот) одновременно? Примерно, как на картинке ниже:
Наглядный пример:
Буду благодарен за разъяснения и направление, куда копать дальше!
Для тонов с частотами в сотые доли герца нужна другая вычислительная мощность и ресурс. С AVR и указанным способом будет всего каких-то два тона. Рассмотрите как идею напр. NCO генератор, или два внешних генератора DDS. Будет легче.
Сотые доли Герца можно сгенерировать на самом медленном оборудовании, даже на реле Ну если период - десятки секунд - это даже арифмометр "Феликс" осилит Но музыку в диапазоне инфразвука оценит разве что осьминог. СпойлерУ нас НИИ сдавало тему - простейшая цикловая схема управления роботом. Для демонстрации подобрали и подключили к выходам несколько пускателей разных габаритов. При срабатывнии они издавали щелчок разной тональности - зависит от массы якоря. Программа исполняла "Чижик-пыжик" ---------- Как-то в институте на скучной лекции я взялся посчитать - с какой частотой я получаю стипендию. Оказалоь 3,8 * 10^-7 Гц
В крайности уходить не стоит. Перефразирую вопрос проще: как на МК сгенерить одновременное звучание двух и более нот разной частоты? Типа аккорда. Какие есть технические варианты?
...в памяти прописывается массив, отражающий форму элементарного сигнала, а затем делается выборка, сложение с выборкой другой(-ими) ноты, одновременно звучащей, и результирующий код (8 и более бит) подается на R2R, ЦАП, или ШИМ.
Как раз то, что я ищу. У меня записан массив нот в памяти. Поделитесь пжл примером кода, если у кого есть, как производится сложение выборок, чтобы выводить полифонию
кроме массива нот нужен массив частот этих нот. и складываются частоты, а не ноты.
Добавлено after 10 minutes 30 seconds: для лучшего понимания прочитай эту статью https://www.radiokot.ru/circuit/digital/game/51/ а также запусти поиск по фразе "синтез мелодий на AVR", и найдешь уйму информации по синтезу мелодий.
_________________ Мудрость приходит вместе с импотенцией... Когда на русском форуме переходят на Вы, в реальной жизни начинают бить морду.
Для складывания двух и более звуков, если используется AVR и ШИМ 8-10 бит, достаточно их все тупо сложить (с учетом знака) и при выходе за пределы разрядности тупо ограничить, т.е. если сумма для 8 бит>255, то результат будет 255, если меньше 0, то 0. Да, при сложении может произойти искажение (перегрузка), ее можно обойти, заранее поделив громкость на количество одновременно складываемых звуков, но в этом скорее всего не будет необходимости, учитывая условия.
_________________ Неправильно собранная из неисправных деталей схема нуждается в отладке и сразу не работает... (С)
Тут у меня как раз не ШИМ, а СТС режим. Будет аналогично или есть особенности?
Э погодите-ка, тут есть варианты... Если вы генирируете звук при помощи сложения разных меандров, т.е. используете режим CTC для задания частот именно самих звуковых волн и ЭТИ волны надо микшировать, то действительно, это проще сделать внешним смесителем. Кстати, можно использовать один железный таймер, а частоты меандров синтезировать программно, используя вариант ниже.
А вот если режим CTC нужен для задания частоты квантования и по этой частоте вы бы через ШИМ выводили семплы, то суммировать их можно методом, который я описал выше.
Кстати, и для задачи частоты квантования и для ШИМ (даже стерео) можно использовать всего ОДИН таймер, причем частоту квантования можно задавать в широких пределах, в том числе близкую к стандартным 11-22-44кГц. Для этого таймер настраивается в режим CTС со сравнением с ICR, в ICR заносится нужное для заданной частоты квантования значение. А семплы нормируются с учетом этого значения и выводятся в OCRA и OCRB в прерывании по переполнению.
_________________ Неправильно собранная из неисправных деталей схема нуждается в отладке и сразу не работает... (С)
В некоторых моделях PIC микроконтроллеров присутствует периферийный модуль DATA SIGNAL MODULATOR. Возможно подойдет для поставленных топикстартером целей.
_________________ Астролябия-сама меряет, было бы что мерять!!!
Э погодите-ка, тут есть варианты... Если вы генерируете звук при помощи сложения разных меандров, т.е. используете режим CTC для задания частот именно самих звуковых волн и ЭТИ волны надо микшировать, то действительно, это проще сделать внешним смесителем. Кстати, можно использовать один железный таймер, а частоты меандров синтезировать программно, используя вариант ниже.
А вот если режим CTC нужен для задания частоты квантования и по этой частоте вы бы через ШИМ выводили семплы, то суммировать их можно методом, который я описал выше.
Спасибо, а можно поподробнее (или пример)? Пока непонятно. что с чем складывать. У меня два таймера Т0 и Т1 тикают независимо в режиме СТС, далее в прерывании каждый дергает свою ногу, с частотой, заданной регистром сравнения. Динамик соединен с двумя выводами МК и делает всю работу по смешению за меня.
Ну вот так, например... Код для суммирования 4х меандров (2 отключил чтобы понятнее сумма выглядела). Тактовая для Mega88 - 8МГц, частота семплов 22кГц, использован один 16 разрядный таймер, и одно его прерывание, 20 байт ОЗУ, и 270 флеша, загрузка контроллера ~45%.
Спойлер
Код:
;программа сложения 4х прямоугольных сигналов с произвольными частотами
.include "m88def.inc" ;определения для Mega88 .include "MacroAVR.asm" ;макросы для упрощения кода
.equ F_CPU=8000000 ;частота контроллера
.equ SND_SAMPLE_FREQ =22050 ;частота выборок .equ SND_OCR_VALUE =F_CPU/SND_SAMPLE_FREQ ;расчет значения для режима CTC .equ SND_MID_VALUE =SND_OCR_VALUE/2 ;половина выходного сигнала .equ NUMBER_OF_SIGNALS =4 ;количество смешиваемых сигналов
.def ZERO =R2 ;для ускорения арифметики .def STR =R3 ;для ускорения вх/вых в прерывание
;структура данных для одного сигнала .equ PH_OFFSET =0 ;фаза .equ DIV_OFFSET =2 ;делитель .equ OUT_OFFSET =4 ;значение на выходе .equ SIGNAL_SIZE =2+2+1 ;ее размер
.dseg ;сегмент данных SIG: .byte SIGNAL_SIZE*NUMBER_OF_SIGNALS ;данных сигналов
.cseg ;сегмент кода .org 0 RJMP RESET
.org OVF1addr RJMP SAMPLE_OUT ;прерывание для расчета и вывода семпла
RESET: SETSTACK ;установка стека OUTI CLKPR,(1<<CLKPCE) ;сброс делителя тактовой STS CLKPR,ZERO OUTI DDRC,(1<<PC4)|(1<<PC3)|(1<<PC2)|(1<<PC1)|(1<<PC0);тестовые выходы OUTI TCCR1A,(1<<COM1A1)|(1<<WGM11)|(0<<WGM10) ;настройка таймера CTC, TOP=ICR OUTI TCCR1B,(1<<WGM12)|(1<<WGM13)|(1<<CS10) OUTI ICR1H,High(SND_OCR_VALUE) ;период квантования OUTI ICR1L,Low(SND_OCR_VALUE) OUTI OCR1AH,High(SND_MID_VALUE) ;в ШИМ - половина уровня OUTI OCR1AL,Low(SND_MID_VALUE) LDS R16,TIMSK1 ;вкл. прерывания OVF1 ORI R16,(1<<TOIE1) STS TIMSK1,R16 SBI DDRB,PB1 ;включить вывод ШИМ SETZ DIVS*2 ;копирование таблицы из флеша SETY SIG ;в структуру сигнала LDI R17,NUMBER_OF_SIGNALS DLOOP: LPM XL,Z+ ;чтение делителя LPM XH,Z+ STD Y+PH_OFFSET+0,XH ;запись начального значения фазы STD Y+PH_OFFSET+1,XL STD Y+DIV_OFFSET+0,XH ;и это же значение в делитель STD Y+DIV_OFFSET+1,XL STD Y+OUT_OFFSET,ZERO ;значение функции изначально=0 ADIW YL,SIGNAL_SIZE ;переход к следюущему DJNZ R17,DLOOP SEI ;вкл. глобальных прерываний
LOOP: SETX SIG+OUT_OFFSET ;основной цикл LDI R17,NUMBER_OF_SIGNALS ;просто вывод CLR R18 ;значения функций на выходы OLOOP: LD R16,X ;для наглядности BST R16,7 BLD R18,4 LSR R18 ADIW XL,SIGNAL_SIZE DJNZ R17,OLOOP IN R16,PORTC ANDI R16,0xF0 OR R16,R18 OUT PORTC,R16 RJMP LOOP
;процедура вывода семпла SAMPLE_OUT: SBI PORTC,PC4 ;вывод загрузки контроллера процедурой IN STR,SREG ;сохранение SREG и регистров PUSH R17 PUSH R16 PUSHX PUSHY PUSHZ LDI ZL,0x08 ;начальное значение суммы сигналов LDI R17,NUMBER_OF_SIGNALS SETY SIG ;установка на структуру первого сигнала SLOOP: LDD XH,Y+PH_OFFSET+0 ;чтение фазы LDD XL,Y+PH_OFFSET+1 CP XL,ZERO CPC XH,ZERO BREQ SKIP ;если 0, про сигнал не обрабатывать LDD R16,Y+OUT_OFFSET ;чтение значения на выходе SBIW XL,1 ;уменьшаем фазу BRNE NO_INVERT ;если не 0, переход к сохранению COM R16 ;если 0, инвертируем выход STD Y+OUT_OFFSET,R16 ;сохраняем значение на выходе LDD XH,Y+DIV_OFFSET+0 ;перезагружаем значение фазы LDD XL,Y+DIV_OFFSET+1 NO_INVERT: STD Y+PH_OFFSET+0,XH ;сохраняем фазу STD Y+PH_OFFSET+1,XL SBRC R16,7 ;суммируем значения на выходах INC ZL SBRS R16,7 DEC ZL SKIP: ADIW YL,SIGNAL_SIZE ;переход к следующему DJNZ R17,SLOOP
CJNE ZL,0x08,NO_HALF ;проверка суммы значений и преобразование в ШИМ SETZ SND_MID_VALUE ;если сумма=0, выводим половину в ШИМ RJMP OCR_SET NO_HALF:CJLO ZL,0x08,OUT_MIN ;если сумма<0, выводим в ШИМ 0 SETZ SND_OCR_VALUE-1 ;если сумма>0, выводим в ШИМ максимальное значение RJMP OCR_SET OUT_MIN:SETZ 0 OCR_SET: STS OCR1AH,ZH ;вывод в регистры ШИМ STS OCR1AL,ZL POPZ ;восстановление регистров POPY POPX POP R16 POP R17 OUT SREG,STR CBI PORTC,PC4 RETI
Результат. Зеленый (после RC-цепочки) = желтый+синий. Нижний осциллограф - ШИМ, и загрузка контроллера. Спойлер
PS: О, зацените игрушку всего 146 байт флеша и 14 ОЗУ, работает от встроенного RC на 8МГц, 7 кнопок, можно все сразу нажать, будет 7 меандров на выходе и еще 66% процессорного времени свободно. Можно прикрутить читалку нот по таймеру и готова музыкальная шкатулка
Сейчас этот форум просматривают: нет зарегистрированных пользователей и гости: 5
Вы не можете начинать темы Вы не можете отвечать на сообщения Вы не можете редактировать свои сообщения Вы не можете удалять свои сообщения Вы не можете добавлять вложения