Jump to content

    
Serhiy_UA

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

Recommended Posts

4 minutes ago, Arlleex said:

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

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

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

Share this post


Link to post
Share on other sites
47 minutes ago, one_eight_seven said:

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

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

Share this post


Link to post
Share on other sites
1 минуту назад, Forger сказал:

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

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

 

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

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

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

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

Share this post


Link to post
Share on other sites
3 minutes ago, one_eight_seven said:

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

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

 

 

Share this post


Link to post
Share on other sites
10 minutes ago, haker_fox said:

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

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

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

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

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

Edited by one_eight_seven

Share this post


Link to post
Share on other sites
1 minute ago, Arlleex said:

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

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

 

Share this post


Link to post
Share on other sites
2 minutes ago, Forger said:

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

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

Share this post


Link to post
Share on other sites
8 minutes ago, one_eight_seven said:

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

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

 

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

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

 

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

 

 

Share this post


Link to post
Share on other sites
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:

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

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

Share this post


Link to post
Share on other sites
10 minutes ago, Forger said:

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

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

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

Edited by one_eight_seven

Share this post


Link to post
Share on other sites
2 часа назад, Arlleex сказал:

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

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

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

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

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

Share this post


Link to post
Share on other sites
43 minutes ago, AHTOXA said:

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

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

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

 

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

 

Share this post


Link to post
Share on other sites
14 часов назад, AHTOXA сказал:

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

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

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

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

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

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

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

 

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

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

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

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

В Си я пишу

rb_FreeR(&TxRingQ, len);

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

 

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

TxRingQ.FreeR(len);

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

 

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

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

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

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

Share this post


Link to post
Share on other sites
1 час назад, Arlleex сказал:

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

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

Share this post


Link to post
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.