I do. But talking about low-level embedded stuff here.
Generally, the more you deviate from your vendors "happy path", the more busy work/unexpected difficulties you will run into, and a solid grasp of how exactly architecture and toolchain work might become necessary (while staying on the "happy path" allows you to stay blissfully unaware).
I struggle with this deviating from the vendor's "happy path" often. I mostly use the STM32 chips, and I don't particularly care for their HAL library. I find it over complicated and often has bugs in it that I have to track down and fix. But boy is it nice to use their STM32CubeMX program to generate all the low level code so I can just get to work. I tend to end up building my own low level libraries during my free time because I enjoy it and it gives me a better idea of how the hardware is actually working, but using the STM32 HAL library to write my actual client code at work.
Same experience here. What worked for me was using CubeMX purely for pin and clock config, then dropping down to the LL (low-layer) drivers or direct CMSIS register access for anything in a hot path. The HAL interrupt handlers in particular add a surprising amount of overhead — on a tight DMA transfer loop I measured ~40% cycle waste just from HAL callback dispatch.
The LL API is basically thin inline wrappers around register writes, so you still get the CubeMX-generated init code but without the HAL abstraction tax at runtime.
+1 to this and your above points (the embedded team I'm on has started using C++ over the last year or so).
I've definitely learned a lot, and I like the portability of CMake for cross-platform use (our team uses all 3 of Windows, Mac, and Linux). My experience sounds much like yours: there've been a lot of times where using the vendor's flavor of Eclipse-based IDE (STM32CubeIDE, Renesas e2studio, etc) would have saved us a lot of discovered work, or extra work adapting the "happy path" to CMake and/or C++.
Using C++ and/or CMake is fine when it's part of the happy path and for simpler things e.g. STM32CubeMX-generated CMake project + simple usage of HAL. For more complex things like including MCUboot or SBSFU, etc, it's forced me to really dig in. Or even just including FreeRTOS/ThreadX, we've created abstractions like a Thread class on top -- sometimes it's nice and convenient, others it feels like unnecessary complexity, but maybe I'm just not used to C++ yet.
One clear, simple example is needing to figure out CMake and Ninja install. In an Eclipse-based IDE, you install the platform and everything Just Works(tm). I eventually landed on using scoop to install CMake and Ninja, which was an easy solution and didn't require editing my PATH, etc, but that wasn't the first thing I tried.
I have never seen any advantage of CMake over the much simpler GNU make.
Ninja is supposed to be faster for compiling very big software projects, but I have never seen an embedded software project that is well organized and which is not compiled in a few seconds on a powerful development computer with many cores, so I do not see the benefit of Ninja for such projects.
All Eclipse-based IDEs that I have ever seen are extremely slow for anything, both for editing and for building a project and they make the simplest project management operations extremely complicated. Even Visual Code Studio is much faster and more convenient than using Eclipse-based IDEs. Other solutions can be much faster.
While the example programs provided for STM32 MCUs are extremely useful for starting a project for them, I believe that using the methods of project building provided by the vendor results in a waste of time. I have always obtained better results and faster development by building a GNU toolchain (e.g. binutils,gcc,gdb, some libc) from scratch and by using universal GNU makefiles, which work for any CPU target and for any software project, with the customization of a few Make variables. I have written once a set of GNU Makefiles, according to its manual, around 1998, and I have never had to change them since then, regardless what platform I had as a target. For any new platform, there is just a small set of variables that must be changed by generating one per-platform included file, with things like the names of the compilers and other tools that must be invoked and their command-line options.
For new projects, there is one very small file that must be generated for each binary file that must be built, which must contain the type of the file (e.g. executable, static library, shared library) and a list with one or more directories where source files should be searched. No changes are needed when source files are created, deleted, moved or renamed, and dependencies are identified automatically. I am always astonished when I see how many totally unnecessary complications exist in the majority of the project building configurations that I have ever seen provided by the vendors or in most open-source projects.
I do. But talking about low-level embedded stuff here.
Generally, the more you deviate from your vendors "happy path", the more busy work/unexpected difficulties you will run into, and a solid grasp of how exactly architecture and toolchain work might become necessary (while staying on the "happy path" allows you to stay blissfully unaware).
I struggle with this deviating from the vendor's "happy path" often. I mostly use the STM32 chips, and I don't particularly care for their HAL library. I find it over complicated and often has bugs in it that I have to track down and fix. But boy is it nice to use their STM32CubeMX program to generate all the low level code so I can just get to work. I tend to end up building my own low level libraries during my free time because I enjoy it and it gives me a better idea of how the hardware is actually working, but using the STM32 HAL library to write my actual client code at work.
Same experience here. What worked for me was using CubeMX purely for pin and clock config, then dropping down to the LL (low-layer) drivers or direct CMSIS register access for anything in a hot path. The HAL interrupt handlers in particular add a surprising amount of overhead — on a tight DMA transfer loop I measured ~40% cycle waste just from HAL callback dispatch.
The LL API is basically thin inline wrappers around register writes, so you still get the CubeMX-generated init code but without the HAL abstraction tax at runtime.
+1 to this and your above points (the embedded team I'm on has started using C++ over the last year or so).
I've definitely learned a lot, and I like the portability of CMake for cross-platform use (our team uses all 3 of Windows, Mac, and Linux). My experience sounds much like yours: there've been a lot of times where using the vendor's flavor of Eclipse-based IDE (STM32CubeIDE, Renesas e2studio, etc) would have saved us a lot of discovered work, or extra work adapting the "happy path" to CMake and/or C++.
Using C++ and/or CMake is fine when it's part of the happy path and for simpler things e.g. STM32CubeMX-generated CMake project + simple usage of HAL. For more complex things like including MCUboot or SBSFU, etc, it's forced me to really dig in. Or even just including FreeRTOS/ThreadX, we've created abstractions like a Thread class on top -- sometimes it's nice and convenient, others it feels like unnecessary complexity, but maybe I'm just not used to C++ yet.
One clear, simple example is needing to figure out CMake and Ninja install. In an Eclipse-based IDE, you install the platform and everything Just Works(tm). I eventually landed on using scoop to install CMake and Ninja, which was an easy solution and didn't require editing my PATH, etc, but that wasn't the first thing I tried.
I have never seen any advantage of CMake over the much simpler GNU make.
Ninja is supposed to be faster for compiling very big software projects, but I have never seen an embedded software project that is well organized and which is not compiled in a few seconds on a powerful development computer with many cores, so I do not see the benefit of Ninja for such projects.
All Eclipse-based IDEs that I have ever seen are extremely slow for anything, both for editing and for building a project and they make the simplest project management operations extremely complicated. Even Visual Code Studio is much faster and more convenient than using Eclipse-based IDEs. Other solutions can be much faster.
While the example programs provided for STM32 MCUs are extremely useful for starting a project for them, I believe that using the methods of project building provided by the vendor results in a waste of time. I have always obtained better results and faster development by building a GNU toolchain (e.g. binutils,gcc,gdb, some libc) from scratch and by using universal GNU makefiles, which work for any CPU target and for any software project, with the customization of a few Make variables. I have written once a set of GNU Makefiles, according to its manual, around 1998, and I have never had to change them since then, regardless what platform I had as a target. For any new platform, there is just a small set of variables that must be changed by generating one per-platform included file, with things like the names of the compilers and other tools that must be invoked and their command-line options.
For new projects, there is one very small file that must be generated for each binary file that must be built, which must contain the type of the file (e.g. executable, static library, shared library) and a list with one or more directories where source files should be searched. No changes are needed when source files are created, deleted, moved or renamed, and dependencies are identified automatically. I am always astonished when I see how many totally unnecessary complications exist in the majority of the project building configurations that I have ever seen provided by the vendors or in most open-source projects.