Иногда заедают ноты

Обсуждаем контроллеры компании Atmel.
Ответить
Родился
Сообщения: 11
Зарегистрирован: Чт янв 25, 2018 20:28:19

Сообщение Vjatheslav »

Добрый день!
Написал программку для проигрывания одноголосной мелодии. В принципе, адаптировал кусок с книги Белова. Контроллер - Tiny2313. Работает от внутреннего генератора 4МГц. Но почему-то глотает некоторые ноты. Причем, чаще одни и те же. Как будто запаздывает начало ноты, а воспроизводится с середины. Остальные играют хорошо.
Может кто сталкивался. Прошу прощения что на Ассемблере.

;======М У З Ы К А========

m3:
mov YL, count ; счетчик мелодий -> YL
ldi ZL, low(tabm*2) ;в Z -> начальный адрес "tabm"
ldi ZH, high(tabm*2) ;
rcall addw ; сложение смещения по счетчику и
; начального адреса "tabm"

lpm XL, Z+ ; извлечение из "tabm"
lpm XH, Z

;---------------------воспроизведение----------------------------
m4: mov ZH, XH ; пересылка в рабочий регистр
mov ZL, XL ;

m5: ldi b,255
ldi d,255

ldi e, 0 ; установка таймера1
out tccr1a, e

lpm temp, Z ; пересылка данных, находящихся
; по адресу в Z -> temp
cpi temp, 0xFF ; последняя нота????
breq m6 ; выход

andi temp, 0x1F ; выделяем код тона (И=И)
mov fnota, temp ; кидаем его в fnota
lpm temp, Z+ ; первую ноту в temp
rol temp ; 4-кратный круговой сдвиг кода ноты
rol temp ;
rol temp ;
rol temp ;

andi temp, 0x07 ;выделение кода длительности
mov dnota, temp ; помещаем его в "dnota"

rcall nota

rjmp m5
m6: cbi portB,3; заглушка 2 для порта (пищалка не такая как нужно)
ret

; Программа 16-тиразрядного сложения
addw: push YH
lsl YL
ldi YH,0
add ZL, YL
add ZH, YH

pop YH
ret

; Подпрограмма исполнения одной ноты
nota: push ZH
push ZL
push YL
push temp

cpi fnota, 0x00
breq nt1

mov YL, fnota ; fnota ->YL
ldi ZL, low(tabkd*2) ; адрес "tabkd" -> Z
ldi ZH, high(tabkd*2) ;
rcall addw ; переход на нужный адрес

lpm temp, Z+ ;извлечение в temp и temp1 нужного
lpm temp1, Z ;коэффициента деления
out OCR1AH, temp1
out OCR1AL, temp

ldi e, 0x40 ;установка таймера1
out tccr1a, e ;


nt1: rcall wait

ldi e, 0
out tccr1a, e

ldi dnota, 0
rcall wait

pop temp
pop YL
pop ZL
pop ZH
ret

; Подпрограмма формирования задержки

wait: push ZH
push ZL
push YH
push YL

mov YL, dnota
ldi ZL, low(tabz*2)
ldi ZH, high(tabz*2)
rcall addw

lpm YL, Z+
lpm YH, Z
clr ZL
clr ZH

; Цикл задержки
w1: ldi loop, 255
w2: dec loop
cpi loop, 0
brne w2

adiw r30, 1
cp YL, ZL
brne W1

cp YH, ZH
brne W1

pop YL
pop YH
pop ZL
pop ZH
ret

; **********************************
; Таблица длительности задержек

tabz: .dw 4,8,16,32,64,128,256

; **********************************
; Таблица коэффициентов деления
tabkd: .dw 0
.dw 152,143,135,128,121,114,107,101,97,90,85,80
.dw 76,72,68,64,60,57,54,51,48,45,43,40
.dw 38,36,34,30,29,27,25

; Таблица начал всех мелодий

tabm: .dw mel1*2,mel2*2,mel3*2,mel4*2,mel5*2,mel6*2,mel7*2,mel8*2,mel9*2

