Жене мешают смотреть последние новости из телефона и телевизора приходящие поесть (первично ?) и поиграть на компьютере (вторично?) внуки. Она их конечно любит , но звуки их взаимодействия с компьютером ее сильно раздражают. Пришлось надеть на внуков наушники. А звуковой выход у компа в неудобном месте и каждый ребенок хочет звук со своей громкостью. Ну пришлось разработать внешнюю USB звуковую карточку. Хочется и красиво и качественно. Впрочем, внуки скорее только повод для поностальгировать по своей старой специальности радиоконструктора и вообще, так как последние двадцать с лишним лет я далеко от нее и пишу заклинания программы реконструкции изображений для медицинских томографов в больших и не очень фирмах , то есть энжинер-погромист по специальности. Хотел написать статью на эту очень интересную и важную тему (компьютерная томография), но выяснилось что мне нельзя по условиям контракта ...


Итак вернемся к нашим баранам внукам и звуковым карточкам, которые у нас есть в нескольких экземплярах ,овер дофига, купил пока были дешевые, модули из Китая:

  1. Stm32f401ccu6 black pill – сейчас $3 за штучку;

  2. I2S DAC Decoder GY-PCM5102 ->$3.5 за штучку;

  3. SPI display ips 1.3 inch 240x240 (controller st7789) ->$7 за два.

Сначала построим максимальную конфигурацию из двух экранов и I2S GY-PCM5102.

Конфигурируем куб, разбавляем его говно код своим г. кодом, добавляем ФАПЧ ( фазовой автоматической подстройки частоты или PLL на ихнем ) , для согласования скоростей приходящих от компьютера данных и выдачи на i2s внешний ЦАП (DAC). Хмм , звучит очень неплохо, явно лучше большинства встроенных звуковушек. PCM5102 – весьма и весьма качественный ЦАП за свою цену в пару- тройку долларов за модуль с чипом. Добавляем отображение индикаторов уровня на паре неплохих дисплеев st7789...

С ними нужно было немного повозиться. Во первых у этих не выведен CS (Chip Select – ножка выбора чипа) . Поэтому каждому свой SPI ( SPI_1 и SPI_3). Во вторых их DMA (прямой доступ к памяти) сильно тормозит. Соотвественно копирование полного экрана из памяти занимает для SPI_1 - 32 mS , а для SPI_3 - 51 миллисекунды соотвественно.

Исходя из этого стрелки измерителей и их тени(!) отрисовываются и стираются инкрементально что помещается в 8 миллисекунд в сумме на оба экрана. Положение стрелок задается максимумом в примерно 20 мс с постоянной времени затухания 300 мс(примерно как у настоящих VU измерителей).

чудесная и актуальная песня В.Высоцкого "Аисты" (video)

И тут я вспомнил , очень давно, больше 30 лет тому назад меня учили на заклинателя синего дыма , то есть я знаю зачем осциллографу ручки, а микросхемам ножки. Ручки – чтобы их дергать, а ножки - чтобы ими дергать! Может можно ли рендедерить звук самой Stm-кой , без внешнего i2s чипа?

На этом чипе (STM32f401) нет ЦАП-а. К качестве оного можно использовать таймер с его ШИМ (PWM) или SPI.

Таймеры можно инициализировать на два три или 4 канала для N уровней сигнала на частоте Fbus = 84 Мгц мы получим частоту дискретизации Fds = 84 000 000 / N , или наоборот N=Fbus/ Fds . Например для частоты дискретизации 44100 Гц мы получим 84 000 000/44100 = 1904 уровня, что соотвествует примерно log2(1904) = 10.9 бит. Ну или на 96000 Гц соотвественно 84МГц/96КГц = 875 уровней что соотвествует 9.8 бит Маловато будет.

SPI может выдавать только два уровня 0 или 1, правда до 42 мегабит в секунду.

Хмм.. 1 бит мало однако ... есть PDM (Pulse-density modulation не знаю как точно по русски)!

Сформулируем тоже в виде кода(входной сигнал от -1.0 до 1.0 )

struct sigmaDeltaStorage
{
	float integral;
	int    y;
};

