King MM - The Joys of a Harmony-less Life - Part 1, PIC32MM "Hello World!"
Programming Microchip PIC32MM microcontrollers in C++ and assembly
1 Introduction
This is the first article of a series on PIC32MM programming using C++ and assembly.
Here, I show how to set up the minimal hardware and software environment required for a microcontroller Hello World! project, that is the 'blink a LED' application.
2 Background
A microcontroller (MCU
) is a (small) 'computer on a chip', featuring a CPU
, program and data memory, digital and analog ports, peripherals (like, for instance, UART
) on a single piece of silicon.
The MCU
we are considering is a little jewel for the hobbyist: a true 32
bit architecture (MIPS
core) in 28
pin SPDIP
package (see Pictures 1 and 2), at about one-and-half a dollar!
In order to make the MCU
run an application, we have to follow the steps given below:
- Produce an executable on the
PC
, using a cross-compiler (that is a compiler, running on a machine, able to produce code for another machine, having a different hardware architecture). - Transfer the produced executable to the microcontroller internal program (
FLASH
) memory, using a hardware tool called programmer.
3 Requirements
3.1 Hardware Components
Some hardware is required, even for our minimal project.
Microchip
details the bare minimum external components required in order to make the PIC32MM
run (see Chapter 2 of the device datasheet). To such components, we add a:
- breadboard
- red LED
- LED-current-limiting resistor
- bunch of jumping wires
to eventually obtain the hardware shown in Picture 4.
3.2 Hardware Tools
We need to power the MCU
and to 'flash' (program) it, so some hardware tools are required. Some optional tools, are listed as well, for convenience.
PICKit 3 Programmer (Required)
The PICKit 3
is required for programming the PIC32 MCU
(i.e., transferring the executable, produced by the cross-compiler on the PC
, to the PIC32 flash
memory).
Albeit it is the most cheap choice among Microchip
programmers, still the PICKit 3
costs about 50 $. The good news is: it can program practically all the Microchip
microcontrollers (8
, 16
and 32
bit families).
There are cheaper PICKit 3
clones available. The original one, however, is the recommended choice.
Stabilized Power Supply (Required)
A stable power supply is required by the MCU
in order to work properly (i.e., to prevent you from going mad).
A laboratory DC
power supply would be ideal, however there are many cheaper alternatives, like good switching adapters or even batteries. Just make sure your power device can provide a stable DC
source in the allowed range (2.0
to 3.6 V
).
Multimeter (Optional)
Albeit not required, a multimeter is highly recommended, because it is a very useful tool especially if something goes wrong in the circuit. There are many cheap multimeters available.
Oscilloscope (Optional)
The oscilloscope is an expensive instrument. It is also overkilling for a minimalist circuit like the one presented here. However, if you like to experiment with electronics, then I encourage you to buy one (the fun comes with the 'scope!).
Twizeers (Optional)
Twizeers greatly ease the task of inserting components in the breadboard clips.
3.3 Software Tools
MPLABX
MPLAB X is the Integrated Development Environment (IDE
) gently provided for free by the Microchip
.
It is a Java
, NetBeans
-based program: you might run it on your favourite OS
(Windows
, Linux
or MAC
). So far, so good.
The bad news is more abundant: it is slow (did I mention Java
?), bloated (did I mention Java
developers?), somewhat instable and buggy.
Installing the latest release is recommended (the project presented here was built using the release 3.65
).
XC32
Compilers are not bundled in the MPLABX
installation, so you have to download and install separately the XC32 C++ compiler.
XC32
is based on gcc
, that is a great C++
compiler. The version 1.44
, based on gcc 4.8.3
, allows us to use all the nice C++11
features on such a tiny piece of hardware. It is thrilling, isn't it?
Remarks
- We are going to use the free version of the compiler, which doesn't provide any optimization.
- The bundled
C++ Standard Library
is possibly not so updated (check out the related information in the forums).In any case, we are not going to use it.
3.4 Documentation
The MCU
documentation is available at the Microchip website dedicated to the PIC32MM family.
The most important documents are the microcontroller datasheet and the "PIC32 Family Reference Manual".
PIC32MM0064GPL036 FAMILY Datasheet
The datasheet, together with the "PIC32MM0064GPL036 FAMILY Silicon Errata and Data Sheet Clarification" is the ultimate reference on the MCU
.
It shows the exact pinout of the device, details every register, states the electrical specifications and more.
PIC32 Family Reference Manual
The PIC32 Family Reference Manual (FRM
) explains features and peripherals of the microcontrollers of all the Microchip PIC32
families. It contains many sections, individually downloadable.
The datasheet and the FRM
complement each other: the FRM
shows you how to use a peripheral to obtain a task, often providing sample code, while the datasheet reports the exact details of the MCU
you are using.
My usual approach is:
- Read the relevant
FRM
section - Check out the
FRM
sample code - Adapt the
FRM
sample code to theMCU
using the datasheet as reference
In other words: it is hard to do something based on datasheet info, so use instead the relevant section of FRM
to understand. However, be careful while adapting FRM
solutions to your favourite MCU
, keeping the datasheet at hand.
Tutorial, or, Better, Book on the MIPS Architecture
A general understanding of the MIPS
architecture is required. There are many tutorials and books available on such a topic (Google is your friend)
In order to use assembly code, some reference material, like the MIPS® Architecture for Programmers - Volume II-B: The microMIPS32™ - Instruction Set" available on the Imagination Technology
website, is recommended.
4 Building the Circuit
4.1 The Schematic
The circuit is very simple (see the Picture 6 below).
As per datasheet requirement (chapter 2: "GUIDELINES FOR GETTING STARTED WITH 32-BIT MICROCONTROLLERS"), the minimal required components to make the PIC32
run are:
- A
10 uF
tantalum capacitor connectingVCAP
toVSS
- A
0.1 uF
ceramic capacitor connectingVDD
toVSS
- Another
0.1 uF
ceramic capacitor connectingAVDD
toAVSS
- A
10 kOhm
pull-up resistor to tie theMCLR
pin toVDD
The "Hello World!
" specific, additional hardware is just the series formed by the 1 kOhm
resistor and the red LED
, between VDD
and the RB7
digital output.
Remarks
- The tantalum capacitor is polarized, pay attention to connect properly its positive lead to the
VCAP
pin. - The red LED is polarized as well, connect its anode to
VDD
. - Usually, the clock to a microcontroller is provided by an external crystal. Additionally, another crystal can be used to provide the secondary (
RTCC
) one. However, we are going to use neither of them: the clock is provided by the internal Fast Resistor-Capacitor (FRC
) oscillator. - The schematic shows also the connections to the
PICKit 3
tool, required in order to actually program the device.
4.2 The Board, Step 1: Put Components in Place
4.3 The Board, Step 2: add VDD and VSS Jumper Wires
4.4 The Board, Step 3: Add Jumper Wires for the Programming Signals
OK, you may now look at the produced spaghetti mess, that is the pride and joy of every solderless hobbyist.
The board can now be connected to the power supply and to the PICKit 3
programmer.
The PICKit 3
pinout is shown in "PICkit™ 3 - In-Circuit Debugger/Programmer - User’s Guide", available at the Microchip
's website:
MCRL
(marked by white arrow) -> connect to orange wireVDD
-> connect to red wireVSS
-> connect to black wirePGD
-> connect to yellow wire (MCU
pin14
)PGC
-> connect to green wire (MCU
pin15
)
5 Eventually, Let's Program It!
5.1 Creating a Fresh Project
If you didn't do it yet, it's time to install the required software tools: the MPLAB X IDE
and the XC32
cross compiler.
Now open MPALB X
and:
- Start the wizard in order to create a new project, select
Microchip Embedded
category andStandalone Project
in the Choose Project window. - Proudly choose the
PIC32MM0064GPL028
device. - Select the
PICKit 3
hardware tool. - Select the
XC32
compiler toolchain. - Give a meaningful name to your project, like "
PigsOnTheWing
".
5.2 Adding the Code
The Configuration Bits
The configuration bits are flags (stored at special locations of the FLASH
memory) providing the static configuration of the microcontroller.
They control many features of the PIC32
, like, for instance, the selection of the clock source.
Setting the configuration bits is achieved using the XC32
ad hoc directive pragma config
. Luckily, the MPLAB X IDE
's Configuration Bits window provides a convenient way to set such flags, just selecting the decided options. The IDE
then will generate the corresponding code for us.
Our fundamental choices are:
- Run from
FRC
(fastRC
,8 mhz
) internal oscillator (this way, we don’t need an external quartz) using thePLL
module to achieve a24 mhz
clock. - Disable all nice features (like for instance the watchdog) useful on the ultimate product, but rather annoying while experimenting with the board.
Add a new C++
file to the project (call it confbits.cpp) and copy there the generated source code.
I/O Pins Configuration
The microcontroller provides some registers useful to configure the I/O
pins. Typically, we must decide if a pin is:
- analog or digital, using an
ANSEL
register - input or output, using a
TRIS
register
There are other details we skip for the moment. Pins are grouped in ports. The PIC32MM
provides three ports: A
, B
, C
.
There is a corresponding set of ANSEL
and TRIS
registers. Here, we are going to use none of the analog features of the MCU
, hence ANSELA=ANSELB=ANSELC=0;
We are going to use just one output pin (namely RB7
), all the other pins left unused. Microchip
recommends to configure unused pins as outputs and to drive them low (or high). Hence, we set TRISA = TRISB = TRISC = 0;
(all outputs) and LATA=LATB=LATC=0;
(all driven low).
That’s all: write the above statements in the (newly created) config.cpp source file:
#include <xc.h> // definitions of ANSELA, ANSELB, ...
// configure all the pin as digital output, drive all of them low
void config()
{
ANSELA = ANSELB = ANSELC = 0;
TRISA = TRISB = TRISC = 0;
LATA = LATB = LATC = 0;
}
The Actual Application Code
In the main
function, we are going to alternatively switch ON
and OFF
the LED
in an infinite loop (the microcontroller main
should never return, after all there is no OS
sitting out there, waiting for it). In order to toggle the pin value, we might use the LATINV
register: LATBINV = 0x80
does the trick.
Since the human eye is not able to perceive a too fast ON/OFF toggle, we have to add a delay after the instruction. We can simply waste MCU
cycles in a empty loop. Since the PIC32
runs at 24 mhz
, 24,000,000
iterations should do the trick. So we try the following main
function:
#include <cstdint>
#include <xc.h>
#include "cfg.hpp"
void delay()
{
for (uint32_t n=0; n<24000000;++n){} // waste MCU cycles
}
int main()
{
config();
for (;;)
{
LATBINV = 0x80; // toggle the RB7 pin state
delay();
}
}
You might compile and flash the code with the "Make and Program Device Main Project" MPLAB X
command.
Code execution is a bit disappointing, however. The blink is very slow, the LED
is ON
for about 11
(eleven !) seconds and then OFF
for the same amount of time.
What's happened?
Is our empty loop, really empty?
Is it instead full of saturated fat?
Another MPLAB X
window is helpful to get insight. Searching for delay into the Execution Memory window, we eventually find:
Without digging into the generated assembly code, we might still appreciate the inefficiency of the compiler: an empty for
loop expanded into 18
(eighteen!) assembly instructions.
This is what you get for what you pay: the free compiler version provides no optimization at all (malicious folks say the free version of the compiler deliberately puts bloat in the generated code).
Now since we hobbyists are not willing to pay Microchip
the about 1000 $ required for the PRO licence of the compiler, or, put in a more idealistic way 'we strive to understand the inner details of the things', let's turn our attention to MicroMIPS
assembly language and see if we can do better.
Of course, we can: write the following code into the newly created util.S file and then add it to the project:
#include <xc.h>
.section .text, code
.set noreorder
.global asm_delay_1_sec
asm_delay_1_sec:
li t0, 12000000 // 12 millions cycles
asm_delay_1_sec_l1:
bnez t0, asm_delay_1_sec_l1
addi t0, t0, -1 // this is the MIPS branch delay slot instruction,
// always executed after the previous one
jrc ra
The code is pretty simple: execute 12
millions iterations of a two-cycles body. Then modify the main
function this way:
#include <cstdint>
#include <xc.h>
#include "cfg.hpp"
extern "C"
{
void asm_delay_1_sec();
}
int main()
{
config();
for (;;)
{
LATBINV = 0x80; // toggle the RB7 pin state
asm_delay_1_sec();
}
}
And, again, flash it using the PICKit 3
tool.
This time, the blinking is good: the red LED state remains the same for exactly one second. The oscilloscope track, in Picture 12 below, gives a nice confirmation.
RB7
output pin.Of course, one can obtain a similar result empirically, modifying the number of iterations of the C++
code, but that's not the point. The focus here is in control: the assembly code gives us the control the C++
code can't. For instance, in this project, just the MicroMIPS
assembly code has a predictable execution time.
Points of Interest
The Harmony-less Joke
Harmony
is a Microchip
framework, in Microchip
's own words (Harmony
website): "MPLAB® Harmony is a flexible, abstracted, fully integrated firmware development platform for PIC32 microcontrollers. It takes key elements of modular and object oriented design, adds in the flexibility to use a Real-Time Operating System (RTOS) or work without one, and provides a framework of software modules that are easy to use, configurable for your specific needs, and that work together in complete harmony."
As a matter of fact, Harmony
-produced code is very complex, full of bugs and a nightmare to debug. Many developers on Microchip
forums agree at least on one point: Harmony
is just the inevitable evil required while dealing with complex hardware peripherals, like the USB
. Luckily, the PIC32MM
microcontroller family features just simple peripherals. So we don't have to deal with Harmony
and our life is full of joy!
The MicroMIPS Pros and Cons
The original MIPS
is a clean RISC
architecture (do you remember DLX
?). On the other hand, the microMIPS
architecture (the PIC32MM
one) strives to reduce the size of the produced code and it pretty succeeds in the task: as per Microchip
claims, the MicroMIPS
executable code size is about 35%
smaller than the standard MIPS
one, while the execution speed remains pretty similar.
The price we pay for such a miracle is a cluttered instruction set: MicroMIPS
is a bit tricky to grasp, for the hobbyist.
Conclusions
This was the first article of a (in progress...) series on PIC32MM
microcontroller programming using C++
and assembly
.
The very basics of the hardware and software requirements have been sketched, and the usefulness of the assembly language remarked. Unfortunately, no room has left for C++
code or experimentation with fancy peripherals. All of that should be amended in the next articles of the series.
History
- Roma, July 7th 2018: Fixed the schematic picture, thanks to Francisco
- Roma, July 26th 2017: First release