And what most people need is the quickest easiest solution that gets the job done - which is why they use libraries.
In embedded, they often are unusable as is due to the highly hardware-dependent implementation; sidestepping this issue requires extremely good library design skills, rarely seen.
Indeed, software developers like to write generic solutions for problems, especially when it can be platform independent. This has brought us good things, like programming languages such as C, but perhaps also bad things, like overgeneralized solutions. Matching this with hardware often doesn't make sense, because a few examples:
1) Not every microcontroller pin is connected to the analog mux for ADCs or "output" PWM. Some libraries imply every pin can be read analog. See further.
2) More specific example: on STM32F4 not every DMA stream/channel is mapable to any request, and even multiple peripherals share the same channel, thus blocking some multi-DMA setups from working all together. Does a software library assert these limitations and perform preferably compile time checks on this? Is C expressive enough for these static checks? Woops!
Example on 1): take Arduino libraries. These are often praised for throwing together a quick prototype. Let's look at analogWrite:
https://www.arduino.cc/reference/en/language/functions/analog-io/analogwrite/The "contract" for this function, at first glance, seems to be relatively simple:
Parameters
pin: the pin to write to. Allowed data types: int.
value: the duty cycle: between 0 (always off) and 255 (always on). Allowed data types: int
Returns
nothing
But then see the following mess:
On most Arduino boards (those with the ATmega168 or ATmega328P), this function works on pins 3, 5, 6, 9, 10, and 11. On the Arduino Mega, it works on pins 2 - 13 and 44 - 46. Older Arduino boards with an ATmega8 only support analogWrite() on pins 9, 10, and 11.
The Arduino DUE supports analogWrite() on pins 2 through 13, plus pins DAC0 and DAC1. Unlike the PWM pins, DAC0 and DAC1 are Digital to Analog converters, and act as true analog outputs.
You do not need to call pinMode() to set the pin as an output before calling analogWrite().
Makes me wonder, what actually happens if you chose an invalid pin? This could happen because you upgraded from a Mega to a Due board.. The documentation doesn't say much.. well actually this happens:
https://github.com/arduino/Arduino/blob/master/hardware/arduino/avr/cores/arduino/wiring_analog.c#L284Great 1-bit analog output. In pragmatic ways; yes this is the best one can do. But this kind of undefined internal behaviour can drive a programmer crazy.
Not to say some of the side effects functions can have, e.g. this forces the I/O into "output" mode. What happens if you actually had set it up as an open-drain PWM? Is that now disabled?
If you look at e.g. CMSIS libraries; there was a question if these can be written yourself. Yes they can. It's actually not that much more complex than on a PIC microcontroller; it's just there is a bit
more to do. But I don't define that as immediately "complex".
That is because these libraries internally contain almost no state whatsoever. Their functions are just 'convenient' functions to use peripherals, if they contain no spelling mistakes, errors, abuse of floats, etc. (which in my experience is not uncommon, no vendor excluded)
Maybe convenient is not the right word, let me rephrase: they contain some 'mechanical' operations to push a set of values into registers. In many cases you can still mix flags between peripherals (like interrupt flags); as long as the "bits" it's accepted. They are not dummy proof.
When you're designing a system that uses peripherals in a clever way, like a combination of UARTs, SPIs, timers, DMA, interrupts and clock control, you may dive deep into datasheets to find out what operation modes each peripheral should run. While you're at this level, you might as well push these settings into registers yourself.. it's not that much effort anymore.
And if you're really uncomfortable how a peripheral works, there is always a cheat sheet.. namely github.com and CMSIS itself. But don't take any piece of code by heart. Think critical about design decisions. Sure a floating point type will work for baud rate calculation. Maybe it was the easiest solution to a fractional baud rate division calculator. But alternative algorithms exist.
And perhaps you don't even require these run-time calculations of baud rate.. just punch your clocks into a calculator and hard code the numbers. If you're quite pragmatic and worry about documenting your chosen values, this can work reasonably well..