Raspberry Pi Pico W и INMP441 не работает микрофон

В общем, товарищи, проблема, выручайте. Пытаюсь подключить микрофон инмп441 к плате пико В, после чего передаю по вифи на комп в реальном времени. В ответ слышу один треск, то громкий, то тихий (не зависит от того, шумлю ли я)
Использую: Arduino IDE и ядро от Earle F.
Библиотеки:
(их много, потому что в прошивке не только это)

#include <Arduino.h>
#include <stdio.h>
#include <cstdint>
#include <locale.h>
#include <iostream>
#include <string>
#include <list>
#include <vector>
#include <stdio.h>
#include <SPI.h>
#include <functional>
#include <cstring>
#include <unordered_map>
#include <map>
#include "pico/multicore.h"
#include <cassert>
#include "pico/stdlib.h"
#include "pico/cyw43_arch.h"
#include <WiFi.h>
#include <LittleFS.h>
#include "hardware/dma.h"

#include <I2S.h>  // <- ДЛЯ МИКРОФОНА!!

#include <Adafruit_GFX.h> 
#include <Adafruit_ST7735.h>
#define SAMPLES 8192  // 8192 семпла * 2 байта (отправка по вифи) = 16384 байт
#define SAMPLERATE        44100
#define FFTSIZE           1024
#define INMP441_PIN_SD    16
#define INMP441_PIN_BLCK  14
#define INMP441_PIN_WS    15
size_t bi = 0; // buffer index (не используется)
int32_t buffer[2][FFTSIZE]; // buffer с данными (не используется)
int32_t samples32[SAMPLES]; // 32 битные данные с микро
int16_t samples16[SAMPLES]; // переработанные в 16 бит данные с микро
volatile bool canWrite = true; // можно ли вносить данные в samples или он обрабатывается
I2S i2s(INPUT);
void setup(){
  Serial.begin(115200);
  // инициализация всякой остальной всячины...
}

void setup1()
{
  i2s.setBCLK(INMP441_PIN_BLCK);
  i2s.setDATA(INMP441_PIN_SD);
  i2s.setBitsPerSample(32);
  if (!i2s.begin(44100)) {
    while (true);
  }
}
void loop1()
{
  while (true)
  {
    //        ЦИФРОВОЙ
    i2s.read(samples32, sizeof(samples32));
    if (canWrite == true)
    {
      for (int i=0; i<SAMPLES; i++)
      {
        samples16[i] = (int16_t)(samples32[i] >> 8);
      }
      canWrite = false;
    }
  }
}

void loop()
{
  if (microphone)
  {
    if (client.connected()) {
      if (canWrite == false)
      {
        client.write((const uint8_t*)samples16, SAMPLES);
        canWrite = true;
      }
    } else {
      falldown(L"loop", L"Потеряно соединение с сервером!"); // экран смерти
    }
  }
}

будут дополнительные вопросы — отвечу. Главное помогите! Я проверяю уже 3 INMP441, питание подаётся стабильное 3.3
Компоненты:
Плата: Raspberry Pi Pico W
Аккумулятор: ROBITON LP68 900 мАч
Дисплей: ST7735 128 на 160 пикселей IPS TFT
Микрофон: цифровой микрофон INMP441 MEMS I2S
Плата Type-C для зарядки: TP4056
Тумблер: есть
Попытки:
Менял пины, использовал готовый .pio файл для связи платы с микро
Заменял на тот же микрофон 2 раза
Перерыл гугл, что не пытаюсь — не помогает
Пытался вне Arduino IDE с помощью CMake — там уж легче прыгнуть с окна
Питал и от акума и от компа, акум заряжал и разряжал
Использовал АНАЛОГОВЫЙ микрофон — даже он передавал звук (для него был немного другой код, так как система полегче, но звук был, правда, лишь шум, так как он только для детектинга звука)
Вифи модуль работает — запускал тг бота
| *На микропайтон писать НЕ ПРОБОВАЛ, позже попробую и дополню (советуете вообще попробовать? не привлекает производительностью)
Обращался к нейронкам, все твердят почти одно и то же, причем нерабочее
*
Может ли это быть из за типа проводов? Или, может, у меня какая то палёная плата, которая не работает с таким микрофоном (я максимально отказываюсь в это верить)?

Не знаю, что у вас тут с железом… Но код у вас странный.
Как устроен микрофон, есть ли у него встроенный буфер?
Во всяком случае, код у вас написан так, будто буфер есть… он читает с микрофона порциями по 32К… и если там нет такого буфера, то читаться будет один шум…

Кроме того, в строке 39 отсылаете вы этот буфер неправильно, теряете половину.

Дык он в 25й строке из 16 бит делает 8. А хотя нет, из 32 бита → 24.
А потом это всё приводится к 16 битам… Бррр…
Моя не понимать… :frowning:

и что? тип данных-то у него остается int16_t

