В Софт, хард и два колеса: как мы строили IT-инфраструктуру в Whoosh упоминалось, что первые версии IoT мы ставили под непрозрачную крышку, замещая стандартный модуль управления от самоката Xiaomi M365. То есть, после установки и проклеивания крышки, связь с модулем была только через облако. В идеальном мире этого было бы достаточно, но реальность диктовала условия, в которых нужна индикация процессов — как в режиме обслуживания, так и в городе, у пользователей. Прием управляющих команд, отправка телеметрии, подключение к сети и выход устройств в онлайн — разные этапы нужно было различать сервисной команде для быстрой диагностики, а пользователя — предупреждать об изменениях в базовых сценариях использования — командах, ошибках, ограничениях и т.д. Отдельная тема для рефлексии — методы озвучки тихого электротранспорта для повышения пассивной безопасности в условиях шумной городской среды.

Ок, нужны звуки — решили ставить buzzer (или зуммер). Это такая электромеханическая штуковина, которая под воздействием внешнего переменного напряжения умеет деформироваться и издавать звуки.

Под катом рассмотрим базовые принципы работы излучателей, детали дизайна управляющей электроники, а в продолжении расскажем, как мы выжали из зуммера максимум возможностей.

Немного теории

Зуммеры обычно бывают двух типов - электромагнитные и пьезокерамические. Первые работают на более низких напряжениях и более высоких токах (~ 1.5V - 12V, >20mA), а вторые — наоборот (~ 12V - 200V, <20mA). В электромагнитных зуммерах ток пропускается через катушку, которая создает магнитное поле; гибкий ферромагнитный диск притягивается к катушке при наличии тока и возвращается в положение покоя, когда ток через катушку не протекает — такими колебаниями и создается звук. Это устройство, управляемое током; ток, проходящий через катушку, определяется приложенным напряжением и собственным импедансом.

Пьезокерамические зуммеры используются в тех же целях, что и магнитные. Изготавливаются путем размещения электрических контактов на двух гранях диска из пьезоэлектрического материала и последующей поддержки диска по краям в корпусе. Когда напряжение подается на два электрода, пьезоэлектрический материал механически деформируется. Это перемещение пьезодиска внутри зуммера создает звук, аналогично перемещению ферромагнитного диска в магнитном зуммере.

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

У Murata есть неплохой документ Piezoelectric Ceramic Sensors — там нет именно зуммеров, но есть много интересного про пьезоэлектрическую керамику, используемую в сенсорах (вообще, зуммеры тоже можно заставить генерировать ЭДС, воздействуя на них механически). А у TDK есть каталог Piezoelectronic buzzers, откуда отдельно хотелось бы извлечь следующие утверждения (к ним вернемся чуть позже, в описании схемы управления):

  • Do not apply DC bias to the piezoelectric buzzer; otherwise insulation resistance may become low and affect the performance

  • Do not supply any voltage higher than applicable to the piezoelectric buzzer

Ключевые характеристики зуммеров:

  • Frequency Response — насколько эффективно зуммер воспроизводит звук на заданной частоте

  • Sound Pressure Level (dB Pa) – уровень звукового давления, представляет собой отклонение от атмосферного давления, вызванное звуковой волной, выраженное в децибел-паскалях. Обычно он пропорционален входному напряжению и затухает на 6dB при удвоении расстояния от зуммера

  • Resonant Frequency (Hz) — все вещи имеют определенную частоту, на которой они склонны вибрировать, эта частота называется резонансной. Для зуммеров резонансная частота — та, при которой они звучат наиболее громко

  • Impedance (Ohm) — электрический импеданс представляет собой отношение приложенного напряжения к току и зависит от частоты

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

Установка в IoT модуль

Важной частью звукового тракта являются характеристики объема распространения сигнала — материал, форма и т.д. Однако, при разработке IoT не хотелось заниматься проектированием звуковода — на его расчет и реализацию не было ни времени, ни достаточных знаний в акустике, да и форма колодца под установку была продиктована производителем самокатов. Поэтому, речь в статьях пойдет в основном про программно-аппаратную составляющую, а так же будет много экспериментов.

Не пересчитать, сколько разных моделей излучателей мы испробовали в поисках наиболее громкого — что бы впаять в плату, закрыть крышкой — и его было слышно. Вот примеры старых IoT с разными излучателями, которые удалось найти:

