DUC-DDC Трансивер UA3REO: уменьшаем джиттер

Джи́ттер — фазовое дрожание цифрового сигнала данных нежелательные фазовые или частотные отклонения передаваемого сигнала. Большое его значение сильно искажает принимаемый сигнал, а точнее уменьшает соотношение сигнал-шум. И с увеличением частоты влияние джиттера становится всё более заметным.

Если кратко, джиттер это когда переход между логическими состояниями тактового сигнала происходит через рваные промежутки времени, хоть и сохраняет при этом частоту.

Это приводит к тому, что выборки из АЦП происходят раньше или позже необходимого времени, и чем этот разброс больше, тем хуже.

Джиттер измеряется в пикосекундах (плохие значения) и фемтосекундах (хорошие значения).

В текущей реализации трансивера основным источником джиттера являются:
1. низкое качество кварцевого генератора
2. отсутствие синхронизатора тактового сигнала вроде микросхемы MC10EL32 или подобной, вот тут замечательная статья http://www.russianelectronics.ru/leader-r/review/2190/doc/48469/
3. использование FPGA в процессе передачи тактового сигнала — идеально, когда АЦП подключен к генератору напрямую.
4. использование PLL блоков в FPGA — в микросхемах семейства Cyclone IV джиттер может доходить до 600пикосекунд!

В этой статье опишем как побороть проблему 3 и 4 (первые 2 потребуют нового генератора на 48мгц, которого у меня нет, либо нового АЦП под мой генератор в 96мгц). А также опишем общие понятия, как перестроить систему под любую тактовую частоту.

Подаём на АПЦ сигнал напрямую с генератора, его частота не должна превышать максимальную частоту АЦП. Для меня это будут 50мгц генератор от FPGA и 65msps АЦП.

Далее в Quartus удаляем выходы ADC_CLK / DAC_CLK, они нам больше не нужны.

Сейчас тактование всей логики FPGA происходит через PLL модуль, который снижает частоту с 50мгц до 48мгц, будем использовать 50мгц напрямую, т.к. частота смесителей, NCO генератора и CIC фильтров должна соответствовать АЦП.

Перестраиваем NCO на новую частоту.

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

Обновляем параметры CIC фильтра RX и TX части.

Рассчитываем новый CIC компенсатор в MatLab.

Устанавливаем название, коэффициент децимации (для RX) или интерполяции (для TX), указываем параметры нашего CIC фильтра и границу среза.

Устанавливаем разрядность входных и выходных данных.

Собираем код в последовательной архитектуре (можно попробовать параллельную, но это увеличит количество блоков, используемых в FPGA).

В окне Matlab будет указано, во сколько раз частота подаваемая на модуль, должна превышать частоту входного аудио-сигнала.

Поводим эту операцию и для TX и для RX компенсатора.
Переносим файлы в папку проекта, в Quartus нажимаем по ним правой кнопкой и выбираем Create Symbol Files for Current File.

ВАЖНО! удаляем из схемы проекта старые компенсатора и добавляем новые, просто заменить файлы не достаточно, т.к. сохранятся коэффициенты.

Подстраиваем частоту PLL под наш аудио-поток.

Собираем программу и прошиваем FPGA.

Правим значение тактового генератора в STM32 прошивке (файл fpga.h), это значение используется для расчёта фразы частоты, передаваемой в NCO.

Компилируем, прошиваемся, наслаждаемся более чистым сигналом!

