Перейти к содержанию
    

Ввод спец.регистров UART в классы C++ для STM32

4 minutes ago, Arlleex said:

И чем тогда подход в стиле ООП с классами/наследованием и т.д. лучше, допустим, того, что использую я в чистых Сях? Где тут я мог бы что-то универсализировать?

Да, особо, нигде.

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

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

47 minutes ago, one_eight_seven said:

экономия памяти на методе instance?

А вы бы создавали драйвер с помощью конструктора по месту? В своё время мы пришли к такой комбинации из синглтона с параметром только для того, чтобы не насоздавать кучу драйверо, обращающихся к одному и тому же железу. Например, в моём случае если хотим работать с шиной 2 из нескольких разных потоков, мы гарантированно получим ссылку только на её драйвер.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

1 минуту назад, Forger сказал:

Меньше объем текста программы и проще в ней разбираться.

Наверное да, но... Вот, к примеру, в моем случае - неужели можно написать с меньшим объемом текста?:blush:

 

2 минуты назад, one_eight_seven сказал:

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

Вот. Как я понял, это и означает, что в LL-работе с периферией применение практик ООП не совсем удачно.

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

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

3 minutes ago, one_eight_seven said:

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

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

 

 

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

10 minutes ago, haker_fox said:

А вы бы создавали драйвер с помощью конструктора по месту?

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

Из того, чем занимался недавно, с С++ - это были тесты железа. Когда есть абстрактный драйвер, абстрактный DMA с базовыми виртуальными методами, и, используя эти методы пишутся тестовые сценарии. Но тут именно такая задача - железо - оно реально меняется, а сценарий теста этой железки - он не изменяется.

А когда вспоминаю времена электроники - там было наоборот - микроконтроллер и его потроха - вещь постоянная, а сценарии использования - разные. Потому выше я и написал, что в реальном исопльзовании программирования ООП лучше применять в решении задачи в области практического применения, а не в области написания драйверов. Потому что таков мой специфический опыт - я быстрее и лучше напишу драйыер на С, а прикладную программу - на C++.

У других инженеров практический опыт другой, и им может быть удобнее всё писать на С++ (как было у меня в вышеописанных тестах).

Изменено пользователем one_eight_seven

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

1 minute ago, Arlleex said:

Наверное да, но... Вот, к примеру, в моем случае - неужели можно написать с меньшим объемом текста?:blush:

Все можно, но вряд ли первостепенная цель стоит именно в этом. Это - лишь результат продуманного и грамотного кода, но не самоцель.

 

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

2 minutes ago, Forger said:

Вся наша жизнь - ООП, нет никакой разницы к чему применять.

Вы не являетесь регистром в каком-нибудь I2C IP-блоке. Ваша жизнь и окружающий мир хорошо описывается в терминах ООП. Прямо очень удачно. А жизнь регистра в IP-блоке - сильно от этого отличается.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

8 minutes ago, one_eight_seven said:

Вы не являетесь регистром в каком-нибудь I2C IP-блоке. Ваша жизнь и окружающий мир хорошо описывается в терминах ООП. Прямо очень удачно. А жизнь регистра в IP-блоке - сильно от этого отличается.

Вот она и типична ошибка восприятия ООП как такового. ООП не делит объекты на конкретные реальные и абстрактные.

 

На первой странице я привел конкретный пример из реального проекта.

Самое ценное, что я ценю в ООП - инкапсуляция. Это то, что делает код масшабируемым, переносимым и понятным.

 

зы подозреваю, что мы сейчас толкуем о разные вещах ))

 

 

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

23 minutes ago, Arlleex said:

Один фиг приходится что-то допиливать, дорабатывать функционал классов и т.д.

Это да, это есть. Но типовые дейсвия (инициализация железки, обработка прерываний) можно всё равно засунуть в класс драйвера. Если предполагается конфигурируемая работа с другой периферией (например, таймер + ПДП + АЦП), то я обычно стараюсь оставить место для добавления такого функционала на будущее. Как пример, использую конфигурационные структуры для настройки периферии. В них, при необходимости, можно будет добавить поля, которые укажут как работать.

