Taming the micro:bit v2 Audio with Zephyr RTOS

від

у

Озвучення micro:bit v2 з використанням Zephyr RTOS

Я почав з простого бажання: перетворити BBC micro:bit v2 на диктофон за допомогою Zephyr RTOS. В підсумку я отримав щось зовсім інше. Ось історія про апаратні обмеження, ілюзії драйверів і математику, необхідну для того, щоб 5×5 світлодіодна матриця танцювала під людський голос.

Мрія: Мікро-Реєстратор
BBC micro:bit v2 – це справжня потужність. У нього є SoC nRF52833, MEMS-мікрофон і динамік. Зрозуміло, моїм першим інстинктом було: “Запишу аудіо і відтворю його”.

Я налаштував середовище Zephyr. Я сконфігурував АЦП. Я побудував систему кругового буфера.

І це спрацювало! Я міг бачити зміни значень RMS в консолі, коли говорив.

Але потім я спробував відтворити це.

Спроба 1: PWM “Динамік”
micro:bit не має ЦАП. У нього є п’єзо-магнітний зуммер, підключений до GPIO-піна. Щоб відтворити аудіо, мені довелося модуляціювати робочий цикл ШІМ, щоб підробити аналогову хвилю.

Ми пробували біт-бенг (занадто повільно). Я спробував стандартний API ШІМ Zephyr (занадто важко). Нарешті, я написав прямий драйвер апаратного забезпечення, використовуючи Nordic HAL і EasyDMA, щоб подавати зразки в периферію ШІМ без блокування ЦП.

Цей код “Технічно працює”, але “Аудіодисастер”
“`c
static void audio_hw_play(const int16_t *buf, size_t n) {
NRF_PWM_Type *pwm = (NRF_PWM_Type *)DT_REG_ADDR(DT_NODELABEL(pwm0));

// Міняємо 12-бітне аудіо на робочий цикл ШІМ
for (size_t i = 0; i < n; i++) { int32_t duty = 500 + (buf[i] * 500) / 2048; pwm_seq_buf[i] = (uint16_t)duty; } // Запускаємо послідовність DMA pwm->SEQ[0].PTR = (uint32_t)pwm_seq_buf;
pwm->TASKS_SEQSTART[0] = 1;
}
“`

Результат? Це звучало, як білка, що застрягла в блендері. Я сповільнив такт. Це звучало, як демон, що застряг у блендері.

Осяяння: Вбудований компонент (MLT-8530) – це магнітний зуммер. Він фізично налаштований на резонанс на 2-4 кГц (пищики). Він не може відтворювати складні частоти людської мови. Я намагався зіграти симфонію на дверному зумері.

Отже, я змінив підхід. Якщо я не можу чітко чути аудіо, давайте переконаємось, що я можу його бачити.

Зміна: Звуковий рівнемір
Мета змінилася: створити “мікрофонний тестовий бар” у стилі Zoom на 5×5 світлодіодній матриці micro:bit.

Пастка #1: Фантомний PDM
Zephyr має відмінну підтримку PDM (модуляція щільності імпульсів) мікрофонів, які поширені на платах nRF52. Ми активували CONFIG_AUDIO_DMIC, налаштували пін-коди і зафлешили плату.

Але консоль видавала помилки:

[err] dmic_nrfx_pdm: Запитувана конфігурація не підтримується

Я провів години, налаштовуючи тактові частоти. 16 кГц? 15.625 кГц? 1.28 МГц? Нічого не спрацювало.

Отже, я відкрив схему.

Мікрофон на micro:bit v2 (Knowles SPU0410LR5H) НЕ є PDM. Це аналоговий мікрофон, підключений до P0.05 (AIN3).

Я намагався змусити цифровий драйвер спілкуватися з аналоговим контактом.

Виправлення: Я викинув драйвер DMIC і написав власний накладний файл дерева пристроїв для налаштування SAADC (АЦП послідовного наближення). Зверніть увагу на специфічний коефіцієнт підсилення: мікрофон має 1.65В постійної складової, тому стандартні налаштування підсилення відразу наситяться.

