When Microchip first released their PIC32MX parts they also provided a HAL library that they called the ‘PLIB” or “Peripherals Library” to help users along.
For instance to setup a PIC32MX SPI clock using the original PLIB implementation one would need to call this function,
SpiChnSetBrg(chn, brg);
This is still better than having to dig into the data sheet to determine all the register names and addresses directly, but it isn't all that friendly either and you still need intimate knowledge of the hardware to set the ‘brg’ register to get the proper SPI clock rate.
After this initial PLIB library was released Microchip correctly realized that the 32 Bit Microprocessor market is a commodity market and that they would have to differentiate themselves in some way. Since the hardware is a commodity, they chose to differentiate themselves with the software layer.
They named this new software “Harmony’ and discontinued the original PLIB concept going forward.
In Microchips marketing literature they touted Harmony as the “Do all, end all Framework” as it encompasses not only configuring the chips peripheral devices but includes a lot of higher level system functions like: File Systems, Graphics, Bluetooth and RTOS support to name a few, all under one roof.
Confusion Reigns, sowing Dis-Harmony
This has all caused some confusion as many people now think that to use Harmony means: Bringing in a lot of ‘Framework Baggage’ - this is simply not the case and since Microchip has not sought to clarify this, the rants about Harmony being just a lot of ‘Framework Baggage’ continue unabated.
What Microchip needed to do was write a simple application note describing how to use Harmony as a simple chip configurator or how to use harmony in ‘Bare Metal’ mode. Harmony is a decent chip configurator, about the same as everyone else’s and it is way better than resorting to the ‘Assembly Language’ approach of writing all the peripheral libraries yourself from register maps.
So I will take it upon myself to write a brief tutorial on what Microchip should have done some time ago to clarify the situation.
Note: This tutorial is based on Harmony 1.4x and 2.0x, Harmony 3 is still being flushed out, but I would expect it will follow that same basic principles as Harmony 1 and 2. Currently (July 2019) the first release of Harmony 3.0 just adds support for the Atmel ARM Chips.
The Missing Application Note: “Using Harmony as a Chip Configurator in Bare Metal Mode”
Using Microchip Harmony as a chip configurator will allow speedy configuration of nearly all of the peripherals in a PIC32 microprocessor and also stub out an application that will at the very minimum will get the chip up and running. Harmony will also setup the proper header paths so that the current High Level PLIB commands correctly target your selected chip. Another advantage of using harmony is in maintenance and updating the application later, since all the links in the libraries will be correctly kept up to date as the chip configuration or even the chip itself changes.
Prerequisites: You should be familiar with both of these Microchip provided tutorials,
“MPLAB Harmony Tutorial – Creating Your First Project”
“Creating a “Hello World” application Using the MPLAB Harmony Configurator”
These tutorials will help you in figuring out the steps required in operating the Harmony Configurator IDE and as with every manufacturers configuration solutions, there is at least a days worth of learning curve that needs to be overcome before you can be proficient and get off on your own. The tutorials above will help you through that first day learning process [1].
We will only be discussing the proper setup of the peripherals and how to get the generated application to “Bare Metal Mode” in this note using the SPI as an example.
Setting Up the Clock and IO Pins
Follow the procedure in the tutorials listed above to set the Clock and IO pins. There is no difference between the “Bare Metal Mode” described here and the procedure as described in the tutorials.
Setting Up and Using Peripherals
Microchip provides two types of drivers in Harmony: “Static” and “Dynamic”. Refer to Reference [2] for a discussion of how each driver type works. We will be focused on “Static” drivers here as they provide the expected “Bare Metal” performance with no additional software overhead required.
SPI Example
A basic “Bare Metal” setup would be for a plain old SPI configuration, just “Point and Shoot” with no interrupts, FIFO’s or external buffers, etc. As you gain experience you can add those later if you wish either by yourself or with Harmony’s help. Configuring most peripherals in Harmony is done in 3 easy steps.
Step 1 - Expand the “Drivers” section as shown below
Step 2 - Expand the SPI section as below,
The settings shown above will be the simplest possible. For instance,
Standard Buffer: Not all PIC32’s have an enhanced (or FIFO) buffer, so using the standard buffer (or FIFO disabled) is safest.
8/16/32 bit Mode: Set this to what your SPI Devices expect as far as bit width. This parameter is easily changed on the fly using other PLIB_SPI commands.
Number of SPI Instances: Set this to the number of SPI peripherals you will be using. Here we will just be using one.
Step 3 - Now you have to configure one or more of the actual SPI hardware peripherals of the chip as shown below,
Here “Instance 0” is set to the Physical Hardware SPI peripheral: “SPI_ID_1”. Most of the other information is as described in the data sheet. Of note: it is nice to be able to set the SPI Clock rate and not have to worry about the frequency setting registers directly. Also of note, just because I set in this example 1 MHz, it does not mean that the SPI clock will end up being exactly 1 MHz. The configurator will simply pick the closest register settings that get closest to 1 MHz. If you want to know exactly the clock rate you can find the clock rate equation on the parts data sheet and calculate what it will exactly be from that.
For the “Clock Mode” setting there are 4 options, while most people are familiar with the usual SPI terminology of “SPI Mode 0” through “SPI Mode 3” or the slightly more descriptive: “CPOL” and “CPHA” for clock polarity and clock phase [4]. The figures below should help with the SPI setup.
Harmony configurator setting for SPI Mode is selected from one of the 4 options from the dialog as shown above.
Table 1 - The table above will help in translating from the standard SPI Mode number to the Harmony descriptive text.
We have now setup a basic instance of SPI peripheral #1, at this point we should have also configured the clocks and IO pins as per the earlier tutorials, this includes setting the SPI Peripheral #1 to the proper IO pins on the chip. Save and generate the code as per the tutorials, then do a compile to make sure everything is OK.
What Harmony Builds
Before we can get into using our “Bare Metal” SPI we need to decide what portions of the generated Harmony Code we will use.
As can be seen below Harmony builds for us a: main.c, app.c and its corresponding app.h files.
Figure 6 - The most important Harmony built files that we will explore here as shown in the MPLAB-X IDE.
When the program first starts it loads and runs some C startup code, then execution starts at main() and this is located in the file “main.c” as shown,
Figure 7 - The main() as built by Harmony. You can’t get much simpler than this.
Harmony has built a completely working project at this point that will,
1) Configure the clocks and hardware as setup by the user via the function SYS_Initialize().
2) Enter an infinite loop that calls a function called SYS_Tasks().
Taking a look at SYS_Initialize() we see another easy to understand function,
Figure 8 - SYS_Initialize() function as built by the Harmony Configurator. This unction is where all the clocks and peripherals get initialized.
As can be seen in the SYS_Initilize() function above, its function is straightforward. This is where the chip and any peripherals get initialized. First the clocks are initialized, then the ports are initialized, then you can see that the SPI instance is initialized. Note: This SPI initialization function contains all the commands to initialize the SPI that we set on the “SPI Driver Instance 0” Harmony configuration page above, it can be very informative to navigate to and look at this function if you have the need to change anything on the fly later in your program as all the configuration is done via PLIB_SPI functions.
The interrupts (if any) are next. At the very end we see that a call is made to a function APP_Initialize().
APP_Initialize() is located in app.c. APP_Initialize is a great place to set any user initialize functions that your application may need such as, initial conditions or flashing splash screens, etc. It is the place for anything that needs to be run or set once at the beginning of your application.
Figure 9 - As initially built, APP_Initialize() has some comments and sets the default state of the ‘roughed out’ state machine that Harmony builds at first. If you are not going to use the state machine approach, you can delete all of the contents of APP_Initialize() and put in here anything that your application needs to run once at the launch of your application.
Now that we are done with APP_Initialize() and SYS_Initialize() we are back in main().
The next part of main() (See main.c above) is a forever while loop that repeatedly calls SYS_Tasks(), drilling into SYS_Tasks(), which is located in a generated file named: system_tasks.c we find the following code,
Figure 10 - SYS_Tasks() Code as generated by Harmony.
As can be seen in the function SYS_Tasks(), there is a call to “Maintain Device Drivers”, but since we have set the driver to ‘Static’ and will be using the driver directly this function really will do nothing more than check a few flags and the return.
The next call is to APP_Tasks() which is located in the file app.c.
APP_Tasks() stubs out a basic State Machine that we can either use or not as shown below,
Figure 11 - App_Tasks() as the default build generated by Harmony.
Many (Most?) people don’t like to use a State Machine, preferring instead to use a ‘Big Loop’ implementation, no worries, this is easy to fix as shown below,
If you are familiar with how the Arduino stubs out a sketch (program) then all this should also look familiar to you. Think of APP_Initialize() as the Arduino function: “setup()” and the APP_Tasks as the Arduino function “loop()”. As with the Arduino when you return from ‘loop()’ the hidden Arduino main() will call it again after doing any necessary housekeeping, similarly if you return from APP_Tasks() the Harmony supplied main() will service any needed tasks and call APP_Tasks() again, endlessly. Using APP_Tasks() this way allows any required internal tasks to be serviced in main() as required (See also Figure 10 above: SYS_Tasks()).
Note to all you 'Super Control Freaks' out there: Many people seem to not like ceding even this much structure to Harmony, and alternatively, you can simply delete all references to APP_Initialize() and APP_Tasks() from the project and start your program directly in main().
Adding the “Bare Metal” SPI commands
Harmony has a big advantage on many competitive software HAL’s in that they actually have good and up to date documentation, both printed and in Compiled Help HTML (CHM) formats. The CHM help file is located at,
C:\microchip\harmony\v2_05_01\doc\help_harmony.chm
Naturally, you will have to change the path segment “v2_05_01” to your specific version of Harmony path.
I prefer to use the CHM help file format as it is searchable and example code is easily clipped from it.
Searching the CHM file for SPI leads one to a document titled “Library Interface” which describes all the possible SPI API calls available to you in the Harmony HAL.
Figure 13 - The “help_harmony.chm” compiled help file is very easily searched to find the driver API calls and example code. Here we easily locate the SPI library information.
As an example lets find the call to change the SPI clock speed to contrast how much easier the Harmony PLIB HAL is compared with the original PLIB library call presented earlier.
Figure 14 - A small portion of the help docment that is used to set a SPI Peripherals Clock speed (or as they call it: Baud Rate).
To use this you only need to know the peripheral bus clock speed that you set in the Harmony Clock Configurator, which in my case was 100 MHz as I am using a PIC32MZ chip that is running on a 200 MHz system clock. Then you also input the desired SPI clock speed and the function will do the rest.
We also need to get the proper module_id index, if you touch the blue hyperlink as shown in the figure above, the help file will show you the definition for the SPI_MODULE_ID enum as follows,
Figure 15 - Touching the SPI_MODULE_ID enum hyperlink in the CHM file will bring up the definition of that enum.
We know that we setup the SPI module as the physical peripheral #1 so we would want to use: “SPI_ID_1” in all our code when working with this peripheral.
Let’s add this SPI clock rate set to our APP_Initialize() code as follows,
Figure 16 - Making a PLIB_SPI function call to set the SPI_1 clock speed to 10 MHz. The 100 MHz is the SPI Peripheral bus speed.
You will notice that I did not have to add in any extra header files for this PLIB_SPI function to be recognized. Harmony has done that setup for us the moment we told it to use the SPI Driver library and placed the proper header files in “app.h” for you.
As you go around exploring in Harmony you will also find that you can get the clock speeds through API calls. In this instance you can get the SPI default clock source PBCLK2 value by using the call,
SYS_CLK_PeripheralFrequencyGet(CLK_BUS_PERIPHERAL_2)
Then the code example can be made more robust to future clock changes by making the call like this,
Figure 17 - Changing the SPI Clock Speed using not only the PLIB_SPI function, but also getting the peripheral bus clock frequency at runtime.
Notice that you do not need to add in any header functions for the library “SYS_CLK” either as Harmony has added those linkages for us in “app.h” when we added the specific driver to the Harmony Configurator.
Now let’s add a SPI write command to the ‘Big Loop’ that we wrote in APP_Tasks(), as shown below,
Figure 18 - The function APP_Tasks() above writes byte 0x55 to the SPI port once every second, forever.
At this point some have asked: “Isn’t using the registers directly faster and smaller than calling these PLIB functions?”, the answer is: “Probably not”. The simple reason is that the definition of most of these PLIB functions is to include an ‘inline’ declaration. This tells the compiler to prefer to ‘inline’ the generated code instead of making a function call. As long as the XC32 compiler optimization level is set to ‘O1’ or greater, then inlining of functions is enabled. Note: You will find that optimization level ‘O1’ is quite aggressive and really minimizes the generated code [4].
Figure 19 - Almost all of the Harmony PLIB statements are marked to the compiler as “Inline”
PLIB_SPI_BufferWrite() is declared “inline” by this “PLIB_INLINE_API” statement.
PLIB_SPI_BufferWrite() then calls another abstracted function and that function is also declared as ‘inline,’ so the chances of really having these PLIB function calls end up as ‘inline code’ is pretty high.
Other Useful SPI Functions
Browse around the help_harmony SPI “Library Interface” for other useful SPI functionality. Basically if you need to do something with the SPI it will be there, including a function to check to see if the SPI is done transmitting the last character,
PLIB_SPI_IsBusy(SPI_ID_1);
This is a useful command to check to see if the SPI hardware is done with the last character being sent before sending another character.
Other Useful PLIB Libraries
Other common PLIB libraries are available for nearly all the PIC32 series peripherals. To find the overview page of all of them, simply search for: “Peripheral Libraries Help” in the help_harmony.chm help file as shown below,
Figure 20 - This simple search will get you to a list of all the PLIB API’s in Harmony.
Conclusion
I have shown how to use the Harmony Configurator and the Harmony Static Peripheral Libraries as a means to do “Bare Metal” programming on a PIC32 device peripheral. Step by step, the code startup process was described and followed via the Harmony generated application files: main.c, app.h and app.c. Also shown was how to find and use the other “Bare Metal” or as Microchip Harmony calls then the “Static” peripheral drivers.
In future projects you can simply setup everything in Harmony, generate the code, then focus on only App_Initialize() and APP_Tasks() and you don’t have to worry about anything else that Harmony generates or calls first. This is pretty much what every Microprocessor suppliers code configurators do and the advantage of using Harmony is it has setup a consistent HAL layer of software that is portable between projects and even between most chips in the PIC32 series.
Harmony when used this way does not overwhelm the chip with a large or burdensome ‘Framework’ as many people seem to think and fear, in fact the Harmony ‘footprint’ on a PIC32 is quite small and the biggest point of all: The Harmony PLIB’s get your project running quickly with very little detailed knowledge of the hardware registers required.
References:
[1] Microchip also has a Youtube Channel where you can find some useful Harmony Tutorials.
https://www.youtube.com/watch?v=wyUPpTfxRDE&list=PL9B4edd-p2agL42JeLytmJrRNh6CsyGiS
[2] Harmony static and dynamic driver explanation,
https://microchipdeveloper.com/harmony:intro-flexibility#Static_Dynamic
[3] Wikipedia article on SPI,
https://en.wikipedia.org/wiki/Serial_Peripheral_Interface
[4] https://analoghome.blogspot.com/2019/08/microchip-xc32-compiler-optimization.html
[Edit]
12May20 - Table 1 was incorrect. Changed the text around the 'Big Loop'.
Article By: Steve Hageman / www.AnalogHome.com
We design custom: Analog, RF and Embedded systems for a wide variety of industrial and commercial clients. Please feel free to contact us if we can help on your next project.
Note: This Blog does not use cookies (other than the edible ones).
Wow that was really helpful!! Thank you for taking the time to put this together. :)
ReplyDeleteWarning: Big Loop may kill some Harmony support/processes, for example many drivers require dropping out of App_Tasks() to allow those non-interrupt Harmony processes to run.
ReplyDeleteSo I suggest to add a **warning** to your AppNote that "BigLoop" should only be used if user isn't going to use **any** ongoing Harmony non-interrupt support (Like DRV_SPI_Tasks()).
If you are doing everything bare metal then no problem, but I've found many of us do a mixture, using Harmony for some things (Internet, Files, ...) and direct coding for others (UART, SPI, ...)
Paul
Hi Paul - I appreciate your comments and have updated the article to reflect that the safest course is to use APP_Tasks() like the Arduino model and 'fall out of it', letting main service any needed tasks then calling APP_Tasks() again.
ReplyDelete