; **********************************
; Таблицы мелодий
mel1:
.db 146,141,146,141,146,145,177,145,141,145,141,145,146,178,255

и т. д.
Реклама
akl
Друг Кота
Сообщения: 4450
Зарегистрирован: Пт мар 07, 2008 06:54:43
Откуда: Ижевск

Сообщение akl »

Было бы лучше привести весь код целиком... Типа
TEST_TN2313.asm
(2.86 КБ) 374 скачивания
Реклама
Родился
Сообщения: 11
Зарегистрирован: Чт янв 25, 2018 20:28:19

Сообщение Vjatheslav »

Вот, прилагаю. Это таймер с проигрыванием музыки
Вложения
main.asm
(11.02 КБ) 237 скачиваний
Родился
Сообщения: 11
Зарегистрирован: Чт янв 25, 2018 20:28:19

Сообщение Vjatheslav »

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

Добавлено after 1 minute 25 seconds:
Как будто долго выбирает из памяти.
Реклама
Эиком - электронные компоненты и радиодетали
Друг Кота
Аватара пользователя
Сообщения: 39197
Зарегистрирован: Сб сен 13, 2014 16:27:32
Откуда: СпиртоГонск созвездия Омега

Сообщение musor »

скоросои мало надо такт мег 12 делать а лучше 25мег
ZМудрость(Опыт и выдержка) приходит с годами.
Все Ваши беды и проблемы, от недостатка знаний.
Умный и у дурака научится, а дураку и ..
Алберт Ейнштейн не поможет и ВВП не спасет.и МЧС опаздает
Реклама
Родился
Сообщения: 11
Зарегистрирован: Чт янв 25, 2018 20:28:19

Сообщение Vjatheslav »

Спасибо! Буду пробовать

Добавлено after 1 hour 44 minutes 16 seconds:
Все, я понял! Во первых неправильно написал. У меня внутренний тактовый 8МГц.
В общем, чтобы обеспечивать при помощи счетчика Т0 частоту 1Гц, я понизил общую тактовую до 250кГц. Вот он и стал заикаться при проигрывании музыки. Попробовал уменьшить коэффициент деления с 32 до 16 (соответственно поменяв коэффициенты в таблицах). Все работает как автомат Калашникова :)
Теперь задача - как сделать чтобы и музыка работала стабильно и таймер отсчитывал секунды. Создать программный счетчик? Может есть какие-нибудь ещё варианты?
Реклама
Друг Кота
Аватара пользователя
Сообщения: 39197
Зарегистрирован: Сб сен 13, 2014 16:27:32
Откуда: СпиртоГонск созвездия Омега

Сообщение musor »

ты музыку играшь ДАЖЕ без кварца??? о какой стабилности может идтиречь???
НУ ВЫ БОЛИН ДАЕТЕ :facepalm:
ZМудрость(Опыт и выдержка) приходит с годами.
Все Ваши беды и проблемы, от недостатка знаний.
Умный и у дурака научится, а дураку и ..
Алберт Ейнштейн не поможет и ВВП не спасет.и МЧС опаздает
Родился
Сообщения: 11
Зарегистрирован: Чт янв 25, 2018 20:28:19

Сообщение Vjatheslav »

Да ноги просто все занял. Он же вроде калиброванный. Или плавает все-таки?
Родился
Сообщения: 11
Зарегистрирован: Чт янв 25, 2018 20:28:19

Сообщение Vjatheslav »

Добрый день! Подскажите, пожалуйста, как может влиять то, в каком месте расположен блок программы с закодированными мелодиями на возникновение разного рода глюков при проигрывании музыки? Если я его перемещаю в самый конец, то музыка превращается в какие-то непонятные звуки. Если перемещаю ближе к основному циклу мелодия играет, но некоторые ноты глючат. Если располагаю почти сразу за основным циклом - все играет нормально. Может быть в этом была причина? Почему это может происходить?
Идёт направо - песнь заводит, Налево - сказку говорит.
Аватара пользователя
Сообщения: 5000
Зарегистрирован: Чт апр 21, 2011 17:55:50
Откуда: Иркутск