Внутри них нет никакой дополнительной электроники — лишь пластина керамики (на последнем фото, кстати, не пьезокерамический излучатель, а магнитный) с выводами для DIP монтажа в печатную плату. Существуют излучатели, на которые достаточно подать напряжение питания — и они звучат на одной единственной частоте, что создает готовое решение — подключай, подавай питание и звучи. Это удобно, когда не нужно перегружать схему модуляцией управляющего сигнала, но у нас все равно стоял микроконтроллер с кучей аппаратных таймеров, да и хотелось оставить возможность играть сложные звуки и мелодии.

Теперь несколько слов про то, как этим управлять.

Основы управления

Для раскачки требуется внешний возбуждающий сигнал — обычно используют транзистор, усиливающий воздействие. Для магнитного зуммера дополнительно ставят диод отсечки обратного напряжения, возникающего при быстром закрытии ключа. В схеме управления пьезозуммером диод уже не нужен, поскольку индуктивность его мала, но требуется параллельный резистор сброса напряжения при разомкнутом ключе. Вот пример типичной схемы управления из гайда от TDK c одним транзистором:

Есть несколько разновидностей схем управления. Та, что выше — самая простая, требует небольшого количества недорогих элементов, но рассеивает мощность на резисторе сброса и ограничена напряжением питания VCC. Кстати, совершенно не важно, подключаем ли мы зуммер к питанию или к земле — на работе излучателя это никак не отразится. Эта схема также может быть построена на полевом транзисторе вместо биполярного.

Murata в документации приводит следующие схемы раскачки, собранные на RC цепочках, транзисторах или логических элементах:

Можно избавиться от резистора сброса, используя комплементарную пару транзисторов, работающих в режиме полумоста. А можно пойти дальше и поставить два полумоста, работающих в противофазе: то есть, собрать полный мост, состоящий из четырех ключей. Преимущество ее состоит в том, что повышается доступное напряжение питания, дважды (чем мы и воспользуемся чуть позже) и как следствие — повышается громкость звука на ~6dB.

В первой версии IoT мы использовали N-канальный MOSFET, затвор которого раскачивается таймером микроконтроллера в режиме capture/compare — это аппаратный PWM с возможностью установки частоты и скважности импульсов. Приблизительный код управления можно найти в статье STM32: FreeRTOS и пьезокерамический излучатель.

В итоге, звуковой семпл описывается структурой BuzzerSample_t, мелодия - массивом структур; разные потоки RTOS накидывают семплов в очередь, а поток-драйвер динамика разбирает ее и проигрывает мелодию:

typedef struct
{
	NOTE_t Note;
	u32 Amplitude;
	u32 Duration;
	u32 Pause;
} BuzzerSample_t;

Вот как выглядит вступление Start Wars - The Imperial March:

#define BUZZER_STAR_WARS_DURATION_DEF	130
#define BUZZER_STAR_WARS_PAUSE_DEF		25

