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

С++. Обработчик прерывания и класс - как связать?

У меня дежавю, причем в тяжёлой форме :crazy: Срач начнётся странице ко 2й, реальное обсуждение закончится чуть позже :tease:

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

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

 

Либо можно сделать сам объект так, что бы любой его экземпляр был сразу связан с ресурсом. Это различные способы статически привязать экземпляр к ресурсу прямо в момент создания (или даже ещё раньше). Это могут быть статические методы объекта, намертво прописанные в теле метода объекта источники, шаблоны.

 

В общем выбирайте - тут есть из чего выбирать. Но сначала определитесь, чего именно вы хотите (желательно максимально конкретно)

 

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


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

23 минуты назад, xvr сказал:

советую обратить внимание, что экземпляр любого класса создаётся в процессе исполнения программы

Ну вообще-то уже есть constexpr-конструкторы. Так что теоретически можно даже получить встроенный вызов даже используя нелюбимые мной делегаты.

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


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

2 hours ago, Forger said:

Я сделал на делегатах, тогда любой метод любого класса может быть обработчиком прерывания.

А не можете фрагмент дизассемблера (листинга) приветсти? Во что, всё-таки, это компилируется. Просто интересно, что за "минимальные расходы")

4 hours ago, Eddy_Em said:

А не проще ли свой стартап написать? На С++, коль уж вы их используете. Зачем вам ассемблер?

+1!!!!

@ViKo, примеров в сети куча на тему написания стартапа на Си(++). Я сам это дело недавно освоил) Ничего особо сложного там нет.

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


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

16 minutes ago, haker_fox said:

А не можете фрагмент дизассемблера (листинга) приветсти?

Вызов делегата:

257098436_.jpg.11170e80170643751a552ec715b76171.jpg

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


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

30 minutes ago, Forger said:

Вызов делегата:

В принципе действительно немного. Но вот на днях занимался отладкой системой сбора данных (на базе микроконтроллера с ядром Cortex-M4F). В прерывании, возникающим каждые 25 мкс, нужно было произвести некоторые расчёты (часть даже по таблицам). Так вот, не успевал. Пришлось делать всё, чтобы компилятор инлайнил функции, убирать все лишние проверки... Конечно, мой случай частный. Но именно в нём делегат бы был непозволительной роскошлью из 3 команд))))

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


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

Ниче не понял. Чего опять затевает @ViKo?:prankster2:

 

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

Вызов делегата...

А можно вопрос?

Как я понял, делегат - это что-то похожее на указатель на функцию.

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

А тогда что такое "вызов делегата"? Ведь прерывания асинхронны...

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


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

Just now, haker_fox said:

В принципе действительно немного. Но вот на днях занимался отладкой системой сбора данных (на базе микроконтроллера с ядром Cortex-M4F). В прерывании, возникающим каждые 25 мкс, нужно было произвести некоторые расчёты (часть даже по таблицам). Так вот, не успевал. Пришлось делать всё, чтобы компилятор инлайнил функции, убирать все лишние проверки... Конечно, мой случай частный. Но именно в нём делегат бы был непозволительной роскошлью из 3 команд))))

В прерываниях никогда ничего не считаю, только быстренко просимафорил и скорее наружу.

Раньше делал так - копировал в ОЗУ таблицу векторов, и перенастраивал на нее VTOR (не ниже CM3). Так можно настроить вектор на любую статичную функцию класса.

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

 

 

Just now, Arlleex said:

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

А тогда что такое "вызов делегата"? Ведь прерывания асинхронны...

И что? Где прервались туда же и вернемся, даже если прервали между заполнением указателя this и вызовом метода. Указатель на функцию - это по сути делегат на статическую функцию класса. Чуть-чуть быстрее работает, чем вызов метода класса.

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


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

2 minutes ago, Forger said:

это некого рода костыль.

Согласен, и осознаю, что быстродействие нашей системы на пределе. Но это архитектурный косяк, на переделывание которого нет времени. Пока получается так пооптимизирвоать)

3 minutes ago, Forger said:

В прерываниях никогда ничего не считаю, только быстренко просимафорил и скорее наружу.

Здесь льётся довольно большое количество данных, поэтому читаю сразу в прерывании и пишу в кольцо.

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


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

Just now, haker_fox said:

