Difference between revisions of "NU32v2: A Detailed Look at Programming the PIC32"

From Mech
Jump to navigationJump to search
 
(76 intermediate revisions by 2 users not shown)
Line 1: Line 1:
'''THIS PAGE REFERS TO A PRE-RELEASE VERSION OF THE NU32 PIC32 DEVELOPMENT BOARD. FOR INFORMATION, SAMPLE CODE, AND VIDEOS RELATED TO THE PRODUCTION VERSION (2016 AND LATER), AND TO THE CORRESPONDING BOOK "EMBEDDED COMPUTING AND MECHATRONICS WITH THE PIC32 MICROCONTROLLER," VISIT [[NU32|THE NU32 PAGE]].'''
'''** This page is under construction and is not complete. KML 1/17/2011. **'''


After you have [[NU32v2: Starting a New Project and Putting it on the NU32v2|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.
After you have [[NU32v2: Starting a New Project and Putting it on the NU32v2|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.
Line 33: Line 33:
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.)'''
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.)
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's CPU 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 how the FMT works. If you're curious, however, the translation is a simple bitwise AND:

physical address = virtual address & 0x1FFFFFFF

If you are directly accessing memory, independent of the CPU, as when an external device is engaged in direct memory access (DMA) with the PIC, you 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.
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 information is stored in the cache by the prefetch cache module, which speeds up execution by limiting wait states when fetching data or instructions from flash. The prefetch cache module is activated when we execute the command SystemConfigPerformance().
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 pre-fetch cache module, which speeds up execution by eliminating some wait states needed when fetching data or instructions from flash. The pre-fetch 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.
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.
Line 78: Line 82:
<tr>
<tr>
<td>0x80000000</td>
<td>0x80000000</td>
<td>BMXDKPBA</td>
<td>BMXDKPBA (max 128 K)</td>
<td>KSEG0 (cacheable)</td>
<td>KSEG0 (cacheable)</td>
<td>data RAM</td>
<td>data RAM</td>
Line 92: Line 96:
<tr>
<tr>
<td>0x9D000000</td>
<td>0x9D000000</td>
<td>BMXPUPBA</td>
<td>BMXPUPBA (max 512 K)</td>
<td>KSEG0 (cacheable)</td>
<td>KSEG0 (cacheable)</td>
<td>program flash</td>
<td>program flash</td>
<td>same physical address as KSEG1 program flash</td>
<td>same physical address as KSEG1 program flash</td>
</tr>
</tr>
<!--
<tr>
<td>0x9FC00000</td>
<td></td>
<td>KSEG0 (cacheable)</td>
<td>boot flash</td>
<td>same physical address as KSEG1 boot flash; '''not listed in Ref Manual Table 3-2'''</td>
</tr>
-->
<tr bgcolor="gray">
<tr bgcolor="gray">
<td>0xA0000000</td>
<td>0xA0000000</td>
<td>BMXPUPBA</td>
<td>BMXPUPBA (max 128 K)</td>
<td>KSEG1</td>
<td>KSEG1</td>
<td>data RAM</td>
<td>data RAM</td>
Line 122: Line 117:
<tr bgcolor="gray">
<tr bgcolor="gray">
<td>0xBD000000</td>
<td>0xBD000000</td>
<td>BMXPUPBA</td>
<td>BMXPUPBA (max 512 K)</td>
<td>KSEG1</td>
<td>KSEG1</td>
<td>program flash</td>
<td>program flash</td>
Line 130: Line 125:
<td>0xBF800000</td>
<td>0xBF800000</td>
<td>1 MB</td>
<td>1 MB</td>
<td>kernel (KSEG1)</td>
<td>kernel (KSEG1, non-cacheable)</td>
<td>peripheral SFRs</td>
<td>peripheral SFRs</td>
<td></td>
<td></td>
Line 137: Line 132:
<td>0xBFC00000</td>
<td>0xBFC00000</td>
<td>12 KB</td>
<td>12 KB</td>
<td>kernel</td>
<td>kernel (KSEG1, non-cacheable)</td>
<td>boot flash</td>
<td>boot flash</td>
<td></td>
<td></td>
Line 145: Line 140:
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.
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 =
= 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. '''Give examples of what might be there, e.g., when programming with a programmer, or when the bootloader is on.''' Say something about the crt0 code. DiJasio talks about crt0 on p. 59. Mentions defining _on_reset() to do anything time critical before crt0 finishes executing. one function of crt0 is to fill statically allocated arrays with zeros in RAM, or to copy from flash to RAM any statically allocated initialized arrays.
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 version of the) code that typically sits there, which is put there by a PIC programmer device, can be found in [[Media:NU32v2-crt0.S|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 the PIC's flash memory by a programmer device. More on the bootloader below.
<!--
'''Give examples of what might be there, e.g., when programming with a programmer, or when the bootloader is on.''' Say something about the crt0 code. Defining _on_reset() to do anything time critical before crt0 finishes executing. one function of crt0 is to fill statically allocated arrays with zeros in RAM, or to copy from flash to RAM any statically allocated initialized arrays.
-->


= Programming a PIC32 with a Programmer =
= Programming a PIC32 with a Programmer =


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, [http://www.microchip.com/stellent/idcplg?IdcService=SS_GET_PAGE&nodeId=2519&param=en534451&page=wwwdevMPLABEmulatorDebuggers listed here]. These programmers have many functions, including programming and debugging, with more functionality built into the more expensive programmers.
here 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 NU32v2 can easily be programmed with any of these programmers, but has been designed to work with the [http://www.microchipdirect.com/productsearch.aspx?Keywords=PG164130 PICkit 3], available for around $45.
= The PIC32 Bootloader =


= The PIC32 Bootloader and Bootloader App =
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.)


To avoid the expense of providing a PICkit 3 programmer with every NU32v2 kit, we opted to use our PICkit 3 programmer to install a bootloading program on your NU32v2. This allows you to program your PIC using only a USB cable and the free MPLAB IDE and compiler. By not using a programmer, you 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.
= Programming a PIC32 Loaded with a Bootloader =


The bootloader code executes upon reset of the PIC. This code tests whether a digital input is low (i.e., whether the user is pressing a button) when the PIC is reset. (Aside: the bootloader code sets pin G6 as a digital input and configures an internal pull-up resistor to 3.3 V, which is why a simple external normally open switch, connecting G6 to ground when it is pressed, suffices for the digital input.)
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.


* If the digital input is high (no button press), the bootloader jumps to a hard-coded virtual memory address to begin executing code there. In the case of our bootloader, that memory address is 0x9D001000 (i.e., in KSEG0 program memory).
= Creating a .hex File from the MPLAB IDE =
* If the digital input is low (the button is being pressed), the PIC attempts to establish communication with a "bootloader app" on you computer. The bootloader app will send over your new .hex program, and the PIC will write the program into KSEG0 program flash beginning at 0x9D001000. The next time the PIC is reset (and the button is not being pressed), it will begin executing the program you loaded.


In more detail, if the PIC enters "bootloader mode," (button is pressed at reset) the PIC's bootloader sends the string 'v1.1' over a serial port to your computer to indicate that it is ready to receive data from the bootloader app. An 'E' commands the bootloader to erase the program memory. This must be performed before any program is loaded. A 'B' command checks to see if the program memory is blank. If it is, an 'S' is returned, otherwise an 'F' is returned. A 'P' command lets the bootloader know that packets of 512 bytes are about to be sent to the PIC32. After the arrival of every packet, the PIC32 writes the bytes, in the order that they were received, to program memory. After not receiving a packet for ~1 second, the PIC32 considers the programming process complete and returns an 'S.' (A 'V' character lets the bootloader know that the user would like to see if a hex file matches the current content of the PIC32. This process is standard in most bootloaders, but is not implemented on the NU32v2.) An 'R' command resets the NU32v2 without needing to reset power. This effectively gets the NU32v2 out of bootloader mode.
procdefs.ld, and what it does


'''Important:''' The bootloader does one other important thing: it sets ''configuration bits'' for the PIC. Configuration bits are SFRs that are set on startup and control some basic behavior of the PIC. For example, the configuration bits are used to configure phase-locked loops on the PIC to turn our 8 MHz external oscillator into an 80 MHz system clock, 80 MHz peripheral bus clock, 48 MHz USB clock, etc. '''Unfortunately, you cannot see the configuration bits being set in the bootloader .S assembly code (below), because the bit settings were chosen in the IDE that compiled the bootloader. In future, we will set these configuration bits in the code itself, for portability and so you can see what's going on. This is also the standard for the MPLAB X IDE.'''
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?


More information about configuration registers can be found in Section 32 of the Reference Manual.
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)


'''CODE'''

* The NU32v2 bootloader project, written in MPLAB Assembly, with a "hello_world" type blinking LED example tacked on, is provided here: [[Media:NU32v2_pic32bootloader_source.zip|PIC32 code]].

* The NU32v2_serial_bootloader app source code, written in Processing, is provided here: [[Media:NU32v2_serial_bootloader_source.zip|PIC32 code]]. '''New for Feb 14 2011- Should crash less. Email Nick if you repeatedly get Error # in the message area.'''

* The Mac OSX executable version of the bootloader app is here: [[Media:NU32v2_serial_bootloader_osx.zip|NU32v2 serial bootloader for OSX]]. '''New for Feb 14 2011- Should crash less. Email Nick if you repeatedly get Error # in the message area.'''

The NU32v2_serial_bootloader app allows the user to navigate to and select a .hex file. No other file type selection is allowed. The app also creates a button for each available serial COM port when the app loads, so the NU32v2 board must be powered up and the USB cable must be plugged in before the app is run.

'''Note about serial communication:''' The NU32v2 board has an [http://www.ftdichip.com/Products/ICs/FT232R.htm FT232RL] built on to allow for easy communication over USB with your computer. This chip translates between the PIC's UART (RS-232) and your PC's USB protocol. The communication is viewable as a "virtual com port" on your computer using the driver provided by FTDI. This chip provides an easy way for your computer and the NU32v2 to communicate, for bootloading, debugging, and general communication.

= Creating a .hex File Using MPLAB =

OK, everything is in place. You've got a bootloader on the PIC and a bootloader app on your computer. Now we just need to create a program to put on the PIC!

Let's use the digital I/O code sample on [[NU32v2: Digital Input and Output|this page]] as an example. Following the basic procedure on [[NU32v2: Starting a New Project and Putting it on the NU32v2|this page]], you create a new project and add the code sample to Source Files. Let's call this file digio.c. You add the [[Media:procdefs_for_NU32v2.zip | procdefs.ld file]] to Other or Important Files. You choose the C32 Compiler and the device PIC32MX795F512L. Then you choose "Build," and if all has gone well, you should get the message BUILD SUCCEEDED and a .hex file ready to be loaded on to your PIC.

So what was the process, really? Three things happened:
* a ''compiler'' turned your C code into assembly language (you can see the assembly code by going to ''View->Disassembly Listing'' in MPLAB);
* an ''assembler'' produced a machine-level "object" file (with suffix .o) for each .c file; and
* a ''linker'' took the .o file(s) and created an [http://en.wikipedia.org/wiki/Executable_and_Linkable_Format executable and linkable format file (.elf)] and a [http://en.wikipedia.org/wiki/Intel_HEX .hex file].
The purpose of the linker is to assign specific addresses in memory for each function and global variable defined in your C files (and therefore .o files) according to rules specified in a linker script file ending in .ld (e.g., procdefs.ld). One purpose of your procdefs.ld is to tell the linker to place your code in the virtual memory map at the memory location that your bootloader code will jump to. Thus your procdefs.ld should be designed to work with your particular bootloader code. (Even if you define your procdefs.ld incorrectly, the bootloader will not be able to overwrite itself.) '''More details on procdefs.ld.'''

Now let's dig a little deeper. In our C code, we used statements like
<pre>
#define LED0 LATGbits.LATG12
</pre>
and
<pre>
SYSTEMConfig(SYS_FREQ, SYS_CFG_ALL);
</pre>
So it looks like we defined one constant (LED0) to be equal to a variable, the field "LATG12" of a struct "LATGbits." But where was that struct defined? And where was the function SYSTEMConfig() defined?

The key is the first line of our code:
<pre>
#include <plib.h>
</pre>
Like Alice in Wonderland, we're about to go down a rabbit hole...

When you installed MPLAB and the C32 compiler, it installed a large tree of directories under Program Files\Microchip\MPLAB C32 for MPLAB 8.xx, or mplabc32/v.11a (or similar) for MPLAB X. To give you an idea of the directory structure, we highlight some of the important directories and files below:


----

'''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; ignore for now)
* 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
**** system.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/topic
* pic32mx
** include (looks similar to the include directory above)
** lib (contains compiled libraries with .a extensions)
*** mips16
**** .a libraries for DSP functions for different PIC32's
*** proc
**** 32MX795F512L
***** processor.o (definitions of SFR virtual memory addresses for our PIC)

----


OK, that's a lot of stuff, and it's just a small fraction of all the stuff that's there. But for now, notice '''plib.h''' in bold above. When we put <tt>include <plib.h></tt> in our program, the compiler searches an "include path" (a list of directories to look under) for a file of this name. By default, our include path allows us to find pic32-libs/include/plib.h. Try opening this file in a text editor. You'll see that all that plib.h does is include the peripheral header files pic32-libs/include/peripheral/{adc10.h, ports.h, system.h, etc.}.
: Open pic32-libs/include/peripheral/system.h. The first thing it does is include p32xxxx.h (along with a number of other .h files).
:: Open pic32-libs/include/p32xxxx.h. This file checks which processor we told the IDE we are using and then includes the header file pic32-libs/include/proc/p32mx795f512l.h. (It also does some other things, like make some standard definitions of register names for assembly code.)
::: Open pic32-libs/include/proc/p32mx795f512l.h. Whoa! This is a big file defining a lot of the variables and variable types that we use to interact with the SFRs. We've also reached the bottom of the include chain, since this file doesn't include anything else. There is some syntax here that you're probably not familiar with, but we should be able to find something recognizable. Search in the file for LATGbits. About 13% into the file, you will see the following code.
<pre>
extern volatile unsigned int LATG __attribute__((section("sfrs")));
typedef union {
struct {
unsigned LATG0:1;
unsigned LATG1:1;
unsigned LATG2:1;
unsigned LATG3:1;
unsigned :2;
unsigned LATG6:1;
unsigned LATG7:1;
unsigned LATG8:1;
unsigned LATG9:1;
unsigned :2;
unsigned LATG12:1;
unsigned LATG13:1;
unsigned LATG14:1;
unsigned LATG15:1;
};
struct {
unsigned w:32;
};
} __LATGbits_t;
extern volatile __LATGbits_t LATGbits __asm__ ("LATG") __attribute__((section("sfrs")));
</pre>
::: The typedef command is creating a new variable type consisting of the union of two structs. The first struct clearly indicates which pins belong to port G: RG0...RG3, RG6...RG9, and RG 12...RG15. Confirm this by consulting Table 1-1 in the Data Sheet. The last line of code declares a variable of type __LATGbits_t, and the name of the variable is LATGbits. To reference bit/pin 12, for example, we would use the C code LATGbits.LATG12. (The :1 indicates one bit, :2 indicates two bits.)

::: We can also see that LATG, LATGCLR, LATGSET, and LATGINV are declared. The only thing that is missing, now, is the addresses of these SFRs. The compiler needs to know these addresses, because they are fixed by the hardware of the PIC. (Note also that LATG and LATGbits are actually pointing to the same locations in memory.)

::: If you scroll down further, about 30% of the way through the file you see variable names defined for assembly. These are our answers as to the virtual memory addresses of the various SFRs. If you search for LATG, about 34% into the file you will see that the address is 0xBF8861A0, and the other addresses are offset by four. ('''Why four?''')

::: But the addresses are still not actually defined here (these addresses are in comments). The addresses are actually defined in pic32mx/lib/proc/32MX795F512L/processor.o, which is the first thing included at the top of our linker file procdefs.ld. The "extern" keyword allows this file to use the variable definitions in another file.

::: Searching further for LATG12, about 67% of the way into the file you see the code
<pre>
#define _LATG_LATG12_POSITION 0x0000000C
#define _LATG_LATG12_MASK 0x00001000
#define _LATG_LATG12_LENGTH 0x00000001
</pre>
::: These define the offset of bit 12 from the base address of LATG (_LATG_LATG0_POSITION), a "mask" (if you do a bitwise AND of the mask with LATG, all bits will be made 0, except for bit 12, which will be left unchanged), and a length of 1 that indicates that LATG12 is only one bit. These mnemonic constants are available for the programmer's use.

: OK, let's pop back up a couple of levels and return to pic32-libs/include/peripheral/system.h. At the end of this file, we see the definition of SYSTEMConfig, which we use in our digital I/O program to optimize the PIC flash wait states and turn on the pre-fetch cache.

Certainly lots of other files get included in your program, but you get the idea. The point is that by digging deeper into the code, you find that the software you are writing eventually interacts with the hardware, e.g., by modifying the bits at specific memory locations (the SFRs that control the peripherals, which correspond to transistors being on or off in the PIC's circuitry). These lower-level include files help you understand how the hardware information in the Data Sheet and Reference Manual is reflected in the software.

<!--
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?
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?


Line 176: Line 310:


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?
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?
-->

Latest revision as of 06:17, 16 January 2016

THIS PAGE REFERS TO A PRE-RELEASE VERSION OF THE NU32 PIC32 DEVELOPMENT BOARD. FOR INFORMATION, SAMPLE CODE, AND VIDEOS RELATED TO THE PRODUCTION VERSION (2016 AND LATER), AND TO THE CORRESPONDING BOOK "EMBEDDED COMPUTING AND MECHATRONICS WITH THE PIC32 MICROCONTROLLER," VISIT THE NU32 PAGE.

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's CPU 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 how the FMT works. If you're curious, however, the translation is a simple bitwise AND:

     physical address = virtual address & 0x1FFFFFFF

If you are directly accessing memory, independent of the CPU, as when an external device is engaged in direct memory access (DMA) with the PIC, you 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 pre-fetch cache module, which speeds up execution by eliminating some wait states needed when fetching data or instructions from flash. The pre-fetch 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 version of the) 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 the PIC's flash memory by a programmer device. More on the bootloader below.

Programming a PIC32 with a Programmer

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.

The PIC32 Bootloader and Bootloader App

To avoid the expense of providing a PICkit 3 programmer with every NU32v2 kit, we opted to use our PICkit 3 programmer to install a bootloading program on your NU32v2. This allows you to program your PIC using only a USB cable and the free MPLAB IDE and compiler. By not using a programmer, you 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.

The bootloader code executes upon reset of the PIC. This code tests whether a digital input is low (i.e., whether the user is pressing a button) when the PIC is reset. (Aside: the bootloader code sets pin G6 as a digital input and configures an internal pull-up resistor to 3.3 V, which is why a simple external normally open switch, connecting G6 to ground when it is pressed, suffices for the digital input.)

  • If the digital input is high (no button press), the bootloader jumps to a hard-coded virtual memory address to begin executing code there. In the case of our bootloader, that memory address is 0x9D001000 (i.e., in KSEG0 program memory).
  • If the digital input is low (the button is being pressed), the PIC attempts to establish communication with a "bootloader app" on you computer. The bootloader app will send over your new .hex program, and the PIC will write the program into KSEG0 program flash beginning at 0x9D001000. The next time the PIC is reset (and the button is not being pressed), it will begin executing the program you loaded.

In more detail, if the PIC enters "bootloader mode," (button is pressed at reset) the PIC's bootloader sends the string 'v1.1' over a serial port to your computer to indicate that it is ready to receive data from the bootloader app. An 'E' commands the bootloader to erase the program memory. This must be performed before any program is loaded. A 'B' command checks to see if the program memory is blank. If it is, an 'S' is returned, otherwise an 'F' is returned. A 'P' command lets the bootloader know that packets of 512 bytes are about to be sent to the PIC32. After the arrival of every packet, the PIC32 writes the bytes, in the order that they were received, to program memory. After not receiving a packet for ~1 second, the PIC32 considers the programming process complete and returns an 'S.' (A 'V' character lets the bootloader know that the user would like to see if a hex file matches the current content of the PIC32. This process is standard in most bootloaders, but is not implemented on the NU32v2.) An 'R' command resets the NU32v2 without needing to reset power. This effectively gets the NU32v2 out of bootloader mode.

Important: The bootloader does one other important thing: it sets configuration bits for the PIC. Configuration bits are SFRs that are set on startup and control some basic behavior of the PIC. For example, the configuration bits are used to configure phase-locked loops on the PIC to turn our 8 MHz external oscillator into an 80 MHz system clock, 80 MHz peripheral bus clock, 48 MHz USB clock, etc. Unfortunately, you cannot see the configuration bits being set in the bootloader .S assembly code (below), because the bit settings were chosen in the IDE that compiled the bootloader. In future, we will set these configuration bits in the code itself, for portability and so you can see what's going on. This is also the standard for the MPLAB X IDE.

More information about configuration registers can be found in Section 32 of the Reference Manual.


CODE

  • The NU32v2 bootloader project, written in MPLAB Assembly, with a "hello_world" type blinking LED example tacked on, is provided here: PIC32 code.
  • The NU32v2_serial_bootloader app source code, written in Processing, is provided here: PIC32 code. New for Feb 14 2011- Should crash less. Email Nick if you repeatedly get Error # in the message area.
  • The Mac OSX executable version of the bootloader app is here: NU32v2 serial bootloader for OSX. New for Feb 14 2011- Should crash less. Email Nick if you repeatedly get Error # in the message area.

The NU32v2_serial_bootloader app allows the user to navigate to and select a .hex file. No other file type selection is allowed. The app also creates a button for each available serial COM port when the app loads, so the NU32v2 board must be powered up and the USB cable must be plugged in before the app is run.

Note about serial communication: The NU32v2 board has an FT232RL built on to allow for easy communication over USB with your computer. This chip translates between the PIC's UART (RS-232) and your PC's USB protocol. The communication is viewable as a "virtual com port" on your computer using the driver provided by FTDI. This chip provides an easy way for your computer and the NU32v2 to communicate, for bootloading, debugging, and general communication.

Creating a .hex File Using MPLAB

OK, everything is in place. You've got a bootloader on the PIC and a bootloader app on your computer. Now we just need to create a program to put on the PIC!

Let's use the digital I/O code sample on this page as an example. Following the basic procedure on this page, you create a new project and add the code sample to Source Files. Let's call this file digio.c. You add the procdefs.ld file to Other or Important Files. You choose the C32 Compiler and the device PIC32MX795F512L. Then you choose "Build," and if all has gone well, you should get the message BUILD SUCCEEDED and a .hex file ready to be loaded on to your PIC.

So what was the process, really? Three things happened:

  • a compiler turned your C code into assembly language (you can see the assembly code by going to View->Disassembly Listing in MPLAB);
  • an assembler produced a machine-level "object" file (with suffix .o) for each .c file; and
  • a linker took the .o file(s) and created an executable and linkable format file (.elf) and a .hex file.

The purpose of the linker is to assign specific addresses in memory for each function and global variable defined in your C files (and therefore .o files) according to rules specified in a linker script file ending in .ld (e.g., procdefs.ld). One purpose of your procdefs.ld is to tell the linker to place your code in the virtual memory map at the memory location that your bootloader code will jump to. Thus your procdefs.ld should be designed to work with your particular bootloader code. (Even if you define your procdefs.ld incorrectly, the bootloader will not be able to overwrite itself.) More details on procdefs.ld.

Now let's dig a little deeper. In our C code, we used statements like

#define LED0     LATGbits.LATG12

and

SYSTEMConfig(SYS_FREQ, SYS_CFG_ALL);

So it looks like we defined one constant (LED0) to be equal to a variable, the field "LATG12" of a struct "LATGbits." But where was that struct defined? And where was the function SYSTEMConfig() defined?

The key is the first line of our code:

#include <plib.h>

Like Alice in Wonderland, we're about to go down a rabbit hole...

When you installed MPLAB and the C32 compiler, it installed a large tree of directories under Program Files\Microchip\MPLAB C32 for MPLAB 8.xx, or mplabc32/v.11a (or similar) for MPLAB X. To give you an idea of the directory structure, we highlight some of the important directories and files below:



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; ignore for now)
  • 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
        • system.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/topic
  • pic32mx
    • include (looks similar to the include directory above)
    • lib (contains compiled libraries with .a extensions)
      • mips16
        • .a libraries for DSP functions for different PIC32's
      • proc
        • 32MX795F512L
          • processor.o (definitions of SFR virtual memory addresses for our PIC)


OK, that's a lot of stuff, and it's just a small fraction of all the stuff that's there. But for now, notice plib.h in bold above. When we put include <plib.h> in our program, the compiler searches an "include path" (a list of directories to look under) for a file of this name. By default, our include path allows us to find pic32-libs/include/plib.h. Try opening this file in a text editor. You'll see that all that plib.h does is include the peripheral header files pic32-libs/include/peripheral/{adc10.h, ports.h, system.h, etc.}.

Open pic32-libs/include/peripheral/system.h. The first thing it does is include p32xxxx.h (along with a number of other .h files).
Open pic32-libs/include/p32xxxx.h. This file checks which processor we told the IDE we are using and then includes the header file pic32-libs/include/proc/p32mx795f512l.h. (It also does some other things, like make some standard definitions of register names for assembly code.)
Open pic32-libs/include/proc/p32mx795f512l.h. Whoa! This is a big file defining a lot of the variables and variable types that we use to interact with the SFRs. We've also reached the bottom of the include chain, since this file doesn't include anything else. There is some syntax here that you're probably not familiar with, but we should be able to find something recognizable. Search in the file for LATGbits. About 13% into the file, you will see the following code.
extern volatile unsigned int        LATG __attribute__((section("sfrs")));
typedef union {
  struct {
    unsigned LATG0:1;
    unsigned LATG1:1;
    unsigned LATG2:1;
    unsigned LATG3:1;
    unsigned :2;
    unsigned LATG6:1;
    unsigned LATG7:1;
    unsigned LATG8:1;
    unsigned LATG9:1;
    unsigned :2;
    unsigned LATG12:1;
    unsigned LATG13:1;
    unsigned LATG14:1;
    unsigned LATG15:1;
  };
  struct {
    unsigned w:32;
  };
} __LATGbits_t;
extern volatile __LATGbits_t LATGbits __asm__ ("LATG") __attribute__((section("sfrs")));
The typedef command is creating a new variable type consisting of the union of two structs. The first struct clearly indicates which pins belong to port G: RG0...RG3, RG6...RG9, and RG 12...RG15. Confirm this by consulting Table 1-1 in the Data Sheet. The last line of code declares a variable of type __LATGbits_t, and the name of the variable is LATGbits. To reference bit/pin 12, for example, we would use the C code LATGbits.LATG12. (The :1 indicates one bit, :2 indicates two bits.)
We can also see that LATG, LATGCLR, LATGSET, and LATGINV are declared. The only thing that is missing, now, is the addresses of these SFRs. The compiler needs to know these addresses, because they are fixed by the hardware of the PIC. (Note also that LATG and LATGbits are actually pointing to the same locations in memory.)
If you scroll down further, about 30% of the way through the file you see variable names defined for assembly. These are our answers as to the virtual memory addresses of the various SFRs. If you search for LATG, about 34% into the file you will see that the address is 0xBF8861A0, and the other addresses are offset by four. (Why four?)
But the addresses are still not actually defined here (these addresses are in comments). The addresses are actually defined in pic32mx/lib/proc/32MX795F512L/processor.o, which is the first thing included at the top of our linker file procdefs.ld. The "extern" keyword allows this file to use the variable definitions in another file.
Searching further for LATG12, about 67% of the way into the file you see the code
#define _LATG_LATG12_POSITION                    0x0000000C
#define _LATG_LATG12_MASK                        0x00001000
#define _LATG_LATG12_LENGTH                      0x00000001
These define the offset of bit 12 from the base address of LATG (_LATG_LATG0_POSITION), a "mask" (if you do a bitwise AND of the mask with LATG, all bits will be made 0, except for bit 12, which will be left unchanged), and a length of 1 that indicates that LATG12 is only one bit. These mnemonic constants are available for the programmer's use.
OK, let's pop back up a couple of levels and return to pic32-libs/include/peripheral/system.h. At the end of this file, we see the definition of SYSTEMConfig, which we use in our digital I/O program to optimize the PIC flash wait states and turn on the pre-fetch cache.

Certainly lots of other files get included in your program, but you get the idea. The point is that by digging deeper into the code, you find that the software you are writing eventually interacts with the hardware, e.g., by modifying the bits at specific memory locations (the SFRs that control the peripherals, which correspond to transistors being on or off in the PIC's circuitry). These lower-level include files help you understand how the hardware information in the Data Sheet and Reference Manual is reflected in the software.