Error handling in embedded systems development
Using enums for error codes is common practice. The whole purpose of using enum over for example bool is to get more information.

The pattern you suggest with a common "last error" handler is not recommended though. This has been tried historically many times, never with good results. Must infamously the Windows API GetLastError() function.

The problem with "last error" handlers are several: they are not thread/interrupt safe and they only remember the last error. If you set FW_ERROR_KEY from a function, from the time that happens until you print the error, another error could have happened. You then print the wrong reason and get the wrong diagnostic.

The common way to handle errors is instead rather something like this:

for(;;)
{
  kick_watchdog();

  result = state_machine[state]();

  if(result != OK)
  {
    error_handler(result);
  }
}


That's what the main loop looks like in many bare metal MCU projects. All errors are passed down from drivers up to the application layer, and error handling is centralized. 

The application layer might then make the call to ignore certain errors, or handle them, or replace their error codes with another code. Etc.