MPLAB® Harmony Configurability

Embedded Software Parts

An embedded application can be broken down into a number of different sections:

  • Build Configuration Options are static definitions that select fixed parameters and selections that are known at build time. These items can be captured in one or more configuration files.
  • There is always a certain amount of code required to initialize the peripherals and the overall system. This code will be different depending on which processor, board, and libraries are used. Likewise, you will probably need to initialize your application logic.
  • Finally, the application, peripherals, and system level logic that defines the behavior of your overall system must be developed.
Click image to enlarge.

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:

  • Engineering prototype configuration (includes debug code)
  • Production configuration
  • Full featured (expensive) product version
  • Cost reduced product version

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:

  • minimize the amount of code that you must implement
  • focus on your application code instead of the processor peripherals

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.


  • Defines all static build options
  • Is included by all libraries


  • Processor configuration bits
  • Initializes all libraries & applications


  • Calls all polled system logic
  • Maintains system state


  • Implements all interrupt vector stubs
  • Calls all Interrupt Service Routines (ISRs)
Click image to enlarge.

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.

© 2018 Microchip Technology, Inc.
Notice: ARM and Cortex are the registered trademarks of ARM Limited in the EU and other countries.
Information contained on this site regarding device applications and the like is provided only for your convenience and may be superseded by updates. It is your responsibility to ensure that your application meets with your specifications. MICROCHIP MAKES NO REPRESENTATIONS OR WARRANTIES OF ANY KIND WHETHER EXPRESS OR IMPLIED, WRITTEN OR ORAL, STATUTORY OR OTHERWISE, RELATED TO THE INFORMATION, INCLUDING BUT NOT LIMITED TO ITS CONDITION, QUALITY, PERFORMANCE, MERCHANTABILITY OR FITNESS FOR PURPOSE. Microchip disclaims all liability arising from this information and its use. Use of Microchip devices in life support and/or safety applications is entirely at the buyer's risk, and the buyer agrees to defend, indemnify and hold harmless Microchip from any and all damages, claims, suits, or expenses resulting from such use. No licenses are conveyed, implicitly or otherwise, under any Microchip intellectual property rights.