Сообщение СКАЗОЧНИК »

Программу не смотрел и не разбирался, но по описанным симптомам, что-то похожее на вылет за приделы памяти... Смотрите, как организована у вас отсылка к данным в массиве с нотами. Т.е. может адресация страдает. Относительные переходы и все такое. ))))
Станислав
Мудрый кот
Сообщения: 1849
Зарегистрирован: Вс дек 25, 2016 08:34:54

Сообщение Dimon456 »

При сборке проекта предупреждение

Код: Выделить всё

warning: .cseg .db misalignment - padding zero byte
директива .DB в сегменте кода используется с нечетным количеством байт
Модератор
Аватара пользователя
Сообщения: 19055
Зарегистрирован: Сб авг 14, 2010 15:05:51
Откуда: г. Озерск, Челябинская обл.

Сообщение Starichok51 »

СКАЗОЧНИК писал(а):вылет за приделы памяти
приделы есть только у церкви. у памяти приделов нет...
Мудрость приходит вместе с импотенцией...
Когда на русском форуме переходят на Вы, в реальной жизни начинают бить морду.
Вымогатель припоя
Сообщения: 615
Зарегистрирован: Вс дек 28, 2014 21:54:05

Сообщение dgrett »

А может всё дело в том, что в прерываниях что-то не сохраняется, SREG, регистры какие..
PS Язвите, старичёк:)
Я всё-всё узнAю и стану профессором.
Родился
Сообщения: 11
Зарегистрирован: Чт янв 25, 2018 20:28:19

Сообщение Vjatheslav »

[uquote="Dimon456",url="/forum/viewtopic.php?p=3310745#p3310745"]При сборке проекта предупреждение

Код: Выделить всё

warning: .cseg .db misalignment - padding zero byte
директива .DB в сегменте кода используется с нечетным количеством байт
[/uquote]

А как это можно исправить? Добавлять по нулям в нечетных строках?
Мудрый кот
Сообщения: 1849
Зарегистрирован: Вс дек 25, 2016 08:34:54

Сообщение Dimon456 »

Vjatheslav писал(а):А как это можно исправить? Добавлять по нулям в нечетных строках?
Компилятор и так нули добавляет, может в этом и есть пропуск нот.
Посмотрите реализацию этого проекта, там есть как раз на tiny2313.
afz
Опытный кот
Аватара пользователя
Сообщения: 744
Зарегистрирован: Сб дек 22, 2012 08:17:42
Откуда: Караганда, Казахстан

Сообщение afz »

Точно, там не сохраняются SREG и регистр e=r21. Не вникал, может этот регистр и должен управляться из прерывания, но про SREG топикстартер точно забыл.

2 Vjatheslav: Я подобные программы делаю так.

Во-первых выбираю квант времени. Он должен быть достаточно маленьким, чтобы обеспечить все нужные значения времени и, в то же время, достаточно большим, чтобы за это время можно было выполнить вменяемое количество команд, допустим, 1000, лучше - 10000. Допустим, для 8-й меги с тактовой частотой 8 МГц квант в 1 мс - это около 8000 команд за квант, очень неплохо.

Инициализирую таймер на многократные прерывания с периодом, равным кванту. Не все таймеры могут это делать аппаратно, такие приходится подправлять в прерывающей программе. В частности у 8-й Меги нулевой таймер не имеет аппаратной перезагрузки количества тиков до прерывания, приходится его каждый раз обновлять из прерывающей программы. Саму прерывающую программу делаю предельно короткой - отметил факт произошедшего прерывания в специально выделенной ячейке ОЗУ, допустим, с меткой TQOK (Time Quantum OK, исходно в ней лежит ноль, а программа прерывания заносит туда единицу), далее (возможно) подправил таймер и RETI. Да, естественно, не забыть сохранить и в конце восстановить рабочий регистр и SREG.