Пример, конфигурация таймера (да, громоздко, и есть поле для оптимизации, но мне пока не надо).

    struct Setup {
        union IrqEnable {
            __packed struct fields_t {
                bool update         : 1;
                bool trigger        : 1;
                bool inputCapture   : 1;
                bool outputCompare  : 1;
                bool breakInput     : 1;
            }
            fields;
            std::uint8_t mask;
            IrqEnable()
                : mask(0) {}
        };
        struct Dma {
            union RequestEnable {
                __packed struct fields_t {
                    bool trigger    : 1;
                    bool cc4        : 1;
                    bool cc3        : 1;
                    bool cc2        : 1;
                    bool cc1        : 1;
                    bool update     : 1;
                }
                fields;
                std::uint8_t mask;
                RequestEnable()
                    : mask(0) {}
            };
            enum class BurstLength : std::uint16_t {
                Transfer1 = 0 << 8,
                Transfer2 = 1 << 8,
                Transfer3 = 2 << 8,
                Transfer4 = 3 << 8,
                Transfer5 = 4 << 8,
                Transfer6 = 5 << 8,
                Transfer7 = 6 << 8,
                Transfer8 = 7 << 8,
                Transfer9 = 8 << 8,
                Transfer10 = 9 << 8,
                Transfer11 = 10 << 8,
                Transfer12 = 11 << 8,
                Transfer13 = 12 << 8,
                Transfer14 = 13 << 8,
                Transfer15 = 14 << 8,
                Transfer16 = 15 << 8,
                Transfer17 = 16 << 8,
                Transfer18 = 17 << 8,
            };
            RequestEnable request;
            BurstLength burstLength;
            std::uint8_t dmaOffset;
            Dma()
                : burstLength(BurstLength::Transfer1)
                , dmaOffset(0) {}
        };
        std::uint32_t freqHz;
        IrqEnable irq;
        std::uint32_t reloadValue;
        Dma dma;

        Setup( std::uint32_t freqHz = 1000, IrqEnable irq = IrqEnable() )
            : freqHz(freqHz)
            , irq(irq)
            , reloadValue(0xffffffff) {}
    };

Но, естественно, встречаются ситуации, когда всё это не работает. Тогда приходится писать напрямую в регистры, смешивать в драйверах обращение к разному железу: обработчие прерывания по завершению преобразования АЦП пишет напрямую в регистры ПДП. Обычно такие правки для конкретного проекта. Другого выхода я не нашёл. Не получается полностью отгородится от железа. Более того, оно сильно различается не то, что от разных производителей МК, оно разница и в одной ленейке МК. Ну, и конечно, мы используем конкретный МК из-за его периферии, которая наиболее подходящим образом решает нашу задачу. Не приходится говорить о переностимости на другое железо: ведь в другом МК может не оказаться так нужного нам ПДП со связными списками...

30 minutes ago, Arlleex said:

И чем тогда подход в стиле ООП с классами/наследованием и т.д. лучше

В основном, это дело вкуса и привычки)

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

10 minutes ago, Forger said:

Вот она и типична ошибка восприятия ООП как такового.

Тут не у меня ошибка, а у вас - ошибка адепта. Слышали про молоток Маслоу? Если ваш единственный инструмент - это молоток, то всё вокруг - гвозди.

У меня - не так, я вообще большинство проектов пишу в диком ООП - SystemVerilog+UVM, SystemC+TLM. И работаю с кучей разного железа. А раньше работал наоборот, с малым количеством железок (3-4 семейства МК), но применял их для решения разных задач. И сейчас вижу, где великолепно себя чувтсвует структурное программирование и чистый С (или verilog в случае железа), а где гораздо лучше применяется ООП.

Изменено пользователем one_eight_seven

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

1 hour ago, one_eight_seven said:

Слышали про молоток Маслоу?

 

1 hour ago, Forger said:

зы подозреваю, что мы сейчас толкуем о разные вещах ))

 

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

2 часа назад, Arlleex сказал:

А какой смысл вообще делать какие-то универсальные интерфейсы для низкоуровневой работы с физическими интерфейсами МК?

