King MM – The joys of a Harmony-less life – Part 1, PIC32MM “Hello World!”

0
20

1 Introduction

This is the first articles 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 hobbyst: a true 32 bit architecture (MIPS core) in 28 pin SPDIP package (see Pictures 1 and 2), at about one-and-half a dollar!

The PIC32MM MCU very simplified block diagram

Picture 2: Very simplified block diagram of a PIC32MM micrcontroller.

In order to make the MCU running an application, we have to follow the following steps:

  • 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.

The build and program process

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 the 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.

The required hardware

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 pragramming 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.

The PICKit 3 programmer

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 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 usufeul tool expecially 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 are more aboundant: 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 datsheet 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 architecure

A general understanding fo 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.
  • A10 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.

The circuit schematic

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

Board construction, step 1

Picture 7: Bread board after step 1: be carefult with polarized components.

4.3 The board, step 2: add VDD and VSS jumper wires

Board construction, step 2

Picture 8: Bread board after step 2.

4.4 The board, step 3: add jumper wires for the programming signals

Board construction, step 3

Picture 9: Bread board after step 3: the ‘final mess’.

OK, you may now look the produced spaghetti mess, that is the proud and joy of every solderless hobbyst.

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:

  1. MCRL (marked by white arrow) -> connect to orange wire.
  2. VDD -> connect to red wire.
  3. VSS -> connect to black wire.
  4. PGD -> connect to yellow wire (MCU pin 14).
  5. 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 desidered 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.

Configuration bits excerpt

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 microntroller 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 it 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

Execution Memory window

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 provide no optimization at all (malicious folks say the free version of the compiler deliberately puts bloat in the generated code).

Now since we hobbysts 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.

Oscilloscope track

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 hobbyst.

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 26th 2017, first release.

LEAVE A REPLY