|
С++, метод шаблонного класса как обработчик прерывания |
|
|
|
Jun 12 2017, 16:45
|
Гуру
     
Группа: Свой
Сообщений: 2 563
Регистрация: 8-04-05
Из: Nsk
Пользователь №: 3 954

|
есть базовый класс, пусть будет уарта, которому через шаблон задаётся базовый адрес регистров и размер фифо. template <int USARTx_BASE, int fifoSize = 32> class UartBase{ void ISRBase(); };
теперь делаем классы под каждый uart, так как немного разная инициализация (как минимум клоки включаются отдельно), и статический обработчик прерывания.
template <int fifoSize = 32> class Uart0 : UartBase<LPC_USART0_BASE, fifoSize>{ Uart0(){...} static void ISR() { UartBase<LPC_USART0_BASE, fifoSize>::ISRBase(); } };
вопрос1: как теперь сделать Uart0::ISR() обработчиком прерывания? с шаблоном-то тип у него тип теперь к размеру FIFO привязан. void UART0_IRQHandler() { Uart0<?????????>::ISR(); }
кроме как убрать шаблон можно по другому это как-то по-человечески сделать? class Uart0 : UartBase<LPC_USART0_BASE, UART0_FIFO_SIZE>
вопрос2: метод базового класса UartBase<LPC_USART0_BASE, fifoSize>::ISR(); тоже вызываться не хочет из статического метода. error: cannot call member function 'void UartBase<USARTx_BASE, fifoSize>::ISR() [with int USARTx_BASE = 1074151424; int fifoSize = 32]' without object
наверняка не раз уже обсуждалось, но что-то сходу не нашлось, ткните как с однотипной периферией на плюсах правильно работать.
|
|
|
|
|
Jun 12 2017, 19:40
|
Участник

Группа: Участник
Сообщений: 56
Регистрация: 3-11-11
Пользователь №: 68 126

|
2. Нестатический метод класса можно вызывать только для обьекта. UartBase::ISR (или ISRBase?) у вас метод нестатический и его можно вызывать только для существующего обьекта класса UartBase или обьекта производного класса. О чем компилятор самым явным образом вам сообщает. 1. Не понятно в чем вопрос. Значения для шаблонных аргументов становятся частью спецификации типа. Классы порожденные от одного и того же шаблона с разными значениями аргументов есть разные типы. Не понятно зачем нужна эта помойка
Сообщение отредактировал conan - Jun 12 2017, 19:43
|
|
|
|
|
Jun 12 2017, 21:42
|

фанат дивана
     
Группа: Свой
Сообщений: 3 387
Регистрация: 9-08-07
Из: Уфа
Пользователь №: 29 684

|
Цитата(_pv @ Jun 13 2017, 01:33)  Uart <UART0> pc; Uart <UART1, 4> sensor; Uart <UART2, 100> asdf; ... хотелось бы понять как можно сделать на плюсах. А в чём проблемы-то? Пишите: Код void UART0_IRQHandler() { pc.ISR(); } void UART1_IRQHandler() { sensor.ISR(); } void UART2_IRQHandler() { asdf.ISR(); } и всё.
--------------------
Если бы я знал, что такое электричество...
|
|
|
|
|
Jun 12 2017, 22:01
|
Участник

Группа: Участник
Сообщений: 56
Регистрация: 3-11-11
Пользователь №: 68 126

|
Цитата(_pv @ Jun 12 2017, 23:33)  помойка нужна для, как мне казалось, упрощения кода.
Uart <UART0> pc; Uart <UART1, 4> sensor; Uart <UART2, 100> asdf; ...
sensor.read() pc.write()
как похожее сделать некрасиво на С с препроцессором представляю или что при объявлении в конструктор можно передавать указатель на структуру со всей кучей индивидуальных для каждого уарта регистров.
хотелось бы понять как можно сделать на плюсах. Можно сделать шаблон для общего случая, и к нему специализации: Код template<int USARTx_BASE, int FIFO_SIZE> class Uart {};
template<int FIFO_SIZE> class Uart<UART0, FIFO_SIZE> {};
template<int FIFO_SIZE> class Uart<UART1, FIFO_SIZE> {};
...
template<int FIFO_SIZE> class Uart<UART16, FIFO_SIZE> {}; Если для какого-то USARTx_BASE нет специализации, то будет использоваться общий вариант. Можно сделать специализации не только по одному аргументу, но и по нескольким: Код template<> class Uart<UART1, 32> {}; Но типы полученные от одного шаблона с разными значениями шаблонных аргументов -- разные типы. Нельзя объект от Uart<UART0, 16> передать в функцию, которая ждет Uart<UART1, 16>& или Uart<UART, 32>& (но можно сделать функцию тоже шаблонной). Еще есть подход Type erasure
|
|
|
|
|
Jun 19 2017, 18:32
|

