KEEN SIDE успешно заменяет аналогичные продукты таких известных брендов, как Phoenix Contact, Weidmueller, Degson, Winstar, Hsuan Mao, KLS, G-NOR, Mean Well и др.

Автономная система записи телефонных разговоров на микроконтроллере AVR. Часть 2

В первой части мы познакомились с аппаратной частью устройства. Рассмотрели используемые интерфейсы и протоколы и общие принципы работы системы.

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

Сравнительное тестирование аккумуляторов EVE Energy и Samsung типоразмера 18650

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

Устройство отслеживает состояние телефонной линии и отображает статус на дисплее: “DISCONNECTED” – линия не подключена (отсутствует напряжение на линии), “IDLE” – в ожидании(on-hook), “IN USE” – линия занята, проводится запись (off-hook).

При помощи клавиатуры пользователь может выбрать запись для прослушивания (клавиши «влево», «впрво»). Центральная клавиша «Enter» может использоваться для прерывания воспроизведения, а также для выбора режима отслеживания: всегда, только когда линия в состоянии «трубка снята» и выключен. Клавиши «вверх» и «вниз» пока не задействованы.

При обнаружении входящего звонка или DTMF посылки АТС (АОН) устройство переходит в режим записи, на второй строке индикатора отображается определенный номер. На первой строке отображается счетчик количества звонков и таймер. Запись продолжается до момента освобождения линии, если звонок принят. Если звонок не принят, запись прекращается через несколько секунд в отсутствии активности.

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

DTMF команды

Устройство распознает несколько команд, которые можно подать с помощью DTMF сигналов. Команды начинаются с префикса «**».

  • 01HHMMSS: установка времени в формате HH (часы), MM (минуты), SS (секунды);
  • 02YYMMDD: установка даты в формате YY (год), MM (месяц), DD (день);
  • ##: прекращение записи текущего звонка.

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

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

Основной временной параметр в программном обеспечении для микроконтроллера нашего проекта - это частота дискретизации, генерируемая при помощи Таймера/счетчика 0 в режиме CTC (Clear Timer on Compare Match). Код инициализации Таймера0 устанавливает делитель тактовой частоты CLK/8, при этом получаем частоту 1.3842 МГц. Прерывание Timer0 Compare Match устанавливает в регистре Output Compare 0 значение 171 каждые 5 раз или 172 в остальных случаях. В среднем, это эффективно, если разделить на (4×171+172)/5=172.8 – мы получаем точно 8000 Гц с максимальным джиттером 0.46%, что допустимо для наших целей.

АЦП и сэмплирование аудио

АЦП микроконтроллера настроен на автоматический запуск по прерыванию Timer0 Output Compare Match и вырабатывает прерывание по готовности результата (по окончанию преобразования). Это происходит регулярно каждые 823 машинных цикла, после старта преобразования (аналого-цифровое преобразование занимает 13 циклов АЦП и мы используем предделитель тактовой частоты на 64 – CLK/64).

Подпрограмма обслуживания прерывания от АЦП представляет результат в двух видах:

  • как беззнаковая байтовая величина в диапазоне 0-255, где 0 В = 128 (из-за смещения задаваемого входным каскадом). Это значение присваивается переменной sample8 (см. исходный код);
  • как 16-битное значение со знаком, где 0 В это фактически 0 – то есть мы вычитаем смещение по постоянному току. Переменная хранящее это значение называется sample16. Она представлена как 16-битная переменная, хотя фактический результат АЦП – 10-битный.

Беззнаковые значения используются для передачи по последовательному протоколу, для записи на карту памяти и воспроизведения. Старшие 8 бит значения со знаком используются в алгоритме определения DTMF сигналов. Полное 16-битное значение со знаком используется в экспериментальном компрессоре ADPCM.

DTMF детектор

Современные телефонные аппараты используют тональный набор номера DTMF (Dual-Tone Multi-Frequency – двухтональный многочастотный аналоговый сигнал), при котором каждая нажатая на клавиатуре телефона кнопка вызывает генерацию тонального сигнала, представленного сложением двух синусоидальных сигналов определенной частоты.

 

1209 Гц

1336 Гц

1477 Гц

1633 Гц

697 Гц

1

2

3

A

770 Гц

4

5

6

B

852 Гц

7

8

9

C

941 Гц

*

0

#

D

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

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

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

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

Основной параметр – размер блока. Чем больше блок, тем чувствительнее фильтр и тем меньше полоса пропускания. Мы используем блок размером N=206 выборок (назовем его - аудио блок или звуковой блок). Получаем минимальную длительность обнаружения сигнала:

206/8 кГц = 25.75 мс

и полосу пропускания приблизительно 40 Гц.

