company_banner

Как мы тестируем системы микрофонов на STM32: опыт разработчиков устройств Яндекса



    Привет, я Геннадий «Крэйл» Круглов из команды аппаратных решений Яндекса.

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

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

    Это кажется несложным только на первый взгляд. В этом материале я объясню, как мы решили задачу передачи звука с семи микрофонов с PDM-интерфейсом на компьютер через USB, с какими аппаратными и программными нюансами столкнулись и как их преодолели (спойлер: этот подход может быть адаптирован для матриц с числом микрофонов ≤ 8). В конце поста поделюсь ссылкой на стрим, где я показываю процесс разработки на микроконтроллере STM32, и расскажу о следующей серии.

    Постановка задачи


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

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

    1. Получить данные с микрофонов.

    2. Отправить их в компьютер.

    В готовом устройстве при обращении пользователя к Алисе сигналы с цифровых микрофонов подаются прямо в центральный процессор (корректней назвать его SoC — System-on-Chip, но «процессор» привычнее и удобнее), он обладает достаточной мощностью для их обработки. Но для отладки алгоритмов гораздо удобнее получать эти данные напрямую в компьютер разработчика. Самый простой способ – подключиться по USB: таким образом, на плате должен быть микроконтроллер с соответствующим блоком. Мы любим контроллер STM32, но подать звуковой поток с микрофонов непосредственно на него невозможно: нет блока приёма сигнала PDM (модуляция плотностью импульсов) — выходного интерфейса цифровых микрофонов.

    Другой вариант — подключить микрофонную плату к отладочной плате от производителя используемого SoC. Но это решение завязано на линуксовый alsamixer, и его параметры сильно влияют на результат преобразования PDM в PCM. Эти блоки могут отличаться не только для процессоров разных производителей, а даже для двух моделей одного вендора. Нам же, напоминаю, требовалось простое решение, прозрачное и предсказуемое.

    Аппаратное решение


    Смиримся с невозможностью STM32 принять многоканальный PDM. Можно было бы воспользоваться блоком SPI для приема PDM-сигнала, но по одной SPI-шине можно завести только один микрофон. Мы работаем с контроллером STM32L476RC, где таких шин всего три. Дополнительная сложность: PDM-сигнал достаточно высокочастотный, необходимо делать его децимацию, усреднение, обработку, фильтрацию — для семи микрофонов эта задача представляется достаточно сложной.

    Поскольку речь идёт об отладочной плате, а не прототипе для массового производства, остановимся на специализированной микросхеме TSDP18xx. Она делает всё необходимое: генерирует нужные частоты и сигналы для PDM, производит усреднение и обработку PDM-сигнала, превращает это всё в I2S-сигнал. Точнее, TDM (Time Division Multiplexing), потому что I2S-шина предполагает два канала, а если по этим же проводам гнать больше, уже не совсем корректно называть это I2S.

    Плюс этого подхода в том, что всю работу по подготовке и усреднению берет на себя TSDP. Минус — все алгоритмы намертво зашиты внутри этой микросхемы, и изменить их не получится. В частности, нельзя регулировать громкость путём модификации параметров усреднения. Но для отладки это не критично.

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

    Итак, поднимаем восьмиканальный TDM на STM32, получаем восьмиканальный аудиопоток. Как двигаются данные:



    SAI — аппаратный блок STM32 для работы с I2S/TDM. Он весьма гибкий и позволяет реализовать множество вариантов протокола. Но из-за этого легко запутаться в требованиях к частотам.

    Дерево тактовых частот заслуживает более детального рассмотрения. К микроконтроллеру подключен кварцевый резонатор на 12 МГц. Эту частоту перед подачей на блоки ФАПЧ (PLL) делим на 3 и получаем 4 МГц. Дальше это работает так:

    1. Частоту ядра хорошо бы сделать повыше, чтобы всё успевать: например, максимальные для этого контроллера 80 МГц. Используем первый блок ФАПЧ: умножаем 4 МГц на 40 и делим на 2.

    2. Для USB потребуется 48 МГц. Для этого используем второй блок ФАПЧ: умножаем 4 МГц на 24 и делим на 2.

    3. О микрофонах. В наших тестовых платах используется частота дискретизации Fs = 16 кГц — стандарт, принятый в сфере распознавания речи. Из исходной частоты 4 МГц нужно получить что-то, что можно превратить в 16 кГц частоты фреймов шины TDM (она же LRCK, она же FCK, она же FrameSync). При этом:

    [частота битовой синхронизации (BCLK, BitClk, Sync, SCK)] = Fs ∙ [количество каналов] ∙ [количество бит на канал]

    То есть: SCK = 16 кГц ∙ 8 ∙ 16 = 2048 кГц.

    4. В даташите указано, что соотношение между Master Clock и частотой дискретизации Fs следующее: MasterClock = 16 кГц ∙ ДелительMCLK ∙ 256. Тут 256 — константа, а делитель можно задать в регистре. Проверим схему — для нужной функциональности есть коэффициенты деления частоты ФАПЧ на 7 или 17:



    Обобщим задачу: нужно подобрать такой набор множителей и делителей ФАПЧ и SAI, чтобы получить частоту дискретизации 16 кГц и битовую частоту в 128 раз больше. Поскольку в наборе был обязательный делитель на 7 (или 17), то получить в точности нужный результат не вышло. Пришлось построить таблицу множителей и делителей, чтобы получить 24.571 МГц. Разделив эту частоту на 6 (ДелительMCLK), а затем на 256 (константа), наконец, получим число достаточно близкое к 16 кГц. Сейчас объясню, почему это так важно.

    Работа с USB


    Для работы с мультимедийными данными USB использует изохронный тип передачи: в этом случае на шине USB гарантируются определённая полоса пропускания и величина задержки. Доставка данных не гарантируется: если пакет пришёл со сбоем, то он будет считаться потерянным. Это связано с жёсткими ограничениями по времени: нет возможности переспрашивать.

    При изохронном типе передачи при скорости USB FullSpeed (это 12 Мбит/с; именно на этой скорости умеет работать USB-блок STM32) компьютер приходит к девайсу за данными каждую миллисекунду: через этот промежуток времени он должен забрать накопившиеся данные. Напомню вводные: частота дискретизации 16 кГц, 8 каналов, каждый канал требует два байта, потому что звук шестнадцатибитный. Итого 16000 ∙ 2 ∙ 8 / 1000 = 256 байт за миллисекунду. Размер одного пакета для изохронного типа передачи может достигать 1023 байт, так что в этом месте проблем нет.

    Итак, размер пакета 256 байт. Казалось бы, все хорошо. Шестнадцать раз получили данные по TDM, положили в буфер, USB пришёл, отдаём ему пакет, повторяем… Но так происходит только в идеальном мире. Проблема в том, что с одной стороны у нас неидеальные 16 кГц (чуть поменьше), и в итоге данные приходят чуть реже, чем раз в миллисекунду. С другой — миллисекунда компьютера тоже плавает, так как он занят: когда смог, тогда и пришёл. То есть и частота опроса микрофона отличается от 16 кГц (но всегда одинаково), и миллисекунда USB тоже отличается по длине (при этом отличие, скорее всего, плавает: получается то чуть больше, то чуть меньше идеальной миллисекунды).

    Почему это проблема? Можно потерять пакет. Наверное, излишне объяснять, что для верной отладки алгоритмов необходимы полные данные. Как теряется пакет: накопили 256 байт результатов, положили их в буфер, продолжили измерение. Пришёл компьютер, забрал первые 256, всё ещё продолжаем измерять. Компьютер снова пришёл, но измерение ещё не завершено – компьютер ушёл с пустым пакетом. Дальше заканчиваем заполнять буфер и начинаем заполнять ещё один, следующий, пока компьютер не пришёл снова. Компьютер забирает только последний пакет, в итоге один пакет оказывается потерян.



    Проблема, на самом деле, известная. Для борьбы с ней существует три подхода:

    • Синхронный. Все частоты на стороне девайса генерируются с опорой на миллисекундные сигналы USB. Минус такого подхода — период этих сигналов плавает. Существует понятие «джиттер» — дрожание фазы сигнала. В случае USB джиттер довольно серьёзный и вносит существенное мультипликативное искажение в сигнал. В данной задаче это не очень важно, потому что передаётся не музыка, а голос (снаружи есть шумы, используется 16 бит), но всё равно хочется сделать всё хорошо. Да и генерировать эти частоты не очень просто, ещё и неточность частоты дискретизации никуда в итоге не денется.
    • Адаптивный. Девайс должен менять частоту дискретизации в соответствии с частотой опроса на стороне компьютера.
    • Асинхронный — лучший для данной задачи. На девайсе есть генератор стабильной частоты. Частота дискретизации поддерживается строго одинаковой без привязки к USB. При этом нужно передавать на девайс данные так, чтобы не возникало существенных расхождений.

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



    Но наша задача обратная, отладка требует получения данных с микрофонов в компьютер, а вопрос записи сигнала с микрофонов на компьютер в лучшем случае только упоминается. Почему бы не сделать аналогично: ввести обратную связь от компьютера? Есть вариант проще.

    Вот он


    Используем частое добавление отсчетов и два буфера для хранения данных на отправку. 16 раз в миллисекунду добавляем к выбранному буферу очередной отсчет. В некоторый момент времени случается прерывание: USB забрал предыдущий пакет. Если буфер № 1 заполнен, происходит переключение на буфер № 2. Когда USB приходит за следующим пакетом, он оказывается уже подготовлен. Отправляем буфер № 2 и снова переключаемся на № 1.



    USB приходит за данными в разные моменты времени, в пакет входит разное число отсчётов. Оно может оказаться и больше, и меньше шестнадцати, поэтому есть шанс превысить пакет размером 256 байт, лучше оставить пространство для манёвра. Пусть будет 384 = 256 + 128: это даст запас в полмиллисекунды, то есть простит плавание фазы USB сигнала на 50% — такого запаса должно быть более чем достаточно. Итого: отправляется то больше, то меньше 256 байт, но никогда не пустой пакет, что позволяет избежать потери данных. То есть проблема неравномерности была решена увеличением пакета, ценой увеличения части пропускной способности шины, выделенной для нашего устройства и уменьшения этой части для остальных устройств.

    На этом доставка данных в компьютер подошла к концу. Разработчики могут отлаживаться, а вы — задавать вопросы в комментариях, если для полного понимания не хватило какого-то пакета данных.

    Мои стримы и следующая серия


    В последнее время я дважды стримил из своей домашней паяльной лаборатории. Сначала просто показал процесс пайки и рассказал, какие устройства я использую. Вторая серия была как раз посвящена разработке на STM32.

    Стримы продолжаются. В эту пятницу в 19:00 мой коллега из команды разработки аппаратных решений Андрей Лаптев устроит онлайн-разбор Яндекс.Станции Мини — покажет внутренности и поделится историями производства. Для большего веселья Андрей прикрутит к колонке аккумулятор — не всё же от провода работать. В финале вы получите гайд, который позволит повторить этот опыт самостоятельно или придумать конструкцию поинтереснее.

    Зарегистрируйтесь, чтобы смотреть стрим. Вам придёт письмо с файлом для календаря и напоминание в день эфира. Спасибо, что дочитали!
    Яндекс
    Как мы делаем Яндекс

    Комментарии 28

      0
      Почему был выбран именно STM? Есть же более подходящие чипы для обработки звука, если конечно планируется его обрабатывать прямо в устр-ве. К примеру, процессора Analog Devices DSP Sharc (если плавучка нужна) или DSP Blackfin (если достаточно целочисленных).
        +2
        Нам не нужно обрабатывать звук, нам нужно его перевести из 8х PDM в USB :) Никакой сложной обработки. А все алгоритмы удобно писать и отлаживать на большом компе.
        0
        Существует готовая плата, правда на 7 микрофонов www.seeedstudio.com/Sipeed-6-1-Microphone-Array-for-Dock-Go-Bit-p-2875.html
          0
          По ссылке две платы: одна — просто текстолит с микрофонами (и светодиодами, но это неважно); вторая — SoM с двухъядерным RISC-V с блоками ускорения нейросетей и цифровой обработки. Но без USB :) Так что для исходной задачи — трансляции сигнала в USB — этот SoM плохо подходит. Плата же с микрофонами нам тоже не очень интересна, потому что микрофонные платы наших устройств точно будут другими.
            0
            Просто дополнение к статье, вдруг кому пригодится)
          +2
          Мало картинок/фоток. Выглядит как кусок вырванный из контекста.
            0
            Есть вот такое вот решение www.minidsp.com/products/usb-audio-interface/uma-8-microphone-array.
            По дефолту там зашиты алгоритмы бимформинга и какие-то DOA, но преимущества в том, что у них есть прошивка, которая просто отдает сырые данные. Референсные данные они тоже дают. Т.е. можно просто плату повторить вообще без заморочек с софтом. Из приятного, драйвера работают под все ОС.

            Из доп. вопросов было бы интересно посмотерть на работу с USB стеком(понятно, что использовался стек от STM, но интересно именно его использование).

            Также интересно узнать как калибруете микрофоны. Или просто используются какие-то «робастные» алгоритмы на основном процессоре, которые не требуют калибровки?
              0
              Плата от XMOS выглядит существенно сложнее нашей. Опять же, STM32 гораздо доступнее по сравнению с XMOS. Насчёт отсутствия заморочек с софтом — вопрос тоже спорный, потому что же придётся в этот XMOS чип как-то заливать прошивку. На отладочной плате есть встроенный программатор для этого, а на своей что делать? Возможно, можно подключиться стандартным JTAG, — но тут уже начинаются вопросы и сомнения. Пока что у нас нет стремления изучать XMOS, хотя ребята они хорошие :)
                0
                Что Вы подразумеваете под «существенно» сложнее? Там ни одной BGA микросхемы, односторонний монтаж и класс точности 3-й или 4-й на глаз. Разводится все в 2-х слоях. Даже на картинке видно что ~60% компонентов не установлено и использеются как опциональные. Можно на сайте XMOS посмотреть референсы. Там чип работает фактически из коробки. Пока что выглядит все, как «всегда использовали STM32, поэтому смотреть на что-то готовое не хочется». Если есть страх перед JTAG, так там загрузчик через USB работает (serial bootloader). Подключаете к компу, запускаете програмку для обновления прошивки и заливаете прошивку для передачи сырых данных на 8 каналов.
                  0
                  Тут ещё вопрос целеполагания. Потому что, как известно, «любой шаг, не ведущий к прогрессу, ведёт к деградации».
                  Можно, конечно, повторить плату от XMOS, изучив её схему и выкинув лишнее. Понадобится изучить ряд вопросов. Загрузчик там сразу есть, с завода, по USB? А какая микросхема FLASH подойдёт для хранения прошивки? Точно? А то они как-то подтвердили Part Number, а оно не заработало :) А как быть с микросхемой генератора частот? (Там есть нюансы, XMOS заказывает её предпрошитую, чтоб она генерировала что надо при включении питания).
                  То есть, это не просто срисовать схему. Потребуется её творчески переработать. Возможно, это будет быстрее, чем сделать свою плату со своим софтом, но какой опыт при этом будет получен? Разве что аппаратные особенности конкретной системы XMOS, и это не очень интересно.
                    0
                    и это нормально. STM справляется полностью, есть в наличии, умеют работать — замечательно. В т.ч. в этом заслуга менеджеров от ST, продававших STM*Discovery дешевле себестоимости, отсутствие шакалинга по поводу клонов ST-Link и разработки GD32 и т.п.
                    Вот еще бы в Middlewares добавили класс video…
                  0
                  Стандартные библиотеки от ST мы не используем, потому что они нам не нравятся.
                  В некоторых наших устройствах на STM32 используется ChibiOS в довольно урезанном виде (потому что, например, HAL от Chibios нам тоже не нравится). Но USB стек там неплохой, его мы и применяем. Тот кусок, который работает как восьмиканальный микрофон, мы писали сами. Дескрипторы сооружали тоже сами. читая кусок стандарта USB про аудиодевайсы.
                    0
                    А можно чуть подробнее. Если не нравится, то что именно. Часто приходится слышать, что «этот стек не нравится», «HAL у них вообще отстой», но редко кто аргументы приводит. Если получается написать свой лучше, то где-то выкладываете?
                      0
                      Для того, чтобы написать что-то с использованием стандартных библиотек, надо изучить:
                      * datasheet (+Reference Manual);
                      * документацию на библиотеку, если есть;
                      * пример использования библиотеки;
                      * код библиотеки (а он зачастую прям труден для понимания).
                      Если же писать что-то на регистрах, достаточно первого пункта.
                      Разработка с использованием стандартных библиотек шла ужасно медленно, и закончилась вот как. Нужно было вывести ШИМ с определённого канала таймера, при этом выполнив какой-то хитрый Remap (подробности сокрыты туманом времени, давно было дело). Было потрачено четыре часа на тщетные попытки сделать это через HAL. И тогда на регистрах всё было написано за пятнадцать минут.
                      То есть, на HAL становится чудовищно неудобно писать, как только появляется какая-то нестандартная задача. Точнее, так: некоторый набор задач учитывался при разработке HAL, и их решать удобно. Но если задача не закладывалась в HAL, то её решить средствами HAL либо сложно/некрасиво, либо вообще нельзя (и приходится писать в регистры).
                      Ещё один момент: для инициализации периферии через HAL требуется несколько экранов кода, потому что задание параметров через структуры очень громоздкое. А хочется использовать что-то вроде
                      PinSetupAlterFunc(GPIOB, 9,  omPushPull, pudNone, AF13);

                      В целом, конечно, тут в большой степени дело вкуса. Кому-то вполне удобно использовать HAL (или LL), плюс HAL неизбежно будет:
                      * известен большему количеству разработчиков
                      * лучше документирован
                      потому что исходно создаётся для общественного пользования. Наши библиотеки, увы, документированы разве что комментариями в коде.
                        +1
                        Использование MXcube совместно с HAL сильно упрощает ситуацию с начальной инициализацией.
                        По большому счёту, никто не запрещает прямую запись в регистры и в случае использование HAL.
                        HAL серьёзным образом упрощает перенос проекта между микроконтроллерами различных серий и вторичное использование кода. Есть некоторый проигрыш в объёме кода и производительности, но при этом получаешь достаточно корректный обработчик ошибок.
                        В результате я таки перешёл в прошлом году на HAL, stm32CubeMX, sw4stm32 и CMSIS-RTOS2. Времени на освоение пришлось потратить немало, надеюсь оно окупится уже в этом году.
                    0
                    Вообще, параметры микрофонов достаточно похожи. На производстве каждая плата проходит через специальный стенд, который проверяет микрофоны: проигрывается синусоида меняющейся частоты, записывается сигнал каждого микрофона, а дальше этот сигнал сравнивается с образцовым. Если параметры отличаются больше дозволенного — плата считается бракованной.
                      0
                      Вот это довольно интересные вещи. Например, какие допустимы отклонения в собственных шумах считаете примемлимые. Или гармонические искажения. Очень интересно было бы понять как влияют эти параметры на качество алгоритмов бимформинга, echo cancellation, DOA и т.д.
                        0
                        Там всё проще. Можно сказать, что микрофоны или работают, или не работают. То есть, в случае проблем характеристики катастрофически хуже, и это сразу видно.
                        Собственные шумы и гармонические искажения влияют слабо, потому что исходно система работает в сильно зашумлённой обстановке: там же ещё в сантиметрах от микрофона играет громкая музыка! И в нашем случае мы распознаём речь, а не классическую музыку записываем, так что требования к гладкости характеристики ниже.
                    0
                    На многих платах последнее время вижу по периферии ободок со множеством via, причем не покрытый маской. Причем не только в массовом производстве, но и в DIY (можете на ошпарке глянуть).
                    Это необходимость (почему так много переходов?), или просто «чтобы выглядело красива и бохато»?
                      0
                      по идее в этих местах идет контакт до экрана, либо экранирующей поверхности пластиковых корпусов, это все великая и ужасная электромагнитная совместимость.
                        0
                        Чтобы в торцы платы не фонило. Ну и «дораха багата» никто не отменял конечно же, особенно если покрытие ENIG
                        +2
                        ну и да, зачем клоки микрофонов выравнивать? наносекундную фазу звуковой волны ловить? =)
                          0
                          Это правда: максимальная частота PDM, с которой мы работаем — примерно 4 МГц, на ней выравнивать длины проводников нет необходимости. Но тут идея такая: как лучше, выравнивать или не выравнивать? Выравнивать — немножечко лучше :) При этом выравнивание нам ничего не стОит. Так давайте выравнивать :)
                            0
                            ну вообщет стоит
                            если импеданс линии не согласован источником то эти гармошки дают больше помех в эфир и даже на 4мгц
                          +2
                          Вообще-то, используя модуль SAI, а не SPI, STM32 позволяют подключить 8 цифровых микрофонов напрямую.

                          Кроме того, конкретно ваш проц имеет ещё один аппаратный модуль для подключения PDM-микрофонов напрямую, который и обработает и проредит PDM-данные. DFSDM называется, имеет 8 каналов, на которые вешается одна стерео-пара.

                          Как-то вы плохо доки читали.
                            0
                            познавательно, но собственно как всегда у Вас. Спасибо!
                              0
                              Было бы интересно взглянуть на сорс. Линк «Вторая серия» ведет на первую серию.
                                0
                                А можно ли как-то Яндекс-станцию использовать как хороший микрофон, со всеми этими подавлениями шумов вне луча и т.д.?

                                Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

                                Самое читаемое