Профессионал
    
Группа: Свой
Сообщений: 1 215
Регистрация: 22-02-05
Пользователь №: 2 831

|
Цитата(conan @ Jun 13 2017, 01:01)  Нельзя объект от Uart<UART0, 16> передать в функцию, которая ждет Uart<UART1, 16>& или Uart<UART, 32>& (но можно сделать функцию тоже шаблонной). Еще есть подход Type erasure Можно проще: базовый абстрактный класс (с несколькими чисто виртуальными методами), от него наследуется шаблонный класс, в котором реализуются эти виртуальные методы из базового класса. Остальным, кто планирует обращаться к этим шаблонным классам, передаем ссылки на базовый класс. В таком решении прекрасно работает static_cast (если нужно), т. е. проблем с приведением типов нет никаких. Этот базовый класс по сути есть интерфейс. Также этот базовый класс может участвовать в списках (например, в конструкциях типа for_each). Разумеется, в реальных приложениях этих базовых классов может быть сколько угодно. Если двигаться в этом направлении, то не далеко и полноценного применения паттернов проектирования
--------------------
Кругозор некоторых людей - круг с нулевым радиусом. Они называют его "точкой зрения".
|
|
|
|
|
Jun 20 2017, 05:34
|

Профессионал
    
Группа: Свой
Сообщений: 1 215
Регистрация: 22-02-05
Пользователь №: 2 831

|
Цитата(sigmaN @ Jun 20 2017, 00:35)  Ну там в зависимости от везения может появляться небольшой оверхедик на разруливание этих виртуальных функций. Да, "оверхедик" будет, но есть обратная сторона медали: подобный подход позволяет более грамотно спроектировать проект, объем текста программы сокращается, вырастает его читаемость. В сложных проектах последние пункты имеют неоспоримое преимущество перед эти символическим оверхедом. Тут уж каждый волен выбирать сам ))
--------------------
Кругозор некоторых людей - круг с нулевым радиусом. Они называют его "точкой зрения".
|
|
|
|
|
Jun 20 2017, 09:35
|
Гуру
     
Группа: Свой
Сообщений: 2 563
Регистрация: 8-04-05
Из: Nsk
Пользователь №: 3 954

|
Цитата(conan @ Jun 13 2017, 04:01)  Можно сделать шаблон для общего случая, и к нему специализации: ... Но типы полученные от одного шаблона с разными значениями шаблонных аргументов -- разные типы. вот с этим и вопрос, как объявить обработчик прерывания, если тип этого конкретного уарта становится известным только при объявлении из-за шаблона. Цитата(Forger) Можно проще: базовый абстрактный класс (с несколькими чисто виртуальными методами), от него наследуется шаблонный класс, в котором реализуются эти виртуальные методы из базового класса. а что делать с обработчиком прерывания? как получить статический метод шаблонного класса? завести отдельный глобальный указатель на функцию, которая передаётся обработчику прерывания, и в конструкторе pUART0ISRfunc = this->isr
|
|
|
|
|
Jun 20 2017, 10:40
|

Профессионал
    
Группа: Свой
Сообщений: 1 215
Регистрация: 22-02-05
Пользователь №: 2 831

