ВходНаше всё Теги codebook 无线电组件 Поиск Опросы Закон Воскресенье
28 апреля
1246830 Топик полностью
VVB (06.10.2022 10:26, просмотров: 199) ответил Dingo на Тоже спрошу. Как оформляются обработчики прерываний? Поймал себя недавно на том, что "и попробовал бы Цэ++, но не знаю, как делается ...". Это одна из базовых вещей.
Реализация очень сильно зависит от архитектуры и возможностей контроллера прерываний. 

Например, для Cortex-R4 (TI, серия RM4x)


Дано:

1. имеется контроллер прерываний, в котором имеется таблица обработчиков

2. в нужный элемент таблицы заносишь адрес нужного тебе объекта, имеющего публичный метод handlerIrq


Пример кода (естесственно, всё разделено по разным модулям; я здесь всё в кучу свалил для демонстрации):


/* интерфейсный класс */
class IrqCapable { public: IrqCapable(const IRQ irqnumber, const IRQTYPE irqtype); virtual void handlerIrq() = 0; virtual ~IrqCapable() {} };

IrqCapable::IrqCapable(const IRQ irqNumber, const IRQTYPE irqtype) : irqnumber(irqNumber) { if (irqtype == IRQTYPE::IRQ) { registerIrqVectorInVim(irqNumber, reinterpret_cast<uint32_t>(this)); } else { registerFiqVectorInVim(irqNumber, reinterpret_cast<uint32_t>(this)); } }

static class Rti* rti = nullptr;
/* класс, реализующий требуемую функциональность по обработке */
class Rti : private ::irq::IrqCapable {
virtual void handlerIrq() override { flagSystick = true; ++counterSysticks; clearInterruptFlagForCompare0(); } public: Rti() : IrqCapable(irq::IRQ::RTI_COMPARE_0, IRQTYPE::IRQ) {/* прочее */}
}


/* функция, которая создаёт требуемые экземпляры класса */
/* при необходимости передаются параметры, которые транслируются в Rti::Rti(), например, номер IRQ для VIM, или номер периферийного блока */ void create()
{
rti = new Rti(); /* тут можно и без "кучи" обойтись, если использовать in-place new */
}


/* обработчик IRQ, прописываемый в таблице прерываний, выглядит как-то так: */ extern "C" __attribute__((target("arm"), interrupt("IRQ"))) void irqHandler(); extern "C" __attribute__((target("arm"), interrupt("IRQ"))) void irqHandler() { const auto irqvecreg = reinterpret_cast<uint32_t*>(0xFFFF'FE70U); /* сюда контроллер прерываний автоматически кладёт адрес из таблицы прерываний */ auto obj = reinterpret_cast<irq::IrqCapable*>(*irqvecreg);
obj->handlerIrq(); }

Как это работает для RM4x?

1. вызывается create()->Rti::Rti()->IrqCapable::IrqCapable()->registerIrqVectorInVim(), тем самым адрес объекта запоминается в таблице VIM, настраиваются приоритет, тип прерывания, разрешаются прерывания от этой периферии

2. при срабатывании IRQ: выполняется команда по адресу 0x18->в регистр PC загружается адрес irqHandler()->выполняется irqHandler()->из VIM считывается адрес объекта, вызвавшего IRQ->вызывается метод handlerIrq() данного объекта->выполняется Rti::handlerIrq(), в котором кодируется требуемая функциональность


Какие преимущества даёт (ИМХО)?

1. TDD, возможность вызвать Rti::handlerIrq() из тестового кода; я так несколько раз писал сложные драйверы

2. унификация обработчиков

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

4. уменьшение кода-спагетти для рабочего кода модуля Rti за счёт выноса настройки прерываний в отдельный модуль

5. унификация драйверов и обработчиков прерываний для разных проектов ПО для разных МК, я бОльшую часть тупо копирую в нужный проект, и подстраиваю немного

6. уменьшение связности модуля Rti


Всё это также легко решаемо на С, и в проектах средней и более высокой сложности так и делается, на сях.