“`c
/* app.overlay */
&adc {
channel@3 {
reg = <3>; // AIN3
zephyr,gain = “ADC_GAIN_1_4”; // Діапазон до 2.4В покриває 1.65В постійної складової
zephyr,reference = “ADC_REF_INTERNAL”;
zephyr,acquisition-time = ;
zephyr,input-positive = ;
zephyr,resolution = <12>;
};
};
“`

Візуалізація: Зробимо світлодіоди танцювати
Тепер у мене були чисті аудіодані. Я хотів відобразити гучність на світлодіодах.

Пастка #2: Невидимий дисплей
Я розрахував гучність і відправив її на дисплей, використовуючи display_write. І… екран залишився чорним.

Стандартний драйвер дисплея Zephyr є загальним. Він очікує буфер пікселів. Я припустив 1 байт на піксель (25 байтів).

Але драйвер micro:bit упакований по бітах. Він очікує бітову карту, де 1 біт = 1 піксель. Відправивши 25 байтів, я відправляв “дані заголовка”, які виглядали як порожній простір для драйвера.

Отже, я виправив відображення буфера. 5 рядків = 5 байтів.

“`c
/* Правильне відображення бітової карти */
static void update_display(int level) {
uint8_t buf[5];
memset(buf, 0, sizeof(buf));
for (int i = 0; i < 5; i++) { int row_index = 4 - i; // Інвертуємо, щоб заповнювати знизу вгору if (i < level) { buf[row_index] = 0x1F; // 0x1F = Бінарний 11111 (Усі світлодіоди в рядку УВІМКНЕНІ) } } // Записуємо 5 байтів до контролера дисплея display_write(display_dev, 0, 0, &desc, buf); } ``` Остаточний бос: Фізика проти сприйняття У мене були вогні. У мене був звук. Але коли я говорив, вогні ледве рухалися. Коли я подув у мікрофон, вони ненормально божеволіли. Проблема: RMS проти піку: Я розраховував RMS (середня енергія). Мова має короткі сплески енергії (приголосні), розділені тишею. Середнє значення 100 мс мови дає низьке число. Фізика: Дування створює фізичний тиск вітру (величезна напруга). Мова створює маленькі повітряні коливання (маленька напруга). Отже, я реалізував алгоритм "Підсилювач мови". Виявлення піків: Замість RMS, ми обчислюємо Макс - Мін у буфері. Це миттєво фіксує "підвищення" вашого голосу. Віднімання шумового фону: Ми емпірично з'ясували, що шумовий фон тиші становить ~94. Ми віднімемо це, щоб обнулити кімнату. Цифрове підсилення: Ми множимо залишковий сигнал на 3. Це розтягує маленькі варіації людської мови, щоб заповнити повний діапазон 0-5 світлодіодної матриці. ```c /* Логіка підсилювача мови */ int32_t raw_amp = calculate_peak_amplitude(sample_buffer, BUFFER_SIZE); // 1. Віднімемо шумовий фон int32_t signal = raw_amp - 94; if (signal < 0) signal = 0; // 2. Цифрове підсилення int32_t boosted = signal * 3; // 3. Відобразимо на світлодіодах з утриманням піку (гравітація) if (boosted > 40) target_level = 5;
else if (boosted > 30) target_level = 4;

// … тощо
“`

Висновок
Я не зміг побудувати диктофон, але досягнув успіху в створенні надійного звукового рівнеміра.

Цей проект є відмінним прикладом вбудованої інженерії:

Не вірте припущенням (перевіряйте схему!).

Не боріться з апаратним забезпеченням (якщо це зуммер, не змушуйте його співати).

Математика – ваш друг (логарифмічне масштабування робить сенсори “природними”).

Вихідний код на Github
Повний вихідний код доступний нижче. Зафлеште його на свій micro:bit v2, плесніть у долоні і спостерігайте, як світлодіоди танцюють.

Відповідний PR до офіційного репозиторію Zephyr RTOS та новий приклад для micro:bit v2 скоро з’являться.

November 25, 2025 at 05:32PM