|
Цитата(_pv @ Jun 20 2017, 12:35)  вот с этим и вопрос, как объявить обработчик прерывания, если тип этого конкретного уарта становится известным только при объявлении из-за шаблона. Опишу схему, которую реализовал для ARM процов (любые cortex). Перед стартом производится копирование таблицы векторов из флэш в озу. Копируются только главные вектора, относящиеся к ядру. startup свой, в нем нет обработчиков периферии (не ядра). В зависимости от семейства таблицы имеют разный размер (2 таблицы, есть еще третья - отладочная, для своих целей). Для создания класса нужно лишь знать номер вектора обработчика, т.е. индекс, который идет с файлом ARMCMxx.h. В качестве обработчиков использовать голую С-функцию неинтересно и проблематично (разумеется с допиской extern "C"), мне важно было при вызове функции обработчика вызывался соотв. класс обработчика (точнее его наследника), т.е. в стек должен класться соотв. this. Это дает простую возможность наделять класс Interrupt<> таким функционалом, который раньше было сделать крайне геморрно. Сам класс Interrupt может быть базовым для более высокооуровеных классов (например class SerialPort), либо быть полем в другом классе. Код template <class Owner, InterruptIndex INDEX, InterruptPriority PRIORITY = IRQ_DEFAULT_PRIORITY> class Interrupt : public AbstractInterrupt { ... private: static void userVector() { AbstractInterrupt::callVector(INDEX + IRQ_OFFSET); } ... } Вот так используется: Код class SerialPort : public Interrupt<CommunicationThread, USART3_IRQn> { public: SerialPort() : Interrupt<CommunicationThread, USART3_IRQn>("USART3") { port = USART3; }
virtual void initialize(); ....
private: virtual void body(); void doRecieveDataRegisterNotEmptyEvent(); void doTransmitDataRegisterEmptyEvent(); void doTransmissionCompleteEvent(); ... } serialPort; вот так выглядит обработчик: Код void SerialPort::body() { doRecieveDataRegisterNotEmptyEvent(); doTransmitDataRegisterEmptyEvent(); doTransmissionCompleteEvent(); } Цена этому - расход озу для хранения таблицы векторов, таблицы указателей на абстрактные классы С++ обработчиков и по мелочи. И небольшой оверхед, т.к. при вызове обработчика еще вызывается соотв. С++ класс-обертка, т.е. нужно совсем чуть-чуть больше стека. Делал сравнительные замеры по производительности - никакого криминала не узрел, теперь использую такую схему на всех своих проектах. В итоге в проекте нет НИ ОДНОГО ГЛОБАЛЬНО ОБЪЕКТА, даже с квалификатором static. Строгая и полностью контролируемая иерархия классов, никакой самодеятельности  Забыл добавить, что использую полностью вытесняющую модель обработчиков, т. к. это тоже накладывает расходы на main стек. Еще раз - речь про ARM процы, у которых есть полный доступ к регистру VTOR.
--------------------
Кругозор некоторых людей - круг с нулевым радиусом. Они называют его "точкой зрения".
|
|
|
|
|
Jun 20 2017, 10:40
|

I WANT TO BELIEVE
     
Группа: Свой
Сообщений: 2 617
Регистрация: 9-03-08
Пользователь №: 35 751

|
Цитата вот с этим и вопрос, как объявить обработчик прерывания В соответствии с мануалом на компилятор и HAL выбранного вами процессора. Для AVR обработчик прерывания объявляется вот так. Код ISR( TIM0_OVF_vect ) { } В этом обработчкие вы вольны делать всё что хотите. В том числе вызвать либо статический метод вашего шаблонного полностью статического класса. Либо инстанциировать класс и вызвать не статический метод. Цитата а что делать с обработчиком прерывания? как получить статический метод шаблонного класса? Из обработчика прерывания вы можете вызвать любой метод, хоть статичный хоть нет. Код ISR( TIM0_OVF_vect ) { SomeInstance.CallFromISR(); SomeFullyStaticClass::SomeStaticMethod(); } Вы немного путаетесь, вам надо теорию почитать что есть шаблон что статик что не статик. Шаблон не обязан быть стататиком. Всё что было сказано выше про виртуальные функции подразумевает не статичные методы и работу именно с экземплярами класса по ссылке(указателю) на родительский класс. Это называется полиморфизм. Опять же надо теорию почитать.
--------------------
The truth is out there...
|
|
|
|
|
Jun 20 2017, 14:08
|
Гуру
     
