Pull to refresh

За рулем с D

Reading time7 min
Views8.9K
Original author: Dylan Graham

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

Вот мой почтенный автомобиль Holden VZ Ute. С завода он поставлялся с убогой четырехступенчатой автоматической коробкой передач. За 18 месяцев владения автомобилем я сломал четыре коробки передач. В то время я не мог позволить себе новый автомобиль, поэтому мне пришлось творчески подойти к делу. Я купил железобетонную, дуракоустойчивую шестиступенчатую автоматическую коробку передач от другого автомобиля. Но на этом я уперся в тупик. Чтобы заставить ее работать, мне пришлось собрать собственную печатную плату, систему управления и прошивку для управления соленоидами, гидравликой и сцеплениями внутри коробки передач, обработки команд водителя, принятия решений о переключении передач и взаимодействия с автомобилем, притворяясь четырехступенчатым автоматом.


Я очень горжусь своим решением. Оно может выполнять переключение за 250 миллисекунд, что очень удобно для гонок. У него крутая первая передача, что обеспечивает быстрый старт. Оно дало фору некоторым более мощным автомобилям. У него есть удобные подрулевые лепестки, диагностические данные на экране и возможность изменить принцип работы, когда я захочу.

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

Компьютер разделен на две части: плата пользовательского интерфейса, которая управляет OLED-дисплеем и использует STM32F042, и основная плата, которая управляет всем остальным, используя STM32F407. Эти два устройства взаимодействуют через шину CAN (Controller Area Network). Вся микропрограмма для управления этим оборудованием написана на языке D.

Я выбрал D (в версии -betterC) из-за его гениального унифицированного синтаксиса вызова функций (UFCS), метапрограммирования, простоты взаимодействия с C, юнит-тестирования, переносимости, shared и @safe. Еще один бонус - это отзывчивое, гостеприимное сообщество. Мне было очень приятно обсуждать D на форуме, а также с основателями и лидерами сообщества.

Преимущества D

Универсальный синтаксис вызова функций (UFCS)

Благодаря этому мой код стал значительно чище. Мой код может точно следовать потоку данных, не загрязняя стек одноразовыми переменными, вложением множества вызовов функций или другими видами нагромождений. Например, вот код, который я использую в своем новом проекте ECU (блок управления двигателем):

immutable injectorTime = airStoich(100.kpa, 25.degCelsius)
    .airMass
    .fuelMass((14.7f).afr)
    .fuelMol
    .calculateInjectorWidth;

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

Метапрограммирование

Принцип "Не повторяй себя" (DRY) часто пропагандируется программистами. Метапрограммирование в D является невероятным инструментом для достижения этой цели. Я использую его в своей реализации шины CAN. Например:

struct CANPacket(ushort ID) {
    enum id = ID;
    ubyte[8] data;
}
alias HeartbeatPacket = CANPacket!10;
alias BeepHornPacket = CANPacket!140;

У меня есть специальные псевдонимы типа HeartbeatPacket и BeepHornPacket, но мне не нужно повторять никакой код. Все они следуют одной и той же базовой структуре, поэтому если я изменяю CANPacket, каждый псевдоним также обновляется.

Интерфейс к С

Мне часто приходится общаться с HAL и RTOS моего микроконтроллера; интерфейс D's C сделал это проще простого. Просто добавьте extern(C), и все будет нормально.

extern(C) c_setPwm(int solenoid, void* userData); // declaration
c_setPwm(4, null); // usage, pretty easy!

Юнит-тестирование

Встроенное в D юнит-тестирование несколько раз спасало меня от отстрела ног. Я могу запустить все свои модульные тесты в Windows, чтобы гарантировать логическую корректность, а затем собрать конечную версию для микроконтроллера.

Переносимость

В продолжение вышесказанного, D поддерживает удивительно большое количество платформ с помощью GDC и LDC. Если бы не переносимость D, мне пришлось бы писать свой проект на C++ (ох). Я использую LDC, и кросс-компиляция может быть выполнена простым изменением аргументов командной строки.

Shared

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

shared int sensorValue;
sensorValue = 4; // using it like a single-thread variable, error
atomicStore(sensorValue, 4); // works with atomics

SafeD

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

Умственное сопротивление

Адам Д. Руппе выразил это кратко: D имеет низкий уровень умственного трения. Гибкость и выразительность языка позволяют легко переводить свои мысли в письменный код и сохранять свою продуктивность. Мне не приходится бороться с компилятором D. Это мое личное мнение, но я чувствую, что D - это язык, на котором я наиболее продуктивен.

Заключительные мысли

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

Дополнения от переводчика:

  1. Такая модификация автомобиля законна в месте проживания автора, он это уточнял.

  2. В режиме -BetterC, который использует автор, сборщика мусора (GC) нет.

  3. Далее, автор разработал для своих эмбеддед задач и режима BetterC минимальный рантайм, описание его с форума переведено ниже.

  4. Чем D лучше С для эмбеддед? - Прежде всего надежностью и безопасностью.

  5. Чем D хуже С для эмбеддед? - Более сложен и бинарник будет существенно тяжелее.