Во-вторых, строю таблицу задач. Каждая строка имеет два-три поля: (1) счетчик квантов, т.е. количество квантов времени, оставшееся до запуска этой задачи, (2, возможно) какие-то флажки, типа "задача активна" или что-то подобное, и, наконец, адрес подпрограммы, выполняющей эту задачу.

Ну, и, собственно, программа. Все ожидания делаются в основной задаче. Основной цикл состоит в периодической проверке того самого флага TQOK. Конкретно - закрываем прерывания и проверяем TQOK. Если там ноль, открываем прерывания и засыпаем, затем идем в начало цикла. Если же в TQOK не ноль, очищаем его и открываем прерывания.

Далее проходим по таблице задач. Для каждой задачи первым делом проверяем флаг активности (если он предусмотрен), неактивные задачи просто пропускаем. Активной же задаче уменьшаем на единицу счетчик квантов и смотрим не ноль ли получился. Если ноль, вызываем подпрограмму этой задачи. После проверки и (возможно) исполнения всех задач, уходим на начало основного цикла, где закроют прервыания и проверят TQOK. Все!

Это получилась простейшая примитивная таймерная RTOS. Задачи в ней должны быть предельно короткими - что-то включить, что-то выключить, что-то подкрутить, и выйти. Тем не менее, поставленную задачу она решает влет.

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

Часы. Раз в секунду запускаем программу часов, которая, собственно, и считает время.

Будильник. Раз в минуту запускаем задачу будильника, которая сравнивает показания часов и установленное время побудки. При совпадении, активизирует задачу "проиграть мелодию", а именно, задает ей адрес начала мелодии, заносит ей в счетчик квантов единичку (запуск на следующем кванте) и ставит ей флаг "Задача активна". Получается, здесь этот флаг нужен, то есть таблица задач будет иметь три поля. Что бывает, в общем-то, далеко не всегда.

Проигрывание мелодии. Смотрим, последняя у нас нота, или нет. Если последняя, снимаем свой флажок "задача активна" и выходим, иначе отсылаем нужный код в таймер Т1, отсылаем себе в счетчик квантов код длительности этой ноты, вестимо, в квантах, и можно выходить.

При желании можно сделать отдельную задачу "Злобный будильник", которую должен будет активировать основной будильник, опять же на следующем кванте, вместо проигрывания мелодии. Злобный будильник запускает мелодию и ставит свой повтор через, допустим, 5 мин. При повторном запуске можно выдать и мелодию позлее и погромче, и все, на что хватит фантазии.

Да, при такой организации, если не принять довольно сложных мер, задачи ничего не могут хранить в регистрах, также сложно использование динамической памяти, но, учитывая, что памяти у 2313 и так копейки, ее следует распределить статически, у каждой задачи свой набор статических переменных, и все. При запуске задача поднимает из них в регистры то, что надо, по окончании сохраняет что надо в них же, и выход. Все регистры при входе в задачу можно считать свободными, кроме, может быть указателя на обрабатываемую строку таблицы задач. Впрочем, никто не мешает главному циклу сохранить этот указатель перед вызовом задачи и восстановить его после возврата из задачи.
Кто мешает тебе выдумать порох непромокаемый? (К. Прутков, мысль № 133)
Вымогатель припоя
Сообщения: 615
Зарегистрирован: Вс дек 28, 2014 21:54:05

Сообщение dgrett »

Кстати, сейчас только глянул, и в глаза бросилось:
addw: push YH
lsl YL
ldi YH,0
add ZL, YL
add ZH, YH

pop YH
ret

Vjatheslav, У вас неправильно вычисляется адрес, поэтому "проигрывается" код из программной области. Вы играете не ноты, а саму программу.
Поменяйте так:
adc ZH, YH
Я всё-всё узнAю и стану профессором.
Родился
Сообщения: 11
Зарегистрирован: Чт янв 25, 2018 20:28:19

Сообщение Vjatheslav »

Благодарю! Буду пробовать.
Ответить

Вернуться в «AVR»