# Цифровой микрофон SPH9855 на Tang Primer 25K Прошивка принимает поток PDM с микрофона SPH9855LM4H-C, приводит его к **24 бит / 12 500 Гц** и отдаёт отсчёты кадрами по UART. MIC_CLK 3.125 МГц -> CIC sinc^4 /50 -> 62.5 кГц -> ФНЧ /5 -> **12 500 Гц** -> кадры по 50 отсчётов -> UART 921600. ## Тактирование и частоты | Параметр | Значение | Обоснование | |:--|:--|:--| | Системный клок | 50 МГц (E2) | генератор платы | | MIC_CLK | 3.125 МГц = 50/16 | Normal mode микрофона 1.1...4.8 МГц | | CIC | sinc^4, децимация 50 | до 62.5 кГц, без умножителей | | ФНЧ | 221 отвод, децимация 5 | до 12 500 Гц | | Выходная ЧД | 12 500 Гц | | | UART | 921600, 8N1 | 157 байт на кадр, 250 кадров/с, 39.25 кБ/с (43% канала) | Делитель 16 выбран не произвольно: чтобы обойтись без PLL, частота PDM должна быть 50 МГц / N, а чтобы децимация до 12 500 Гц была целой, N обязан делить 4000. Из подходящих делителей 16 даёт самую высокую частоту в пределах Normal mode, то есть наибольшее передискретизирование (250). ## Дециматор Двухступенчатый, потому что одного CIC мало: его нули стоят на кратных 12.5 кГц, и в рабочей полосе он подавил бы сворачиваемые участки только на ~24 дБ. Поэтому CIC снимает поток до 62.5 кГц, а всю избирательность даёт ФНЧ. | Показатель | Значение | |:--|:--| | неравномерность полосы 0...5000 Гц | 0.032 дБ | | подавление от 6250 Гц | 72 дБ | | подавление сворачиваемых полос | 77 дБ | Спад CIC в полосе (до 0.36 дБ на краю) не компенсируется отдельным звеном: он задан прямо в желаемой АЧХ при расчёте ФНЧ. Коэффициенты и разрядности лежат в `src/fir_coefs_pkg.vhd`, файл генерируется: ```bash py -3 tools/gen_fir_coefs.py ``` ## Шкала Постоянная `'1'` на выводе данных микрофона даёт полную шкалу 24 бит (код 8 388 567). Отсюда шкала выхода — dBFS, ровно та, в которой микрофон специфицирован. Вольт в тракте нет, `V_REF` в приёмных скриптах не нужен. ## Формат кадра UART | Поле | Байт | Описание | |:--|--:|:--| | SYNC | 2 | `A5 5A` | | FRAME_CNT | 2 | uint16 BE, инкремент на кадр | | STATUS | 1 | бит0 — переполнение FIFO, бит1 — насыщение дециматора | | DATA | 150 | 50 отсчётов x 24 бита, MSB first | | CRC16 | 2 | CCITT, poly 0x1021, init 0xFFFF, по FRAME_CNT..DATA | SYNC в CRC не входит: он нужен приёмнику для ресинхронизации до того, как тот начнёт что-либо считать. ## Распиновка | Цепь | Назначение | Пин ПЛИС | Контакт J1 | |:--|:--|:--:|:--:| | clk_50m | тактовый генератор платы, 50 МГц | E2 | — | | UART_TX | на встроенный мост BL616, ПЛИС -> ПК | **C3** | 21 | | LED | индикация | E8 | 17 | | MIC_CLK | тактирование микрофона, 3.125 МГц | J11 | 33 | | MIC_DAT | однобитный поток PDM | J10 | 35 | | MIC_SEL | вывод SELECT микрофона | K7 | 34 | C3 (IOB56B) — это то, что на Dock-плате подписано `FPGA_UART_TX` и заведено на приёмник моста BL616, банк 4 с фиксированными 3.3 В. Обычный контакт гребёнки для UART не годится: мост туда не смотрит. Под внешний мост, посаженный прямо на гребёнку, вывод меняется на нужный контакт. Микрофон сидит в банке 7, а VCCIO банков 6/7 на этой плате подаётся снаружи через контакты 29 и 31 гребёнки — без 3.3 В на них выводы микрофона не работают. Питание самого микрофона 1.65...3.6 В. `MIC_SEL` прошивка держит в `'0'`: микрофон выставляет бит по спаду MIC_CLK, выборка идёт в середине низкой фазы. Если вывод SELECT посажен на питание платой, поменять `SEL_HIGH` в `Top.vhd` на `'1'` — точка выборки переедет сама. ## Сборка Выводы `clk` (E2) и `LED` (E8) на этом корпусе сидят на двойных функциях (SSPI/CPU и READY), поэтому в свойствах проекта их надо разрешить как обычные выводы, иначе размещение падает с PR2017. В IDE это флажки CPU, SSPI и READY, в `gw_sh` -- `set_option -use_cpu_as_gpio 1 -use_sspi_as_gpio 1 -use_ready_as_gpio 1`. Занято на GW5A-25: 778 LUT/ALU (4%), 616 регистров (3%), 5 BSRAM (9%), 1 DSP (4%). Fmax 164 МГц при требуемых 50, нарушений установки и удержания нет. ## Состав | Файл | Назначение | |:--|:--| | `src/Top.vhd` | верхний уровень: только экземпляры и связи | | `src/pdm_rx.vhd` | тактирование микрофона и выборка бита в нужной фазе | | `src/cic_decim.vhd` | CIC sinc^4, децимация 50 | | `src/fir_decim.vhd` | ФНЧ с децимацией 5, последовательная свёртка | | `src/fir_coefs_pkg.vhd` | коэффициенты ФНЧ, генерируется | | `src/fifo_top.vhd` | буфер отсчётов, заглушка под IP-ядро Gowin | | `src/Packets_creator.vhd` | сборка кадра с CRC-16 | | `src/UART.vhd` | передатчик | | `src/led_status.vhd` | индикация частотой мигания | | `src/DigMic.cst` | привязка выводов | | `tb/pdm_mic_model.vhd` | поведенческая модель PDM-микрофона | | `tb/tb_top.vhd` | сквозной тест: модель -> ПЛИС -> UART -> разбор кадра | | `tools/gen_fir_coefs.py` | расчёт коэффициентов ФНЧ | | `tools/uart_capture.py` | приём кадров, запись `.raw` и `.bin` | | `tools/uart_live.py` | живой просмотр: осциллограмма, спектр, счётчики | ## Симуляция (GHDL) ```bash mkdir -p build && ghdl -a --std=08 --workdir=build src/fifo_top.vhd src/UART.vhd src/led_status.vhd src/pdm_rx.vhd src/cic_decim.vhd src/fir_coefs_pkg.vhd src/fir_decim.vhd src/Packets_creator.vhd src/Top.vhd tb/pdm_mic_model.vhd tb/tb_top.vhd && ghdl -e --std=08 --workdir=build -o build/tb_top tb_top && build/tb_top --stop-time=400ms ``` Модель прогоняет четыре режима. Три из них дают точно вычислимый ответ: постоянные `'1'` и `'0'` — плюс и минус полная шкала, чередование `1010` — ровно ноль (эта частота попадает в нуль АЧХ CIC). Четвёртый — синус, у него проверяется размах. Тракт настраивается около 10 мс, поэтому в каждом режиме кадры читаются до первого сошедшегося. ## Запись с железа ```bash py -3 tools/uart_capture.py --list ``` ```bash py -3 tools/uart_capture.py --port COM5 --seconds 10 --out test1 ``` Получаются `test1.raw` (весь поток как пришёл, для отладки канала), `test1.bin` (только отсчёты, читается `read_24bit_bin()`) и `test1.log`. Кадр с несошедшейся CRC отбрасывается, пропуск по FRAME_CNT добивается нулями, чтобы не сдвинулась ось времени. ## Живой просмотр ```bash py -3 tools/uart_live.py --port COM10 ``` Осциллограмма в долях шкалы, спектр в dBFS и счётчики кадров в реальном времени. Управление кнопками внизу окна, те же действия — клавишами `r`, `a`, `d`, `s`, `f`, выход `q`. Запись встроена в просмотр по необходимости: COM-порт держит один процесс, и запустить `uart_capture.py` на том же порту параллельно нельзя. Разбор кадров у обоих инструментов общий (`FrameParser` в `uart_capture.py`), так что просмотр и запись не могут разойтись.