Renesas RA Flexible Software Package (FSP)

Published


Hi again! In the previous tutorial we installed the e2 studio IDE with the FSP to work with the RA MCUs. Now I’ll tell you in more detail about what exactly FSP is and how it simplifies the life of the programmers. You may put your development boards aside for now, as this tutorial will be theoretical. I know, that’s boring but some theoretical background won’t hurt.

So, FSP is the set of the code provided by Renesas and some third-parties that is specially designed and optimized to work with the RA microcontrollers. First, this means that you don’t have to use the FSP in your project and can operate directly with the registers, if you so desire. Though this approach will neglect the simplicity and low entrance threshold into the RA family, so Renesas strongly recommends you use it. Second, the FSP can be used only with the RA MCUs and with no others, even other MCUs produced by Renesas. Third, the FSP code is claimed to be optimized (and we will check later if this is true), thus it consumes very little memory and has high implementation speed.

The FSP consists of several layers. Let’s consider them using the image from the RA Family Beginner's Guide (Fig. 1)

FSP structure
Figure 1 - Structure of the FSP
  1. The basic layer of the FSP is called Board Support Package (BSP). This is the software customized for every hardware kit and microcontroller of the RA family. It provides the startup and the peripheral initialization code which guarantees that the current board will work without an issue. This means that this layer configures the clock, interrupts, stack, heap, and so on. Also, it configures the pins according to their connection on a certain board. The BSP also provides some functions to the user to be freely used in the application, which “include locking/unlocking the hardware and software, interrupt handling, registering callback functions and clearing flags, software delay and register protection” (RA Family Beginner's Guide).

  2. Above the BSP layer there is Hardware Abstraction Layer (HAL). It provides the drivers that work with certain peripheral modules, like ADC, UART, CAN etc. (see fig. 1). As you can assume from the name of the layer, it insulates the programmer from the hardware. You don’t need to read the data sheets and hardware user manuals to find how to initialize a peripheral module and work with it. You just use the simple API functions whose names are standardized. If you know the logic behind their names, you don’t even need to look at the documentation to find them. Moreover, the HAL drivers allow you to use the same API functions to operate with different hardware which implements the same things. For instance there is the serial communication interface (SCI) which can work in UART, SPI or I2C mode. And also there is the IIC interface which also can implement the I2C communication. So the HAL functions to operate with both interfaces will be the same, and you can switch between hardware freely (not during operation but during the code generation, which we will talk about later).

  3. The Middleware stacks and protocols. They provide API functions that simplify complex operations, like Ethernet, Bluetooth, USB communication, graphic processing, etc. Some of these functions can work without any operating system, and others require RTOS to work.

  4. FreeRTOS Real-Time Operating System. This is one of the most widespread operating systems for microcontrollers. The port of FreeRTOS for RA MCUs is available within the FSP and is included into the project by a single mouse click. FreeRTOS is very lightweight yet quite powerful. It provides a real-time kernel with different types of scheduling along with the means for threads synchronization and communication, like mutexes, semaphores, queues, message boxes etc. To use or not to use the RTOS in your project is the question that needs a separate investigation but for now you just need to know that including the FreeRTOS into the RA project is very simple.

Now, as we know which parts the FSP is composed of, it would be good to distinguish them when meeting them in the program. As I mentioned before, all the names of the API function of FSP are standardized. I think I’ll quote the RA Family Beginner's Guide here because they are described there very well:

“In general, internal functions follow the “NounNounVerb” naming convention, e.g. communicationAbort(). Any data is returned in the output argument of the function and the first parameter is always the pointer to its control structure. As this structure holds the memory of the module instance, the user application is flexible to decide where to place it.

Following is a list of commonly used prefixes in the FSP:

  • R_BSP_xxx: The prefix for a common BSP function, e.g. R_BSP_VersionGet().

  • BSP_xxx: The prefix for BSP macros, e.g. BSP_IO_LEVEL_LOW.

  • FSP_xxx: The prefix for common FSP defines, mostly error codes, e.g. FSP_ERR_INVALID_ARGUMENT and version information, e.g. FSP_VERSION_BUILD.

  • g_<interface>_on_<instance>: The name of the constant global structure of an instance that ties the interface API functions to the functions provided by the module. One example would be g_spi_on_spi for a HAL layer function.

  • r_<interface>_api.h: The name of an interface module header file, e.g. r_spi_api.h.

  • R_<MODULE>_<Function>: The name of an FSP driver API, e.g. R_SPI_WriteRead.

  • RM_<MODULE>_<Function>: The name of a middleware function, e.g. RM_BLE_ABS_Open().

The FreeRTOS™ operating system also follows a naming convention. File scope static functions for example have the prefix prv. API functions adhere to the scheme <return type><Filename><Operation> where the return type follows the convention for variables, or in case a function is void, it is prefixed with the letter “v”. The File field contains the name of the file in which the API function is defined, and the Operation field names the operation being executed in UpperCamelCase syntax. Examples are vTaskStartScheduler(void) which is a void function that is defined in tasks.c and starts the RTOS scheduler, or uxQueueMessagesWaiting(), a function which returns data of the non-standard type unsigned BaseType_t, is defined in the file queue.c, and returns the number of messages waiting in a queue.”

So, as you can see, it seems all API functions can be easily distinguished by their prefixes and structure. To see if that’s actually the case, let’s try to consider the code from the Tutorial 2 which was generated to implement the LED blink functionality (Fig. 2)

Blinky code
Figure 2 - Blinky Code

I will try to explain the code without looking into any manuals, just based on the functions and variables names, and we’ll see if that’s possible.

In line 21 we include some “hal_data.h” file. That one seems to be auto generated by the HAL layer and may consist of some function definitions.

Line 23 has the function R_BSP_WarmStart (bsp_warm_start_event_t event) which, according to the list above belongs to the general BSP layer. According to its name it’s called at startup and handles some events. And the comments in lines 100-105 confirm my guess. The function itself is implemented in lines 106-128. Actually, the comments in it are quite clear, and also it seems like we don’t need to do anything in this function, it just exists and does what it has to do.

Let’s return to the line 33 where the hal_entry() function starts. According to the name of the function, it’s the entry point of the HAL-based application. But why not main()? I will answer this question next time when we consider working in e2 studio in more detail. Anyway, it looks like this is the main function of the program.

Lines 35-39 are grayed, which means they are not used in the current build, so we’ll skip them for now. They seem to be used if we select the TrustZone application during project creation (see Tutorial 2).

In line 42 there is a definition

const bsp_delay_units_t bsp_delay_units = BSP_DELAY_UNITS_MILLISECONDS;

And again, according to the list above the BSP_DELAY_UNITS_MILLISECONDS is the BSP macros, and according to its name it means that the delay units are milliseconds.

In lines 45 and 48 we declare two more constants:

const uint32_t freq_in_hz = 2;

const uint32_t delay = bsp_delay_units / freq_in_hz;

The comments above them are quite clear and don’t need additional explanation.

In line 51 these is more interesting declaration:

bsp_leds_t leds = g_bsp_leds;

bsp_leds_t looks like the name of some BSP-related structure while g_bsp_leds seems to be a global constant that consists of information about all LEDs of this specific board.

In lines 54-60 we check if the number of LEDs is not 0. And if it is then we just hang there. From this part we now know that the bsp_leds_t structure has the member led_count which is the number of the available LEDs.

In line 63 we declare another variable:

bsp_io_level_t pin_level = BSP_IO_LEVEL_LOW;

Now it’s already easy to understand. bsp_io_level_t is the structure that defines the possible pin levels, supposively high and low. And BSP_IO_LEVEL_LOW is the BSP macro which corresponds to the low level of the pin.

In lines 65-97 there seems to be the main loop of the program.

In line 70 the function R_BSP_PinAccessEnable() is invoked. So this one belongs to the common BSP functions and enables access to the PFS registers (as follows from the comment in lines 67-69). Well, this function is not clear for me for now. I mean, I understand that it grants access to some IO-related registers, but why do we need to enable and disable it every time we work with the IO pins. This is something I didn’t see in any microcontrollers before, and that’s why it seems odd to me. So at least this line leaves some questions which can’t be answered from the program code.

In lines 73-80 there is a loop to set the state of all the LEDs on the board.

In line 76 there is a definition of the pin variable:

uint32_t pin = leds.p_leds[i];

Aha, there is another member of the bsp_leds_t structure called p_leds. Looks like it consists of the number of the MCU pin to which the corresponding LED is connected.

In line 79 there is the function R_BSP_PinWrite((bsp_io_port_pin_t) pin, pin_level) which is also very clear from its name and parameters structure. This one should write the new state (which is set as the second parameter) to the pin, whose number is set as the first parameter.

In line 83 the R_BSP_PinAccessDisable() function is invoked. According to its name it does the opposite to the R_BSP_PinAccessEnable() function, which is disabling the access to the PFS registers (whatever they are).

In lines 86-93 the new state of the pin is defined. Not a very elegant solution but easy to read and understand. And from this part we can see the second macro that defines the high level of the pin - BSP_IO_LEVEL_HIGH. Actually that was very predictable, and even if I didn’t see this part of code I’d guess the correct macro name.

And, finally, in line 96 there is a delay function R_BSP_SoftwareDelay(delay, bsp_delay_units), which seems interesting in comparison to other delay functions I’ve seen before. It’s an interesting solution to set both value and the units in one function. As you remember, we defined the bsp_delay_units as BSP_DELAY_UNITS_MILLISECONDS. I’m pretty sure there should be other similar macros like BSP_DELAY_UNITS_MICROSECONDS or BSP_DELAY_UNITS_SECONDS.

Well, it’s cheating but I’ve looked at the declaration of the BSP_DELAY_UNITS_MILLISECONDS macro and found the following:

typedef enum

{

BSP_DELAY_UNITS_SECONDS = 1000000, ///< Requested delay amount is in seconds

BSP_DELAY_UNITS_MILLISECONDS = 1000, ///< Requested delay amount is in milliseconds

BSP_DELAY_UNITS_MICROSECONDS = 1 ///< Requested delay amount is in microseconds

} bsp_delay_units_t;

So I was totally right! And that really is awesome! Well, not that I am right but that one can guess the other options available in the FSP API and use them without even looking into the documentation. That really makes things easier!

I don’t know if my excitement will last long when I get more familiar with the RA family but so far they seem to have quite a low entrance threshold.

And that’s all for now. The next time I’ll tell in more detail about the e2 studio interface and features, and also about the FSP visual configurator which is supposed to be one of the main features of the RA family.

Make Bread with our CircuitBread Toaster!

Get the latest tools and tutorials, fresh from the toaster.

What are you looking for?