Обсуждаем контроллеры компании Atmel.
Ответить

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

Чт янв 25, 2018 20:43:15

Добрый день!
Написал программку для проигрывания одноголосной мелодии. В принципе, адаптировал кусок с книги Белова. Контроллер - 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

и т. д.

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

Пт янв 26, 2018 07:04:29

Было бы лучше привести весь код целиком... Типа
TEST_TN2313.asm
(2.86 KiB) Скачиваний: 357

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

Пт янв 26, 2018 12:09:13

Вот, прилагаю. Это таймер с проигрыванием музыки
Вложения
main.asm
(11.02 KiB) Скачиваний: 210

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

Сб янв 27, 2018 10:15:43

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

Добавлено after 1 minute 25 seconds:
Как будто долго выбирает из памяти.

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

Сб янв 27, 2018 10:17:43

скоросои мало надо такт мег 12 делать а лучше 25мег

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

Сб янв 27, 2018 14:22:46

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

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

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

Сб янв 27, 2018 14:30:27

ты музыку играшь ДАЖЕ без кварца??? о какой стабилности может идтиречь???
НУ ВЫ БОЛИН ДАЕТЕ :facepalm:

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

Сб янв 27, 2018 17:31:31

Да ноги просто все занял. Он же вроде калиброванный. Или плавает все-таки?

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

Сб фев 17, 2018 08:38:27

Добрый день! Подскажите, пожалуйста, как может влиять то, в каком месте расположен блок программы с закодированными мелодиями на возникновение разного рода глюков при проигрывании музыки? Если я его перемещаю в самый конец, то музыка превращается в какие-то непонятные звуки. Если перемещаю ближе к основному циклу мелодия играет, но некоторые ноты глючат. Если располагаю почти сразу за основным циклом - все играет нормально. Может быть в этом была причина? Почему это может происходить?

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

Сб фев 17, 2018 10:20:58

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

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

Сб фев 17, 2018 11:06:25

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

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

Сб фев 17, 2018 15:51:27

СКАЗОЧНИК писал(а):вылет за приделы памяти
приделы есть только у церкви. у памяти приделов нет...

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

Сб фев 17, 2018 17:53:31

А может всё дело в том, что в прерываниях что-то не сохраняется, SREG, регистры какие..
PS Язвите, старичёк:)

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

Вс фев 18, 2018 07:32:52

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


А как это можно исправить? Добавлять по нулям в нечетных строках?

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

Вс фев 18, 2018 08:03:26

Vjatheslav писал(а):А как это можно исправить? Добавлять по нулям в нечетных строках?
Компилятор и так нули добавляет, может в этом и есть пропуск нот.
Посмотрите реализацию этого проекта, там есть как раз на tiny2313.

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

Вс фев 18, 2018 09:43:32

Точно, там не сохраняются 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 и так копейки, ее следует распределить статически, у каждой задачи свой набор статических переменных, и все. При запуске задача поднимает из них в регистры то, что надо, по окончании сохраняет что надо в них же, и выход. Все регистры при входе в задачу можно считать свободными, кроме, может быть указателя на обрабатываемую строку таблицы задач. Впрочем, никто не мешает главному циклу сохранить этот указатель перед вызовом задачи и восстановить его после возврата из задачи.

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

Вс фев 18, 2018 10:12:09

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

pop YH
ret

Vjatheslav, У вас неправильно вычисляется адрес, поэтому "проигрывается" код из программной области. Вы играете не ноты, а саму программу.
Поменяйте так:
adc ZH, YH

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

Вс фев 18, 2018 11:06:26

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