Исходя из этого, для каждой частоты мы можем вычислить коэффициенты:

k = int(0.5+f×N/S) и c = 2×cos(2 ×k × pi / N);

где, N- размер блока, f – частота, для которой будет проводится измерение интенсивности, S – частота дискретизации. Параметр с называется коэффициентом косинуса и он будет использоваться в остальной части алгоритма Герцеля.

В нашей реализации используется арифметика с фиксированной точкой с 9-битными коэффициентами, интерпретируемыми как 1 бинарная цифра до десятичной точки и 8 цифр после. Так, для частот, которые нам нужны, мы получили следующие коэффициенты:

 

Коэффициент c в формате
с фиксированной точкой

Частота

k

c

decimal

hex

440

11

1.8885

483

1E3

697

18

1.7061

437

1B4

770

20

1.6393

420

1A3

852

22

1.5664

401

190

941

24

1.4876

381

17C

1209

31

1.1706

300

12B

1336

34

1.0176

260

104

1477

38

0.8004

205

0CC

1633

42

0.5714

146

092

Заметьте, что мы добавили еще одну частоту 440 Гц. Эта частота сигналов вызова номера, занятого номера.

Для внутренней части алгоритма Герцеля, для каждой из этих частот и для каждой выборки, мы должны вычислить:

y = d1 × c - d2 + s
d2=d1
d1=y,

где d1 и d2 – промежуточные результаты, с – коэффициент косинуса (см. таблицу выше), s – значение текущей выборки. Первое вычисление по данному выражению называется «умножение и накопление». Конвертирование данного выражения в единицы с фиксированной точкой дает:

y=((d1×c)>>8)-d2+ss8,

где ss8 – значение текущей выборки в 8-битном формате со знаком (это 8 старших значащих бита переменной sample16) и «>>» – оператор сдвига вправо. Переменные d1, d2, c должны быть 16-битными значениями со знаком и фактически будут массивами, потому что они нужны будут для каждой частоты. И в результате мы должны будем проделать следующее:

for (n=0;n<9;n++) {
y=((d1[n]*c[n])>>8)-d2[n]+ss8;
d2[n]=d1[n]; d1[n]=y;
}

В конце звукового блока мы должны будем произвести завершающие вычисления:

m = d1×d1 + d2×d2 - d1×d2×s

Результатом m является величина или интенсивность сигнала для данной частоты. Конвертируя в формат с фиксированной точкой, получаем:

d1=d1>>8;
d2=d2>>8;
mag=d1*d1+d2*d2-d1*((d2*c[n])>>8);
d1=d2=0;

В итоге, алгоритм Герцеля позволяет нам определить интенсивность сигнала на заданной частоте, произведя одно умножение 16-битных значений, одно сложение, одно вычитание и одну 8-битную операцию сдвига вправо за одну выборку для обнаруженной частоты. В конце блока из 206 выборок проводятся дополнительные вычисления: три 8-битных сдвига вправо, три перемножения 16-битных значений, одно сложение и одно вычитание. И благодаря встроенному в микроконтроллер модулю аппаратного умножения, это происходит очень быстро (перемножение двух 8-битных операндов за два машинных цикла).

Однако, при попытке организации данных вычислений с использованием языка С, мы видим что это приводит к умножению 16-битного числа (d1) на 16-битное число (d2) и результат 32-битное число. Но не только в этом расточительство, т.к. требуется четыре операции 8-битного умножения, ведь мы еще потеряем младшие значащие 8 бит.

В итоге в ходе написания кода на Си мы увидели, что лишь один круг вычислений, которые мы рассмотрели выше, занял 100 машинных циклов. Вспомните, ведь мы должны проделать это для 9 частот, а это около 900 машинных циклов или 65% от нашего «бюджета» в 1376 машинных циклов. А мы еще учли часть финализации, где в три раза больше операций. По грубой оценке в общем потребовалось бы около 2700 машинных циклов.

Но финализацию мы можем провести просто. Ключ к этому – наблюдение, что мы не нуждаемся во всех результатах по окончанию звукового блока. Мы можем иметь два набора значений d1 и d2, один используется во внутренней части алгоритма, другой используется в финализирующей части с результатами предшествующего блока. Кроме того, мы можем распределить вычисления на весь блок из 206 выборок, делая только одну или две операции за отсчет. Тогда завершающая часть примет вид:

if (nn switch (cc) {
case 0: d1[n]>>=8; break;
case 1: d2[n]>>=8; break;
case 2: mag =d1[n]*d1[n]; break;
case 3: mag+=d2[n]*d2[n]; break;
case 4: coef=c[n]; break;
case 5: coef*=d2[n]; coef>>=8; break;
case 6: coef*=d1[n]; break;
case 7: mag-=coef; break;
case 8: // results interpretation,
// omitted for brevity
case 9: d1[n]=d2[n]=0; break;
}
cc++; if (cc==10) cc=0,n++;

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

Следующий шаг – оптимизация внутренней части алгоритма. С этой целью мы объединили два массива d1 и d2 в один массив d, упростив т.о. индексацию, и смогли перемещаться по массиву с помощью одной 8-битной операции инкремента (это также означает, что массив не может пересечь 256-байтную границу). Операции умножения в этой части алгоритма были реализованы с помощью ассемблера. Таким образом внутренняя часть алгоритма Герцеля заняла 42 машинных цикла, вместо 100.

В итоге весь алгоритм Герцеля, примененный в исходном коде проекта, забирает себе менее 500 циклов за выборку (менее 37% из нашего «бюджета» машинного времени).

Последовательный протокол

Последовательный протокол мультиплексирует аудио данные и команды управления на одном канале. Используется метод, при котором все передаваемые байты, за исключением определенного, называемого префиксом команды (в нашем случаем 0xFE), являются звуковыми данными. При обнаружении префикса команды следующие байты являются параметрами команды.

Определены следующие исходящие команды (от регистратора к компьютеру)

Имя команды

Формат

Описание

CMD_NOTIFY_ON_HOOK

FE 01

Линия занята

CMD_NOTIFY_OFF_HOOK

FE 02

Линия свободна

CMD_NOTIFY_DISCONNECTED

FE 03

Отсутствует напряжение на линии

CMD_DTMF_DETECTED

FE 10 XX

DTMF цифра XX (в ASCII) определена

CMD_LOCAL_RING_DETECTED

FE 11

Входящий звонок определен

CMD_ADPCM_STATE

FE 00 XX YY YY

Старт ADPCM сжатого звукового блока

CMD_RAW_BLOCK

FE 0F

Старт несжатого («сырого») звукового блока

CMD_LITERAL

FE FE

Литеральное значение 0xFE в аудио потоке

Определены следующие входящие команды (от компьютера к регистратору)

Имя команды

Формат

Описание

CMD_LCD_BACKLIGHT_ON

FE 80

Включить подсветку дисплея

CMD_LCD_BACKLIGHT_OFF

FE 81

Выключить подсветку дисплея

CMD_GO_OFF_HOOK

FE 82

Снять трубку (off-hook)

CMD_GO_ON_HOOK

FE 83

Положить трубку (on-hook)

CMD_SET_TIME

FE 90 HH MM SS

Установить время to HH:MM:SS (формат BCD)

CMD_SET_DATE

FE 91 YY MM DD

Установить дату  MM/DD/YY (формат BCD)

CMD_LITERAL

FE FE

Литеральное значение 0xFE в аудио поток

Скорость передачи – 115200 бит/с.

Перед передачей звукового блока из 206 выборок передается стартовый блок. Он информирует компьютер о том, какие данные будут передаваться (сжатые или нет) и если сжатые данные передаются, то будет отправлена информация о сжатии.

Разработка кода для MMC/SD интерфейса

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

При программной реализации интерфейса MMC/SD используется около 80% доступной памяти SRAM. Размер блока MMC/SD составляет 512 Байт и мы используем три таких блока памяти: два для двойного буфера записи/воспроизведения и один для таблицы указателей на начало кластеров для каждой записи.

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

Чтобы разместить 230 указателей в единственном индексном секторе размером 512 Байт, была разработана система адресации: мы разделили карту на кластеры с 16 секторами (8 Кбайт или приблизительно 1 с записи) и каждый номер кластера имеет 16-битную ширину. Таким образом обеспечивается поддержка карт объемом 512 МБайт.

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

Программирование микроконтроллера

Программное обеспечение для микроконтроллера ATmega32 скомпилировано с использованием AVR-GCC 3.4.3 и libc-1.2.3 (WinAVR-20050214). При установке Fuse-битов необходимо учесть, что используется внешний кварцевый резонатор и отключен JTAG-порт (он используется для подключения LCD). Исходный код снабжен комментариями и пользователь сможет экспериментировать с ним (включив отладку перед компиляцией, см. исходный код).

Приложение: описание параметров программы для компьютера, схема, исходные коды и .hex-файлы для микроконтроллеров, исходный код и программа для компьютера, описание алгоритма Герцеля и алгоритма зарядки аккумуляторов в системе резервного питания.

circuitcellar.com

Перевод: Vadim по заказу РадиоЛоцман

На английском языке: AVR Phone Recorder & Telephony Platform. Part 2

Электронные компоненты. Бесплатная доставка по России
Для комментирования материалов с сайта и получения полного доступа к нашему форуму Вам необходимо зарегистрироваться.
Имя