В Софт, хард и два колеса: как мы строили 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. Но этого мало — нужно еще уметь корректировать выходное напряжение. Оригинальная формула расчета выхода выглядит так:
Но есть классный способ добавить внешнее управление, используя DAC + дополнительный резистор R3 в цепи ОС — в такой схеме выход DC-DC поддается линейной регулировке внешним сигналом. Формула выходного напряжение принимает следующую форму:
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 можно найти в статьях:
или по запросу dc dc feedback adjustment.
Хоть технически на пьезопластине и не бывает 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)
MinimumLaw
29.06.2023 15:52АЧХ все еще плохая — но я и не видел керамических излучателей, где бы она была ровной формы — ну не предназначены они для воспроизведения музыки.
Я принципе это главное, что должно быть в статье. Тональные сигналы на резонансных частотах - и это все. Другое дело, что больше и не надо.
А вообще по теме - просто интересно сколько времени решалась обозначенная задача? Какими силами? Сколько встало по бюджету? Если, конечно, это все не секретные сведения.
Katbert Автор
29.06.2023 15:52А какая именно задача? В статье много про сами излучатели, но из разработки там - только хардовая часть: мостовой драйвер управления, да dc-dc конвертер. Это все не сложно было - потестировать и воплотить в дизайн.
А вот как мы уже перерабатывали софтовую часть потом, экспериментировали со звуками и раскатывали на флот - про это будет во второй части, там ресурсов ушло поболее.MinimumLaw
29.06.2023 15:52Хорошо. Спасибо. Ждем второй части. Я просто не понял базовой идеи. Типовые решения, типовым образом выполнены, типовые грабли потоптаны... Т.е. при наличии хоть какого-то опыта разработки и понимания компонентов темы для статьи не было бы в принципе. Это все буквально азбучные вещи для грамотного разработчика аппаратуры.
Тема дальше. Внедрение в парк изделий, адаптация ПО основного контроллера под новые возможности. Опять же - про проблемы пищания вам уже озвучили (к слову - вполне адекватное решение с работой по времени, вместо контроля окружающего шума). Но если это просто вводная статья, то все меняется и становится более интересным.
aol-nnov
29.06.2023 15:52Whoosh: каждый третий арендованный самокат с плохими или полностью отсутствующими тормозами (моя статистика)
Также Whoosh: давайте придумаем как круче пищать!о том ли думы, ребята?! я уже заманался в поддержку каждый раз писать одно и то же, чтобы дефектные самокаты не выходили на линию...
DarkTiger
Подумайте, пожалуйста, про вот какой ньюанс: если звук удалось выжать громкий, а самокаты стоят возле жилого дома, то громкое "пиу-пиу" - последнее, что хочется услышать в 6 утра. Это я вам как живущий в 50м от метро говорю. Вы бы микрофон на уровень внешнего звукового давления поставили и подстраивали громкость баззера по его показаниям. В серии сложно будет микрофон добавить.
В Штатах это уже существующая проблема, приводящая к вандализму в отношении прокатных самокатов, тут была статья на эту тему.
Katbert Автор
У нас почти все звуки отключены по дефолту с 22 вечера по 7 утра, по местному времени. Эти настройки хранятся на IoT модуле и время отслеживается по данным модуля RTC. Стараемся не шуметь, когда не нужно.
saege5b
Если самокаты будут пищать громко и постоянно, то жители ближайших домов будут раздражённые, и помимо скандалов, у вас статистика испортится.
И это вне зависимости от времени суток.
Katbert Автор
Согласен, но мы просто не даем им пищать без необходимости - думаю, тут вопрос не к громкости излучателя, а к проработке сценариев использования озвучки в поле