Согласен, и осознаю, что быстродействие нашей системы на пределе. Но это архитектурный косяк, на переделывание которого нет времени. Пока получается так пооптимизирвоать)

Здесь льётся довольно большое количество данных, поэтому читаю сразу в прерывании и пишу в кольцо.

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

Как говорится - что имеем, с тем и работаем :)

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


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

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

И что? Где прервались туда же и вернемся, даже если прервали между заполнением указателя this и вызовом метода...

Ну как же.

Асинхронный вызов обычно не зависит от написанного в коде, на то он и асинхронный.

Большинство прерываний, реализованных в МК, именно такие - то есть процессор не знает, когда оно произойдет.

А Вы говорите, что делегат вызывается. Вот я и хочу узнать, что значит вызывается? Ведь когда в МК возникает прерывание, никто ничего не вызывает.

 

Как я понял (вот сейчас только): в таблицу векторов заносится адрес делегата. При возникновении прерывания управление передастся на вот этот код, который Вы привели в дизасме. А он уже в свою очередь вызовет прикрепленный обработчик прерывания в каком-нибудь классе. Так?

 

P.S. Мне сам принцип понять главное:mail1:

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


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

Ну конечно ядро МК ничего не знает про такую штуку как делегат - это просто некая программная абстракция, по факту у меня это реализовано просто:

вместо штатного startup.c у меня свой самописный vectors.cpp, где под каждое семейство создается своя таблица (массив) делегатов и равное им число экземпляров шаблонной статической функции, в которых производится вызов соотв делегата из таблицы по заранее известному индексу, так быстрее получается.

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

И конечно предусмотрен ряд методов того же класса Interrupt, которые позволяют управлять тем или иным вектором и соотв. его делегатом. Векторам можно в коде давать нужные имена и использовать это в соотв. софте для мониторинга.

Это, конечно, увеличивает оверхед, но в отладочной версии никак  иначе .

Разумеется, не системные векторы вызываются "классически", ибо их функции никогда не меняют и всегда известны еще на этапе компиляции.

Короче:


Intrrupt.hpp:

...
	class Interrupt final
	{
	public:
		using Vector = Delegate<void(void)>;
....

 

 

  
  

Intrrupt.cpp:

static ...... ::Interrupt::Vector __attribute__((used)) vectorTable[IRQ_VECTOR_TABLE_SIZE]; 

...

template<unsigned VECTOR> static void interruptHandler() { vectorTable[VECTOR](); }


...

static void (* const __vectors[])() __attribute__((section("RESET"), used)) =
{
	(void (*)())(&startupStack[kStartupStackSizeBytes/sizeof(StackItem)]),
	ResetHandler,
	NMI_Handler,			// NMI,
	HardFault_Handler,		// HardFault
	MemManage_Handler,		// MemManage_Handler
	BusFault_Handler,		// BusFault_Handler
	UsageFault_Handler,		// UsageFault_Handler
	0,						// Reserved
	0,						// Reserved
	0,						// Reserved
	0,						// Reserved
	SVC_Handler,			// Keil RTX RTOS Vector
	DebugMon_Handler,		// DebugMon_Handler
	0,						// Reserved
	PendSV_Handler,			// Keil RTX RTOS Vector
	SysTick_Handler,		// Keil RTX RTOS Vector

	interruptHandler<0>, 	interruptHandler<1>,	interruptHandler<2>,	interruptHandler<3>,
	interruptHandler<4>,	interruptHandler<5>,	interruptHandler<6>,	interruptHandler<7>,
	interruptHandler<8>,	interruptHandler<9>,	interruptHandler<10>,	interruptHandler<11>,
	interruptHandler<12>,	interruptHandler<13>,	interruptHandler<14>,	interruptHandler<15>,
	interruptHandler<16>,	interruptHandler<17>,	interruptHandler<18>,	interruptHandler<19>,
  ....
};

 

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


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

1 hour ago, Arlleex said:

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

Нет, вызов делегата - это УЖЕ вызов конкретного обработчика (void(void) метод ЛЮБОГО класса). 

Т.е. после кода дизасм коде выше при выполнении BX R1 происходит вызов конкретного ВАШЕГО обработчика, короче, в R1 - находится адрес вашего обработчика. 

Фактически если сравнивать с "классической" схемой к вызову каждого обработчика прерывания нужно просто добавить четыре эти команды. В принципе все ))

 

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


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

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