static BuzzerSample_t StarWarsBuff[] =
{
	{.Note = NOTE_G4, .Amplitude = BUZZER_AMPL_MAX, .Duration = 4 * BUZZER_STAR_WARS_DURATION_DEF, .Pause = BUZZER_STAR_WARS_PAUSE_DEF},
	{.Note = NOTE_G4, .Amplitude = BUZZER_AMPL_MAX, .Duration = 4 * BUZZER_STAR_WARS_DURATION_DEF, .Pause = BUZZER_STAR_WARS_PAUSE_DEF},
	{.Note = NOTE_G4, .Amplitude = BUZZER_AMPL_MAX, .Duration = 4 * BUZZER_STAR_WARS_DURATION_DEF, .Pause = BUZZER_STAR_WARS_PAUSE_DEF},
	{.Note = NOTE_Ds4, .Amplitude = BUZZER_AMPL_MAX, .Duration = 3 * BUZZER_STAR_WARS_DURATION_DEF, .Pause = BUZZER_STAR_WARS_PAUSE_DEF},
	{.Note = NOTE_As4, .Amplitude = BUZZER_AMPL_MAX, .Duration = 1 * BUZZER_STAR_WARS_DURATION_DEF, .Pause = BUZZER_STAR_WARS_PAUSE_DEF},
	{.Note = NOTE_G4, .Amplitude = BUZZER_AMPL_MAX, .Duration = 4 * BUZZER_STAR_WARS_DURATION_DEF, .Pause = BUZZER_STAR_WARS_PAUSE_DEF},
	{.Note = NOTE_Ds4, .Amplitude = BUZZER_AMPL_MAX, .Duration = 3 * BUZZER_STAR_WARS_DURATION_DEF, .Pause = BUZZER_STAR_WARS_PAUSE_DEF},
	{.Note = NOTE_As4, .Amplitude = BUZZER_AMPL_MAX, .Duration = 1 * BUZZER_STAR_WARS_DURATION_DEF, .Pause = BUZZER_STAR_WARS_PAUSE_DEF},
	{.Note = NOTE_G4, .Amplitude = BUZZER_AMPL_MAX, .Duration = 8 * BUZZER_STAR_WARS_DURATION_DEF, .Pause = BUZZER_STAR_WARS_PAUSE_DEF},

	{.Note = NOTE_D5, .Amplitude = BUZZER_AMPL_MAX, .Duration = 4 * BUZZER_STAR_WARS_DURATION_DEF, .Pause = BUZZER_STAR_WARS_PAUSE_DEF},
	{.Note = NOTE_D5, .Amplitude = BUZZER_AMPL_MAX, .Duration = 4 * BUZZER_STAR_WARS_DURATION_DEF, .Pause = BUZZER_STAR_WARS_PAUSE_DEF},
	{.Note = NOTE_D5, .Amplitude = BUZZER_AMPL_MAX, .Duration = 4 * BUZZER_STAR_WARS_DURATION_DEF, .Pause = BUZZER_STAR_WARS_PAUSE_DEF},
	{.Note = NOTE_Ds5, .Amplitude = BUZZER_AMPL_MAX, .Duration = 3 * BUZZER_STAR_WARS_DURATION_DEF, .Pause = BUZZER_STAR_WARS_PAUSE_DEF},
	{.Note = NOTE_As4, .Amplitude = BUZZER_AMPL_MAX, .Duration = 1 * BUZZER_STAR_WARS_DURATION_DEF, .Pause = BUZZER_STAR_WARS_PAUSE_DEF},
	{.Note = NOTE_Fs4, .Amplitude = BUZZER_AMPL_MAX, .Duration = 4 * BUZZER_STAR_WARS_DURATION_DEF, .Pause = BUZZER_STAR_WARS_PAUSE_DEF},
	{.Note = NOTE_Ds4, .Amplitude = BUZZER_AMPL_MAX, .Duration = 3 * BUZZER_STAR_WARS_DURATION_DEF, .Pause = BUZZER_STAR_WARS_PAUSE_DEF},
	{.Note = NOTE_As4, .Amplitude = BUZZER_AMPL_MAX, .Duration = 1 * BUZZER_STAR_WARS_DURATION_DEF, .Pause = BUZZER_STAR_WARS_PAUSE_DEF},
	{.Note = NOTE_G4, .Amplitude = BUZZER_AMPL_MAX, .Duration = 8 * BUZZER_STAR_WARS_DURATION_DEF, .Pause = BUZZER_STAR_WARS_PAUSE_DEF},

И вот, как оно проигрывается на керамическом и на магнитном излучателях:

Если с частотой все понятно, то с амплитудой уже чуть сложнее. Технически, тут она вообще не применима — излучатель раскачивается постоянным напряжением в ключевом режиме. Однако, на практике максимальная звуковая амплитуда достигается при симметричном сигнале (скважность импульсов 50%) и смещение в любую сторону от середины ослабляет звук, подмешивая соседние гармоники, которые иногда проявляются даже сильнее, чем основная частота:

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

Тут хочется провести промежуточный итог: изначально излучатель звука для нас не был приоритетным направлением. Нужен был простой и дешевый способ коммуникации продукта и пользователя; нам хотелось подсвечивать какие-то действия с IoT модулем (самокатом) — исполнение команды, отправка телеметрии, сигнализация и т.д. Выбирая источника звука, хотелось сэкономить как на аппаратной начинке IoT, так и на программных ресурсах — не хранить большие мелодии, не использовать тяжелые кодеки, не встраивать полноценный динамик в устройство. При разработке новой версии IoT все еще не было возможности уделить этому направлению слишком много внимания, поэтому задача стояла одна — получить максимальную громкость, вписавшись в размеры нового корпуса. Но внести некоторые правки в железо все же пришлось.

Обновленный hardware дизайн

В новой версии IoT мы выбрали PKM22EPPH4012 (или его аналог от L-KLS3-PT-22*08). Это был самый большой излучатель, чьи размеры идеально подходили для установки на печатную плату и в корпус, не превышая допустимых габаритов расположения в рулевой стойке.

АЧХ все еще плохая — но я и не видел керамических излучателей, где бы она была ровной формы — ну не предназначены они для воспроизведения музыки.

Для раскачки взяли микросхему DRV88703.6-A Brushed DC Motor Driver. Да, она позиционируется, как микросхема управления щеточным мотором, но по сути является мостовым драйвером, а значит умеет не просто переключать выход от нуля до питающего, а инвертирует напряжение, попеременно подавая его то на одну обкладку, то на другую, увеличивая амплитуду колебаний излучателя в два раза.

Тут стоит отметить, что мы нарушили правило из гайда от TDK и запитали мостовой драйвер напряжением 25V, что дает напряжение 50Vp-p на излучателе. Хотя, не совсем так, но до этого еще доберемся.

Согласно таблице управления драйвером, для смены направления приложенного напряжения, на входы IN1/IN2 нужно подавать взаимно инверсные сигналы; в остальных случаях драйвер засыпает, переводя выходы в Hi-Z или же вешает их на землю.

Лучшим решением было бы использовать таймер, который умеет выдавать комплементарный PWM, но насколько сейчас могу вспомнить, эти выводы были заняты, а complementary output PWM доступен на сильно ограниченном числе таймеров. Инвертор на входе нужен как раз для того, что бы переключаться от одной ножки микроконтроллера, но с его использованием связан неприятный нюанс — ведь как было сказано выше, на излучатель не рекомендуется прикладывать постоянную составляющую напряжения. А подать нужную для отключения комбинацию на IN1/IN2 невозможно - не позволит инвертор. В нашем случае решение пришло из разряда “не баг, а фича” — мы просто запитали мостовой драйвер от DC-DC конвертора, который в выключенном состоянии на выходе имеет напряжение со входа — 5V. А это ниже минимально допустимого напряжения питания драйвера и он просто отключается. Повезло то, что нужно!

Но ведь мы стремились повысить громкость за счет увеличения напряжения, а не только силами мостового драйвера, разве нет?

Да. Вот так:

DC-DC можно включать и отключать, используя вход ENABLE. Но этого мало — нужно еще уметь корректировать выходное напряжение. Оригинальная формула расчета выхода выглядит так:

Vout = \dfrac{\dfrac{R1}{R2}+1}{Vref}

Но есть классный способ добавить внешнее управление, используя DAC + дополнительный резистор R3 в цепи ОС — в такой схеме выход DC-DC поддается линейной регулировке внешним сигналом. Формула выходного напряжение принимает следующую форму:

Vout = Vref * (1 + \dfrac{R1}{R2}) + (Vref-Vdac) * \dfrac{R1}{R2}

Cигнал +25V_DAC ∈ [0, 3.3]V — в нашем случае, это выход RC low-pass фильтра на ножке таймера, настроенного на частоту фильтра. DAC в MCU можно использовать тоже, но лучше таймер — небольшие колебания и ослабление фильтра не критичны, а закладывать в схему DAC, которого может не быть в чипе, не хотелось. В итоге имеем следующие выходные граничные напряжения:

>>> 1.23 * (1 + 169/10) - (169/27.4) * (0.0 - 1.23)
29.603496350364964
>>> 1.23 * (1 + 169/10) - (169/27.4) * (3.3 - 1.23)
9.249481751824817

Отлично, тюнить дальше уже можно программно, подстраивая параметры PWM сигнала. Более подробно технику Digital Adjustment of DC-DC Converter Output Voltage можно найти в статьях:

Хоть технически на пьезопластине и не бывает 50V сигнала, но попеременная инверсия 25V раскачивает ее в обе стороны на удвоенную амплитуду, что слегка выходит за допуски, заложенные производителем. В любом случае, теперь есть возможность контролировать приложенную амплитуду программно.

Раз у нас есть функционал настройки амплитуды, можно совсем убрать управление скважностью частотозадающего сигнала, а всю настройку DC-DC конвертора обернуть в одну функцию, где параметр ampl задает диапазон выходного напряжения:

#define TIM_DACPWM_MIN_PWM		0
#define TIM_DACPWM_MAX_PWM		100

void Pl_Buzzer_SetAmpl(float ampl)
{
	ASSERT_NORM_FLOAT(ampl);
	CUT_NORM_FLOAT(ampl);

	u8 amplTIM_PWM = ampl * TIM_DACPWM_MAX_PWM;
	u8 amplMinTIM_PWM = TIM_DACPWM_MIN_PWM;

#if TIM_DACPWM_SIGNAL_INVERTED
	amplTIM_PWM = TIM_DACPWM_MAX_PWM - amplTIM_PWM;
	amplMinTIM_PWM = TIM_DACPWM_MAX_PWM;
#endif /* TIM_DACPWM_SIGNAL_INVERTED */

	if(amplTIM_PWM == amplMinTIM_PWM)
	{
		TIM_DACPWM_SetAmpl(amplTIM_PWM);
		GPIO_BuzzerEnableDCDC_SetState(GPIO_RST);
		return;
	}

#if !SOUND_DISABLE
	GPIO_BuzzerEnableDCDC_SetState(GPIO_SET);
	TIM_DACPWM_SetAmpl(amplTIM_PWM);
#endif /* !SOUND_DISABLE */
}

Что дальше

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

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

P.S. Кстати, узнать много всего про зуммеры, в доступной форме и с красивыми картинками, можно на этом сайте: https://www.cuidevices.com/blog/audio

картинка: Magneticmag.com

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


  1. DarkTiger
    29.06.2023 15:52
    +1

    Подумайте, пожалуйста, про вот какой ньюанс: если звук удалось выжать громкий, а самокаты стоят возле жилого дома, то громкое "пиу-пиу" - последнее, что хочется услышать в 6 утра. Это я вам как живущий в 50м от метро говорю. Вы бы микрофон на уровень внешнего звукового давления поставили и подстраивали громкость баззера по его показаниям. В серии сложно будет микрофон добавить.

    В Штатах это уже существующая проблема, приводящая к вандализму в отношении прокатных самокатов, тут была статья на эту тему.


    1. Katbert Автор
      29.06.2023 15:52
      +2

      У нас почти все звуки отключены по дефолту с 22 вечера по 7 утра, по местному времени. Эти настройки хранятся на IoT модуле и время отслеживается по данным модуля RTC. Стараемся не шуметь, когда не нужно.


      1. saege5b
        29.06.2023 15:52

        Если самокаты будут пищать громко и постоянно, то жители ближайших домов будут раздражённые, и помимо скандалов, у вас статистика испортится.

        И это вне зависимости от времени суток.


        1. Katbert Автор
          29.06.2023 15:52
          +1

          Согласен, но мы просто не даем им пищать без необходимости - думаю, тут вопрос не к громкости излучателя, а к проработке сценариев использования озвучки в поле


  1. MinimumLaw
    29.06.2023 15:52

    АЧХ все еще плохая — но я и не видел керамических излучателей, где бы она была ровной формы — ну не предназначены они для воспроизведения музыки.

    Я принципе это главное, что должно быть в статье. Тональные сигналы на резонансных частотах - и это все. Другое дело, что больше и не надо.

    А вообще по теме - просто интересно сколько времени решалась обозначенная задача? Какими силами? Сколько встало по бюджету? Если, конечно, это все не секретные сведения.


    1. Katbert Автор
      29.06.2023 15:52

      А какая именно задача? В статье много про сами излучатели, но из разработки там - только хардовая часть: мостовой драйвер управления, да dc-dc конвертер. Это все не сложно было - потестировать и воплотить в дизайн.
      А вот как мы уже перерабатывали софтовую часть потом, экспериментировали со звуками и раскатывали на флот - про это будет во второй части, там ресурсов ушло поболее.


      1. MinimumLaw
        29.06.2023 15:52

        Хорошо. Спасибо. Ждем второй части. Я просто не понял базовой идеи. Типовые решения, типовым образом выполнены, типовые грабли потоптаны... Т.е. при наличии хоть какого-то опыта разработки и понимания компонентов темы для статьи не было бы в принципе. Это все буквально азбучные вещи для грамотного разработчика аппаратуры.

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


  1. aol-nnov
    29.06.2023 15:52

    Whoosh: каждый третий арендованный самокат с плохими или полностью отсутствующими тормозами (моя статистика)
    Также Whoosh: давайте придумаем как круче пищать!

    о том ли думы, ребята?! я уже заманался в поддержку каждый раз писать одно и то же, чтобы дефектные самокаты не выходили на линию...