Легковесный рантайм для D

LWDR (Light Weight D Runtime) v0.3.0 - оригинал форума

Github

DUB

Описание предыдущей версии (устар)

LWDR (Light Weight D Runtime) - это реализация с нуля среды выполнения D, ориентированная на микроконтроллеры ARM Cortex-M и другое голое железо. Она работает, предоставляя ряд базовых API хуков (как определено в rtoslink.d, которые вы должны реализовать и/или указать на вашу реализацию в RTOS).

Это V0.3.0 LWDR. Начиная с предыдущей версии V0.2.3, была проведена следующая работа:

  1. Поддержка Локальных Переменных Потоков (Thread Local Storage, TLS)

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

  3. Переход к системе опциональной функциональности (opt-in)

  4. Замена delete на LWDR.free(...) в связи с устареванием

  5. Улучшение документации по исходному коду

  6. Реализации RefCount!T и Unique!T, специфичные для LWDR

Thread Local Storage

Эта возможность довольно абстрактна, и она является опцией при LWDR_TLS. Вы должны обеспечить поддержку секций tdata и tbss в своем скрипте линкера. Она работает, используя реализацию TLS в базовой RTOS (пример). При вызове LWDR.registerCurrentThread() выделяется блок памяти D, содержащий переменные TLS для текущего потока, а указатель на блок хранится в TCB (Thread Control Block) потока. При обращении к переменной TLS (т.е. статической переменной) вызывается __aeabi_read_tp, выдающая указатель.

Отслеживание памяти

Это очень примитивно. Оно предназначен только для того, чтобы помочь остановить утечку GC-зависимых аллокаций из stdlib (Phobos). Оно ведет себя практически так же, как определено здесь.

Опциональная функциональность (opt-in)

Чтобы иметь возможность уменьшать размер TypeInfo vtables и тому подобного, LWDR использует систему opt-in, которая опирается на функцию version языка D. В настоящее время в список опций входят:

  1. LWDR_TLS - Включает поддержку TLS

  2. LWDR_DynamicArray - Включает динамические массивы

  3. LWDR_TrackMem - Включает бардак с отслеживанием (см.выше)

Замена delete

delete была отменена. Вместо нее реализована LWDR.free для предотвращения предупреждений компилятора.

Документация по исходному коду

Рантайм - страшная и муторная штука, поэтому я начинаю прилагать больше усилий для документирования того, как все работает. Пока что это только комментарии ddoc.

RefCount!T и Unique!T

Чтобы компенсировать неудобства от отсутствия сборщика мусора (GC), я реализовал специфическое для LWDR решение, вдохновленное automem.

Что реализовано

  1. Class allocations and deallocations (via new and LWDR.free)

  2. Struct heap allocations and deallocations (via new and LWDR.free)

  3. Invariants

  4. Asserts

  5. Contract programming

  6. Basic RTTI (via TypeInfo stubs)

  7. Interfaces

  8. Static Arrays

  9. Virtual functions and overrides

  10. Abstract classes

  11. Static classes

  12. Allocation and deallocation of dynamic arrays (opt in by version LWDR_DynamicArray)

  13. Concatenate an item to a dynamic array (opt in by version LWDR_DynamicArray)

  14. Concatenate two dynamic arrays together (opt in by version LWDR_DynamicArray)

  15. Dynamic array resizing (opt in by version LWDR_DynamicArray)

  16. Thread local storage (opt in by version LWDR_TLS)

Что не работает (пока)

  1. Exceptions and Throwables (экспериментальная реализация удалена)

  2. Module constructors and destructors

  3. Static constructors and destructors

  4. Shared static constructors and destructors

  5. Module info

  6. There is no GC implementation (primitive memory tracking is now available with LWDR_TrackMemRefCount!T and Unique!T are now available)

  7. Delegates/closures

  8. Associative arrays

  9. Object monitors

  10. shared/synchronised

  11. Object hashing

  12. Другие вещи, которые я не могу вспомнить с ходу.

Это все еще бета-версия - так что ожидайте ошибок и недостатков. Некоторые части были тщательно протестированы, другие - не очень.

Поскольку рантайм разросся слишком быстро, я хочу немного приостановить разработку, чтобы начать использовать LWDR в соответствующем проекте и найти и устранить ошибки. Проект является практически преемником моей статьи Driving with D (это автомобильный проект) (прим.пер - верхняя часть статьи). Я также думаю подать заявку с LWDR на участие в проекте Autumn of Code.

Как только LWDR станет достаточно стабильным, я хочу, чтобы следующая версия включала информацию о модулях, поддержку статических ctor/dtor и рассматривала поддержку монитора объектов и других инструментов многопоточности (мьютексы, условия и т.д.).

Tags:
Hubs:
Total votes 19: ↑18 and ↓1+17
Comments1

Articles