CST 337 Lab 1 -- Introduction to C
on the PIC32MZ
During this lab we will will look at some introductory concepts
related to using C for programming embedded systems. We are only
going to do some basic programming in terms of simply flashing LEDs,
but we can use this to introduce using C on the PIC32MZ and some
basic ideas relating to how C statements relate to the generated
machine code and how this can affect performance. Note that we will
also use Timer2 in order to time the LED flashes, so you may want to
review the section 14 in the reference material in order to brush up
on PIC32 timers.
We will begin by writing a simple program to turn one (or several)
of the LEDs on and off.
The HMZ144 processor board has an LED connected to RH2 (port H bit
2). The OT CSE uP1 board, on which the HMZ144 is mounted, also has 8
more LEDs -- connected to RH8 through RH15.
The Lab
1) See the MPLab setup notes section below for a refresher on
setting up the MPLab software and bringing up the source
editor. Instructions on setting up MPLab on your own computer
are also given at the end of the document.
MPLab setup
notes:
- Open MPLab X IDE
- Launch a new project from the File menu or by clicking on
the New Project icon on the toolbar.
- In step 1, select "Standalone Project" from the
"Microchip Embedded" category.
- In step 2, select the PIC32MZ2048ECG144
device from the 32-bit MCUs (PIC32) family.
Note: If the HMZ144 board in
your kit happens to use an EFM processor, select
PIC32MZ2048EFM144instead.
If it uses an EFG processor, select the PIC32MZ2048EFG144
device.
- Also in step 2, select the Real ICE development tool (or
select the ICD 4 tool if appropriate to your kit). You will
need to check the "See all options" checkbox to see these
tools.
- In step 4, (3 is automatically skipped) keep the default
(no optional plugin board selected). (Step 4 is
skipped for ICD 4.)
- In step 5, select (highlight) the latest XC32 compiler
toolchain:X C32 (i.e.: v4.30) [C:\Program
Files\Microchip\xc32\v4.30\bin]
- In step 6, select a project name and browse to create a
new folder on your Z drive. Avoid spaces in the file name
and in the project location path. Note that you can
create a folder on the C drive, and project building might
be faster if you do, but that anything stored on the C drive
will be lost at the end of the day (or if the lab machine
you are working on must be rebooted). So, if you use the C
drive you will have to copy files to and from your Z drive
(or another storage device) each time you use the lab.
Check the "set as main project" box and then click Finish.
- Create a new c source file by right clicking on the "Source
Files" section of your project in the Projects window and
selecting New > Other, Then C > C Source file. In step 2
choose an appropriate file name. Again, avoid spaces in the
file name. You likely won't need to select a new folder. Make
sure to use a ".c" extension. Avoid using an uppercase .c as
the compiler interprets these as C++ files. Click Finish. Put
an appropriate header comment at the top of the file that
includes the name of the Lab, along with your name and date
and a short description of the purpose of the lab.
- Then add the following include:
#include <xc.h>
int main() {
- I recommend setting the "Debug Startup" option under Tools
> Options > Embedded > Generic Settings to "Halt at
Main". Otherwise the debugger will start you code in run mode
each time you make changes and start a debug session.
Alternately you can make sure to set a breakpoint on the first
instruction in main before starting the debugger. You might
also set "Reset" to Main as well. Also, it is a good idea to
uncheck "Open source file in editor when debugger halts".
2) The "#include <xc.h>" statement will select the
appropriate include file to provide names and addresses for the
various processor resources. This is based on the device we selected
in the project wizard. In our case this is C:\Program
Files\Microchip\xc32\v4.30\pic32mx\include\proc\PIC32MZ-EC\p32mz2048ecg144.h
.
Note, substitute PC32MZ-EF and efg144 or efm144 in the above
instructions if your kit uses an EFG or EFM processor. Also do this
in the following instructions.
3) Open p32mz2048ecg144.h with an appropriate editor (i.e.
notepad++) but be careful not to
make or save any changes! Search for LATH. Remember
that LATH is the special function register (sfr) that controls what
appears on port H pins. Note how it is defined as an "extern
volatile uint32_t". The "extern" means its address is defined
elsewhere (in the "sfrs" section). The "volatile" descriptor means
that the contents of the SFR may be changed in ways the compiler
cannot be aware of (which is appropriate for sfrs since they are
attached to I/O devices). Assuming the include file is accurate, the
resource names defined here should be the same as those used in the
Family Reference Manual and Datasheets. Note how a typedef of a
union of two bit field structs is defined for LATHbits. Thus,
LATHbits.LATH0 can be used to reference bit 0 of the port H latch
and LATHbits.w can refer to all 32 bits -- as can LATH alone. Also
note how similar definitions are provided for PORTH and TRISH and
how definitions are provided for LATHSET, LATHCLR and LATHINV and
the set clear and invert addresses for TRISH and PORTH etc.
Note that many of the peripherals, including port H, are actually
16-bit devices (to be backward/hardware compatible with earlier
16-bit PICS) but they are still defined as 32-bit unsigned ints.
There is no issue with this on this processor since all device
addresses are aligned to 32-bit addresses and with a 32-bit bus,
there is no loss of performance in writing or reading an aligned
32-bit value as opposed to a 16-bit value. (We will talk more about
alignment later on in lecture.)
(By the way, while bitfields are appropriately defined in
p32mz2048ecg144.h, such definitions should generally be avoided in
your own code. The order of bitfields (LSB first or MSB first) is
hardware dependent, so code with bitfields in it is not portable.
However, p32mz2048ecg144.h is obviously used for specific hardware
and so, by definition it is not intended to be portable!)
4) Search further down p32mz2048ecg144.h for .extern LATH. This
statement is further needed by the linker to resolve the external
address for LATH. Note how its address is shown using a comment.
Note that the comment does
not define the address, it is just there for our
convenience. Make a note of the LATH* addresses for your future
reference in this lab. The address assignment is actually done in an
assembly file
(...\xc32\v4.30\pic32mx\lib\proc\32mz2048ecg144\p32MZ2048ECG144.S).
The ultimate authority for SFR addresses is the Datasheet.
5) Though I'm fairly sure all SFRs have already been given symbolic
names, assume that one wasn't defined, or that it was defined
wrongly. How could we access it? (Also note that the
development environments for other processors may not provide these
resources.) Consider the following two approaches to defining
and referencing mylatH --
a)
volatile
unsigned int * mylatH;
mylatH = (volatile unsigned int *) 0xBF860730; /* setting up the
address */
*mylatH |= 1; /*
setting bit 0 of LATH */
b)
#define mylatH (* (volatile unsigned int *) 0xBF860730)
mylatH |=
1; /* setting bit 0
of LATH */
Note how the second approach should result in less code and storage
space than the first approach (it doesn't need space for a pointer
and doesn't need to initialize it), though both may ultimately end
up generating the same amount of code, if compiler
optimization is available and turned on. However, we won't
need either of these since the LATH SFR is already defined for us in
p32mz2048ecg144.h and p32MZ2048ECG144.S.
6) Search down further in p32mz2048ecg144.h for _LATH_LATH0_MASK and
note how mask positions and lengths are defined for all the LATH
bits.
7) Finish writing the program to toggle an LED bit. First,
use code like:
LATH |=
_LATH_LATH0_MASK; // Set the LED
LATH
&= ~_LATH_LATH0_MASK; // Clear the LED
to toggle the LED (assuming we wanted to toggle a hypothetical LED
on bit 0 -- you will want to toggle a
different bit because there isn't actually an LED
tied to port H bit 0! (The port H bits that do have LEDs wired
to them are noted at the top of the Lab). Structure your program to
toggle the LED in an infinite while loop. We will first test it by
using the simulator and a watch on the port containing the LED.
Note that you will have to configure the port pins (and only the
port pins) connected to the LEDs for output by writing the
appropriate value to the TRISH sfr. You don't want to
inadvertently configure a port pin for output that is already
connected to the output of another device, creating a conflict and
potentially damaging either the other device or the PIC32. You might
also want to turn off the analog input functionality of the pins
used for LEDs via the ANSELH sfr. (Note that only PortH bits 0,1, 4,
5 and 6 have analog input capability). Doing this is not
strictly necessary in actual hardware, but in an actual system it
would lower the power used by the LED pins. In past years, for some
students, the version of the Simulator being used at that time did
not show PORTH bits toggling unless they also turned off analog
input functionality. Let me know if you see
this happen with the version of MPLab X we are currently using.
8) Select the Simulator
tool from the Hardware tools pane in the Project Properties window
(File > Project Properties or Right click on the project name in
the Project window and select Properties or the wrench button in the
Dashboard). Next, compile and debug your code (Debug Project icon).
Note that first setting the "Debug Startup" option to "Main" as
described in the MPLab setup section above will cause the Debug
Project Icon to stop execution at the first instruction in main
rather than proceeding directly to running (simulating in this case)
your code. If you didn't do this, then your code is now running
(simulating). If this is the case, clicking the pause button will
stop your code and highlight the line in your code that will be
executed next (assuming the program hasn't already exited). The
reset button will (by default) reset the device to the beginning of
main.
9) Note (as seen in the output > (Build, Load, ...) window) that
during the build, all .c files added to the project are compiled to
object (.o) files using xc32-gcc. Then xc32-gcc is used to link all
the object files just created and any that were added to the project
directly (these would appear in the object files list of the Project
window). It also searches libraries for any library routines that
were used in the project. Both libraries explicitly added to the
project (Library Files in the Project window) and standard libraries
(see the gcc command line in the output window) are linked. The
linker is guided by the default linker script
"...\xc32\v4.30\pic32mx\lib\proc\32mz2048ecg144\p32mz2048ecg144.ld".
This file contains the reference to processor.o fie (compiled from
the p32MZ2048ECG144.S file) that contains the sfr address
definitions as well as memory region (i.e. Flash and SRAM) sizes and
interrupt and exception vector addresses. Should we need to provide
additional instructions to the linker this can be done through a .ld
file added to the project (in the Linker Files section of the
Project window). Finally a binary format file (.elf format) is
produced and loaded.
10) Open the Watch window (Window > Debugging > Watches) and
right click in the window to add a new watch or do Debug > New
Watch) and add the PORTH SFR. Single step through the LED
set and clear statements to verify that the bit(s) are being
properly set and cleared. Note that you can also right click on a
variable or SFR reference in your code and select "New Watch".
11) Open the disassembly window (Window > Debugging >
Disassembly) and observe the code that was generated for the LED
"set" and "clear" statements. When you first open the disassembly
window, it does not usually properly sync the display to the current
value of the PC (shown towards the upper right side of the icon
bar). The quickest way to sync the listing to the PC is to do a
"Step Into", but this will also execute the first assembly
instruction at the current PC.
Note how "Step Into" stepping when the source window is highlighted
steps C instruction lines, while "Step Into" stepping while the
disassembly window is highlighted steps assembly level instructions.
"Step Over" stepping, while the disassembly window is highlighted,
steps C instruction lines.
Observe how the LED "set" statement was implemented in the
disassembly and copy this section
of the disassembly listing for your report.
Be careful to include only the
instructions that implement the C LED "set" statement
in your copy. Note that the Disassembly window sometimes shows
duplicate instructions and instructions out of sequence. You can
catch this by paying attention to the instruction addresses which
would normally go up by 4 for each instruction.
(You can also see a disassembly listing using Window >
Debugging> Output > Disassembly Listing File, but this listing
is not correlated to C instructions.)
Go through the assembly instructions that were generated to
implement the C LED "set" statement so that you understand how it
works, noting what SFR addresses are read from or written to as well
as what value is written. SFRs are mapped in the memory space of the
processor so they would be accessed using load (l[whb][u]) and store
(s[whb]) instructions. (Also note that lui is NOT a load
instruction!! It does not read from an address in
memory!)
Since load and store instructions use offset addressing, to find
what addresses are read and written, you will need to add the
sign-extended offset in the instruction to the contents of the
address register used by the instruction. You can observe the
contents of the address register by setting a watch on that register
(i.e. often v0) and stopping the code just before the load or store
instruction is executed.
(Note that Window > Target Memory Views > Execution Memory
also shows the binary encoding of the instructions which can
sometimes be helpful in this process if you remember what part of
the I-type instruction format used for loads and stores holds the
offset.)
12) Change the C LED "set" statement to one that looks like
LATHbits.LATH0 = 1. Then rebuild and debug your code (to make sure
it works). Observe how the set statement was implemented in the
disassembly and copy this portion of the disassembly listing
for your report. Is the implementation
substantially different than what you saw in step 11? How many
instructions are used and how many times is an SFR
is referenced (i.e. read or written with load or store
instructions) in each case (step 11 and step 12)?
Record the answers to these
questions for your report.
Note: make sure that you always
stop the debugger before making code changes and running a new
debug session, or you may end up getting bad results! You
should almost never have more than one "(Build, Load, ...)" output
window active in MPLab X.
Also note that you will have to re-open the disassembly window since
the debugger stop will close it. This is to make sure the
disassembly stays in sync with the source.
13) Change the C LED set statement to one that looks like LATHSET =
_LATH_LATH0_MASK. Debug your code (to make sure it works). Observe
how the set statement was implemented in the disassembly and copy
this portion of the disassembly listing for your report. Is the
implementation different than what you saw in steps 11 and 12?
What are the differences and why? How many instructions are used
and how many times is an SFR referenced in this case? Record the answers to these questions for your report.
14) Note that LATHINV =_LATH_LATH0_MASK can be used to perform both
the LED "set" and "clear" functions. Modify your code
to use a single LATHINV write per pass of the LED toggle loop and use
this approach from this point forward.
15) Now add a delay based on polling Timer2 to your infinite
loop to allow the LED to be on for 1/2 second and off for 1/2
second. Assume an 84MHZ system clock (SYSCLK) and a 21MHZ peripheral
bus 3 clock (PBCLK3 -- which clocks the MZ timers). That is, we are
assuming that there is a divide by 4 between the SYSCLK and
PBCLK3. Note that in the PIC32MZ, SYSCLK clocks the processor
core and PBCLK3 is the clock source for the timers and that the
PBCLK3 is derived from the SYSCLK via a divider that can be set to
divide by 1 through 128. Refer to the PIC32MZ Reference manual
section 14 as needed to set up Timer2. Note that Timer2 is a type B
timer. Configure it for 16-bit un-gated operation with an
appropriate prescale value. You will have to pick a prescaler just
big enough so that the resulting clock frequency doesn't cause the
timer to roll over (reach 2^16 counts before 1/2 second has elapsed.
Then determine, using the prescaler value you have chosen, what
count TMR2 will reach at 1/2 second of elapsed time.
Note that microchip recommends first turning off the timer by
setting the TON bit in T2CON to 0, and setting the initial values of
TMR2, PR2 and other bits in T2CON as needed, then separately setting
TON to start the timer.
As an example, to initialize T2CON, use an approach like the following:
// Set T2CON for a
prescale value of 5 (1:32) along with setting the T32 bit
// configuring it for 32 bit mode
all other bits including TON are 0 (so it will also
// turn T2 off)
T2CON = ((5 << _T2CON_TCKPS_POSITION) &
_T2CON_TCKPS_MASK) | _T2CON_T32_MASK;
(Note that the above approach is much more self documenting than T2CON = 0x0058 and
it is my preferred style!)
Note that your settings will need to be different than the ones
shown here! You won't need 32-bit mode, and the prescaler will
need to be different.
Many students think that they should use a T2CONSET assignment to
turn bits on and T2CONCLR assignment to turn bits off, because they
have learned that this approach only sets or clears the bits that
they want without changing any other bits. And, while this would be
true in the body of the code, it is not needed at
initialization! At initialization we want to set the state of
all the control bits. A simple assignment to T2CON is usually
much more readable and reliable for this purpose.
Be careful with the relative precedence of bitwise logical and shift
operators and any comparison operators!
Note that polling for a set
timer 2 interrupt flag (T2IF) is generally a safer approach than
polling for a specific TMR2 value. When timer 2 reaches the
value in PR2, it sets T2IF and automatically resets TMR2 to
0 and begins counting again. In the PIC32MZ, T2IF is held in
the IFS0 register (in bit 9) and can be tested by ANDing IFS0 with
the _IFS0_T2IF_MASK. Also note
that just because we are using an interrupt flag does not require
us to enable or use interrupts!
16) If you attempt to simulate the adjusted code with a 1/2 second
delay in it, single stepping the delay loop is out of the question.
Even setting a breakpoint after the delay loop and running to that
point will take a very long time since the simulator does not
run in real time! Temporarily reduce the delay (lower the value in
PR2 a lot), and set a breakpoint on the LED toggle statement to make
sure your modified code is getting through the delay loop.
(Breakpoints can be set by single clicking on the line number to the
right of the line where you want to set the breakpoint.)
17) Before going on, set the delay
back to what it needs to be for 1/2 sec. between LED toggles.
Then, consider how we might check, using the simulator, that the
delay is correct. Open the StopWatch tab (Window > Debugging >
StopWatch). Note that the stopwatch records the number of SYSCLK cycles between breakpoints.
Run to your breakpoint several times. (Note that with the delay set
for 1/2 sec., it may well take a long time for the simulator to
reach the breakpoint.)
a) Run one more pass of the loop
and record the number of SYSCLK
cycles it takes to execute one pass of the loop (don't concern yourself with the reported time
values, only the cycle count).
b) Record the PR2 value and the prescaler value that your code is
currently using for your report.
c) Assuming a 21MHz PBCLK3, about how many PBCLK3 cycles do you expect to
elapse over one pass of the timing loop (which should be 1/2
sec.)? Alternately you can determine exactly how many PBCLK3
cycles there should be using your actual PR2 and prescaler values.
d) Assuming an 84MHz SYSCLK, about how many SYSCLK
cycles do you expect to elapse over one pass of the timing loop
(1/2 sec)?
e) Using this information, and the number of SYSCLK cycles you actually
measured using the stopwatch in part a) above, determine
what the actual clock division is between the measured SYSCLK
cycles and the expected PBCLK3 cycles for the current code
configuration (according to the simulator).
Record your data, work and
your answers to these questions for your report. Make sure
you show your work in your report. (Note that the time values shown
in the Stopwatch window are based on a simulator configuration
setting in project properties that defaults to a 1MHz clock cycle.
This configuration value can be changed, but it is often simpler
just to work from the cycle counts and ignore the reported time
values.)
18) In the PIC32MZ, the PBCLK3 divider is controlled by the PB3DIV
register. By default, PB3DIV is set to divide the system clock
by 2. To change it so it divides by 4, add the following code to
your program above the code that sets up timer 2.
SYSKEY = 0;
// Ensure lock
SYSKEY = 0xAA996655; // Write Key 1
SYSKEY = 0x556699AA; // Write Key 2
PB3DIV = _PB3DIV_ON_MASK | 3 & _PB3DIV_PBDIV_MASK; // 0 = div
by 1, 1 = div by 2, 2 = div by 3 etc up to 128
SYSKEY = 0; // Re lock
Note that the PIC32 architecture wants to make sure that we
don't accidentally modify peripheral clocks. This is the reason
behind the SYSKEY statements above. In order to modify PBxDIV
registers (along with several other registers), they must first be
unlocked. Unlocking is done by writing Key1 followed by Key2 to the
SYSKEY register. Exactly how soon Key2 must be written
following Key1 is not precisely defined (anywhere that I can find)
in the documentation, but the manuals indicate that doing this with
back to back C statements is sufficiently fast, but that if we were
to put another C statement between the Key 1 and Key2 writes, it may
no longer work. Writing any other value to SYSKEY will lock the
registers again. Writing 0 to SYSKEY before writing Key 1 helps to
make sure that an unlock sequence wasn't already partially started
in previous code. Also note that the unlock sequence should not be
run when the sequence could be interrupted. In our case, interrupts
have not been enabled, so this is not an issue.
19) Once the above code has been added, rebuild and re-run the code.
Ensure that the PB3DIV register has its ON bit set and its PBDIV
field set to 3. A quick way to do this when the debugger is paused
at a point the the code after the PB3DIV assignment statement is to
hover the mouse over the PB3DIV text in the code (this is referred
to as "bitfield mouseover" in MPLab X). Now, repeat the
stopwatch measurement. Did the measurement change from what
you found in part 16?
Now, comment out the write of Key1 and Key 2 to SYSKEY, and rebuild
and re-run the code. Was PB3DIV modified according to the simulator?
Should it have been? What two things does this tell you
about the simulator?
Record your stopwatch measurement
and your answers to all these questions for your
report.
20) Before we run the code in actual hardware, we need to take care
to also configure the system bus clock. The PIC32 processor is
capable of running with many different clock configurations, but the
initial clock configuration must be determined before the processor
boots up since the processor needs a clock in order to do
anything. In the PIC32 the initial clock configuration along
with other initial settings are controlled by the bits in DEVCFGn
registers. These values in these bits are determined when the device
is programmed (specifically when the boot flash memory is
programmed). MPLab X has a default set of values that it will
use to configure the PIC32MZ. The Configurations Bits window
(Window > Target Memory Views > Configuration bits) shows the
current configuration bits settings, and could be used to see what
the default settings are at this point. Unfortunately, the default
settings are not compatible with our system.
21) The values to program in the configuration registers can be set
by #pragma config statements in the code itself. This is a good idea
since clock (and other initial) settings are often integral to a
project and not having these settings be recorded in the source code
can lead to problems down the road. We only need to use #pragma
configs for configuration bit values that are different than the
MPLab X defaults.
// PIC32MZ2048ECG144, EFM144 or or EFG144 based HMZ144 board
Configuration Bit Settings
// DEVCFG2
#if defined(__32MZ2048EFG144__) || defined(__32MZ2048EFM144__)
#pragma config FPLLIDIV =
DIV_4 // System
PLL Input Divider (4x Divider) for 24MHz clock (Rev C1 board w
EFG) 24MHz/4 = 6MHz
// also 24MHz clock rev C board w EFM (weird - went back to C. rev
D also is EFM but with Osc)
#pragma config UPLLFSEL =
FREQ_24MHZ // USB PLL Input Frequency Selection
(USB PLL input is 24 MHz)
#else
#pragma config FPLLIDIV =
DIV_2 // System
PLL Input Divider (2x Divider) for 12 MHz crystal (Rev B and C
boards w ECG) 12MHz/2 = 6MHz
#pragma config UPLLEN =
OFF
// USB PLL Enable (USB PLL is disabled)
#endif
#pragma config FPLLRNG =
RANGE_5_10_MHZ // System PLL Input Range (5-10 MHz Input)
#pragma config FPLLICLK =
PLL_POSC // System PLL Input Clock
Selection (POSC is input to the System PLL)
#pragma config FPLLMULT =
MUL_112 // System PLL
Multiplier (PLL Multiply by 112) 6MHz * 112
= 672MHz
#pragma config FPLLODIV =
DIV_8 // System
PLL Output Clock Divider (8x Divider) 672MHz
/ 8 = 84MHz
// DEVCFG1
#pragma config FNOSC =
SPLL
// Oscillator Selection Bits (Primary Osc (HS,EC))
#pragma config FSOSCEN =
OFF
// Secondary Oscillator Enable (Disable SOSC)
#if defined(__32MZ2048EFG144__)
#pragma config POSCMOD =
EC
// Primary Oscillator Configuration EC - External clock osc
// Rev C1 board w EFG uses an Oscillator (Rev D boards too))
#else
#pragma config POSCMOD =
HS
// Primary Oscillator Configuration HS - Crystal osc
// Rev B and C (w ECG or EFM) use Crystals
#endif
#pragma config FCKSM =
CSDCMD
// Clock Switching and Monitor Selection (Clock Switch Disabled,
FSCM Disabled)
#pragma config FWDTEN =
OFF
// Watchdog Timer Enable (WDT Disabled)
#pragma config FDMTEN =
OFF
// Deadman Timer Enable (Deadman Timer is disabled)
#pragma config DMTINTV =
WIN_127_128 // Default DMT Count Window Interval
(Window/Interval value is 127/128 counter value)
#pragma config DMTCNT =
DMT31
// Max Deadman Timer count = 2^31
// DEVCFG0
#pragma config JTAGEN =
OFF
// JTAG Enable (JTAG Disabled)
#pragma config ICESEL =
ICS_PGx2 // ICD/ICE is on
PGEC2/PGED2 pins (not default!)
Add the above pragmas before the first #include in your code and
then re-make it. Open the Configurations Bits window (Window >
Target Memory Views > Configuration bits) and verify several of
the above settings.
(Note that changes can be made in the configurations bits window (in
the Options column), but that doing so does not automatically make
changes to the pragmas in your code. However, the "Generate Source
Code to Output" button in this window can create a set of pragmas
that you can cut and paste into your source code.)
In the above, we only included pragmas for configuration bits that
were different from the MPLab X defaults, and this is the approach
that we will use in lab. However, it should be noted that it would
be better in an industrial setting to include pragmas for all the
configuration bits, since using our approach would rely on new
versions of MPLab X to always have the same defaults.
For the ECG144 processor, The POSCMD, FNOSC and FPLLICLK pragma bits
put the Primary oscillator in HS mode (means the Primary oscillator
is driven by an external crystal) and connects the primary
oscillator to the system PLL. The external crystal on the ECG
based HMZ144 board has a frequency of 12MHz. The external
crystal is then divided by 2 (FPLLIDIV). The FPLLRNG bits
configure the PLL to operate with an input clock between 5 and 10
MHz (6 MHz in our case). The FPLLMULT bits set the PLL to multiply
its input clock by 112, which produces a 672MHz clock at the PLL
output. This value is then divided (FPLLODIV) by 8 to produce an
84MHz system clock. The other bits set the correct ICD
channel, disable the secondary oscillator, USB PLL and the watchdog
and deadman timers etc.
For the EFG144 and EFM144 processors the settings are similar, but
differ mainly where needed to account for the fact that the EFM
based HMZ144 board uses a 24MHz external crystal and the EFG based
HMZ144 board uses a 24MHz external clock chip rather than a 12MHz
external crystal.
22) Before going to actual hardware we will experiment
with the Simulator's Logic Analyzer.
- Stop the debugger, if it isn't already stopped, since project
properties can't be changed while debugging.
- Under Project Properties > Conf: > Simulator >
(Option Category) Trace , select "Instruction Trace" for the
Data Collection Selection. Also select Reset Data File on Run.
Then click "OK".
- Select Window > Simulator > Logic Analyzer
- Click the "Settings" button (it uses a hammer and wrench
icon). In the "Add/Remove Pins" window that opens, select the
port output pin (i.e. RH2 or etc.) that you are toggling,
and add it to the Selected Pins list (click the right arrow).
Then click OK.
- Note that you will need to adjust your PR2 value (down to
around 10) and/or increase the trace buffer size (File >
Project Properties > Conf:Simulator > Trace) in order to
see several toggles.
- Remove any breakpoints from your code. Restart the debugger
and run then halt the simulation after a pause (long enough to
have the LED toggle several times).
- The Logic Analyzer window should show a square wave.
Show the result to the professor. Note: MPLab X version 5.35 broke the trace and
logic analyzer features. This appears to be fixed in our
version (5.50 and later). Let me know if you have problems.
23) Stop the debugger, and close the Logic Analyzer window.
Make sure the configuration bits are set as indicated in part 20.
Connect up the Real ICE (or ICD 4) emulator and the target hardware
following instructions given by the professor. Remember to
connect the ICE to the target board (if it isn't already), then power up the ICE before
powering up the target hardware (and when shutting down, remember
to power down the target hardware before powering down the ICE).
Also, make sure to power the target board from the standalone USB
power supply. Don't use a PC USB port
to power the target board. Select the Real ICE
(or ICD4 if that is the debugger in your kit) as the debugger
hardware. Reset your timer delay (PR2) to the right value for
a 1/2 sec delay. Re-build your project and program the target
hardware. Reset and run. Observe that the LED(s) toggle
as required. Make sure the timing is correct -- that is, make
sure that the LED flashes about once each second. Debug as needed.
Use the debugger to check to make sure that PB3DIV has the right
value. Demonstrate your running
program.
24) Verify that the unlock sequence must be executed in order
to change the PB3DIV value. Record
how you did this and
your conclusion for your report.
25) Use the Stopwatch to time one iteration of the timing loop. When
the Real ICE or ICD 4 is the debugger, there must be two breakpoints
for the stopwatch to function. One will start the stopwatch and the
other will stop it. Set the first breakpoint on the instruction just
after the LED toggle and the second breakpoint on the LED toggle
instruction itself, with the intent to time from one LED toggle to
just before the next one, which would be close to the time between
toggles. (If there isn't an instruction after the LED toggle
instruction, you may need to rearrange your code or add a dummy
instruction). Using the properties button in the Stopwatch
window, select the breakpoint on the instruction after the toggle
for the start condition and the other for the stop condition. Also,
uncheck the "Start condition will cause the target device to halt"
option.
Note that the Real ICE and ICD 4 stopwatch counts SYSCLK
cycles, like the simulator stopwatch, but unlike the
simulator stopwatch, it is a 24-bit
timer so it may roll over when timing this loop. (I'm
not sure how many bits are available in the simulator's stopwatch,
but it appears to be more than 32.)
- Record the system cycle count reported by the stopwatch in
your report.
- From this value alone (i.e. without accounting for rollovers),
what is the time between LED toggles? Show your work.
- Does the stopwatch measurement alone correlate with what you
observe by eye?
- Determine how many times the stopwatch must have rolled over
while timing the loop. Show your work.
- Based on this, how many SYSCLK cycles actually elapsed for one
pass of the loop?
- Does the fact that the stopwatch rolls over make it unusable
for verifying the accuracy of the loop timing?
Record your work, results and
answers, for your report.
26) In the breakpoint tab (Window > Debugging > Breakpoints)
right click on a breakpoint and select Customize. Then experiment
with the "pass count" options. What does this do? Record your answer for your report.
27) Debug > New breakpoint dialog (or right click in the
Breakpoints Tab) allows you to set breakpoints on Data (data memory
accesses), Address (instruction or data reference to an address) or
various other system events. Right clicking on a variable name in
the code window also lets you set data breakpoints. Experiment with
data breakpoints to see if they can be set and work for both global
and local variables and for SFRs. Record
the results for your report.
Observe the disassembly window while running to these
breakpoints along with choosing "break on read", "break on write"
and "break on read or write" options for them.
Note that with local variables you can't use the right click option
to set them. You will need to use the breakpoint dialog to do this.
You will have to manually supply the local variable's address which
you can get by hovering over the variable in the code window, or
through the watch window when debugging. To get the correct
address requires that the variable be "in scope" meaning that you
have to be inside the routine that declares it (and in this
version of MPLab X, possibly even at or below the first point in the
code where it is used). Also, do not
type anything in the Symbol box in the set breakpoint dialog for
local variables! Be
careful whenever you re-compile since the variable's address can
change if the code is modified, but the data breakpoint would still
have the old address!
With SFRs, not all can be selected by name via the Data breakpoint
dialog (i.e. T2CONSET or LATHINV) but breakpoints for these can be
set (as with local variables) via an explicit address.
Also note that some global variables may be written to by startup
code that runs before main begins.
You can also set Sequential breakpoints (run till the first
breakpoint in a sequence is triggered, then look for the next
breakpoint) and Anded breakpoints (break only when a code breakpoint
and a data breakpoint are triggered) through the Breakpoints dialog.
This is accomplished through right clicking on a breakpoint in the
breakpoints tab and selecting "Complex Breakpoint".
28) What is the difference between how software and hardware
breakpoints are implemented? You can use the Real ICE user's guide
on the class webpage for information about this. Also, how many
software breakpoints, how many hardware data breakpoints, and how
many hardware instruction breakpoints are available in the PIC32MZ
under Real ICE? (Give a separate number for each category.)
You can find some of this information on the Dashboard window under
Debug Resources. Software breakpoints can be enabled under
Project Properties > Real ICE > Debug Options or with the
resources button in the Dashboard. Why, as reported in the Real ICE
User's guide, would using software breakpoints impact "device
endurance"? Include your answers
in your report.
Note that, at least in versions 5.xx of
MPLab and forward, the Real ICE and ICD 4 provide no support for
software breakpoints in our PIC32MZ processors. I am not sure if
this is because that support has not been developed yet for the MZ
processors, or maybe Microchip has decided that the MZ processors
provide for enough hardware breakpoints so that software
breakpoints are not needed. Answer these questions assuming
that software breakpoints were supported.
29) Power down the target board, then the Real ICE (or ICD4) and
return to the simulator. The Simulator's Trace function (that was
used to do the logic analyzer earlier) is used primarily to allow
code execution to be traced. Turn tracing on as described in the
first bullet in item 21 above. You might also want to shorten the
delay loop. Run one pass of the loop and look at the results in the
Trace window (Window > Debugging > Trace). (Note that right
clicking on the trace window provides an option to clear the trace
buffer). By examining the trace window, determine what each of the
SA, SD, S2A, S2D, DA and DD headings in the Trace window likely
mean. Record your answer
for the report. Note that the Real ICE can also
trace, but extra hardware setup is required to support this.
Report
A formal report is not required, simply turn in the following items.
- Provide complete answers to the questions posed in and results
from each of parts 11, 12, 13, 17, 19, 24, 25, 26, 27, 28, 29,
and 30 (below).
- Also answer the following question:
30) Why doesn't the LATHSET SFR have bit definitions (say bit0,
bit1 etc) in p32mz2048ecg144.h like LATH (has LATH0, LATH1 etc)?
In other words,
a) Why does it not make sense to
say that we want to set bit 0 of the LATHSET SFR (i.e.:
LATHSETbits.bit0 = 1)?
b) What is wrong with the statement LATHSET |= 1? (Hint, check
what the Family Reference Manual says will be returned when
reading LATxSET/INV/CLR SFRs). Specifically, --
- What will this instruction actually do?
- What did the programmer who wrote this instruction
probably want it to do?
- Copy and paste your final commented source code into the end
of the document you used for your answers. Use a mono-spaced
font (i.e. 10 pt Lucida Console) for the code section. Then save
or print this as a pdf and submit it to your lab instructor
electronically via Canvas. (Note, if you choose to print
code from MPLab X to pdf, I would ask you to please set the
print options to change the default text font size to 12 points
mono-spaced, turn on line wrapping, turn off the border
and set the set the background color to white. The default
header and footer settings are fine.)
Setting up
MPLab on your own computer
Versions 6.00 of MPLAB X and version 4.30 of the XC32 compiler
are used in the lab. It would be best to use these same versions
on your own machine. Previous and subsequent versions
have bugs and issues that may not be covered in lab
instructions.
- Navigate to https://www.microchip.com/en-us/tools-resources/archives/mplab-ecosystem
. Find MPLAB X v6.00 in the "MPLAB X IDE Archives" section
(you can search for 6.00 in the search box at the top of this
section or go to the last page in the section). Click on
the link for MPLAB X v6.00 column for your operating system.
Then install it.
- Note that version 6.00 is only available for 64-bit
operating systems. If you have a 32-bit OS, you will need to
get version 5.35 of MPLAB X from the MPLAB X IDE Archives
area.
- Next, scroll down to find the "MPLAB XC32 Compiler
Archives" section of the page. Then, find, download and
install version 4.30 of the XC32 C-Compiler as
appropriate for your operating system.