Прерывания принадлежат не объекту, а контрполлеру прерываний и процессору
Получается, что контроллер прерываний должен сам обрабатывать десятки и сотни прерываний? Как данные от UART, реального или виртуального, попадут тому, кто их ждёт? Ну и вопрос наследования очень интересен. Как расширять и изменять обработку данных? При моём подходе обработчик прерывания есть виртуальная функция со всеми плюшками ООП, а тут простая функция. Как реализовать полиморфизм при таком подходе?
jcxz писал(а):
Почему именно нужно "переписывать стартап в стиле си++"? Чем ассемблерный стартап не подходит?
Хм, зачем ассемблер, когда есть С/С++? В комплекте с gcc есть пример стартапа на С. В стартапе, например, можно сначала установить штатную частоту МК, а потом запустить то, что построил С/С++, и делать это, обычно, лучше на максимальной частоте тактирования МК, а не на 16МГц как это делает стартап от Куба. Я последние годы принципиально ухожу от асма.
Ещё раз: взял образец из примеров к GCC. Зачем асм, когда мне удобней и приятней делать это на С/С++.
jcxz писал(а):
Для чего переписывать?
Для того, чтобы он работал так, как мне нужно. И, самое главное, дописать в него поддержку использования обработчиков прерываний в виде виртуальных функций.
jcxz писал(а):
Зачем виртуальная функция для ISR???
Для следования парадигме С++. Инкапсуляцию и полиморфизм проще и логичней реализовывать в случае, когда объект, например, UART или таймер, может обрабатывать прерывания, предназначенные ему.
Ещё раз: Зачем переписывать, если он уже есть? Для чего переписывать?
Оно уже давно есть и на асме, и на Си и на С++. Ничего переписывать не надо. С другой стороны, вы прямо каким-то священным действие написание обычного стартапа представляете. Вы же IAR, пользуетесь. Вот стартап Cortex-M под IAR.Где тут асм нужен? Легким движением руки туда добавляется поддержка тулчейнов на GCC и Keil. startup_iar_gcc_keil_stm32g071xx.c потому что Си он и в африке Си, а ассемблеры у них друг друга не переваривают.
Получается, что контроллер прерываний должен сам обрабатывать десятки и сотни прерываний? Ну да, это его работа.
Как данные от UART, реального или виртуального, попадут тому, кто их ждёт? Вызовом соответствующего метода.
Какой смысл в промежуточном объекте "контроллер прерываний"? Какое это даёт преимущество перед подходом, когда прерывание обрабатывает тот объект, который явно ассоциирован с периферией, относящейся к нему, например, таймер?
1. Не промежуточный, а реальный. Он есть в контроллере, он работает. Почему вы его обижаете? 2. Какое преимущество даёт обработчик виртуальным методом? Ну, кроме риска вызваться через таблицу виртуальных методов и замедлить его работу.
Заголовок сообщения: Re: Микроконтроллеры STM32 - тонкости работы, отладочные пла
Добавлено: Пн дек 01, 2025 14:46:54
Держит паяльник хвостом
Карма: 16
Рейтинг сообщений: 205
Зарегистрирован: Вс дек 02, 2012 16:58:33 Сообщений: 936 Откуда: от туда
Рейтинг сообщения:1
Стремление написать всю программу для микроконтроллера на плюсах можно объяснить, но невозможно понять. Объяснить тем, что: "когда у тебя в руках молоток, все вокруг кажутся гвоздями". Это просто зуд кодописательства. Кесарю кесарево, а пекарю пекарево. Я работал и разбирался с проектами для микроконтроллеров, написанных на Си++21 с виртуальными и лямбда функциями, размером больше полутора гигабайт исходного кода и невозможностью развития. Эти проекты были выкинуты в мусорку и заменены нормальными исходниками на Си, с меньшим размером исходного и исполняемого кода и большим функционалом, поддерживающим развитие проекта. Я не против С++, но для его применения нужны веские основания. Это язык более высокого уровня, и парадигму ООП не натянуть на глобус железа микроконтроллера. Дело в том, что железо различных микроконтроллеров сильно отличается друг от друга даже у именитых производителей. Например, I2C контроллер STM32F103 и STM32F401. Для их поддержки нужны принципиально различные интерфейсы, и одним родительским классом эти интерфейсы не породишь. Похожее, но не менее амбициозное стремление STM сделать универсальный HAL также потерпело крах в виде уникальных библиотек для разных микроконтроллеров, почитайте про stm32hal_*_ex библиотеки. ИМХО нет ничего лучше сниппетов - заготовок для низкоуровневых работ не придумано. А языку высокого уровня оставьте свою работу, вроде работы с интернетом вещей, облачными сервисами, ГУИ и прочим.
Какое преимущество даёт обработчик виртуальным методом?
Кто хранит данные, тот их и обрабатывает. Инкапсуляция.
GARMIN писал(а):
нормальными исходниками на Си, с меньшим размером исходного и исполняемого кода
Статистика по проектам, которую собирали умные дядьки из профильных комитетов по стандартизации языков говорит об обратном. В С++ выше повторное использование кода, поэтому размер исполняемого кода меньше, чем у программ на С. Моя практика подтверждает это.
GARMIN писал(а):
не менее амбициозное стремление STM сделать универсальный HAL также потерпело крах в виде уникальных библиотек для разных микроконтроллеров, почитайте про stm32hal_*_ex библиотеки.
Я это давно знаю, поэтому везде говорю, что STM нужно было не лепить HAL для конкретного МК, а делать библиотеки для периферии, находящейся в МК. У STM есть таблица, в которой указаны МК и версии периферийных блоков в них. STM периферию покупает на стороне и собирает под крышей своих МК, поэтому логичней привязывать HAL именно к периферии.
Кто хранит данные, тот их и обрабатывает. Инкапсуляция.
Вот, из недавнего. На USART2 и USART3 висят одинковые интерфейсы управления. На LPUART1 датчик DUG51. Кому прикажете отдать прерывания, датчику или нитерфейсу управления? И зачем классам датчика и интефейсов управления знать про устройство контроллера прерываний? Спойлер
VladislavS, но зачем побайтно принимать в прерываниях, если можно заполнять буфер при помощи DMA, а в прерывании лишь символ '\n' ожидать - конец строки? Ну и прерывание по DMA - если пошло переполнение буфера, чтобы флаг ошибки выставить… Даже в случае модбаса не нужно на каждый символ прерывание, достаточно по IDLE мониторить, ну и все то же DMA TC.
_________________ Windows must die! And the users of this crap should either become smarter or become janitors.
Я не против С++, но для его применения нужны веские основания. Это язык более высокого уровня, и парадигму ООП не натянуть на глобус железа микроконтроллера.
Для примера, берем микропрограммку на ассме для RP2350 PIO которая рулит дисплеем с 8-ми битной шиной:
Спойлер
Код:
static constexpr auto pioLcdPar8a = R"( .side_set 3 opt .out 8 left auto 32 .in 8 .define SetupTime 1 ; 1(200..300MHz) .define HoldTime 0 ; 0(200MHz), 1(300MHz)
mov pindirs,~null
.wrap_target loop: out x,16 side 0b111 ; RD/WR/DC jmp x--, cmd_data side 0b111 ; 0 - 16bit color, 1 - 8bit data, 2 - 8bit cmd out pins,8 side 0b101 [SetupTime] nop side 0b111 [HoldTime] out pins,8 side 0b101 [SetupTime] .wrap
cmd_data: jmp !x,if_data side 0b111 out pins,16 side 0b100 [SetupTime] jmp loop side 0b110
if_data: out pins,16 side 0b101 [SetupTime] jmp loop side 0b111 )"_ss;
А это парочка функций транслятора для нее:
Спойлер
Код:
// JMP (cond) target // cond: always, !x, x--, !y, y--, x != y, pin, !osre // target: instruction address to jump to. consteval bool processJmp() { uint16_t instr = 0x0000;
if (check("!")) // !x, !y, !osre { if (check("x")) instr |= 1 << 5; // !x else if (check("y")) instr |= 3 << 5; // !y else if (check("osre")) instr |= 7 << 5; // !osre else return Error("unexpected identifier ..."); check(","); } else if (check("x")) { if (check("--")) { instr |= 2 << 5; // x-- } else if (check("!=")) { if (check("y")) instr |= 5 << 5; // x != y else return Error("expect 'x != y'"); } else return Error("expect 'x--'"); check(","); } else if (check("y")) { if (check("--")) instr |= 4 << 5; // y-- else return Error("expect 'y--'"); check(","); } else if (check("pin")) { instr |= 6 << 5; // pin check(","); }
auto tok = getToken(); if (tok.isText()) { // Label auto* label = findLabel(tok); if (label) instr |= label->addr; else jmpPatch.push({ tok.str, (int)code.size, line }); } else if (tok.isNumber()) { instr |= tok.value; if (tok.value > maxJmpValue) { maxJmpValue = tok.value; maxJmpLine = line; } } else return Error("expect label or number.");
if (!sideSetOptional && sideSet && !hasSide) return Error("instruction requires 'side' to specify side set value.");
if (hasSide) { int value = matchNumber(0, powf(2, sideSet) - 1, "side set value is out of range."); if (error) return false; instr |= value << (8 + 5 - sideSet - sideSetOptional) | (sideSetOptional << 12); }
if (!check("[")) { code.push(instr); return true; }
int value = matchNumber(0, powf(2, 5 - sideSet - sideSetOptional) - 1, "instruction delay is out of range."); if (error) return false; code.push(instr | uint16_t(value << 8));
if (!match("]", "expect ']'.")) return false;
return true; }
Как видно никаких заумностей там нет, написано довольно прямолинейно, тем не менее этот код отрабатывает на стадии компиляции основной программы, кладет во флеш 22 байта инструкций PIO и помимо этого будут только сопутствующие данные в виде констант, никакого кода в прошивке не будет. Ну и сообщение об ошибке выдает при необходимости через static_assert, вместе со строкой в которой она произошла. Полный ребилд тестового проекта с менюшкой компилится за 2с. Это достаточно продвинутый пример, но он показывает насколько далеко C++ ушел от С, в том числе в плане оптимизаций. А так, конечно, можно писать на C++ как на ПК и будет все жирно и медленно )
Кому прикажете отдать прерывания, датчику или нитерфейсу управления?
Конечно датчику, это ведь его данные и его протокол.
VladislavS писал(а):
И зачем классам датчика и интефейсов управления знать про устройство контроллера прерываний?
Датчику знать о контроллере прерываний категорически не нужно, но ему нужно знать, что у объекта UART есть методы send() и receive(). Объекту UART нужно знать у объекта NVIC_t только только методы, подключающие его, UART'а, метод обработки прерывания.
Тут ООП отсутствует как класс, обработка прерываний приколочена гвоздями и размазана между несколькими функциями (не объектами даже!) и почти одинакова для всех UART, то есть код повторяется, а не повторно используется.
lubitel5, проверьте вариант с двойным вызовом прерывания. Во-первых, очистка флага неправильная. Сделайте просто TIM4->SR = 0; Во-вторых, поднимите команду выше по коду прерывания. Да вообще первой её поставьте.
PS: Команда GPIOC->BSRR |= GPIO_BSRR_BS13; тоже ошибка, если что. Регистр BSRR только для записи.
VladislavS спасибо.Я вот так писал :GPIOC->BSRR = GPIO_PIN_13; GPIOC->BSRR = (uint32_t)GPIO_PIN_13 << 16U;. Почему придумал GPIOC->BSRR |= GPIO_BSRR_BS13;- не знаю. А по флагу-да, перенес в самое начало и все заработало. Эту часть я перенес с STM32F407 - там все работает, я, помню, что здесь я пробовал очищать флаг и первым и последним, разницы не было. А в 103 есть.
На 103 это тоже не всегда проявляется. У вас просто шина APB1 очень замедлена. Пока по ней дойдёт сброс флага процессор успевает выйти из прерывания и застать его несброшенным.
Сейчас этот форум просматривают: нет зарегистрированных пользователей и гости: 22
Вы не можете начинать темы Вы не можете отвечать на сообщения Вы не можете редактировать свои сообщения Вы не можете удалять свои сообщения Вы не можете добавлять вложения