Getting Started with MCU Projects Using MPLAB® Code Configurator (MCC) Melody
Debugging an MCC Melody Project
Editing an Existing MCC Melody Project |
In this section, you will cover the fundamentals of debugging in MPLAB® X IDE using the MPLAB Code Configurator (MCC) Melody project and the associated Curiosity Nano Board.
Debugging with the Data Visualizer
There are a few strategies at your disposal to debug your application's code and ensure its seamless performance. Let’s examine one method that utilizes the integrated Data Visualizer plugin .
The PIC18F56Q71 Basic Universal Asynchronous Receiver/Transmitter (UART) Comms project example can be used to demonstrate this. The UART2_Write() function is used to transmit the character 'S' to the Data Visualizer's serial monitor, and from the serial monitor to toggle the LED when the character ‘T’ was received by the PIC18F56Q71. Essentially, this is a form of debugging, confirming that the pushbutton activation or specific character sent from the Serial Monitor triggered certain actions within the application. This technique can be employed to monitor the execution of specific code segments or the initiation of certain subroutines.
Now that you've seen how printf can be used, disable printf capabilities and regenerate the Melody code to conserve memory space.
If you would like to learn more about the Data Visualizer, please check out the Visual Debugging with MPLAB Data Visualizer class on Microchip University.
MCU Debug Interface
To debug a microcontroller, debugging hardware is needed. You can use a standalone programmer/debugger (e.g.: MPLAB PICkit™ 5), or use an on-board programmer/debugger included on many of Microchip's evaluation boards (e.g.: PIC18F56Q71 Curiosity Nano Demo Board.
Opening the Users Guide will show you the interface features of the board:
- A debugger interface that facilitates programming and debugging of the PIC18F56Q71 within the MPLAB X IDE.
- A mass storage device that enables drag-and-drop programming for the PIC18F56Q71.
- A virtual serial port Communications Device Class, or CDC for short, interface connected to a UART on the PIC18F56Q71 to communicate with the target application via terminal software. This was used to transmit the character from the Discover example used in previous sections of this course to visualize in the Data Visualizer tool.
- A Data Gateway Interface (DGI) specifically designed for use within the Data Visualizer tool to display code instrumentation with logic analyzer channels to visualize the program's flow.
Debugging a microcontroller using these methods will require the debugger tool interface with specific pins on the target device.
The ICSP circuitry is embedded within the PIC18 device used and aids in both programming and debugging. Scrolling to the ICSP section of the datasheet, you can see that three pins are used for debugging: a data line (SDL), a clock line (SCL), and a master clear line (MCLR).
Only the data and clock lines are needed when purely programming the microcontroller.
Making sure that the Curiosity Nano board is connected to an available USB port on the computer, let’s go ahead and configure MPLAB X IDE for debugging.
Building for Debug vs Building for Production
Earlier in this course, you used the Make and Program device button , found at the top of MPLAB X IDE, to program the PIC18F56Q71 device.
However, to debug the project, you will need to build it specifically for debugging purposes. Debug Build mode is used during the developmental and testing phases of the project. This mode includes additional debugging information in the output file, enabling the use of debugging tools such as breakpoints, watch variables, stepping into/over functions, and more which will be discussed shortly in greater detail.
To build the project and program the device for debugging, use the Debug Project button .
Basic Debugging with Breakpoints
Let’s go ahead and set up this project for basic debugging. Like the Make and Program Device button, you can also build our project for debugging and start a debugging session by clicking on the Debug Project button located at the top of the MPLAB X IDE. Let’s go ahead and do that.
The project will undergo a build process tailored for debugging, as previously mentioned, and the PIC18F56Q71 microcontroller will be programmed. Once programming is completed, the application will be running on the microcontroller.
MPLAB X IDE will now display new buttons associated with the debugging process in the toolbar. This section of the toolbar, like any other section, can be moved around to make it easier to access by grabbing the dotted vertical separator next to the section and dragging where it works best for your needs. We’ll discuss each of these buttons shortly.
There is a function called UART2_IsRxReady() that gets checked as part of the if statement condition check. A breakpoint can be used to visualize and verify this function call. Go ahead and navigate to the function code to do this.
If you click in the margin next to the line of code that returns the value of the RXBE bit, or Receive Buffer Empty status, inside of the UART2 FIFO register, a little red box appears indicating that you've set a breakpoint.
A breakpoint is a debugging tool used to temporarily stop the execution of a program at a specific point. Think of it as a trap that you set; when the program hits this trap, it pauses, allowing you to examine the current state of the program, check the values of variables, and understand how the program is running up to that point. This helps in finding and fixing errors or understanding the program's behavior.
More on this in a moment. For now, select the Dashboard tab for the project, which is situated next to the Navigator window, and locate the Debug Resources information.
What the window says here is that a breakpoint has been used and there are two left that can be used as either program or data breakpoint types. Program breakpoints halt the execution of a microcontroller's code when a specific line is reached, while data breakpoints trigger a stop when a certain condition, like a specific value, is met in a microcontroller's register. Both are hardware breakpoints that rely on the microcontroller's built-in mechanisms to monitor and respond to these events. You will only use program hardware breakpoints in this class.
A third type of breakpoint called a software breakpoint has been disabled by default. These types are different than hardware breakpoints in that they can interrupt program execution at a specified point without the need for dedicated hardware resources. Software breakpoints are implemented by altering the program code itself, typically by inserting a specific instruction that causes the program to halt.
This allows for a greater number of breakpoints compared to hardware breakpoints, which are limited by the microcontroller's debugging capabilities. While software breakpoints are versatile, they are also not covered in this class. If you’re interested in learning more about data hardware or software breakpoints, further information can be obtained from the docs folder inside of the MPLAB X IDE installation directory or through more in-depth courses available through Microchip University.
At the time of this class’s development the PIC18F56Q71 Curiosity Nano board doesn’t support software breakpoints at all and are disabled.
You can restart program execution by clicking on the Continue button in the debugger toolbar.
As mentioned earlier, code execution should stop again at that line where the breakpoint is since this function is called every time through the while loop as the UART RX buffer is checked or in other words polled.
Remove the breakpoint by clicking on it in the left side margin and return to main() inside of the main.c source file.
Inside of the if statement that checks the UART2_IsRxReady(), again hold CTRL and click on the UART2_Read() to go to the definition inside of uart2.h. Another way to find how various macros and functions are used is to search the project. Right-click on the UART2_Read to the right and select Find Usages which will open the Find Usages dialog to search all instances of the UART2_Read macro inside of the project when Find is clicked.
Return to Data Visualizer now and type anything into the terminal window. The debugger will halt program execution and bring me right to uart2.c file so you can confirm that the return is about to be executed.
Clicking on a button inside of the debugger toolbar called Step Into will execute the return line and then stop execution. The green arrow moves to the next line in code which is the curly brace to indicate what bit of code is going to be executed next.
Clicking the step into button again moves to the next line of code to be executed which is the next if statement inside of the main function. The debugger will display the file that the current line of code being executed is located in.
I’ll click on the continue button and enter another character into the data visualizer terminal. Program execution stops again at the breakpoint.
Viewing Variable and Register Values with the Watches Window
The Watches window lets me monitor and evaluate the values of variables, registers, or expressions in real time as the program runs. It is used to see how data changes, enables the modification of variable values on the fly, and assists in testing program behavior under various conditions without altering the source code. Again, a complete discussion is in the resources mentioned earlier should you like to do more elaborate debugging.
Looking at some of the other buttons in the debugger toolbar, next to the Continue button is the Reset button which is used to Reset the program back to the beginning of code execution which is the state the Microcontroller will be in when it is either powered on or a RESET condition has occurred. The reset condition is outside of the scope of this class, but a detailed discussion can be found in the device datasheet.
Next to the RESET button is the Pause button which stops code should you wish to see what line is currently executing.
Now, the way this demo is configured is that when the UART receives an upper-case letter ‘T’, then a conditional statement calls a macro which will Toggle the LED on the Curiosity Nano board ON/OFF by driving the associated pin LOW or HIGH.
Click on the Continue button to begin program execution once again.
Next, check the if statement that handles what happens when the switch is pressed. As previously mentioned, when the switch is pressed, the voltage level on the associated pin changes from a HIGH (3.3V) to LOW (0V). When this happens the UART2_Write() is called and an upper case ‘S’ is sent.
To visualize this, first get rid of the breakpoint in the UART2_Read() and set a breakpoint in the main() next to the UART2_Write().
Now, you will notice that the upper case ‘S’ does not appear in the Data Visualizer terminal. This is because the character is transmitted over the UART and subsequently the USB both of which have timing requirements. Obviously, clicking the step into button will break those requirements.
Clicking the Step Into button again will take program execution into the DELAY_milliseconds() routine inside of the delay.c file.
This code will execute a while loop that decrements the milliseconds variable by 1 each time through the loop executing a macro called __delay_ms(1) until it is zero at which point it will exit the loop and return.
Now you could hit the step into button 349 more times if you wanted to. However, in the interest of time I’m just going to click on the Step Out button which will continue executing the program until the current function completes and returns to the calling function in main() or in software terms allow me to quickly exit the current scope and resume execution at the point where the function was called. This is a way to exit a large function should you only wish to see some of it’s execution.
The toolbar also includes advanced features such as Run to Cursor , Set Program Counter at Cursor , and Focus Cursor at Program Counter . While these functions extend beyond elementary debugging, comprehensive explanations of their capabilities can be found in the MPLAB X IDE documentation.
Common Debugging Problems
There is such a thing as a broken breakpoint. So, for example, if you go to the main.c source file and try and create a breakpoint at the while loop here. You’ll notice that the breakpoint icon in the left margin is broken.
The reason for this is that breakpoints are applied to the associated machine code instruction which is the very low-level language that any microcontroller understands. Programs like C and Assembly essentially translate human language into machine code. As part of the translation process there is additional information created as part of the C language to make it more readable to us humans which doesn’t really translate directly into a specific machine code instruction. A while statement is one of those translation mismatches. To overcome this situation, a common practice is to use what’s called a NOP instruction.
This is a machine language instruction that basically does nothing. However, you can place a NOP instruction right after the line of code generating the broken breakpoint as a workaround. Note that the project needs to be rebuilt for debug as this line of code needs to be added into hardware’s program memory.
To fix this, you would then need to go into the project properties by clicking the Project Properties button in the Dashboard and disable software breakpoints.
In this case by selecting the PKOB listing under the Configuration listing in the Project Properties dialog, then Debug Options from the Option Categories drop-down and deselecting the Use Software Breakpoints radio button and then clicking Apply. Keep in mind to continue debugging, you will need to disable the extra breakpoints.