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!
Picture 2: Very simplified block diagram of a PIC32MM microcontroller.
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.
Picture 3: The build and program process.
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.
Picture 4: The required hardware, a red '+' marks the positive lead of the polarized components.
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.
Picture 5: The PICKit 3 programmer. The white arrow marks the MCLR position.
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
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 the MCU
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 connecting VCAP
to VSS
- A
0.1 uF
ceramic capacitor connecting VDD
to VSS
- Another
0.1 uF
ceramic capacitor connecting AVDD
to AVSS
- A
10 kOhm
pull-up resistor to tie the MCLR
pin to VDD
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.
Picture 6: The circuit schematic.
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
Picture 7: Bread board after step 1: be careful with polarized components.
4.3 The Board, Step 2: add VDD and VSS Jumper Wires
Picture 8: Bread board after step 2.
4.4 The Board, Step 3: Add Jumper Wires for the Programming Signals
Picture 9: Bread board after step 3: the 'final mess'.
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 wire VDD
-> connect to red wire VSS
-> connect to black wire PGD
-> connect to yellow wire (MCU
pin 14
) PGC
-> connect to green wire (MCU
pin 15
)
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 and Standalone 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
(fast RC
, 8 mhz
) internal oscillator (this way, we don’t need an external quartz) using the PLL
module to achieve a 24 mhz
clock. - Disable all nice features (like for instance the watchdog) useful on the ultimate product, but rather annoying while experimenting with the board.
Picture 10: Configuration Bits window excerpt, showing the clock source selection
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;
Note the ANSEL registers have all bits set at reset, hence all of the pins are configured analog by default. This is somewhat unexpected and, more importantly can produce unexpected, annoying results. If something goes wrong with a pin (even while controlled by a peripheral), have a look at the corresponding bit in the ANSEL register as the most probable culprit.
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, ...
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.
The MIPS core doesn't provide atomic bit manipulation instructions, so Microchip augmented peripheral registers like TRISx, LATx, ... with additional -INV, -SET, -CLR register (e.g. the TRISAINV, TRISASET, TRISACLR are linked to TRISA). All the bits set in any -INV register will toggle atomically the corresponding bit in the linked register while all the bit cleared in the -INV registers do not affect the linked register (similar operation are performed by -SET and -CLR registers, see the documentation).
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){} }
int main()
{
config();
for (;;)
{
LATBINV = 0x80; 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:
Picture 11: Execution Memory window excerpt, showing the machine code of the delay function.
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
asm_delay_1_sec_l1:
bnez t0, asm_delay_1_sec_l1
addi t0, t0, -1
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; 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.
Picture 12: The oscilloscope track, showing the voltage level on 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!
Note: My experience dates back to 2016, when I decided to discard such framework even while developing for the PIC32MZ family. I hope things have been changed for the best, and Harmony has been greatly improved, in the meantime.
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