14 мыслей о “DUC-DDC Трансивер UA3REO: уменьшаем джиттер”

  1. Дважды переустанавливал матлаб, не вылечился ( Попытаю удачи с более ранней версией, когда скачается.

    1. Версия 2017 года оказалась более восприимчивой к лечению. Но, видимо, все это пока что зря, т.к. мой-то генератор на 122.88, а АЦП 65, так что буду ждать введения Вами в схему генератора на 96МГц.

      1. Пока меня останавливает сложность реализации делителя, я рассчитывал на PLL в FPGA, Но у него такой бешенный джиттер, что от этой идеи я отказался.
        Думаю в сторону микросхемы https://ru.mouser.com/datasheet/2/308/MC10EL32-D-109777.pdf — даже в даташитах к АЦП она и его брат на делитель 4 участвуют. Схему подключения можно взять с даташита AD9226.
        Соответственно делим и стабилизируем им частоту на 2, а дальше всё как в статье.
        Также, возможно начну свои разработки с более дешевого триггера 74HC574D, самый быстрый из того что смог найти у себя в городе.

        1. А для Вашей реализации схемы обязательны подтягивающие резисторы в ПЛИС на линиях АЦП?

  2. Что-то я, видимо, упускаю при портировании с vet6 на zet6 — кодек инициализируется и активируется, интерфейс отрисовывается, тач работает, но ни водопада, ни спектра, ни звука. Кубовые проекты сверяю до каждого значения, в fpga.h GPIOC на свой меняю (к слову, может быть лучше было бы поставить дефайн вместо прямого определения?), но эффект нулевой. Заметил, что Вы добавили тестирование шины данных с ПЛИС, завтра посмотрю, что выдается.

    1. Обратите внимание на смещения << в FPGA_writePacket (fpga.c) и FPGA_readPacket. Например в bitRead(packet, 2) << 6 & FPGA_OUT_D2_Pin, 6ка значит 6й вывод, т.е. я читаю напрямую из регистра, а не по именам пинов, если ножки другие то надо менять эти значения. Ну и тестирование да, тоже скажет какая нога не отвечает на пару приём-передача.

    2. И обратите внимание, что в последней версии прошивки, обмен по шине STM32-FPGA тактируется с помощью PLL на 48кгц от FPGA, а не по таймеру

  3. Переназначил ноги ПЛИС, перестал компилироваться проект (что-то про старые и новые значения было), сделал очистку, теперь не формируется sof файл 🙂

    1. Если пишет что-то про honor location — помогает удаление папок db и incremental_db и потом переоткрыть проект

      1. Оказывается, квартус вылечился не до конца ) Поправил лицензию, файлы стали формироваться. Правда, особого эффекта это не оказало, хоть тест шины ПЛИС и выдает ОК, ни звука, ни спектра нет. Завтра скачаю текущий архив с проектом и буду с ним разбираться.

        1. Я для отладки использую UART и вывожу через него данные.
          Например в файле stm32f4xx_it.c функция TIM6_DAC_IRQHandler — по таймеру раз в секунду можно вывести отладку.
          Думаю стоит вывести следующие значения:
          1. FPGA_Audio_Buffer_Q[0] и FPGA_Audio_Buffer_I[0] — посмотреть что в аудио-буффере прилетающим из FPGA
          2. FPGA_Audio_Buffer_Index — двигается ли индекс по буферу FPGA
          3. FPGA_Audio_Buffer_Q_tmp[0] и FPGA_Audio_Buffer_I_tmp[0] — кеширующий буфер
          4. Processor_AudioBuffer_A[0] и Processor_AudioBuffer_B[0] — буфер обработчика звука
          5. Processor_AudioBuffer_ReadyBuffer — активный буффер обработчика, должен периодически меняться
          6. CODEC_Audio_Buffer_RX[0] — буффер аудио-кодека
          7. WM8731_DMA_state — состояние DMA аудио-кодека (полупустой или полный, должен периодически меняться)
          8. FFTInput_A[0] и FFTInput_B[0] — буфер водопада
          9. FFTInputBufferInProgress — текущий буфер водопада

          если вообще ничего и по нулям:
          1. вывести FPGA_samples — сколько раз в секунду запустилась шина получения аудио с FPGA, Должно быть около 48000
          2. запускается ли функция FPGA_fpgadata_getiq (в ней что-нить в UART вывести)
          3. запускается ли FPGA_fpgadata_iqclock (аналогично)

          а уже FPGA_fpgadata_iqclock дёргает прерывание EXTI15_10_IRQHandler — если оно не вызывается то нет клока от FPGA на 48кгц

          1. Довел проект до состояния отображаемого водопада. Правда, картинка неправильная, жаль, прикрепить нельзя.
            Тест шины ОК, FPGA_samples 48к с небольшим, все остальное, что было закомментировано в TIM6_DAC_IRQHandler по нулям, кроме WM8731_DMA_state, всегда 1. Если нажать TUNE, AUDIOPROC_TXA_samples и AUDIOPROC_TXB_samples примерно по 750. FPGA_Audio_Buffer_Q[0] и FPGA_Audio_Buffer_I[0] по нулям.
            processRxAudio в TIM5_IRQHandler вызывается, но из-за того, что Processor_NeedBuffer, видимо, постоянно false, сразу происходит выход.

Добавить комментарий

Ваш e-mail не будет опубликован. Обязательные поля помечены *