Группа: Свой
Сообщений: 2 563
Регистрация: 8-04-05
Из: Nsk
Пользователь №: 3 954

|
Цитата(sigmaN @ Jun 20 2017, 16:40)  Из обработчика прерывания вы можете вызвать любой метод, хоть статичный хоть нет. Код ISR( TIM0_OVF_vect ) { SomeInstance.CallFromISR(); SomeFullyStaticClass::SomeStaticMethod(); } спасибо, капитан. хотелось немного красоты, то есть просто объявить Uart0 <64> pc; и после этого не лезть потом в обработчик прерывания чтобы указать там ещё разимя этого конкретного экзэмпляра этого uarta, он ведь всё равно один единственный. ну и ещё размер фифо, если сделать метод статическим ISR( TIM0_OVF_vect ){ Uart0<64>::pc.CallFromISR(); } в результате сделал просто глобальный указатель на функцию Uart0CallFromISR, который зовётся из прерывания и инициализируется как this->isr в конструкторе uart0.
|
|
|
|
|
Jun 20 2017, 15:42
|

I WANT TO BELIEVE
     
Группа: Свой
Сообщений: 2 617
Регистрация: 9-03-08
Пользователь №: 35 751

|
ну вот это вот Uart0<64> то зачем по всей программе размазывать? А если вы FIFO поменять захотите, то будете в 100500 местах это делать? typedef вам в помощь. Ибо шаблон это ТИП! Поэтому весь шаблон со всем своим сколько угодно длинным набором параметров можно просто обозвать как-то удачно. Например typedef UartDriver<Uart0, Fifo64, ParamsDefault> Uart0; Ну а дальше уже в зависимости от реализации. Если в шаблоне UartDriver<> вообще нет состояния и там все методы статичны то экземпляр вам не нужен. Сразу приступаем к использованию: Uart0::Tx("blablabla"); Uart0::ISR(); А если там есть какое-то состояние то потребуется инстанциировать этот класс(тип) путем объявления перменной Uart0 Uart0Instance; И дальше уже обращаться к Uart0Instance.Tx("blablabla"); и Uart0Instance.ISR(); У вас же тут Код ISR( TIM0_OVF_vect ){ Uart0<64>::pc.CallFromISR(); } Какое-то масло масленное......
--------------------
The truth is out there...
|
|
|
|
|
Jun 21 2017, 06:24
|

Профессионал
    
Группа: Свой
Сообщений: 1 215
Регистрация: 22-02-05
Пользователь №: 2 831

|
Цитата(sigmaN @ Jun 20 2017, 18:42)  Какое-то масло масленное...... И не говорите Цитата(_pv) хотелось немного красоты... Небольшой офф. Статические методы в классе нужны крайне редко и практически всегда они имеют квалификатор "private", но исключение составляет, пожалуй, лишь паттерн "Singleton" с его методом ::getInstance(). Все дело в том, что бездумное применение статичных методов где надо и не надо превращает любой объектно-ориентированный (C++) код в типичный процедурный (голый С). Конечно, если проект крохотный, то это не имеет особого значения, но для крохотных проектов не нужен C++. Вот неплохая статья на эту тему.
--------------------
Кругозор некоторых людей - круг с нулевым радиусом. Они называют его "точкой зрения".
|
|
|
|
|
Jun 21 2017, 09:49
|

I WANT TO BELIEVE
     
Группа: Свой
Сообщений: 2 617
Регистрация: 9-03-08
Пользователь №: 35 751

|
Цитата Статические методы в классе нужны крайне редко и практически всегда они имеют квалификатор "private", но исключение составляет, пожалуй, лишь паттерн "Singleton" с его методом ::getInstance(). Категорически с вами не соглашусь. Объединение группы методов под крышей одного типа позволяет очень эффективно решать ряд задач! Часто прямо на этапе компиляции. Ознакомьтесь с http://easyelectronics.ru/rabota-s-portami...erov-na-si.html А вот вам моя почти на 100% переработанная и допиленная реализация этой идеи.
neiverio_dxEdition_24_.zip ( 120.61 килобайт )
Кол-во скачиваний: 35 Там только для стм32 пока реализации нет, не дошли руки. Да, это по сути куча функций, но в данном случае стэйт и инстансы нам и не нужны! И как результат имеем адсолютно отсутствующий оверхед и не отступаем от заветов объектно-ориентированного программирования. Таки пины пркерасно передаются как параметр шаблона куда угодно(в драйвер).. В прочем, всё это описано в оригинальной статье. P.S. #include "neiverio.hpp" и прописать путь к папке с нужной реализацией GPIO (сейчас это AVR, XMEGA или STM8)
--------------------
The truth is out there...
|
|
|
|
|
Jun 21 2017, 10:21
|

