Информация
- В рейтинге
- 584-й
- Откуда
- Владивосток, Приморский край, Россия
- Дата рождения
- Зарегистрирован
- Активность
Специализация
Инженер встраиваемых систем, Разработчик приложений
Ведущий
От 250 000 ₽
Linux
Java
Apache Maven
Java SE
Разработка программного обеспечения
Программирование микроконтроллеров
Системное программирование
Qt
Git
Bash
"Вы LWIP тоже на Джаву переведете :)" - Уже была такая мысль. Может быть. Не забывайте, что TCP/IP это уже существующий и множество раз реализованный стек. Его не нужно изобретать.
Разработчику способному создать RTOS на ассемблере такая задача вполне по зубам. Особенно если будет команда. Особенно, если проект наберет популярность. Может быть даже мой любимый SCTP реализую - ведь это не про 8-бит, это про vm5277 который я планирую и на десктопы развернуть.
P.S. Вы меня пытаетесь напугать MAC'ом? Серьезно?
Судя по вашим аргументам, наше обсуждение вышло из технического русла и перешло в область идеологического фанатизма.
Ваши вопросы и тезисы («зачем всё это нужно», «перенубедите меня», «всё сложно») имеют исключительно холиварный характер. Вы требуете от меня универсальных ответов на субъективные вопросы, на которые embedded-сообщество спорит десятилетиями.
Дискутировать в таком ключе непродуктивно. Я не планирую тратить силы на то, чтобы переубеждать лично вас или доказывать очевидную ценность HAL, переносимости кода и безопасности памяти для бизнеса. Все технические ответы, исходный код Альфы и примеры работы на реальном железе (включая WS2812B и экраны) уже выложены в моем репозитории. Кому интересно - тот изучает и пробует. Всего вам доброго
Я не знаю чем Вы занимаетесь, чтобы Вас переубедить. Это инструмент - достоинства которого я описал. Можно еще почитать https://github.com/w5277c/vm5277/blob/main/FAQ.md для дополнительной информации и посетить сайт-визитку https://vm5277.ru/
На самом деле этот вопрос очень прост. У него ровно такие-же ответы как и на вопрос 'зачем писать на Java когда есть Си' - не думаю, что я смогу написать ответ лучше чем уже существующие ответы.
Лично от себя приведу пример:
У меня есть свой проект умного дома, со значительным набором своих устройств (датчики, управление, шлюзы и прочее).
Все прошивки реализованы на чистом ассемблере на базе моей RTOS (core5277). vm5277 - это следующий этап развития core5277 - так как ему не хватало функционала для легкого описания прикладной (бизнес) логики и не хватало переносимости.
Когда мы говорим об одной конкретной прошивке с простым функуионалом - да тратить силы на какое-то стороннее решение (да и еще от него зависеть) - это глупо.
Но как только у вас появляются задачи где нужно реализовать кучу бизнес логики а потом, вполне вероятно ситуация переноса прошивки на другие чипы - вот здесь удобство vm5277 сильно перевешивает нативный кодинг даже на Си.
Плюс, мой язык дает безопасность и легкую читабельность, которой нет в Си.
Я предлагаю инструмент который в будущем позволит Вам писать бизнес логику гораздо с меньшими трудозатратами, с легко читаемым синтаксисом, с существенно меньшим порогом вхождения в embedded, с большей безопасностью итогового кода и с легкой переносимостью на другие чипы. Если для Вас эти характеристики не существенны - то да, Вам мой проект не нужен. А вот бизнесу такой проект нужен, так как существенно сокращает цену разработки и позволяет нанимать менее дорогостоящих специалистов.
Этот проект родился не на пустом месте. У меня есть собственный масштабный проект умного дома, где десятки прошивок (датчики, шлюзы, исполнительные устройства) написаны мной на чистом ассемблере на базе моей же RTOS (
core5277).Я знаю регистры и ассемблер досконально. И именно на практике я упёрся в стену: когда устройств много, а их бизнес-логика усложняется, нативный кодинг (даже на Си) превращается в ад из-за нулевой переносимости кода и постоянного риска словить скрытый баг с памятью.
vm5277 - это закономерная эволюция моей ассемблерной RTOS. Если вам нужно один раз написать простую мигалку — пишите на Си, моё решение вам не нужно. Но если вы строите распределенную экосистему устройств, где бизнес-логика постоянно растет, а чипы из-за дефицита или рынка приходится менять, - безопасность, читаемость и кроссплатформенность J8B окупают себя на 100%
Вы практически описали концепцию J8B, только предложили делать её руками. Ваши кастомные связанные списки (linked list) и ручные индексы в Си - это и есть динамическое распределение ресурсов, на которое процессор потратит ровно те же такты. Разница лишь в том, что в Си Вы пишете это вручную под каждую задачу (регулярно ловя баги с указателями), а в vm5277 этот алгоритм написан один раз, вылизан на ассемблере и скрыт в рантайме.
Но самое забавное - динамика в J8B добровольная. Если Вы так сильно её отвергаете, вас никто не заставляет использовать
new. Вы вольны объявлять переменные глобально, делать методы статическими и писать всю программу на чистой статике, как Вы привыкли в Си. Язык дает выбор: хотите - пишите в старом процедурном стиле, хотите - используете мощь безопасного ООП с экономией ОЗУЗачем вообще использовать ООП и интерфейсы на микроконтроллерах?
В Си-подходе, если вам нужно перенести код экрана с аппаратного I2C микроконтроллера ATmega328 на программный ногодрыг (Bit-bang) для ATtiny (где аппаратного I2C нет в принципе), вам придется переписать всю библиотеку дисплея, обмазав её макросами
#ifdef. А если у вас в системе 5 разных дисплеев и 3 разных датчика - Си-код превратится в нечитаемый ад.В ООП-модели J8B эта проблема решается через полиморфизм интерфейсов. Вы пишете драйвер дисплея один раз в жизни. Ему всё равно, какая под ним железка. В конструктор экрана вы можете передать объект
HardwareI2cна одном чипе, илиSoftwareI2cна другом. Код самого экрана не изменится ни на одну строчку.ООП на 8 битах нужно не ради абстракций ради абстракций. Оно нужно, чтобы избавиться от бесконечного переписывания драйверов, защитить ОЗУ от багов с указателями и собирать прошивки из готовых, безопасных и на 100% переносимых кубиков как в LEGO - все это дико экономит время и нервы разработчика а также снижает его порог вхождения в данную отрасль.
Конечно разумно.
1 - 8 бит имеют свой сегмент и он очень не маленький и у них есть свои преимущества которых нет у чипов на базе Cortex
2 - 8-бит - это старт проекта - если проект зайдет, то он придет и на 32 бита и на десктопы.
3 - вообще тема использования мощных МК достаточно сомнительная, потому что их можно заменить на SoC с еще большими преимуществами.
Это целиком Ваше право и Ваше мнение. Спорить с этим никакого смысла не имеет.
"которое вы (еще только) собираетесь предложить" - сейчас я привлекаю разработчиков к Альфе, а не пользователей к релизу. Вы же меня упрекаете что на Альфе не все готово - естественно не все готово - именно об этом я и пишу в статье.
Этот проект начат не с чистого листа. У меня есть проект RTOS для AVR на ассемблере https://github.com/w5277c/core5277
Вот в нем есть программные драйвера, например https://github.com/w5277c/core5277/blob/devel/core/drivers/i2c_su.inc - I2C Slave основанный на USI
В ядре текущего проекта для AVR будет набор программных драйверов. Конечно они будут более медленные, чем аппаратные и будут больше потреблять ресурсов, но они будут.
При этом огромное преимущество дает верхний уровень - можно писать драйвера (не зависящие напрямую от железа) на j8b один раз с полной переносимостью кода. Пример https://github.com/w5277c/vm5277/blob/main/runtime/drivers/ifaces/SpiSwHd.j8b
Вот пример реализации в моем проекте светодиодной матрицы 8x8 на базе MAX7219 https://github.com/w5277c/vm5277/blob/main/runtime/drivers/display/LedMatrixMax7219.j8b в его конструкторе создается программный интерфейс (аналогично будет и для любого экрана с I2C) iface = new SpiSwHd(dinPort, clkPort, true, 0, false, false, false);
А вот кстати и I2C символьный экран HD44780: https://github.com/w5277c/vm5277/blob/main/runtime/drivers/display/HD44780.j8b там вообще заложен layer как прослойка реализующая работу с разными интерфейсами.
Конечно это все черновые наработки (проект в Альфе, и я недавно поломал I2C и часть примеров перестала работать) но все что у меня есть в примерах (а там разные устройства) почти все они работоспособны (а то что сломал я скоро починю). Но тем не менее архитектура вполне рабочая.
Просто не забываем про мощь ООП модели.
Ну и на закуску, пример работы с ws2812b который максимально привередлив к ресурсам и таймингам МК.
P.S. Конечно в реальности есть куча жестких ограничений по ресурсам, которые не дадут к примеру запустить тот-же ws2812B на частоте менее 8МГц (на 8 - это уже предел возможностей). Но здесь мы уже бессильны.
P.P.S. Еще можно сюда заглянуть https://github.com/w5277c/vm5277/blob/main/examples/j8b/max7219/src/Main.j8b - там вообще магия и она работает прямо сейчас.
;)
Это вывод ассемблер сборщика. Т.е. время за которое avr ассемблер собрал из asm файла и его инклудов hex прошивку.
Вот пример 'hello' вывода работы компилятора (j8b->asm)
работы ассемблера (asm->hex)
И прошивальщика (hex->МК)
Конечно большие проекты будут собираться медленней. Но я еще вернусь к оптимизации процесса сборки - знаю что можно соптимизировать.
P.S. На первом скрине можно заметить, что total time заметно больше чем сумма работы компилятора - при сборке компилятор не показывает затраченное время на обработку автоматически подгружаемых библиотек из runtime. Но в total time это учитывается.
Спасибо. К вопросу выгорания - я этот проект веду почти каждый день уже как полтора года. Давно бы уже выгорел если бы мог. Причина в том, что я интроверт, и выгораю от социума, а в подобных проектах я отдыхаю, наверное это как рыбалка.
Да, безусловно, он может остаться никому не известным. Но причина в этом будет не техническая и не архитектурная - потому, что уже (даже в Альфе) существующие мои доработки доказывают реальность архитектуры и ее преимущества - по сути она закрывает сегмент рынка где особых конкурентов просто нет. Вопрос только в сообществе - удастся ли мне набрать критическую массу этого сообщества.
Только на atmega328 три таймера, где-то WD еще подключают - настраиваются по-разному (элементарно что-то 8 бит, что-то 16). На других avr таймеров другое количество. Где-то UART, где-то USART, где-то USI, где-то вообще ничего. А еще и имена регистров отличаются а также их биты и в принципе их порядок и функционал. И это только Atmega и UART, а еще ATtiny - может сильно отличаться. И это только платформа AVR, а добавьте туда PIC, STM8 и не дай бог STM32.
Сколько у Вас займет времени перенести работу I2C экрана с Atmega328 на attiny где нет I2C? Я даже не буду говорить, что примеры которые я видел на си подобной реализации были с существенными ошибками.
А если Вам нужно 10 таймеров? И пять UART'ов? Будете покупать в несколько раз дороже чип?
Вы просто не представляете о чем Вы говорите - переносимость - это очень серьезная проблема. Попробуйте сделать самое элементарное - прослойку которая будет просто по пину предоставлять возможность вызова процедуры при смене любого выбранного пина как при работе с INT для простого семейства Atmega. Опытный системный разработчик потратит несколько дней на такую задачу. А для Вас я смотрю это просто и интуитивно.
Я даже уже не говорю о том, что работа с регистрами это не безопасно в отличии от вызова метода.
"содержит неверную информацию" - я так упростил ответ, коряво вышло.
"Насчёт наследования классов - нифига, принципиальной разницы с наследованием интерфейсов нет." - не хочу сейчас об этом спорить, тем более я знаю единственный способ это доказать или опровергнуть - это реализовать. И может быть я к этому приду - так как такая доработка не поломает существующую модель. Сейчас меня вполне устраивает композиция, вроде-бы она сейчас даже в тренде.
"а вам сильно помогает хранить размер объекта в ОЗУ" - Это размер всего HEAP. У меня нет отдельного хранения размера блока выделенной памяти. В теории этот размер можно было бы хранить во flash, но я посчитал что там оптимальней.
Аналогично и массивы - есть нечто общее в заголовках. В нем есть размерность на базе которой вычисляется размер выделенной памяти.
Извините, я не понимаю что Вы хотели этим сказать?
Я просто оставлю это здесь:
или это из коллекции
У себя. И оно работает.
У vm5277 есть конечно ограничения - запрет на оператор new на чипах менее 256 байт - таким образом я блокирую возможность использования HEAP и динамической памяти. Все что от 256 может иметь DRAM и HEAP
И да, самые простые программы я запускаю даже на attiny13a, например:
Я разделяю функционал:
1 - то что не требует работы на низком уровне (не критично по времени, не требует работы с IO регистрами (кроме GPIO), не требует максимальной оптимизации) - пишется на j8b (в том числе и драйвера) - это дает полную переносимость.
2 - драйвера, которые не привязаны к железу а только к интерфейсам (i2c, uart, spi и прочие) - работают также на j8b - опять-же полная переносимость (может быть также реализовано на программных интерфейсах)
3 - остальной код реализуется на ассемблере и должен быть реализован в ядре (но часть его также может быть на j8b)
4 - рантайм будет и уже содержит некоторые драйвера конечных устройств (типа bmp580) - прикладнику достаточно просто использовать класс датчика подставляя ему нужные классы реализации интерфейсов. Для этого действительно не нужно знать даташиты.
5 - прикладник может также написать сам драйвер устройства если он основан на популярных и поддерживаемых в vm5277 интерфейсах и даже сами интерфейсы если им достаточно GPIO. И здесь ему также не нужно знать даташиты на МК - как реализовать интерфейс (прерывания, IO регистры и прочее)
6 - реализация стандартных интерфейсов и прочей периферии конкретного семейства МК - задача разработчиков нативной части (ядро и RTOS на ассемблере) - но это задача не прикладника.
Да - прикладнику нужно будет знать названия классов устройств и методов. Для этого по большей части достаточно простого описания методов. например:
public SpiSwHd(byte dataPort, byte clkPort, boolean fastMode, byte delay, boolean cpol, boolean cpha, boolean lsbFirst) {...}
public short comm(byte[] sendBuf, short sendLen, byte[] recvBuf, short recvLen, short timeout) throws DriverException {...}
Это далеко не то-же самое что изучение даташита МК на предмет IO регистров работы с SPI
Вот еще пример:
// Регистрируем обработчик (менеджер сам определит оптимальный механизм детектирования) regId = PinEventManager.register(ArduinoUno.PIN_D2, inst, PinEventManager.TRIGGER_MODE_ANY);
Прикладнику даже не нужно особо заморачиваться как именно реализуется обработка смены состояния пина (прерывания INT или PCINT и т.п.). Конечно если он захочет оптимизировать все по максимум - ему придется спуститься на уровень ниже - мое решение это позволяет - вплоть до ассемблера. (PinEventManager - еще в разработке)
Да, умеет.
Не очень удобно развернуто отвечать в комментариях - ответы урезаны, это может вносить недопонимание.
Я планирую написать несколько статей где смогу развернуто ответить на вопросы как формируются HEAP данные класса , что лежит в метаданных Flash и тому подобное.
Также планирую рассказать как я работаю со ссылками и почему циклические ссылки и дефрагментация памяти не так критична в моей модели.
Забегая вперед приведу заголовок класса(для Thread и Timer заголовок расширен) в HEAP:
CLASS_HEAP_OFFSET__SIZE = 0x0000;2B-total size
CLASS_HEAP_OFFSET__LINK_CNTR = 0x0002;1B-Link counter
СLASS_HEAP_OFFSET__METADATA_ADDR = 0x0003;2B-flash address
Мета-данные в flash:
CLASS_META_OFFSET__CLASS_ID = 0x0000;1B-ИД типа класса
CLASS_META_OFFSET__PAIRS_QNT = 0x0001;1B-Количество пар
CLASS_META_OFFSET__PAIRS_TABLE = 0x0002;xB-таблица пар (ID интерфейса + количество методов)
Этого достаточно для выполнения динамической диспетчеризации вызовов. При наследовании классов боюсь пришлось бы хранить более большие таблицы (и возможно даже использовать RAM), плюс поиск реализации стал бы более затратным. Также усложняется процесс определения мертвого кода.
Счетчик ссылок дорастает до значения 255 и блокируется - память освободиться не сможет. Крайне маловероятно что это произойдет на 8 битах. Но можно будет это учесть - сделать исключение или если компилятор докажет - расширять счетчик до 16 бит.
1. Кольцевые ссылки: Да, в текущей реализации это приведет к утечке. На данном этапе я сознательно не усложнял архитектуру (нет ни weak-ссылок, ни trace-компонента), так как в рамках 8-битных систем сложные цикличные графы объектов практически не встречаются — логика приложений пишется максимально плоско. Избегание циклов сейчас — это ответственность прикладного программиста.
2. Перемещение объектов: Нет, никакого перемещения и полноценного Garbage Collector здесь нет. Механизм максимально детерминированный: чистый Reference Counting с мгновенным освобождением памяти при обнулении счетчика. Поэтому адрес объекта в куче (HEAP) является его постоянным и уникальным идентификатором.
3. Фрагментация: Проблема решается на уровне кастомного менеджера памяти. Аллокатор работает по принципу битовой маски, где 1 бит отвечает за блок в 8 байт (дискретная сетка/слоты). Для 8-битной архитектуры с небольшими объектами такой подход сводит внешнюю фрагментацию к минимуму при крайне низком расходе памяти на метаданные аллокатора.
4. Escape analysis: На данный момент отсутствует и будет рассмотрен как задача оптимизации. И таких задач не мало — Альфа версия. Вы правы: если объект гарантированно не покидает пределы метода, то подсчет ссылок не нужен - это вопрос оптимизации кода.
Важный нюанс, который часто упускают при обсуждении Escape-анализа для 8-битных систем:Статический анализ компилятора никогда не покрывает 100% кейсов. Если алгоритм не может гарантированно доказать, что объект "не убегает", он обязан свалиться в безопасный режим и включить подсчет ссылок в рантайме.
На масштабах прошивок для 8-битных МК, где методы и жизненный цикл объектов экстремально короткие, а память сильно ограничена, доля успешно доказанных и при этом эффективных оптимизаций через Escape-анализ будет ничтожной. Усложнение компилятора получается значительным, а реальный выигрыш в байтах или тактах — копеечным. В условиях жестких физических ограничений внедрение таких тяжелых механизмов оптимизации просто экономически и инженерно нецелесообразно. Особенно когда есть пласт более приоритетных задач.
Дело не в самом усложнении рантайма, а в усложнении библиотеки бэкенд кодогенератора без какой-либо существенной практической пользы (который требуется реализовывать под каждую платформу отдельно) - я не достаточно развернуто ответил на этот вопрос ранее.
Более того, я объяснил существенную причину почему выбран Java синтаксис. Да и в принципе вопрос выбора языка и его ситнаксиса я не рассматриваю, кроме изменений который действительно будут существенно полезны для разработки прошивок на 8 бит Мк, как например замена signed типов на unsigned.
Все перечисленные вами примеры требуют нетривиальной системы зависимостей между типами, подстановки, обобщений и проверок на уровне AST/семантики. Это всё ложится на этап компиляции и кодогенерацию. Для моего компилятора под 8 бит это означает не просто "ещё одна проверка", а перестройку всей инфраструктуры вывода кода которая (изначально достаточно сбалансирована) — и это я считаю неоправданным без явной, измеримой выгоды для конечной прошивки