struct sigmaDeltaStorage left_chanel;
struct sigmaDeltaStorage right_chanel;

int sigma_delta(struct sigmaDeltaStorage* st,float x)
{
	st->integral += x - st->y;
	st->y =  (st->integral>0.0f) ? 1:-1;
	return st->y;
}

в цикле:

  • Прочитать данные левого и правого каналов из приемного циклического буфера USB c интерполяцией (лучше билинейной).

  • Вызвать функцию sigma_delta.

  • Результат отправить на выдачу .

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

Рассмотрим пример. Входной сигнал – постоянный x=0.31415

void testFunc(int NumberSamples)
{
	for(int t=0;t<NumberSamples;t++)
	{
		x[t] = 0.31415f;
	}
	for(int t=0;t<NumberSamples;t++)
	{
		y[t] = sigma_delta(&Ch0,x[t]);
	}
	float meanSumm = 0;
	
	for(int t=0;t<NumberSamples;t++)
	{
		meanSumm += y[t];
		if(NumberSamples<20)
		printf("y[%d]=%f\n",t,y[t]);
		
	}
	meanSumm/=NumberSamples;
	
	printf("meanSum=%f \n",meanSumm);
}

для 10 вызовов фунция даст вот такую последовательность [1,-1, 1,1,-1,1,1,-1,1,1] их среднее будет 0.4 , для 1000 вызовов среднее будет 0.314 , для 10000 -> 0.3142 и тд.

То есть, чем больше частота отсчетов, тем аккуратнее апроксимация ограниченного по частоте входного сигнала. Фокус изобрели в 60-х годах прошлого века.

