[null, {"document_url": "http://microchipdeveloper.com/harmony:intro-configurability", "unformatted_src": "https://microchip.wdfiles.com/local--files/harmony:intro-configurability/parts.jpg", "src": "https://microchip.wdfiles.com/local--files/harmony:intro-configurability/parts.jpg", "formatted_filename": "parts", "alt_text": "parts.jpg", "rendered_width": 200}, null, {"document_url": "http://microchipdeveloper.com/harmony:intro-configurability", "unformatted_src": "https://microchip.wdfiles.com/local--files/harmony:intro-configurability/config.jpg", "src": "https://microchip.wdfiles.com/local--files/harmony:intro-configurability/config.jpg", "formatted_filename": "config", "alt_text": "config.jpg", "rendered_width": 200}, null]
MPLAB® Harmony Configurability

Embedded Software Parts

An embedded application can be broken down into a number of different sections: 
Segmenting Code to Enable Configurability

Embedded code can be segmented into parts to enable configurability. First, let’s factor out the build-time configuration constants. If we do that, we can have different build configurations for the same project. Examples of different build configurations might include:

Maintaining separate configurations can be as simple as maintaining different configuration files and selecting the ones you want before you build the application. MPLAB® X Integrated Development Environment (IDE) provides an easy way to do this using project configurations. You can think of a project configuration as a project within a project. Each project configuration can exclude specific files from the build process. You can even choose different devices and use different compiler versions and optimizations between configurations.

If we also factor out the peripheral and system support into libraries that we at Microchip can implement ahead of time, then your application only has to implement the logic for the desired application behavior. However, since we can't know ahead of time which libraries, peripherals and other hardware you may want to use, there will always have to be some code that needs to be implemented as part of the specific configuration.

This allows Microchip to write peripheral, middleware, and any number of other libraries that make your life easier without having to know the specific processor, peripherals or configuration of libraries that you plan to use. It also allows you to:

System Configuration Files

So, what we end up with is a set of files that make up a specific configuration of a system or application. The top-level main function can be extremely simple, and both it and your application logic can stay exactly the same from one configuration to another. If you want, you can have multiple configurations that use the same basic application logic. Some of Microchip’s demos will do just that. Conversely, you can have only one single configuration. It’s completely up to you. However, one benefit of factoring out the configuration-specific code like this is that Microchip can provide helpful development tools that make it easier to create the configuration that is right for you. 
You might also have noticed that we snuck in one more way that your system logic can be driven — by interrupts (instead of just being polled). But, since different processor families implement their interrupt vectors (their “raw” ISR functions) in sometimes wildly different ways, these ISRs cannot easily be implemented in libraries without requiring different libraries for different part families (even if the peripheral module is exactly the same). So, the raw ISR functions must be implemented as part of the system configuration in a file called system_interrupt.c. However, the “raw” ISR stub can call a library routine that implements the actual logic necessary to make the peripheral run correctly.

System Configuration Example

Here is a brief example of what a set of system configuration files might look like:

When the program is built, any static configuration items are defined in system_config.h and all library and application code includes the same build options.

After the system starts running, a very simple main function calls a system-wide initialization routine, SYS_Initialize, which is implemented in system_init.c as part of the configuration definition. The SYS_Initialize function must call the initialization routine for every library or application module in the system.

After that, the main function simply drops into the usual system-wide “super” loop and calls a system-wide “tasks” function, SYS_Tasks, which must also be implemented in system_tasks.c as part of the system’s configuration definition. The SYS_Task function must call any polled library or application code necessary to keep the entire system running.

If the system has any interrupt-driven code, the “raw” interrupt vector stubs are implemented in system_interrupt.c as part of the system configuration. Each vector stub must then either implement or call the appropriate library’s interrupt routine.

Since all library or application code can be isolated from all configuration code, neither the libraries nor the application(s) have to change in order to change a system configuration. That configuration can include static build options, initialization code (and thus, the choice of which libraries or apps are initialized), and any polled or interrupt-driven libraries or apps chosen to run in the system, as well as which processor and board is used. Thus, the system is completely configurable.


Click on the video title to watch the video on YouTube in full-screen mode.
