NU32v2: A Detailed Look at Programming the PIC32

From Mech
Jump to navigationJump to search

** This page is under construction and is not complete. KML 1/17/2011. **


NOTE: If you are only interested in the programs you will write, and how to put them on the PIC, you can skip the beginning of this page and jump straight to Programming a PIC32 Loaded with a Bootloader, which begins with putting a .hex file on your bootloader-loaded PIC, then follows with the process of creating a .hex file.


After you have programmed your PIC32 for the first time and verified that you can create a new project, compile it, and run it on your NU32v2, it is useful to take a step back and understand the basics of the programming process, beginning with a PIC32 fresh from the factory. We will do that on this page. We will begin by discussing the virtual memory map of the PIC32. To discuss the virtual memory map, it is useful to know hexadecimal (hex, or base 16) notation, where each digit of a hex number takes one of 16 values, 0...9, A...F. Since 16 = 2^4, a single hex digit represents four digits of a number written in binary (base 2). The table below gives examples.

Hex Binary Base 10
7 0111 7
D 1101 13
B5 1011 0101 181

To distinguish hex and binary numbers from base 10 numbers, we begin the numbers with 0x and 0b, respectively. For example, 0xA9 = 10*16^1 + 9*16^0 = 0b10101001 = 1*2^7 + 0*2^6 + 1*2^5 + 0*2^4 + 1*2^3 + 0*2^2 + 0*2^1 + 1*2^0 = 160 = 1*10^2 + 6*10^1 + 0*10^0. The 0x convention is standard in C, but the 0b is not universally used.


The PIC32 Virtual Memory Map

The PIC32 has a virtual memory map consisting of 4 GB (four gigabytes, or 2^32 bytes, where each byte equals 8 bits) of addressable memory. All memory regions reside in this virtual memory space at their unique respective addresses. This includes program memory (flash), data memory (RAM), peripheral special function registers (SFRs), etc. For example, the peripheral SFRs begin at virtual memory location 0xBF800000 and end at virtual memory address 0xBF8FFFFF. Subtracting the begin address from the end address, and adding one byte, we get 0x100000, which is 1*16^5 = 1,048,576 bytes, commonly written as 1 MB. (Note: Section 3 of the reference manual incorrectly indicates that the size of this region is 4 KB.)

In addition to this virtual memory map, there is also a physical memory map. When you are writing a program, you only deal with the virtual memory map. The PIC implements a Fixed Mapping Translation (FMT) unit that takes the virtual memory address and maps it to a physical memory address. In other words, the virtual memory address is translated to a set of bit values on an addressing bus that allows the PIC's CPU to physically address the appropriate peripheral, flash memory location, RAM location, etc. We will focus on the virtual memory map, not the physical memory map, since our goal is to program the PIC and we don't need to concern ourselves with the FMT. (On the other hand, peripherals that access memory independently of the CPU, such as for DMA [direct memory access], must use physical addresses. To learn more about physical addresses, see Section 3 of the PIC32 Family Reference Manual.)

Virtual memory is partitioned into two types of address space: user address space (the lower 2 GB) and kernel address space (the upper 2 GB). By analogy to your personal computer, the kernel address space is to hold the computer's operating system, while the user address space is to hold a program that runs under the operating system. This is for safety: the user's program should not interfere with or compromise the operating system, e.g., it shouldn't be able to overwrite data that the operating system needs to function. We will not be using an operating system, so our programs will reside in the kernel address space.

The kernel virtual address space contains two subsections: one that is cacheable and one that is not. "Cacheable" means that instructions or data can be stored in the cache by the prefetch cache module, which speeds up execution by eliminating some wait states needed when fetching data or instructions from flash. The prefetch cache module is activated when we execute the command SYSTEMConfig() in our C code.

The three major partitions of PIC32 virtual memory, then, are called KSEG0, KSEG1, and USEG/KUSEG, where KSEG0 corresponds to the cacheable kernel address space, KSEG1 corresponds to the non-cacheable kernel address space, and USEG/KUSEG corresponds to the user address space. The "K" in this last name indicates that programs in the kernel can address the user address space. Programs in the user address space cannot access the kernel address space.

Each of KSEG0, KSEG1, and USEG/KUSEG are further broken into the following sections: program flash, data RAM, and program RAM. The PIC may be made to run a program that is stored in RAM (as opposed to the usual case of a program stored in flash), which is why we have this last category.

Finally, we have two more areas of kernel memory for (1) the peripheral SFRs and (2) boot flash, the code that is executed upon reset of the PIC.

The virtual memory map is summarized in the table below:

Start Address Size (bytes) Partition Kind Notes
0x7D000000 + BMXPUPBA BMXPFMSZ - BMXPUPBA USEG/KUSEG program flash
0x7F000000 BMXDUPBA - BMXDUDBA USEG/KUSEG data RAM
0x7F000000 BMXDRMSZ - BMXDUPBA USEG/KUSEG program RAM
0x80000000 BMXDKPBA (max 128 K) KSEG0 (cacheable) data RAM same physical address as KSEG1 data RAM
0x80000000 + BMXDKPBA BMXDUDBA - BMXDKPBA KSEG0 (cacheable) program RAM same physical address as KSEG1 program RAM
0x9D000000 BMXPUPBA (max 512 K) KSEG0 (cacheable) program flash same physical address as KSEG1 program flash
0xA0000000 BMXPUPBA (max 128 K) KSEG1 data RAM
0xA0000000 + BMXDKPBA BMXDUDBA - BMXDKPBA KSEG1 program RAM
0xBD000000 BMXPUPBA (max 512 K) KSEG1 program flash
0xBF800000 1 MB kernel (KSEG1, non-cacheable) peripheral SFRs
0xBFC00000 12 KB kernel (KSEG1, non-cacheable) boot flash

In the table above, BMXPFMSZ and BMXDRMSZ are read-only registers containing the size of the program flash (512 K for us) and data RAM (128 K for us). The registers BMXqBA stand for "bus matrix" (BMX) and "base address offset" (BA), where q = PUP is for the user's segment of program flash, q = DUP is for the user's program space in RAM, q = DUD is for the user's data space in RAM, and q = DK is for the kernel program space in RAM. In our programs, we do not need to set the BMX registers. Leaving them at their default values allows maximum RAM and flash for kernel mode applications. If we want to run code from RAM or set up a user mode partition, we need to configure the BMX registers.

What Happens When the PIC Is Reset

When the PIC is reset, it goes to the reset address 0xBFC00000, which is the location of the boot flash, and executes the code there. The (assembly) code that typically sits there, which is put there by a PIC programmer device, can be found in pic32-libs/c/startup/crt0.S. This code takes care of some initialization tasks, then calls the code for the program you have written, which typically resides in the KSEG0 program flash memory block.

With the NU32v2, we have a "bootloader" program that executes upon reset. This program was placed in boot flash by a programmer. More on the bootloader below.

Programming a PIC32 with a Programmer