Нет, вызов делегата - это УЖЕ вызов конкретного обработчика...

Гхм... Тогда как назвать ту ассемблерную прослойку, которую Вы привели?

Я полагал, что те 4 команды и есть делегат. Делегат как бы "передает полномочия" реальному обработчику. Он является своего рода прослойкой.

 

Вот, чтобы окончательно все устаканилось: полагаю, в таблице векторов прерываний в каком-то векторе хранится адрес 0x0800164D (см. дизасм).

При возникновении прерывания управление передается по этому адресу. PC загружается адресом начала этой самой "прослойки" (см. дизасм), а она уже вызывает наш обработчик.

 

Короче, как я понял, это синтаксически засахаренная вот такая штука

void (*pfUserISR)(void);

void I2C_Handler(void) // та самая прослойка (см. дизасм)
{
  pfUserISR();
}

void MyI2CHandler(void)
{
  ...
}

int main(void)
{
  pfUserISR = MyI2CHandler;
}

где в реальной таблице векторов по вектору периферии пропишется адрес I2C_Handler.

При возникновении прерывания управление перейдет в эту функцию (дизасм выше), а из нее вызовется pfUserISR, который мы назначили в main() (а в плюсах, как я понял, вся эта шарманка скрыта за красивым словом DELEGATE, поэтому глобального указателя pfUserISR не требуется).

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


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

30 минут назад, Arlleex сказал:

Короче, как я понял, это синтаксически засахаренная вот такая штука

Почти. Только у него ещё один уровень косвенности. Потому что в таблице векторов у него лежат функции, которые делают следующее:

берут из массива адрес объекта класса Vector с заданным номером и вызывают его функцию operator(). А уже этот объект в себе может хранить либо просто указатель на функцию, либо указатель на функцию-член класса + указатель на экземпляр класса, либо лямбду. Так что товарищ Forger слегка слукавил, когда показал ассемблерный листинг :-)

Кстати, не исключено (и даже почти наверняка), что operator() - виртуальный, и тогда товарищ Forger слукавил дважды, потому что виртуальные функции тоже вызываются косвенно.

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


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

37 minutes ago, Arlleex said:

Гхм... Тогда как назвать ту ассемблерную прослойку, которую Вы привели?

Прямо над ней написано: вызов делегата, это именно так.

 

37 minutes ago, Arlleex said:

Я полагал, что те 4 команды и есть делегат. Делегат как бы "передает полномочия" реальному обработчику. Он является своего рода прослойкой.

Так и есть, делегат - это типа указатель: размер указателя 4 байта, у указателя на метод класса (и делегата) - 8 байт (для наших 32-битников), т.к. помимо указателя нужно помнить еще и this на конретный класс владельца.

Размер делегата и время его вызова не зависят от того, на кого он ссылается, точно так же как и указатель.

 

37 minutes ago, Arlleex said:

Вот, чтобы окончательно все устаканилось: полагаю, в таблице векторов прерываний в каком-то векторе хранится адрес 0x0800164D (см. дизасм).

При возникновении прерывания управление передается по этому адресу. PC загружается адресом начала этой самой "прослойки" (см. дизасм), а она уже вызывает наш обработчик.

Да, типа того. По крайней мере по числу команд в ASM.

 

37 minutes ago, Arlleex said:

Короче, как я понял, это синтаксически засахаренная вот такая штука

Вы объявили указатель, выделили для него память в ОЗУ, вот там и будет хранится адрес вашей процедуры.

Сама таблица векторов находится во FLASH.

 

37 minutes ago, Arlleex said:

При возникновении прерывания управление перейдет в эту функцию (дизасм выше), а из нее вызовется pfUserISR, который мы назначили в main() (а в плюсах, как я понял, вся эта шарманка скрыта за красивым словом DELEGATE, поэтому глобального указателя pfUserISR не требуется).

В плюсах используется указатель на метод конкретного класса, это не то же самое, что указатель на функцию. Но почти.

А вот делегат позволяет указывать на метод ЛЮБОГО класса. Вообще, главное - совпадающая семантика метода - одинаковые типы и кол-во входных и тип выходного параметра. Для прерываний это - логичные void(void).

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


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

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

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

Гость
К сожалению, ваш контент содержит запрещённые слова. Пожалуйста, отредактируйте контент, чтобы удалить выделенные ниже слова.
Ответить в этой теме...

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

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

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

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

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

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