A code in HLL is highly readable and understandable.
A C code can be ported to different microcontroller architectures from a
variety of manufacturers with only a minimum number of modifications. Assembly is architecture-specific or worse, manufacturer-specific. C is easier to program in, compared to Assembly, especially for semi-skilled programmers. Smaller learning curve than assembly. Faster development times Being easier to use, C allows you to write programs faster. Generally these programs are also easier to debug and easier to maintain. Furthermore, it's easier to manage large, complex programs in C. C and high level programming languages in general have less rigid rules and syntaxes and so potential errors are reduced. C provides mechanisms for modular program design, such as encapsulation using classes and local scopes/local variables. This means that the C code is highly reusable and portable. HLLs especially OOP languages like C++ can implement inheritance, thus leading to smaller, efficient and more readable code. High level languages like C are highly abstract. A HLL separates the definition of a subprogram from a specific instance of its implementation. This leads to a highly clean, efficient, understandable and manageable code. A single instruction in HLL translates to many instructions in Assembly. As such, a HLL can be viewed as a CISC instruction set and Assembly as RISC. C has a standard library, plus an enormous amount of resources written during the past 30 years. C has plenty of pre-made functionality, libraries and resources, so there will be less re-inventing of the wheel. With development of modern advanced C compilers, the primary advantages of Assembly over C, speed and code size are becoming obsolete. Furthermore, memory is so cheap nowadays that it's not worth using Assembly just to save a bit of program space.
HLL and C/C++ lead to a large code with slow execution speed. Hence is applications where speed and/or program memory is of primary concern, Assembly may be preferred. HLL are less efficient than Assembly and require greater computer time and memory for translation into machine code, since Assembly is much closer to machine code than HLLs like C. These two are the biggest advantages of Assembly over HLLs. Things you can't do in C are accessing stack pointers, micro-registers, condition registers and system control registers etc, of the CPU core itself. So if by hardware programming you mean talking with your own CPU, then yes, assembler allows a bit more than C. Programming in Assembly helps the programmer understand the intricacies of the microcontroller core. The HLLs generally place the programmer at a higher functional layer and abstract the details of the controller's registers, stack pointers etc from the programmer. Thus learning and programming in Assembly helps to develop better and efficient programs, even in HLL. Assembly is used in the earliest stages of the bootloader. When the CPU powers on the stack isn't available, and it's tough to keep the C compiler from trying to save things to the stack. The bulk of the bootloader is nonetheless written in C, once the earliest initialization is done. C code is generally not suitable for code caching because of its large program space requirements. On the other hand, a small light Assembly code piece can be very efficiently cached. For example, a code optimizing C compiler many implement optimizations such as loop unrolling to increase execution speed but increases program size. These optimizations are beyond the control of the programmer and can vary dynamically.
Factors affecting choice Skill level of programmer Program memory size Efficiency of compiled code/Code optimization Presence of repetitive operations e.g. loops (Assembly is better if code caching is desirable, see above) Code caching Execution speed Legibility Portability needs Access to core registers, system control registers stack pointers etc. Program maintainability Typical bug rates (say, per thousand lines of code) Cost of the compiler and other development tools