Прерывания принадлежат не объекту, а контрполлеру прерываний и процессору
Получается, что контроллер прерываний должен сам обрабатывать десятки и сотни прерываний? Как данные от 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
Рейтинг сообщений: 215
Зарегистрирован: Вс дек 02, 2012 16:58:33 Сообщений: 950 Откуда: от туда
Рейтинг сообщения:1
Стремление написать всю программу для микроконтроллера на плюсах можно объяснить, но невозможно понять. Объяснить тем, что: "когда у тебя в руках молоток, все вокруг кажутся гвоздями". Это просто зуд кодописательства. Кесарю кесарево, а пекарю пекарево. Я работал и разбирался с проектами для микроконтроллеров, написанных на Си++21 с виртуальными и лямбда функциями, размером больше полутора гигабайт исходного кода и невозможностью развития. Эти проекты были выкинуты в мусорку и заменены нормальными исходниками на Си, с меньшим размером исходного и исполняемого кода и большим функционалом, поддерживающим развитие проекта. Я не против С++, но для его применения нужны веские основания. Это язык более высокого уровня, и парадигму ООП не натянуть на глобус железа микроконтроллера. Дело в том, что железо различных микроконтроллеров сильно отличается друг от друга даже у именитых производителей. Например, I2C контроллер STM32F103 и STM32F401. Для их поддержки нужны принципиально различные интерфейсы, и одним родительским классом эти интерфейсы не породишь. Похожее, но не менее амбициозное стремление STM сделать универсальный HAL также потерпело крах в виде уникальных библиотек для разных микроконтроллеров, почитайте про stm32hal_*_ex библиотеки. ИМХО нет ничего лучше сниппетов - заготовок для низкоуровневых работ не придумано. А языку высокого уровня оставьте свою работу, вроде работы с интернетом вещей, облачными сервисами, ГУИ и прочим.
Какое преимущество даёт обработчик виртуальным методом?
Кто хранит данные, тот их и обрабатывает. Инкапсуляция.
GARMIN писал(а):
нормальными исходниками на Си, с меньшим размером исходного и исполняемого кода
Статистика по проектам, которую собирали умные дядьки из профильных комитетов по стандартизации языков говорит об обратном. В С++ выше повторное использование кода, поэтому размер исполняемого кода меньше, чем у программ на С. Моя практика подтверждает это.
GARMIN писал(а):
не менее амбициозное стремление STM сделать универсальный HAL также потерпело крах в виде уникальных библиотек для разных микроконтроллеров, почитайте про stm32hal_*_ex библиотеки.
Я это давно знаю, поэтому везде говорю, что STM нужно было не лепить HAL для конкретного МК, а делать библиотеки для периферии, находящейся в МК. У STM есть таблица, в которой указаны МК и версии периферийных блоков в них. STM периферию покупает на стороне и собирает под крышей своих МК, поэтому логичней привязывать HAL именно к периферии.
Кто хранит данные, тот их и обрабатывает. Инкапсуляция.
Вот, из недавнего. На USART2 и USART3 висят одинковые интерфейсы управления. На LPUART1 датчик DUG51. Кому прикажете отдать прерывания, датчику или нитерфейсу управления? И зачем классам датчика и интефейсов управления знать про устройство контроллера прерываний? Спойлер
Заголовок сообщения: Re: Микроконтроллеры STM32 - тонкости работы, отладочные пла
Добавлено: Пн дек 01, 2025 16:17:11
Вымогатель припоя
Карма: 2
Рейтинг сообщений: 19
Зарегистрирован: Пн сен 15, 2025 08:43:23 Сообщений: 533 Откуда: Маленький СССР посреди шариатской республики
Рейтинг сообщения:0
VladislavS, но зачем побайтно принимать в прерываниях, если можно заполнять буфер при помощи DMA, а в прерывании лишь символ '\n' ожидать - конец строки? Ну и прерывание по DMA - если пошло переполнение буфера, чтобы флаг ошибки выставить… Даже в случае модбаса не нужно на каждый символ прерывание, достаточно по IDLE мониторить, ну и все то же DMA TC.
Я не против С++, но для его применения нужны веские основания. Это язык более высокого уровня, и парадигму ООП не натянуть на глобус железа микроконтроллера.
Для примера, берем микропрограммку на ассме для 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 очень замедлена. Пока по ней дойдёт сброс флага процессор успевает выйти из прерывания и застать его несброшенным.
Вы не можете начинать темы Вы не можете отвечать на сообщения Вы не можете редактировать свои сообщения Вы не можете удалять свои сообщения Вы не можете добавлять вложения