К сожалению, реализовать этот алгоритм прямо на stm -ке с генерацией бит и выдачей на SPI у меня не получилось, неободимая частота должна быть около 2.884 Мгц (https://en.wikipedia.org/wiki/Super_Audio_CD) . Выглядит малореальным сгенерировать два канала с семплированием с интерполяцией из usb буфера и вызовом функции sigma_delta для каждого бита и упаковки бит на выдачу в SPI при частоте процессора всего 84 Мгц. Всего 15 клоков на левый и 15 клоков на правый каналы на бит при забитой DMA шине – это нереально...

Облом ?

А что если переписать sigma_delta так, чтобы она выдавала не два уровня сигнала [1 -1] (с одним компаратором около нуля ), а больше ? Например 4 уровня [3 1 -1 -3] ? Или еще больше, например N+1 (мы заодно сместили входной и выходной сигнал к диапазону от [0 N], так нужно для выдачи ШИМ на таймер):

int sigma_delta(struct sigmaDeltaStorage* st,float x)
{
	st->integral+= x - st->y;
	st->y = floorf(st->integral+0.5f);  // nearest integer
	if(st->y<0) st->y = 0;                    
	if(st->y>N) st->y = N;

	return st->y;
}

Ура – мы изобрели велосипед. Если бы я внимательней читал статью про Delta-sigma modulation то обратил бы внимание что один бит – это частный случай, о чем там написано черным по белому :(

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

float sigma_delta2(struct sigmaDeltaStorage2* st,float x)
{
	st->integral0+= x             - st->y;
	st->integral1+= st->integral0 - st->y;
	st->y = floorf(st->integral1+0.5f);
	if(st->y<0)st->y = 0;
	if(st->y>MAX_VOL)st->y = MAX_VOL;

	return st->y;
}

Есть аналогия с цифровыми фильтрами низкой частоты высоких порядков. Однако дизайн стабильных фильтров высокого порядка оставим профессионалам (по дизайну фильтров) . В коде мы реализуем простой фильтры первого и второго порядка, с реализацией в числах с фиксированной точкой (у нас на STM это самый быстрый тип int32_t ).

Для реализации я выбрал частоту 101 Кгц соотвественно количество уровней 828 (84МГц/101КГц) на сигма-дельта второго порядка интегрирования, впрочем при желаниии можно поиграть с опциями. Количество дисплеев лучше задать 0 для конфигурации с сигма- дельта, поскольку помехи от них будут слышны в наушниках, впрочем не очень сильные, слушать можно. Но предпочтительный вместе с индикаторами уровня использовать внешний DAC или организовывать развязку питания для них. Без дисплеев , с RC фильтром низкой частоты сигма-дельта на STM32f401 дает очень хороший звук, лучше большинства встроенных звуковых карточек на недорогих компах и явно лучше дешевых китайских звуковых затычек USB->3.5мм для наушников.

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

Еще Видео : Снег кружится группы Пламя удалил ,жадные правообладатели уже нажаловались. Ну и фиг с ними.

На эту тему Analog Devices ( осторожно English, есть много интересных ссылок в конце )

Ну и исходный код на гитхабе

Комментарии (33)


  1. smoluks4096
    20.04.2022 21:21
    +11

    голосую за вариант: замерить коэффициент нелинейных искажений и сигнал/шум у сего устройства


    1. Sdima1357 Автор
      20.04.2022 21:34
      +1

      У какого из них? Первый вариант с i2s очень качественный. Второй вариант все рано нуждается в доработке схемотехники. В теории он весьма и весьма неплох. Да и на практике звучит у меня лучше чем встроенная в лаптопе карточка. Немного шумит за счёт плохой развязки питания, но это решаемый вопрос.


      1. smoluks4096
        20.04.2022 23:44
        +2

        очень качественный

        а как это оценить без измерений-то?

        звучит у меня лучше чем встроенная в лаптопе карточка

        даже PCM270x без внешнего цапа звучит лучше, чем встроенная


        1. Sdima1357 Автор
          20.04.2022 23:53

          Измерений чего ? Откройте документацию на чип https://www.ti.com/lit/ds/symlink/pcm5102.pdf

          и посмотрите , у них все померяно... Я вполне доверяю этой компании. Дайте нормальное питание и Вы в дамках. Тут как раз все просто.

          Вот с сигма дельтой , без внешнего ЦАПа, надо повозиться. Попробую сделать внешний повторитель со стабильным питанием, я специально вывел с таймеров комплементарные сигналы с STM - ки.


          1. butsan
            21.04.2022 16:25
            +1

            "Дайте нормальное питание и Вы в дамках. Тут как раз все просто."

            Возможно, кому-нибудь пригодится мой опыт: В pcm5102 имеется встроенный генератор отрицательного напряжения и для него устанавливаются два конденсатора (между 2 capp(+) и 4 capm(-) ногой и 5 vneg(-) и землёй(+). В скобках указана полярность). Так вот, в даташите их ёмкость рекомендована 2.2мкф. Этого довольно мало, и при двух каналах с выходным сопротивлением 1 кОм образуется некий жутко не линейный ФНЧ 2-го порядка с частотой среза примерно 140 Гц. Так вот, я бы рекомендовал заменить оба конденсатора на 10 мкф x 16в и применить в данном случае специализированные электролиты (мой личный выбор ELNA Silmic). На слух будет на порядок лучше. Эти конденсаторы - самое слабое место данной микросхемы. Так я "патчил" китайские цапы.


            1. Sdima1357 Автор
              21.04.2022 16:40

              Спасибо, интересно. Может чипы левые? :)

              Они вроде пишут , что 1 ком это минимально допустимое сопротивление нагрузки. Видимо замеры они делали на 10 ком нагрузки.


  1. Javian
    20.04.2022 22:11

    Посмотреть бы в RightMark Audio Analyzer что получилось.


    1. Sdima1357 Автор
      20.04.2022 22:21
      +2

      Для этого нужен качественный АЦП. У меня такого нет. Сам процесс я могу просимулировать в матлабе например и померить результаты. На самом деле можно даже учесть времена переключения выходных ключей на stm -ке но не вижу такой необходимости. Меня вполне устраивает качество звука.


  1. Tsvetik
    20.04.2022 22:53

    А как вы в SPI выводите четыре уровня?

    При выходной частоте 101кГц выходной ФНЧ уже не ставили?

    На F401 вроде есть QSPI. Можно было бы на него R2R лестницу повесить и иметь 16 уровней. Ну, или с таймерами пошаманить или даже с FSMC, просто выводя семплы на шину памяти.

    Впрочем, гнать 84Мгц наружу да еще на параллельную шину не очень хорошая идея


    1. Sdima1357 Автор
      20.04.2022 22:58

      Не SPI - a PWM - таймеры. SPI был отвергнут по причине "не успеваю посчитать"... В статье об этом есть. Если прямо на наушники, то ФНЧ вообщем то не нужен. Но если после стоит какой нибудь недорогой усилитель класса D - то обязательно нужен.


  1. erley
    20.04.2022 22:54
    +6

    Обожаю когда люди создают что-то на стыке разных областей. Описанный вами случай - из этой серии.

    И ещё... спасибо вам огромное за real engineering - очень интересно!

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


  1. Jury_78
    21.04.2022 09:33

    Pulse-density modulation не знаю как точно по русски

    Возможно... Широтно-импульсная модуляция.


    1. Sdima1357 Автор
      21.04.2022 09:43

      Не-a ... ШИМ (широтно импульсная) это PWM ( pulse width modulation ), дословно. Это все таки другая модуляция, похожая, но другая.


      1. Jury_78
        21.04.2022 10:12

        Тогда переводим дословно - импульсно-плотностная модуляция. :)


        1. Sdima1357 Автор
          21.04.2022 10:19
          +1

          Хмм. Не импортозамещается :(


          1. Jury_78
            21.04.2022 10:26
            +2

            Из словаря:

             Telecoms (En-Ru)

            pulse-density modulation

            импульсно-плотностная модуляция (для смягчения цифрового звука после декодирования в CD-проигрывателе фирмы Harman/Cardon)


            1. Sdima1357 Автор
              21.04.2022 10:41

              Ну только если Харман/Кардон... На CD , ах да " световая пластинка " :)

              Импульсно-плотностная ... Не очень как то...не звучит.


              1. Jury_78
                21.04.2022 11:22
                +2

                "Других писателей у меня для вас нет!" :)


                1. Sdima1357 Автор
                  21.04.2022 11:29

                  Вот и я о том-же.


                  1. DrMefistO
                    22.04.2022 21:09

                    Импульсно-ёмкостная - очень даже ничего.


                    1. Sdima1357 Автор
                      22.04.2022 21:12

                      Ничего конечно, если емкость подходящая :)


            1. dizatorr
              22.04.2022 15:20
              +1

              Может - амплитудная модуляция?


      1. VT100
        21.04.2022 22:02

        Число-импульсная?


        1. Sdima1357 Автор
          21.04.2022 22:13

          "Да нет наверное"


      1. roach1967
        22.04.2022 10:28
        +1

        Google переводчик (при поиске) из Wiki даёт такой перевод:

        Переведено с английского языка. — Пульсационная модуляция, или PDM, является формой модуляции, используемой для представления аналогового сигнала с двоичным сигналом. Википедия (Английский язык)


  1. romanetz_omsk
    22.04.2022 11:43
    +1

    Встречал решение, где два канала таймера в режиме 8-бит ШИМ управлялись каждый своим байтом двухбайтного отсчёта и их отфильтрованные выходные напряжения складывались в пропорции 1/256. По способу с СДМ - подход с СДМ применительно к МК, конечно, инновационный, но искомый однобитовый поток можно получить напрямую из компа, изменив дескриптор формата, так, чтобы комп понимал, что устройство от него хочет. В этом случае хост будет отправлять не отсчёты PCM, а нужный перекодированный сигнал. Только для него понадобится, скорее всего, usb hs (привет в сторону stm32f723)


    1. Sdima1357 Автор
      22.04.2022 12:16

      Встречал решение, где два канала таймера в

      При работающем USB каналы даже одного таймера иногда расползаются. Писал об этом здесь https://habr.com/ru/post/523836/

      Впрочем может я чтото не так делал

      Только для него понадобится, скорее всего, usb hs

      Достаточно FS - 12 Mbps . 2.884Mbps*2 это всего 5.768Mbps .

      В этом случае хост будет отправлять не отсчёты PCM, а нужный перекодированный сигнал.

      А фиг его знает, будет или нет. PCM - все операционки умеют выдавать.

      Кроме того stm32f401 отлично справляется с перекодированием PCM в модифицированный на 8-9 бит СДМ на 300 кГц, такой вот виртуальный АЦП :). Я немного переделал выдачу звука на открытый сток - смотрите схему. Помеха от питания ушла в ноль. Земля, в отличие от питания, внутри чипа тихая, на нее работающий по SPI дисплей почти не влияет. Сейчас вообще все отлично работает, только немного код еще почищу.


      1. romanetz_omsk
        22.04.2022 13:12

        Если формат DSD over PCM - то, все же, во-первых, нужен UAC2 в дескрипторе , а во-вторых, USB HS на физике, т.к., по стандарту этого формата один байт из каждых трёх служит маркером формата и попеременно маркером канала в составе данных в этом формате. А в дескрипторе при этом оставляем PCM.


        1. Sdima1357 Автор
          22.04.2022 19:57

          Мы рассматриваем принципиально разные классы устройств. Они в разных весовых/ценовых категориях.


  1. DX168B
    22.04.2022 15:01
    +1

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

    Несущую выбрал 384кГц, на эту частоту больше выбора силовых ключей. При этой частоте, разрядность ШИМ получается равной 9 битам (модулятор при этом просит около 200мГц). Больше сделать проблематично, так как от FPGA EP2C5T144C8N можно получить стабильную работу на частотах не более 260мГц. Другой ПЛИСины у меня пока нет, обкатываюсь на этой. Модулятор работает хорошо как в тестбенчах, так и в железе.

    Исходный сигнал - 24 бит, 48кГц. АЦП - PCM1802. Просто потому что нашел ее в виде модуля на Али. До 384кГц довожу оверсемплингом, построенном на самопальном КИХ фильтре на 128 коэффициентов. КИХ тоже работает хорошо. Может работать на тактовой до 60МГц, но его можно еще ускорить, если конвейеризовать некоторые узкие места (в самом умножителе и между его выходом и сумматором. Мне лень было всовывать регистры между ними). Но текущей его производительности хватает и так с головой на 50 МГц. Однако, вместо Дельта-Сигмы я решил попробовать нойзшейпинг. Он точно так же должен сместить шум квантования вправо по спектру.

    Из текущих проблем, чтобы впервые запустить это дело и проверить:

    1. Переделать I2S интерфейс. Я его делал первым, попутно осваивая Verilog и технологии FPGA. Потому там сплошная асинхронщина. На выходных реализую синхронный дизайн этого модуля.

    2. Разобраться с расчетом фильтра в петле ОС нойзшейпера. Можно попробовать сделать третьего порядка и вручную поиграться с коэффициентами, чтобы по получившейся импульсной характеристике провести анализ и определиться с методикой расчетов.

    Ваш вариант с "многобитной" дельта-сигмой тоже попробую.


    1. Sdima1357 Автор
      22.04.2022 18:08

      Ваш вариант с "многобитной" дельта-сигмой тоже попробую.

      Будут результаты, поделитесь пжл...


  1. iShrimp
    22.04.2022 20:48

    Интересный проект. Я не специалист, но мне кажется, что с многобитной дельта-сигма модуляцией

    компромисс между частотой отсчетов и количеством дискретных уровней

    однозначно решается в пользу дискретных уровней. Потому что, при постоянном битрейте, уменьшая частоту линейно (в N раз), мы увеличиваем разрешение сигнала экспоненциально (в 2^N раз). Выходит, что при наличии у МК N свободных выводов можно снизить частоту дискретизации в 2^N/N раз и с помощью R-2R схемы получить сигнал того же качества.


    1. Sdima1357 Автор
      22.04.2022 21:09

      У него (stm32f401) нет DMA на паралельный порт. Поэтому ничего не выйдет. В остальном Вы тоже не правы,

      однозначно решается в пользу дискретных уровней

      Это не так .Однозначно в пользу частоты семплирования. Почитайте документ от Analog Devices по ссылке в конце статьи , там все есть, про профиль шума.

      Допустим что частота таимера F . Количество уровней N . Тогда частота отсчетов K=F/N.

      Обьем передаваемой информации V = K*log2(N) . Подставляем V = F*log2(N)/N. Чем меньше N тем больше результат.