0. Пролог

Есть старая инженерная мудрость:

Если у вас не хватает проводов — значит, вы недостаточно творчески подходите к вопросу.

У нас была ровно такая ситуация.

Работая на одном проекте системы «умного города» мы, уйдя в стандарт «одна плата с разными конфигами для всего» решили делать на ней свой BMS. Зачем да почему? Нужно было универсальное решение, котороя должно работать и с литиевыми, и со свинцовыми батареями, и с ещё парой экзотических химий, встречающихся в природе примерно так же часто, как радужные единороги. Нужно было надёжно определять тип батареи, считывать её состояние, пригодность к использованию, дату производства и всё‑всё‑всё подобное, блокировать несовместимые варианты и вообще предотвращать самое главное — человеческую ошибку. Но как это часто бывает в компаниях где в R&D священный хаос — никто и не задумывался чтобы сесть и обсудить «А как мы вообще это делать будем». В производство ушла тысяча плат.

В любой другой ситуации мы бы пошли по наименьшему пути сопротивления: i2c на коннекторе рядом и EEPROM на аккуме. Но не тут было.

На нашей плате в качестве контроллера заряда (читай как BMS) была установлена STM32G0. Можно конечно бы было сделать серию тестов замеряя потребление, пропускную, токи и так далее, но это заняло бы минуты. Лицо бедного сервисника которому в случае дичайшего ахтунга надо проехать ~200 устройств за два дня и везде проверить батареи, думаю представлять не нужно

Что же мы имеем? А имеем мы коннектор Molex miniFit на 4 пина и три дорожки: +12V, GND, NTC

Первая мысль была вполне инженерная и почти безумно-романтичная:

“А давайте пустим данные прямо поверх питания, как дедовский радиоприёмник по сети или ADSL по медям!”

Звучит красиво. Слишком.

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

— Нет. Убьёшь либо сигнал, либо аккумулятор, либо себя и несколько человек рядом.

Но.. Почему?

Прежде всего, PLC модуляция “хороша” при переменном токе, но не при постоянном.

Да, в каком-то виде она есть в кейсах почти всех TWS наушников дороже 20$ для уведомления “затычек” о том что надо перейти в режим коннекта, или что кейс открыт, или же для привязки к кейсу (как у яблочных). Но токи там сооовсем другие и работает оно как тупой DC сигнал, без импедансного дрочева — батарейкки в затычках управляются процессорами самих затычек.

Так что же не так с свинцовыми батареями-то?

Свинцовые и гелевые баночные батареи — это не аккумуляторы в привычном смысле.

На высоких частотах они могут себя повести так, что любой миллисекундный всплеск усилится, перевернется и прилететь тебе обратно в линию.

Любая модуляция на фоне такого электромеханического цирка становится лотереей. Стабильности нет, повторяемости нет, импеданс прыгает как курс рубля в годы не самых лучших событий.

В общем, в тот момент мечта о PLC-связи по силовой тихо и печально отправилась туда же, куда и все мои ПЕТ-проекты.

Оставалось два пина.

Но поскольку гонять данные по массе — самоубийство, пин остался один.

1. Что тебе в сопротивлении моем?

NTC традиционно никто не трогает и к нему относятся как к священной корове: все ему верят и без него никуда. В нашем же случае это был просто один из пинов группы ADC вышеупомянутой G0 — самый что ни на есть обычный терморезисторный вход, ничем не примечательный.

Если смотреть на ситуацию под углом «нам габэлла, надо сделать на вчера», то NTC — это идеальный пин для трансмиссии данных. Токи смешные, вешай «паразиткой» себе 8051 с EEPROM да самим терморезистором — и в путь.

А поскольку прошивку к G0 писали тоже мы… Ничего не мешало нам пихнуть туда Manchester и в нем, как бы между прочим, передавать данные с термопары.

2. Кто работал в R&D — в цирке не смеётся.

В паразитное питание Cortex-M не повесишь, да и нахрен он там нужен.

Смысл был в том, чтобы:

  • не жрать ток

  • не занимать память

  • не требовать нормального питания

  • не иметь лишних будильников

  • не требовать человеческих условий эксплуатации

То есть нужен маленький, примитивный, послушный чип, который согласится жить от микротока подтяжки пина NTC, просыпаться или включаться по запросу и умирать обратно, а не жарить 7 мА «потому что так надо по даташиту».

Так что мы начали искать что-то, что подходило бы под следующие критерии:

  1. Стоит дешево. Настолько, чтобы никто в финансах даже не заметил столь очевидной дыры в проекте который длился год с лишним.

  2. Работает на очень простом, но предсказуемом ядре.

    В идеале — классика типа 8051, которую уже 40 лет никто не может убить.

  3. Имеет свой ADC. Термопару ведь всё еще надо куда-то повесить

  4. Имеет I²C для EEPROM.

    Чтобы паспортные данные хранить не в RAM, не в облаке и не в душе, а в обычной дешёвой памяти.

  5. Встать и прожить от пары микроампер подтяжки STM32.

    Условно — если чипу хватает питания от того же тока, что меряет NTC, значит он наш друг.

