Home > Mobile >  Bare metal vs API. What's the difference?
Bare metal vs API. What's the difference?

Time:03-17

I've recently started learning Embedded C and have been working with FRDMKL25Z microcontroller which is ARM based. Now for interfacing a LED using switch I've came across two types of coding methods:

Bare metal:

while(1) {
    if(GPIO_D->PDIR & (1<<4)) {
        /* Make the LED as OFF */
        GPIO_C->PSOR = (1 << 8);
    }
    else {
        /* Make the LED as ON */
        GPIO_C->PCOR = (1 << 8);
    }
}

API:

while(1) {
    if(GPIO_ReadPinInput(GPIOA, 12)) {
        GPIO_SetPinsOutput(GPIOC, (1<<9));
    }
    else {
        GPIO_ClearPinsOutput(GPIOC, (1 << 9));
    }
}

Now both of these codes do exactly the same thing but what's the difference between them and which one is better?

CodePudding user response:

Abstraction. If both snippets do the same thing (I'm just assuming here since I have no idea what the implementation of the GPIO functions are and what the register's description), then I would say that the only difference between them is that one is more readable than the other.
Imagine opening a new project and finding GPIO_C->PSOR. You'll have to find the document where the info about this register is found and see what it does. Most of the developers don't need/care about this sort of stuff (although they should know how to do it) so having an abstraction layer that covers all the "low level" part is recommended. So it's a good way to speed things up for people working on "higher levels" (drivers, application)
Plus, it's a good way to avoid mistakes.

CodePudding user response:

Some comments:

  • Please note that the term "bare metal" means running without an OS. You can have an API to hardware peripherals and still have a bare metal application.

  • For simple GPIO specifically, writing abstraction layers never ends up well. Hundreds of people have tried, all have failed (including yours sincerely). The former version is about as clear and readable as C gets, the latter version is just clutter and bloat.

    (AlsoCamelCaseIsHardToReadButThatsAnotherStory)

  • What isn't very clear in either version is what exactly these registers do. The PDIR line specifically, does the code read a data input pin or a data direction register? This isn't obvious and that should have be made clear by comments - the LED comments are good but the PDIR line needs one too.

    In case the MCU manufacturer thought it was a brilliant idea to name a data register PDIR:

    It was not a brilliant idea.

    But if this is a data direction register, then the name GPIO_ReadPinInput is horribly misleading, because then the line isn't reading the pin but the data direction setting.

  • As for the second example, why isn't it using bit mask shifts consistently? Magic number 12 means... what?

  • Also, both snippets have the super common shift bug: never write 1 << ... in C because that shifts a signed type. 1 << 15 (8/16 bit CPU) or 1 << 31 (32/64 bit CPU) is always an undefined behavior bug in C. Always use 1u suffix.

  • Regarding useful abstraction, you should do something like

    #define LED_PORT (GPIO_C->PSOR)
    #define LED_MASK (1u << 8)
    
    LED_PORT |= LED_MASK; 
    

    This allows easy re-routing of pins if needed.

  • In general, don't use magic numbers! Seriously. All of these magic numbers need to be placed behind named constants.

  • Related