Профессионал
    
Группа: Свой
Сообщений: 1 215
Регистрация: 22-02-05
Пользователь №: 2 831

|
Цитата(sigmaN @ Jun 21 2017, 12:49)  И как результат имеем адсолютно отсутствующий оверхед и не отступаем от заветов объектно-ориентированного программирования. Оверхед, оверхед, оверхед... Это напоминает автовладельца, которого беспокоит царапина на крыле его авто при том, что ее кузов уже напрочь прогнил  Если только и думать об этом мифическом оверхеде, то никогда не получится создавать действительно полноценный ООП-код. Цитата(sigmaN) А вот вам моя почти на 100% переработанная и допиленная реализация этой идеи. ~1Мбайт текста ... для портов I/O  Странно, у меня это все влезает в один файлик в 10кб текста (для одного МК). Мне даже как-то стыдно стало
--------------------
Кругозор некоторых людей - круг с нулевым радиусом. Они называют его "точкой зрения".
|
|
|
|
|
Jun 21 2017, 10:45
|

Профессионал
    
Группа: Свой
Сообщений: 1 215
Регистрация: 22-02-05
Пользователь №: 2 831

|
Цитата(sigmaN @ Jun 21 2017, 13:30)  Ну таки если бы это противоречило ООП то я бы с вами согласился. Мегабайты кода на ровном месте как-то не очень вяжутся с ООП Полагаю, что наши понятия читаемого кода сильно разнятся: я лучше потеряю полкило ОЗУ МК на неком оверхеде, чем буду разгребрать кучу файлов с красными глазами, хотя бы пытаясь понять их работу.... Цитата А так - просто бла бла бла и попытка акцентировать внимание на и так всем известным прописным истинам о преждевременной оптимизации и оверхеде.... Скучно ) Судя по вашему коду, оптимизация не то что преждевременная, она по ходу являлась мотиватором по созданию этой груды тесно связанных файлов. Честно говоря, впервые сталкиваюсь с таким. Напоминает "из пушки по воробьям" Один только вопрос: не жалко было тратить столько времени на эту детскую задачку?
--------------------
Кругозор некоторых людей - круг с нулевым радиусом. Они называют его "точкой зрения".
|
|
|
|
|
Jun 21 2017, 11:14
|

Профессионал
    
Группа: Свой
Сообщений: 1 215
Регистрация: 22-02-05
Пользователь №: 2 831