И вот тогда на сцену вышел он, наш новый любимец, как будто созданный специально для того чтобы спасать инженерный идиотизм в подобных ситуациях — EFM8BB10F2G от SiLabs. 50 мегагерц 8051 ядра, 0.25кб ОЗУ, 16кб ПЗУ, 15 каналов ADC, компаратор, свой LDO, 2 UART — это казалось просто сказкой. Оверкилльной сказкой.

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

3. Байты, запись, два стейта.

Итак, у нас есть:

  • батарея,

  • один NTC-пин;

  • подтяжка от STM32;

  • маленький 8051-микрик, который питается буквально «от воздуха»;

  • безудержное желание научить этот микрик разговаривать с BMS, даже ценой веселья третьего типа.

Как всё это объединить?

Es war erstaunlich einfach.

Спасителем тут выступил протокол под названием Manchester (но без United). Чудо в котором по одному пину можно гонять и clock, и data, и сидеть на паразитке при всём при этом.

Но… Как же нам систематизировать данные и что мы вообще будем там слать?

Прежде всего — фрейм. Выглядел он примерно так.

8 байт отправили — 32 байта получили. В С Е Г Д А.

Никаких полумер, никаких “частичных чтений”, никаких плясок с адресами.

Мы не изобретали SMBus, мы не городили Modbus, мы сделали настолько тупой протокол, насколько позволяет чувство собственного достоинства инженера.

Принцип простой:

Запрос всегда фиксированной длины — 8 байт. Ответ всегда фиксированной длины — 32 байта. Стейтов всего два — WRITE или READ.

С таким подходом даже 8051 с 256 байтами RAM не запутается и не спросит “а что вы от меня хотите?”.

Эдакий High-Density Kolhoz.


Формат запроса (8 байт)

0: MAGIC      (0xA7)
1: RW_INDEX   (команда)
2: PAGE       (0–6)
3: DATA0
4: DATA1
5: DATA2
6: DATA3
7: XOR        (XOR первых 7 байт)

Семантика простая до неприличия:

  • 0x00 → «дай мне весь паспорт»

  • 0x1X → «прочитай страницу X» (4 байта)

  • 0x2X → «запиши страницу X» (4 байта)

Где X — номер 4-байтной страницы в 32-байтном паспорте батареи.

А сам паспорт — это просто block of truth™.


Формат ответа (32 байта)

Выглядело это примерно так:

00–01  MAGIC         (2B)
02–17  SERIAL        (16B)
18–1B  VENDOR        (4B)
1C     TYPE          (1B)
1D–1E  ERR           (2B)
1F–20  CRIT          (2B)
21     V_LIMIT       (1B)
22     A_LIMIT       (1B)
23     TEMP          (1B)
24–25  XOR           (2B)
image.png
image.png

Два стейта. Больше не нужно.

Вся система работает в одном из двух режимов:

  1. СТЕЙТ ЧТЕНИЯ (RW_INDEX = 0x00 или 0x1X)

    Нам же нужно знать как там температура у батареи? Ну вот, шлешь себе 0xA7 0x00 0x00 0x00 0x00 0x00 0x00 XOR раз в секунду, берешь третий с конца байт, и живешь счастливо.

  2. СТЕЙТ ЗАПИСИ (RW_INDEX = 0x2X)

    Батарея принимает 4 байта, обновляет страницу в EEPROM и снова молчит, пока не спросят. (Жаль что люди часто так не умеют)


4. НЕ загоняйся в рамки. Или загоняйся. Это твоя жизнь

Так что же такого было в этих рамках что запрос аж ВОСЕМЬ байт, а ответ аж 32, и главное — нахрена батарее серийник, если мы не Apple?

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

Ранее мы начали обвешивать EEPROM’ами всю периферию — от датчиков освещенности и дисплеев — и едва ли не дошли до чипирования выездных ремонтников (прим.: товарищ майор, это лишь шутка)

Но когда у всего внутри устройства есть четкий идентификатор содержащий

  • Дату производства

  • Уникальный идентификатор

  • Серию/партию

  • Особенности

даже ремонтные выезды становятся куда проще. Никакого заполнения бумажек вида “Был в Катовицах, в инфотабло #1337 заменил датчик света” больше заполнять не нужно — при рестарте система видит что серийник сменился в сравнении с Factory Defaults — и мы снова всё знаем. Раньше всех.

С батареями та же история. Неодноразовые истории вида “ООО Рога и копыта заслали 100 бракованных батарей, надо ездить проверять”

Забавно что самым сложным элементом процесса оказалось обьяснение людям что один маленький дополнительный шаг при приёмке — большой шаг при сервисинге.

Так что блоб возвращаемый батареей на пустой запрос выглядит примерно так

0xA7 0x01 0x41 0x31 0x42 0x43 0x32 0x44 0x33 0x34 0x45 0x46 0x47 0x35 0x36 0x37 0x38 0x39 0x52 0x4F 0x47 0x41 0x02 0x00 0x00 0x00 0x12 0x06 0x23 0x00 XOR

или же в читаемом виде

A7 ver1

A1BC2D34EFG56789

ROGA

Li-Ion

0 errors

0 criticals

12V

6A

35°C

image.png
image.png

А что с записью?

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

To be continued...

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