This tutorial explains BeRTOS' Hardware Abstraction Layer and how it fits into the development process.
Background
This section explains what a Hardware Abstraction Layer is and why it is necessary. Feel free to skip to the next section if you know already know the concepts.
BeRTOS supports many different architectures and drivers with a single API. This result is achieved using an abstraction over the physical hardware. A typical application using BeRTOS is composed of a few layers, as depicted below.
The Interface API is the entry point for BeRTOS' users. BeRTOS is committed to provide a stable API across minor releases, so that users can safely upgrade existing programs to get new feature and bugfixes.
It's a specific design decision to make the API hardware agnostic; this way, the API handles high level details only and it avoids irrelevant concepts. For example, the API for an SD card reader must provide only functions to initialize the card and read/write from it, but it must ignore which pins the card is connected to.
The intermediate level, that is the driver itself, must interface to the hardware. However, even this level must ignore the details of the underlying hardware connections, so that it's possible to use the same driver across different boards and devices. Some generic macros or functions are defined in places where it's necessary to access the CPU registers or pins.
BeRTOS HAL implementation
BeRTOS isolates every board-specific detail into hw_* files, which contain macros/functions that are used by higher level modules. These files are contained into the [project_name]/hw/ directory of your project.
As a BeRTOS user, you are required to implement the functions defined in the hw files for the drivers you are using. Each driver has its own file, named hw_[driver_name].h in the hw/ directory.
Hint: it's easy to see which hw files are actually used, because each unimplemented file in use raises a compile warning. You can implement the functions then remove the warning line.
Example
Let's say you want to use the keyboard driver (bertos/drv/kbd.h). The corresponding hw file is located in [your_project]/hw/hw_kbd.h.
The driver asks you to implement a few macros that are used inside the driver:
- KBD_HW_INIT
- kbd_readkeys()
- K_RPT_MASK
The macro KBD_HW_INIT is required to initialize the hardware. The function kbd_readkeys() is required to return the mask of pressed keys. The macro K_RPT_MASK is optional and indicates the keys where autorepeat is set.
A possible implementation on Atmel SAM7X may be:
// include IO register definitions
#include <io/arm.h>
#define KBD_HW_INIT \
do { \
PIOA_PER = (0xFFL << 21); \
PIOA_PUER = (0xFFL << 21); \
} while (0)
INLINE keymask_t kbd_readkeys(void)
{
return (((PIOA_PDSR >> 21) & 0xFF) ^ 0xFF);
}
The macro KBD_HW_INIT enables input from eight external pins (P22-P29) linked to PIO port A.
Then the function kbd_readkeys is used to read data from the same pins. Since we enabled only the pins P22-P29, we filter out unused data bits from the data register and then we find out which keys are pressed using the xor operator.