|
Цитата(Сергей Борщ @ Jun 21 2017, 14:07)  Да чего уж там стыдиться, показывайте. ваше "это все" можно передавать параметром в шаблон? Можно и так, но я в таких случаях передаю не сам шаблонный класс пина, а лишь индекс пина, на котором он должен "висеть". И уже внутри шаблонного класса, где этот пин должен использоваться, создаю экземпляр этого пина. Оно и логично - пин должен принадлежать только тому, кто его пользует. Он же и должен создавать этот пин. Тогда этот пин защищен от других посягателей. CODE //////////////////////////////////////////////////////////////////////////////////////////////////////////////// // Project: Any // Description: Pin (STM32L1xx) ////////////////////////////////////////////////////////////////////////////////////////////////////////////////
#pragma once
////////////////////////////////////////////////////////////////////////////////////////////////////////////////
#include "stm32l1xx_rcc.h"
////////////////////////////////////////////////////////////////////////////////////////////////////////////////
typedef enum { PA0, PA1, PA2, PA3, PA4, PA5, PA6, PA7, PA8, PA9, PA10, PA11, PA12, PA13, PA14, PA15, PB0, PB1, PB2, PB3, PB4, PB5, PB6, PB7, PB8, PB9, PB10, PB11, PB12, PB13, PB14, PB15, PC0, PC1, PC2, PC3, PC4, PC5, PC6, PC7, PC8, PC9, PC10, PC11, PC12, PC13, PC14, PC15, PD0, PD1, PD2, PD3, PD4, PD5, PD6, PD7, PD8, PD9, PD10, PD11, PD12, PD13, PD14, PD15, PE0, PE1, PE2, PE3, PE4, PE5, PE6, PE7, PE8, PE9, PE10, PE11, PE12, PE13, PE14, PE15, PF0, PF1, PF2, PF3, PF4, PF5, PF6, PF7, PF8, PF9, PF10, PF11, PF12, PF13, PF14, PF15, PH0, PH1, PH2, PH3, PH4, PH5, PH6, PH7, PH8, PH9, PH10, PH11, PH12, PH13, PH14, PH15, } PIN;
////////////////////////////////////////////////////////////////////////////////////////////////////////////////
class PinBase { public: PinBase(PIN pin) { portIndex = pin / 16; pinIndex = pin % 16; pinMask = (((int32_t)1) << pinIndex);
switch (portIndex) { #ifdef GPIOA_BASE case (0): port = GPIOA; RCC->AHBENR |= RCC_AHBPeriph_GPIOA; break; #endif #ifdef GPIOB_BASE case (1): port = GPIOB; RCC->AHBENR |= RCC_AHBPeriph_GPIOB; break; #endif #ifdef GPIOC_BASE case (2): port = GPIOC; RCC->AHBENR |= RCC_AHBPeriph_GPIOC; break; #endif #ifdef GPIOD_BASE case (3): port = GPIOD; RCC->AHBENR |= RCC_AHBPeriph_GPIOD; break; #endif #ifdef GPIOE_BASE case (4): port = GPIOE; RCC->AHBENR |= RCC_AHBPeriph_GPIOE; break; #endif #ifdef GPIOF_BASE case (5): port = GPIOF; RCC->AHBENR |= RCC_AHBPeriph_GPIOF; break; #endif #ifdef GPIOH_BASE case (6): port = GPIOH; RCC->AHBENR |= RCC_AHBPeriph_GPIOH; break; #endif default : break; } disablePullUpAndPullDown(); }
void setAsDigitalInput() // MODER = 0 { port->MODER &= ~(((int32_t)0x03) << (pinIndex * 2)); port->MODER |= (((int32_t)0x00) << (pinIndex * 2)); } void setAsDigitalOutput() // MODER = 1 { port->MODER &= ~(((int32_t)0x03) << (pinIndex * 2)); port->MODER |= (((int32_t)0x01) << (pinIndex * 2)); }
typedef int8_t AlternativeFunction; void setAsAlternative(AlternativeFunction function) // MODER = 2 { port->MODER &= ~(((int32_t)0x03) << (pinIndex * 2)); port->MODER |= (((int32_t)0x02) << (pinIndex * 2)); if (pinIndex < 8) { port->AFR[0] &= ~(((int32_t)0x0F) << (pinIndex * 4)); port->AFR[0] |= (((int32_t)function & 0x0F) << (pinIndex * 4)); } else { port->AFR[1] &= ~(((int32_t)0x0F) << ((pinIndex - 8) * 4)); port->AFR[1] |= (((int32_t)function & 0x0F) << ((pinIndex - 8) * 4)); } } void setAsAnalogInput() // MODER = 3 { disablePullUpAndPullDown(); port->MODER &= ~(((int32_t)0x03) << (pinIndex * 2)); port->MODER |= (((int32_t)0x03) << (pinIndex * 2)); } void setAsAnalogOutput() // MODER = 3 { disablePullUpAndPullDown(); port->MODER &= ~(((int32_t)0x03) << (pinIndex * 2)); port->MODER |= (((int32_t)0x03) << (pinIndex * 2)); } void setAsPushPull() { port->OTYPER &= ~(1 << pinIndex); } // OTYPER = 0 void setAsOpenDrain() { port->OTYPER |= (1 << pinIndex); } // OTYPER = 1 void setOutputSpeed400kHz() { port->OSPEEDR &= ~(((int32_t)0x03) << (pinIndex * 2)); port->OSPEEDR |= (((int32_t)0x00) << (pinIndex * 2)); } // OSPEEDR = 0 void setOutputSpeed2MHz() { port->OSPEEDR &= ~(((int32_t)0x03) << (pinIndex * 2)); port->OSPEEDR |= (((int32_t)0x01) << (pinIndex * 2)); } // OSPEEDR = 1 void setOutputSpeed10MHz() { port->OSPEEDR &= ~(((int32_t)0x03) << (pinIndex * 2)); port->OSPEEDR |= (((int32_t)0x02) << (pinIndex * 2)); } // OSPEEDR = 2 void setOutputSpeed40MHz() { port->OSPEEDR &= ~(((int32_t)0x03) << (pinIndex * 2)); port->OSPEEDR |= (((int32_t)0x03) << (pinIndex * 2)); } // OSPEEDR = 3 void disablePullUpAndPullDown() { port->PUPDR &= ~(((int32_t)0x03) << (pinIndex * 2)); port->PUPDR |= (((int32_t)0x00) << (pinIndex * 2)); } // PUPDR = 0 void enablePullUp() { port->PUPDR &= ~(((int32_t)0x03) << (pinIndex * 2)); port->PUPDR |= (((int32_t)0x01) << (pinIndex * 2)); } // PUPDR = 1 void enablePullDown() { port->PUPDR &= ~(((int32_t)0x03) << (pinIndex * 2)); port->PUPDR |= (((int32_t)0x02) << (pinIndex * 2)); } // PUPDR = 2 inline void setToHigh() __attribute__((always_inline)) { port->BSRRL = pinMask; } inline void setToLow() __attribute__((always_inline)) { port->BSRRH = pinMask; } inline bool isHigh() __attribute__((always_inline)) { return (((port->IDR) & pinMask) != 0); } inline bool isLow() __attribute__((always_inline)) { return (((port->IDR) & pinMask) == 0); } inline void toggle() __attribute__((always_inline)) { if (isLow()) setToHigh(); else setToLow(); }
void lock() { volatile int32_t tempRegister = ((int32_t)1 << pinIndex); GPIOA->LCKR = tempRegister + GPIO_LCKR_LCKK; // (1) Write LCKK bit to 1 and set the pin bits to lock GPIOA->LCKR = tempRegister; // (2) Write LCKK bit to 0 and set the pin bits to lock GPIOA->LCKR = tempRegister + GPIO_LCKR_LCKK ; // (3) Write LCKK bit to 1 and set the pin bits to lock tempRegister = GPIOA->LCKR; // (4) Read the Lock register if ((GPIOA->LCKR & GPIO_LCKR_LCKK) == 0) // (5) Check the Lock register (optional) { // TODO: manage an error } } protected: volatile GPIO_TypeDef * port; volatile int8_t portIndex; volatile int8_t pinIndex; volatile int32_t pinMask; };
////////////////////////////////////////////////////////////////////////////////////////////////////////////////
template <PIN pin> class Pin : public PinBase { public: Pin() : PinBase(pin) { } };
////////////////////////////////////////////////////////////////////////////////////////////////////////////////
template <PIN pin> class DigitalOutputPin : public PinBase { public: DigitalOutputPin() : PinBase(pin) { setAsDigitalOutput(); setAsPushPull(); setOutputSpeed40MHz(); } };
////////////////////////////////////////////////////////////////////////////////////////////////////////////////
template <PIN pin> class LedOpenDrain : public PinBase { public: LedOpenDrain() : PinBase(pin) { setAsDigitalOutput(); setAsOpenDrain(); setOutputSpeed10MHz(); off(); }
void on() { setToLow(); } void off() { setToHigh(); } };
......
--------------------
Кругозор некоторых людей - круг с нулевым радиусом. Они называют его "точкой зрения".
|
|
|
|
|
Jun 21 2017, 11:32
|

I WANT TO BELIEVE
     
Группа: Свой
Сообщений: 2 617
Регистрация: 9-03-08
Пользователь №: 35 751

|
Во-первых там 200Кб текста, а не мегабайт(вы внимательнее посмотрите что там папка STM32 затянула на 700кб, в которую я даже не заглядывал) Во-вторых я понятия не имею как большой объем кода вообще вяжется с ООП...... Не путайте теплое с мягким пожалуйста. В-третьих там всё прекрасно структурированно и раскидано на независимые модули. Не думаю, что у вас было достаточно времени на изучение исходников, чтобы отметить неуместную "тесную связь"... Единственное, что там есть сложного - это реализация списков линий и utils, который лежит рядом с ней. Всё что вокруг TPin и обертки регистров - элементарно, понятно и очень модульно. Это подтверждается представленными реализациями для совершенно различных архитектур, в которых меняется только один файл. Цитата Один только вопрос: не жалко было тратить столько времени на эту детскую задачку? Мне нет. Я прекрасно по упражнялся в мета программировании на С++(особенно при реализации списков линий и вводе концепии ForEach для списка типов), получил огромное кол-во опыта и использую полученную библиотеку в продакшине! Она прекрасно и красиво решает поставленную задачу, полностью отделяет реализацию GPIO от кода драйверов, при этом не создавая не нужных экземпляров там, где в асме должна появиться одна инструкция по типу BSET ODR,#1... Я думаю до такого уровня просто надо дорасти. Цитата но я в таких случаях передаю не сам шаблонный класс пина, а лишь индекс пина, на котором он должен "висеть". И уже внутри шаблонного класса, где этот пин должен использоваться, создаю экземпляр этого пина. Спорить здесь в таком ключе контр продуктивно. Вы передаете номер пина, вам нравится? Отлично! Я всего лишь выложил реализацию как аргумент в пользу утверждения, что не все статические классы одинаково вредны. Равно как и создание экземпляров. Всему своё место и применение. Ни больше ни меньше ) Add: Взглянул я на вашу реализацию... Это конечно вещи которые и сравнивать то нельзя. Но если вам нравится то ОК )))
--------------------
The truth is out there...
|
|
|
|
|
Jun 21 2017, 11:44
|

Профессионал
    
Группа: Свой
Сообщений: 1 215
Регистрация: 22-02-05
Пользователь №: 2 831

|
Цитата(Сергей Борщ @ Jun 21 2017, 14:23)  Ооо! На каждую ногу выделить 12 байт памяти под указатель, маску и индексы? Если после сборки проекта остается куча невостребованного ОЗУ во много-много кБ (который обычно нужен для отладки), то это не представляет никаких проблем. Экономия на спичках  По мне важнее, чтобы все это можно было читать обычными людьми, а не т. н."вассерманами". Цитата Во-вторых я понятия не имею как большой объем кода вообще вяжется с ООП ООП подразумевает читаемый людьми код, и чем меньше этого, тем легче его читать и сопровождать. При переводе структурного кода на ООП, читаемость кода обычно резко улучшается (если конечно его объем не возрастает в десятки, а то и сотни раз). Чем проще конструкции, используемые в коде с ООП, тем более читаем этот код и тем более понятен он другим людям. Многие не пользуются даже шаблонами, но при этом создают вполне понятный и простой ООП код (пусть он без шаблонов и не совсем ООП). Простой код не требует документации, он понятен сходу при чтении. Разве что нужна задокументировать хотя бы в простом виде иерархию классов. Поэтому "теплое" в данном случае очень даже "мягкое"  Цитата(sigmaN @ Jun 21 2017, 14:32)  Во-первых там 200Кб текста, Беру свои слова назад, это ж все меняет! Цитата Я думаю до такого уровня просто надо дорасти. Так точно, Ваше Сиятельство, будем стараться! Цитата Взглянул я на вашу реализацию... Это конечно вещи которые и сравнивать то нельзя. Ну, да, куда уж мне до метапрограммирования ... пинов  Цитата Всему своё место и применение. Ни больше ни меньше ) Абсолютно согласен!
--------------------
Кругозор некоторых людей - круг с нулевым радиусом. Они называют его "точкой зрения".
|
|
|
|
1 чел. читают эту тему (гостей: 1, скрытых пользователей: 0)
Пользователей: 0
|
|
|