Раздел: Телефония На
данной страничке сделана попытка рассказать о том, как
самому написать программу декодера DTMF для PIC
(например в целях уменьшения вашей схемы на целую
микросхему апаратного декодера DTMF типа 1008ВЖ18), на основе распостранённого
алгоритма - массово используемого в АОНах, подробное
описание которого имеется вот тут: Теория корреляционного алгоритма для АОН
. Если желания или
времени разбиратся (в чём я был прав, а что лучше
переделать по своему) в моёй программе нет, то можете не
тратя время - скачать архив с исходным текстом на asm, для
включения в свой проект.
Для остальных (тем кому это интересно) попробую
объяснить как это всё работает, те "разобрать свою
программу по косточкам" на самом простом и понятном даже
для новичка уровне:
В
качестве эталонного сигнала в программе используется таблица значений SIN и COS для каждой из
8 частот DTMF. Таблица составлена таким образом, чтоб
при выборке из неё очередных 2х байт (16 бит) иметь в
регистрах Byte1 и Byte2 значения эталонных 8 частот для
данного момента времени(выборке) как по синусам так и по
косинусам одновременно (в этой части я ничего не
изобретал а взял классическое решение от АОНа, вот
здесь: Пример выборки для АОНа ).
Вот
собственно и сам текст хитро перемещаемой подпрограммы
выборки из таблицы более 256 байт ( делал не я, автор Peet_on_B3 ) которая
загружает в регистры Byte1 и Byte2 очередные значения:
tab_dtmf call tab_00 movwf Byte1 call tab_00 movwf Byte2 clrf PCLATH ; 00 - адрес PCLATH откуда был вызов ПП return tab_00 movf dtmf_page,w movwf PCLATH ; выход из таблицы с сохранением значения в W регистре incf dtmf_adrs,f btfsc STATUS,Z incf dtmf_page,f decf dtmf_adrs,w movwf PCLdtmf_file include dtmf.tab ; это файл значений таблицы эталонных частот DTMF
Выборка новых пар значений и сравнение их с входным
сигналом из таблицы происходит с периодом в 70 мкс (так
само собой получилось, при частоте задающего генератора
4 Мгц), итого за ~17,9 мс происходит 256 выборок, что
достаточно для накопления статистики о наибольшем
присутствии какой то одной из 4 частот в каждой из
верхней и нижней группе, но это моё субъективное мнение,
тк я считаю что увеличение выборок до 512 "съест"
половину программной памяти(свёртывать в 2 раза таблицу
по чётным и нечётным строкам(как используют в АОН в
целях экономии) нельзя, тк при разворачивании произойдёт
нарушение временного "статус-кво" и придётся цикл
выборки привязывать к таймеру, что есть потери времени и
ненужное задействование апаратных ресурсов - что мне
никак не хочется), а уменьшение до 128 (любимое число
АОНов тк укладывается в 10 мс)"сгладит" максимумы на
уровне шумов и DTMF декодерирование, например в условиях
радиканала будет не уверенным. В АОНах "окно"
увеличивать принципиально нельзя из-за того что там
принимается "безинтервальник" с фиксированой
длительностью цифр, а в DTMF окно можно без проблем
"растянуть" минимум на 40 мс. Хотя может я и не прав,
но во всяком случае переменная цикла равная 256 и не
требующая перезагрузки это красиво, код в цикле
выполняется строго 70 мкс не зависимо не от чего и не
требует синхронзации от таймера, доп.внешнего кварца итд
- то это тоже красиво, оставшегося времени хватает на
обработку и программа "вписывается красиво по времени" в
1/2 минимального времени обнаружения DTMF и 1/3
минимального времени генерации DTMF и при этом не
требует "эксклюзивного" кварца 3,58 а работает от
классического 4 МГц (в тестовом варианте работает весьма
сносно даже от встроенного
IRC).
Если требуется по
каким-то соображениям, увеличить или уменьшить размеры
таблицы (в моём варианте это 512 байт т.е. 1/4
программной памяти)) а так-же изменить время выборки -
вобщем если у Вас есть желание и время "поиграть" с
этими параметрами, то можете скачать архив с исходной
таблицей в Excel и переделать её на своё усмотрение: Исходная таблица для расчёта эталонных
значений.
Теперь
рассмотрим как это всё работает т.е., сейчас я
прокоментирую самые на мой взгляд - интересные
части:
В этом блоке происходит выборка
"окна" в 17,9 мс, я тут ничего не изобретал а
использовал принцип предложенный Елисеевым
Александром pd_200 call tab_dtmf ; выборка очередных 2х байт из таблицы btfsc CMCON,7 ; смотрим на состояние выхода встроенного компаратора comf Byte1,f ; инвертируем сразу все 16 отчётов если на выходе "1" btfsc CMCON,7 ; или оставляем всё как есть если на выходе компаратора "0" comf Byte2,f btfss Byte1,0 ; подсчитываем все "1" для 16 накопительных ячеек за 256 циклов incf F697_1,f ; это ещё не коэфициенты кореляции а просто счётчики совпадений btfss Byte1,1 ; эталонов синусоид и косинусоид с реальным сигналом incf F697_2,f btfss Byte1,2 incf F770_1,f btfss Byte1,3 incf F770_2,f btfss Byte1,4 incf F852_1,f btfss Byte1,5 incf F852_2,f btfss Byte1,6 incf F941_1,f btfss Byte1,7 incf F941_2,f btfss Byte2,0 incf F1209_1,f btfss Byte2,1 incf F1209_2,f btfss Byte2,2 incf F1336_1,f btfss Byte2,3 incf F1336_2,f btfss Byte2,4 incf F1477_1,f btfss Byte2,5 incf F1477_2,f btfss Byte2,6 incf F1633_1,f btfss Byte2,7 incf F1633_2,f decfsz CScan,1 goto pd_200 ; крутимся 256 раз (17920 мкс) - накапливая статистику (1 оборот строго 70 мкс) В
следующем блоке происходит обработка того что приняли в
окне по принципу вычитания "половинки+поправка"
(поправка нужна так-так в таблицу укладывается не всегда
кратное число периодов и соответственно сумма "1" будет
не 128 !!!) и последующего сложения SIN и COS
составляющих, те имитация вычисления "сложения SIN и COS
по модулю два" из накопительных ячеек F697_1...F1633_2 в
целях получения значений "ноль" если сигнал отсутствует
или "около нуля" если сигнал имеет слабую корреляцию с
искомой частотой при её фазе 0, 90, 180 или 270
градусов
movlw .136 ; вычитаем 128 + плюс поправочный коэфициент subwf F697_1,f ; и сохраняем SIN+COS в Fxxx_1 btfss STATUS,C ; если произошёл заем те число меньше то значит приняли comf F697_1,f ; инверсию (сдвиг фазы на 180), что нас тоже интересует movlw .131 ; теперь переворчиваем фазу и имеем в итоге инфо о корреляции subwf F697_2,f ; при фазе 0 и 180 градусов как для синуса так и для косинуса. btfss STATUS,C comf F697_2,f movf F697_2,w addwf F697_1,f ; обработка частоты 697 Гц закончена, продолжаем то-же movlw .128 ; проделывать и для остальных частот, сохраняя результат subwf F770_1,f ; корреляции в регистрах Fxxx_1(в целях экономии ОЗУ) btfss STATUS,C comf F770_1,f movlw .127 subwf F770_2,f btfss STATUS,C comf F770_2,f movf F770_2,w addwf F770_1,f movlw .130 subwf F852_1,f btfss STATUS,C comf F852_1,f movlw .130 subwf F852_2,f btfss STATUS,C comf F852_2,f movf F852_2,w addwf F852_1,f movlw .128 subwf F941_1,f btfss STATUS,C comf F941_1,f movlw .128 subwf F941_2,f btfss STATUS,C comf F941_2,f movf F941_2,w addwf F941_1,f movlw .130 subwf F1209_1,f btfss STATUS,C comf F1209_1,f movlw .128 subwf F1209_2,f btfss STATUS,C comf F1209_2,f movf F1209_2,w addwf F1209_1,f movlw .129 subwf F1336_1,f btfss STATUS,C comf F1336_1,f movlw .129 subwf F1336_2,f btfss STATUS,C comf F1336_2,f movf F1336_2,w addwf F1336_1,f movlw .135 subwf F1477_1,f btfss STATUS,C comf F1477_1,f movlw .128 subwf F1477_2,f btfss STATUS,C comf F1477_2,f movf F1477_2,w addwf F1477_1,f movlw .131 subwf F1633_1,f btfss STATUS,C comf F1633_1,f movlw .125 subwf F1633_2,f btfss STATUS,C comf F1633_2,f movf F1633_2,w addwf F1633_1,f ; первичная обработка 8x10=80 мкс (всего ~1/240 часть от общего времени выбоки и обработки "окна")
Теперь можно протестировать работу программы наглядно,
те выдать в цикле значения ячеек F697_1 ... F1633_1 в программу-терминалку на Ваш персональный
компьютер через апаратный USART 16F628 (8 бит, 1
стоповый, 9600 бод), в целях контроля работоспособности
алгоритма, само собой зациклив сканирование и подключив
на вход компаратора "бипер" + источник шума, по
следующей схеме:
![]()
Вариант схемы в формате PCAD
4.5 Для
работы этой схемы необходимо выполнить следующую
инициализацию: ;-------------------------------; инициализация модуля компараторов movlw B'00000101' movwf CMCON ; подключен только 2 компаратор (входы 1 компаратора можно использовать bsf STATUS,RP0 ; как цифровые для полноценного ввода-вывода) movlw B'11100110' movwf VRCON ; на вывод RA2 подключаем внутренний источник +Vout=1,25 (при питании +5в) bcf STATUS,RP0;-------------------------------; инициализация USART bsf STATUS,RP0 movlw .25 ; задаём скорость 9600 movwf SPBRG bsf TXSTA,BRGH bcf TXSTA,SYNC bsf TXSTA,TXEN bcf STATUS,RP0 bsf RCSTA,SPEN
Далее включаем в проект следующую тестовую подпрограмму:
out_DTMF call out_busy movwf TXREG returnout_busy clrwdt btfss PIR1,TXIF goto out_busy return Которая выдаёт
значение рабочего регистра в асинхронном режиме в порт
компьютера, само собой в рабочий регистр надо будет
грузить по очереди все 8 регистров F697_1 ... F1633_1 и
дополнять пробелами для наглядности(что-б значения
корреляции для одной и той-же частоты в терминалке
выводились в столбик), например вот так: movf F697_1,w call out_DTMF movf F770_1,w call out_DTMF .............. movf F1633_1,w call out_DTMF movlw 0x20 call out_DTMF .............. Теперь
когда мы своими глазами убедились в том что
корреляционный алгоритм действительно устойчиво работает
(и если такая работа(вероятность,надёжность...) Вас
устраивает) можно провести перевод значений максимумов в
реальное значение DTMF и сохранить его в регистре
"RxDtmf" используя следующий код: clrf RTemp ; очистка битового указателя на начальные максимумы movlw F697_1 ; загружаем указатель на F697_1 movwf FSR movf INDF,w subwf F770_1,w btfss STATUS,C goto pd_300 incf FSR,1 incf FSR,1 bsf RTemp,0 pd_300 movf INDF,w subwf F852_1,w btfss STATUS,C goto pd_320 movlw F852_1 movwf FSR bsf RTemp,1 pd_320 movf INDF,w subwf F941_1,w btfsc STATUS,C bsf RTemp,2 movlw F1209_1 movwf FSR movf INDF,w subwf F1336_1,w btfss STATUS,C goto pd_340 incf FSR,1 incf FSR,1 bsf RTemp,3 pd_340 movf INDF,w subwf F1477_1,w btfss STATUS,C goto pd_360 movlw F1477_1 movwf FSR bsf RTemp,4 pd_360 movf INDF,w subwf F1633_1,w btfsc STATUS,C bsf RTemp,5 btfss RTemp,2 ; разбираемся какое принято базовое число 1,4,7,10 goto pd_400 ; в зависимости от состояния 3 флагов в регистре указателе movlw .10 ; на максимум в верхней группе частот movwf RxDtmf goto pd_500pd_400 btfss RTemp,1 goto pd_420 movlw .7 movwf RxDtmf goto pd_500 pd_420 btfss RTemp,0 goto pd_440 movlw .4 movwf RxDtmf goto pd_500 pd_440 movlw .1 movwf RxDtmf ; блок коррекции по номеру столбца базового числаpd_500 ; те прибавляем к 1,4,7,10 числа 1,2,3 в зависимости btfss RTemp,5 ; от состояния 3 флагов в регистре указателе goto pd_520 ; на максимум в нижней группе частот movlw .3 addwf RxDtmf,1 goto pd_600 pd_520 btfss RTemp,4 goto pd_540 incf RxDtmf,1 incf RxDtmf,1 goto pd_600 pd_540 btfsc RTemp,3 incf RxDtmf,1 ; в RxDtmf конечный результат ( только коду "0" соотв.зачение 11 !!!)
В итоге получили искомый код DTMF за время менее 20 мс,
если-б это был АОНовский "безинтервальник" - можно
было-б сказать что "дело сделано", но тк у DTMF есть
такая неприятность как неизвестная длительность (от 60
мс и больше) и паузы между посылками, то приходится ещё
делать несколько повторных проверок (хватает 2х но
лутчше 3), при этом попутно ловится пауза запись
значения в регистр "00" если DTMF отсутствует, что
так-же можно использовать в Вашей программе. Эту часть
программы я тут не привожу тк считаю что та часть моей
программы где делается сравнение 2-х байт и результат
заносится в счётчик совпадений достаточно проста для
понимания даже
начинающим.
На этом пока
всё - берите готовый проект для MPLab и дополняйте своим
кодом, если найдёте на страничке неточности (подчти всё
в НТМL набрал за один день) или просто "ляпы", или будут
вопросы и пожелания - то отпишите мне: Сергею Смирнову.
Сайт: http://www.zk.ru/smsn/dtmf/
Назад
Разместил: mikesmith
Напечатать
текущую
страницу
|