crt0. describe how programming usually works with a programmer, then say we just use it to put a bootloader on the PIC32, so we only have to use a USB cable in future. helps make it economically feasible to do mechatronics outside the lab (don't need to buy a programmer for everyone).

The PIC32 Bootloader

crt0. give the bootloader code, say what it does. anything related to the memory partitioning? mention that it sets configuration bits, and say what our particular configuration bits do (set the PLLs for clock, etc.)


No code is installed on the PIC32 when it arrives from the factory. To put a program on the microcontroller, a programmer is used. There are a variety of programmers available, including many from Microchip, the manufacturer of the PIC32, listed here. These programmers have many functions, including programming and debugging, with more functionality built into the more expensive programmers.

The NU32v2 can easily be programmed with any of these programmers, but has been designed to work with the PICkit 3, available for around $45. To avoid the expense of providing a PICkit with every NU32v2 kit, we have opted to install a bootloading program on the NU32v2. A bootloader is code that communicates with an application on a computer (in our case NU32v2_serial_bootloader), which takes the code that you have compiled and writes it to the memory on the PIC32.

By not including the PICkit with your NU32v2, we have lost the ability to do "in-circuit debugging", such as adding breakpoints and watches to your code as was done in the simulator. We will discuss alternative ways of debugging your code as the course progresses.

Understanding the bootloading process will increase your understanding of how the PIC32 functions. How the NU32v2 bootloader works is described in the following sections.

Programming a PIC32 Loaded with a Bootloader

give the Processing code, say what it does generally, how it works with the bootloader on the PIC. we must create the .hex file first. That's in the next section.


There are two important parts to the bootloader process. The first is what happens when you write code, and what the PC bootloader application does to prepare the code for the PIC32. The second part is how the PIC32 programs itself.

When you compile code in MPLAB, your C and H files go through several steps to turn them into instructions at specific memory addresses. The PIC32 starts at a specific memory address, and follows the instructions it finds. You can view the instructions the PIC32 will undergo in your code by going to View->Disassembly Listing in MPLAB. When compiled, this list of memory addresses and instructions is put into a file with a .hex extension. The hex file has a format that needs to be further interpreted before it can be put onto the PIC32.

After connecting to the NU32v2 in the NU32v2_serial_bootlader app, the hex file is converted to a binary file. In this process, a block of memory on the PC is set aside that is the maximum size of program memory on the PIC32. As each line of the hex file is read, the instructions are placed into memory addresses, which are not necessarily in order. After each line of the hex file is interpreted, the entire block of memory is sent over to the PIC32, in 512 byte packets. After sending all of the packets, the app closes.

On the NU32v2 side of things, the bootloader has already been programmed onto the PIC32 with a PICkit 3. The bootloader code is the first thing that runs on the NU32v2 when power is applied. The code sets up the configuration of the PIC32, to run at 80MHz, then it checks to see if the G6 pin is being pulled low. If it is not, it skips to another portion of memory, effectively leaving the bootloader code to run the last code that was put on the PIC32 with the bootloader. If G6 is being pulled low, it knows to remain in bootloader mode and get ready to receive a series of 512 byte packets, which it will write to memory.

The memory position that the bootloader jumps to is hard coded into the bootloader. When you code in MPLAB, the code does not necessarily start at that memory location. We use a procdefs.ld file to tell the compiler where to place our code so that it works properly with the bootloader. Using an incorrect procdefs, not putting procdefs in the correct file location, or not using it at all will prevent your code from running. Fortunately the bootloader is not capable of overwriting itself, so you should not be able to disable your bootloader by incorrectly using the procdefs file.

More specific details and source code are provided below.

Creating a .hex File from the MPLAB IDE

procdefs.ld, and what it does

Program Files\Microchip\MPLAB IDE\Device\PIC32MX795F512L.dev seems to be where some of the important memory addresses are defined? but this is probably just for the IDE. These addresses are referred to in MPLAB C32\pic32-libs\include\proc\p32mx795f512l.h file. Should go through this latter file to see what it's about. Where are these addresses used in the peripheral library functions?

your C program

the .c and .h files it includes: NU32v2.h (configuration bits, any simple mnemonics), plib.h, drill down to addresses in the virtual memory map (above)

definition of heap size, optimization level, etc., will still be done in IDE, correct? can all choices be seen in one place somehow (e.g., a makefile or something), or do we have to use the GUI to search through each set of options individually?

mention .a files (libraries, no source code?)

what is the search path for finding .h and .c files? seems we have some of the same library names under MPLAB C32 and MPLAB C32 Suite; which does our compiler find?


When you installed the MPLAB IDE, the directory C:\Program Files\Microchip was created. It has a lot of stuff in it, some of it redundant. This page is to give you an idea of the directory structure, and to help you understand what code gets included when you specify your PIC32 type, and when you include plib.h, for example.

Not all files are mentioned here, just ones that help you figure out what's going on. In the directory C:\Program Files\Microchip, there are a few subdirectories, such as Docs, MPLAB C32, MPLAB C32 Suite, and MPLAB IDE. The contents of MPLAB C32 and MPLAB C32 Suite look very similar. Below we explore the MPLAB IDE and MPLAB C32 directories, highlighting only the directories and files that are of particular interest.


MPLAB IDE

  • Device
    • PIC32MX795F512L.dev (defines memory locations of the peripheral SFRs)


MPLAB C32

  • doc
    • Microchip-PIC32MX-Peripheral-Library.chm
    • MPLAB C32 Libraries.pdf
    • MPLAB C32 User Guide.pdf
  • examples
    • plib_examples (lots of directories containing sample code using the peripherals; below are some examples)
      • adc10
      • timer
  • lib (contains various compiled libraries with .a extensions, and .h header files; none we need to worry about)
  • pic32-libs
    • dsp
      • wrapper
        • various .c files that call mips_XXX DSP functions
    • include
      • math.h (math function prototypes)
      • p32xxxx.h (uses the processor you chose when you created the project to include the right p32mx... file; see below. also does some other minor things.)
      • peripheral
        • adc10.h
        • lots of other peripheral library header files
      • plib.h (includes all the peripheral library headers)
      • proc
        • p32mx795f512l.h (huge file defining SFR names and constants for virtual memory addresses for the particular PIC)
        • ppic32mx.h (some more constant definitions, generic to all PIC32's, included by the file above)
    • peripheral
      • C source code for the peripheral library, one directory per peripheral
  • pic32mx
    • include (looks the same as the include directory above)
    • lib (contains compiled libraries with .a extensions)
      • mips16
        • .a libraries for DSP functions for different PIC32's
      • proc
        • 32MX795F512L
          • procdefs.ld (some virtual memory addresses for the linker for our PIC32)