Да нечего тут понимать.
В строке 25 он как-то преобразует int32 в int16 (как - не важно, для дальнейшего несущественно).

8192 значения типа int16 - это 16Кбайт

А отправляет он в строке 39 только 8К

Про то, что отсылаю половину, заметил. Спасибо.

Как пишет интернет, i2s.read() начинает запись с микрофона в мой массив данных. Есть ли буфер на самом микрофоне — мне знать не дано.

Начинает - это понятно.
Основной вопрос в том, что этот оператор делает, если нужного обьема данных еще нет?
Вот, например, Serial.read(), если попытаться им прочитать данные, которые еще не пришли - возвращает “-1”

Мы проходимся по данным , которые 32 битные

каждая данная сдвигается вправо на 8 бит (так как записываются с этого микрофона только 24 бита (такова судьба INMP441) и то в старшие биты) и преобразуются в 16 бит, чтобы отправить на компьютер и, пока что, прослушивать от туда

В старшие? Что-то я как-то сомневаюсь (но и точно не знаю). Скорее вы из 24 бита (что приходят с микрофона) делаете сдвигом вправо 16 бит.

Вы не “преобразуете 24 бита в 16”, вы тупо отрезаете верхние 8 бит. Не уверен, что звук после этого не пострадает

Нижние, верхние слева (должны быть).

@Danya
какой пакет для РП2040 используете? Эрла Филхофера?

он сначала отрезает нижние 8 бит

а потом еще и верхние 8 бит, когда присваивает из int32 в int16

2 лайка

Ну так то да. Но если с микрофона только 24 бит приходит, то этой операцией он ничего не должен терять (как я это понимаю).

1 лайк

Я не знаю, может, есть и какие другие методы работы с микрофоном по i2s, но по моему опыту, который, правда, приобретен на stm32, данные с микрофона читаются через DMA в буфер. Буфер, естественно, в памяти МК, никакого буфера в микрофоне нет, да он там и не нужен.
i2s - это синхронный протокол, там нет никаких запросов, данные передаются в реальном времени, скорость передачи и частота дискретизации связаны фиксированной константой, которая зависит от режима (МОНО/СТЕРЕО) и от разрядности передаваемых данных.
Да, при разрядности 24 передаются 32-разрядные числа. Один байт - пустой. Где он находится, в старшей или младшей части, если мне не изменяет память, определяется настройками.

А по задаче я бы порекомендовал ТС “есть слона по частям”. Начал бы с того, что сигнал i2s с микрофона сразу бы направил в аппаратный DAC и послушал, что из этого микрофона приходит. Потом попытался бы захватить данные МК и с его аналогового выхода послушать то же самое. И только потом браться за передачу сигнала по WiFi.

3 лайка

думаете, стоит попробовать отказаться от среза и передавать 32 битные значения?

да, и библиотеку егошнюю

из даташита микрофона:
Data-Word Format
The default data format is I²S (two’s complement), MSB-first. In this format, the MSB of each word is delayed by one SCK cycle from the start of each half-frame.”
MSB-first — биг эндиан, значит значащие данные справа
представим, что “1“ == “1111“, тогда маска будет такая
11111100 (первый бит знаковый)
я сдвигаю вправо
00111111 (первый бит всё еще знаковый)
и переформатирую насильно в int16
1111 (первый бит всё еще знаковый)

При сдвиге на 12 звук тише, чем при сдвиге на 8

Спасибо за совет! Мне бы, желательно, закончить к 3 марта, но видимо придется отложить…
В пико нет встроенного аналогового выхода. На ней и i2s то нет:expressionless_face:

Может быть вот этот код поможет?
Не знаю одна и таже библиотека I2S используется или нет, но формирование сэмплов происходит по другому…

#include <I2S.h>

I2S i2s(INPUT);
int32_t l, r, sample;
unsigned long previousMillis, currentMillis;
bool use_SerialMonitor = false;

void setup() {
  currentMillis = 0;
  pinMode(1, OUTPUT); // L/R
  digitalWrite(1, LOW); // LOW=LEFT, HIGH=RIGHT
  
  i2s.setDATA(29);
  i2s.setBCLK(3); // LRCLK = +1
  i2s.setBitsPerSample(24);
  i2s.begin(16000);
}

void loop() {
  i2s.read32(&l, &r);
  sample = l ? l : r;

  if(!use_SerialMonitor) {
    Serial.printf("%.6x\n", sample);
    // play using $ cat /dev/ttyACM0 | xxd -r -p | aplay -r16000 -c1 -fS24_3BE
  }
  else {
    currentMillis = millis();
    if (currentMillis - previousMillis >= 10) {
      previousMillis = currentMillis;    
      sample += (sample & (1<<23)) ? 0xff000000 : 0; // this is actually 24 bits
      Serial.print("-10000 ");
      Serial.print(sample);
      Serial.println(" 10000");
    }
  }
}