ODeV framework
v2
|
This section aim to be a step by step guide to explain how to build an application using the ODeV framework. As example we will reuse the simple demo application delivered with the framework.
Let's start giving a look at the folder organization of the framework as displayed in Fig.19.
There is a place for everything. A new component like a library can be be put inside the system
folder. Services and drivers provided by the application goes inside the system\xxx\services
and system\xxx\drivers
folder [1]. An example are the NucleoDriver.h and NucleoDriver.c files. Generic application files as well as the files implementing the managed tasks of the application can be put inside the include
and the src
folders.
The folder organization respects the application design, that is a soft real time and event driven system designed in multiple (three) layers. So, if you think to a managed task as a tip of a subsystem that uses services and drivers, then Fig.24 is easy to understand, and it displays the relationship between the system layer.
The map between folders and layer is displayed in the following table:
Layer | Folders |
---|---|
App | include, src |
3 | system/xxx/services |
2 | system/xxx/drivers |
1 | system/xxx/cmsis, system/xxx/freertos, system/xxx/stn32-hal |
On the side of the three layers (see Fig.1) there are few common components used across all system layers. These export some interfaces and implement common design patterns in order to decouple the application code from the algorithms implemented in the framework.
The application must provide few files expected by the framework. It is also possible to modify and reuse the ones provided with the simple demo. Basically these are configuration header files. The following list gives a short description of these files. For more details look at the file documentation:
Other than the files generated by CubeMx, in the above list there are some files with a _mx suffix. Those files contains the copy&paste of functions definitions generated by CubeMX.
The CubeMX project used for the simple demo can be downloaded from this link.
In an ODeV based application the main() function and main.c file are provided by the framework and they are not intended to be modified by the developer. The developer needs another file where to define the application entry points in order to integrate the application code with the framework. In the simple demo this is done in the App.c file. In this file are defined two functions.
The first one, SysLoadApplicationContext(), is called by the INIT task after the basic hardware resources (except the ones used by the managed tasks) have been initialized, as well as the AED [2] and APMH [3] objects have been allocated and initialized. We use this function to instantiate the managed tasks, and to add them to the ApplicationContext object passed as a parameter. To add a managed task to the ApplicationContext we use the ACAddTask() function. In this way the INIT task is aware of all the application managed tasks.
The second function redefined in the App.c file is the SysOnStartApplication(). This function is used to connect the managed tasks. In the sample demo case, both managed tasks use the same driver object of type NucleoDriver. This function is called by the INIT task after all managed task have been initialized, so it is safe to assume that the driver has been allocated. Note that to share the driver it is used a pointer to the the base interface IDriver, but we know that actual type of that object is NucleoDriver.
If you remember Fig.21 the INIT task works with two other objects in order to implement the features of a real application. The simple demo does not use any error management strategy, so it does not need to implement the IApplicationErrorDelegate interface. For this reason the demo uses the default NullErrorDelegate. A more complex application, on the other side, has to deal with error. In this case the App.c file is a good place to redefine the SysGetErrorDelegate() function in order to provide an application specific implementation of the AED.
In the same way, it is possible to redefine the SysGetPowerModeHelper() function and provide an application specific APMH object.
The simple demo implements two Managed tasks in order to blink the LED and detect the push button events. At the beginning of the App.c file we can see how the task are declared and added to the ApplicationContext:
// Application managed task. static AManagedTask *s_pxHelloWorldObj = NULL; // Application managed task. static AManagedTaskEx *s_pxPushButtonObj = NULL; sys_error_code_t SysLoadApplicationContext(ApplicationContext *pAppContext) { assert_param(pAppContext); sys_error_code_t xRes = SYS_NO_ERROR_CODE; // Allocate the task objects s_pxHelloWorldObj = HelloWorldTaskAlloc(); s_pxPushButtonObj = PushButtonTaskAlloc(); // Add the task object to the context. xRes = ACAddTask(pAppContext, s_pxHelloWorldObj); xRes = ACAddTask(pAppContext, (AManagedTask*)s_pxPushButtonObj); return xRes; }
Let's give a look to the HelloWorldTask to see how it is implemented. The framework takes some concept from the OO programming [4]. In particular it is widely used the inheritance implemented with the composition of the data structure and the polymorphism implemented with the virtual table.
To implement a Managed task we need three files:
It is possible to use the ODeV template to generate the skeleton for these files.
TO BE CONTINUED ...
[1] xxx stands for src
or include
.
[2] AED is the Application Error Delegate. For more information see the section Advanced support.
[3] APMH is the Application Power Mode Helper. For more information see the section Power Management.
[4] OO stands for Object-oriented programming.