> just hide the register bits in a private TU and expose i2c_init(), i2c_write(), etc.
That's not a zero cost abstraction, unless these "functions" are actually provided in an include file. Embedded programming is often performance sensitive, so this can matter.
It’s a chore because the alternative in C is to do things perfectly without the machine doubling checking your work. That assumption does not hold with tools other than C.
But C has no problems abstracting away peripherals, just hide the register bits in a private TU and expose i2c_init(), i2c_write(), etc.