Смыслов может быть много. Например, написать базовый класс канала обмена по UART/CAN/SPI, содержащий все базовые примитивы обмена - пакеты, их сборку/разборку, с чисто виртуальными функциями handleRxPacket(). И потом использовать их на двух устройствах, которые связаны этим каналом обмена. Удобства:

 * устройства используют гарантированно одинаковый протокол и структуру пакетов;

 * для каждого устройства я должен переписать только одну функцию handleRxPacket().

Ещё вариант - удобно таскать свои наработки между контроллерами. Скажем, если имеется модуль SPI, в котором интерфейс содержит функции чтения/записи байта и чтения/записи буфера, то можно спокойно писать модули устройств, подключаемых по SPI, и без изменений переносить их на разные аппаратные платформы. Это можно сделать и без ООП, но с ООП удобнее.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

43 minutes ago, AHTOXA said:

Ещё вариант - удобно таскать свои наработки между контроллерами. 

Это - одна из самых вкусных фишек подобного подхода! 

Только ради этого уже можно потратить чуток времени и сделать лучше жисть себе лично ))

 

Самое сложное тут - сразу грамотно создать интерфейс и модель взаимодействия, предполагающие расширение всего этого на будущее. Иначе переделывать придется все целиком, и порой не раз :(

 

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

14 часов назад, AHTOXA сказал:

Смыслов может быть много. Например, написать базовый класс канала обмена... Удобства:

 * устройства используют гарантированно одинаковый протокол и структуру пакетов;

 * для каждого устройства я должен переписать только одну функцию handleRxPacket().

Ещё вариант - удобно таскать свои наработки между контроллерами.

Я скорее акцентировал вопрос на том, каким образом ООП может упростить понимание, читаемость или даже повторное использование кода именно для LL-драйверов.

Создание удобного интерфейса, состоящего из некоторого набора API-функций, ИМХО - неотъемлемая часть драйвера. Да вообще любого самостоятельного модуля.

Озвученные удобства я успешно реализую в своих проектах. Последнее время программирование превращается, скорее, в CTRL + C / CTRL + V из наработок.

 

14 часов назад, AHTOXA сказал:

Это можно сделать и без ООП, но с ООП удобнее.

Наверное, да.

Мне, например, очень нравится концепция методов у объектов.

В Си я пишу

rb_FreeR(&TxRingQ, len);

делая эту приписку rb_ к функции для того, чтобы в месте вызова помнить, из какого файла тянется ее реализация, или что это вообще за функция.

 

А в C++, конечно, покрасивее это выглядит

TxRingQ.FreeR(len);

Здесь даже вспоминать не надо, кто такой TxRingQ - оно из контекста понятно. Да и пространство имен более гибким становится.

 

13 часов назад, Forger сказал:

Самое сложное тут - сразу грамотно создать интерфейс и модель взаимодействия, предполагающие расширение всего этого на будущее. Иначе переделывать придется все целиком, и порой не раз :(

Вот это считаю самым большим камнем преткновения.

На этапе, когда не знаком со всеми фишками ООП, написать так, чтобы потом было не стыдно показать, да использовать в будущем, не перелопачивая, очень трудно =)

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

1 час назад, Arlleex сказал:

Я скорее акцентировал вопрос на том, каким образом ООП может упростить понимание, читаемость или даже повторное использование кода именно для LL-драйверов. 

Да, в этом контексте полезность именно ООП поменьше. Но и здесь её можно найти. Например, если контроллер имеет несколько однотипных периферийных устройств. Скажем, SPI. Делаем базовый класс, который инкапсулирует работу с регистрами периферийного блока. А затем наследуем от него реализации для конкретных портов. Получается очень удобно: общая функциональность в одном классе, а специфичные для конкретного порта детали (ножки, номера прерываний, и т. п.) - в наследниках.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Присоединяйтесь к обсуждению

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

Гость
Ответить в этой теме...

×   Вставлено с форматированием.   Вставить как обычный текст

  Разрешено использовать не более 75 эмодзи.

×   Ваша ссылка была автоматически встроена.   Отображать как обычную ссылку

×   Ваш предыдущий контент был восстановлен.   Очистить редактор

×   Вы не можете вставлять изображения напрямую. Загружайте или вставляйте изображения по ссылке.

×
×
  • Создать...