2.14 Debugging 1: Break Points, Stepping, and Call Stack
This section will introduce the debugging capabilities of Microchip Studio, both as video (linked below) and hands-on document. The main topics are; breakpoints, basic code stepping using the Breakpoint and Callstack-Windows and adjusting project compiler optimization settings.
Video: Microchip Studio Debugging-1
The same code as the one created in section Editor: Writing and Re-Factoring Code (Visual Assist) is used.
- Set a breakpoint on the line getting the
switch state, as indicated in Figure 2-38.Info: A breakpoint can be placed at a line of code by:
- Clicking the gray bar on the left edge of the editor window
- In the top menu bar, go to Debug → Toggle Breakpoint
- By pressing F9 on the keyboard
- Launch a debug session . The
breakpoint will be hit when clicking the switch (SW0) on the Xplained Pro kit.
Observe that execution halts when hitting the breakpoint, and the execution arrow
indicates that the line of code will execute where the breakpoint is placed. See
Figure 2-39.Tip: If a breakpoint is hit in a file that is not currently open, Microchip Studio will open the file in a temporary pane. A file containing a hit breakpoint in a debug session will always be in focus.
- Since most of the logic of the program is
handled only when an ISR is processed, it is now possible to check the logical flow
of the program. If the switch is pressed and then released when the ISR is hit -
what will be the state of the switch that the function returns? The assumption is
that since pressing the switch triggered the interrupt, that switch will be set as
pressed, and the LED will thus be turned ON. Code stepping can be used to check this assumption. The key buttons used for code stepping are illustrated in the table below, found in the top menu bar or the Debug menu. The corresponding functionality and keyboard shortcuts are outlined in the figure below.
Table 2-4. Microchip Studio Button Functionality (Code Stepping) Button Functionality Keyboard Shortcut Step Into Function Call F11 Step Over F10 Step Out of Function Call Shift + F11 Run to Cursor Ctrl + F10 Issue System Reset Todo: Find out what state is returned if the switch is pressed and released when hitting the ISR. Is our assumption correct that since pressing the switch triggered the interrupt, it will be set as pressed, and the LED will turn ON?
- A window or view to keep track of the
breakpoints in a program is needed. The Quick Launch bar performs a search of the
Microchip Studio user interface menus, which is demonstrated below by comparing
Figure 2-42 and Figure 2-43. Note that each of the hits in the
Quick Launch bar is from 'break' related entries in the Debug menu.
Open the Breakpoints window by clicking on the top result (Debug → Windows → Breakpoints). The Breakpoints window lists all the breakpoints in the project, along with the current hit count, as depicted in Figure 2-44.
Tip: Temporarily disable a breakpoint by unchecking the checkbox next to a breakpoint in the list.Tip: The Disassembly view can be conveniently displayed alongside the source code, as demonstrated in Figure 2-45.
- Following from the previous section, set a breakpoint on the LED_on() function. Then trigger the breakpoint so that it is hit.
- Open the Call Stack window by typing 'Call'
in the Quick Launch bar, selecting Debug → Windows → Call Stack, as
shown in Figure 2-46. Note: A debug session needs to be active to open this window.
- Expect the Call Stack showing
LED_set_state() as the caller of
LED_on(), as this is how the code is written.
However, in the Call Stack window, _vector_4 is listed as the caller
(see Figure 2-47) because of compiler
optimization. Info: The call order is different because of the compiler optimization. This code is relatively simple to follow, and it is possible to understand what is going on even though the compiler was optimized and made subtle changes to what is expected. In a more complex project, it can sometimes be helpful to disable the compiler optimization to track down a bug.Note: To see why the Call Stack shows that it comes from _vector_4 initially, click on PORTB_PORT_vect and look in the context field for the definition, as shown in Figure 2-48.
- Stop debugging by clicking the Stop Debugging button or pressing Shift + F5.
- Open the project settings by going to Project → <project_name> properties or pressing Alt + F7. Go to the Toolchain tab on the left menu, as in Figure 2-49.
- Under AVR/GNU C Compiler →
Optimization, set the Optimization Level to None (-O0)
using the drop-down menu.Warning: Disabling compiler optimization will result in increased memory consumption and can cause changes in execution timing, which can be important to consider when debugging time is a critical code.
- Launch a new debug session and break code execution inside LED_on().
- Observe the Call Stack. It should now adhere to how the code is written and list LED_set_state() as the caller of LED_on(), as shown in Figure 2-50.
Code Used for Debugging 1
/* LED is turned on when switch is pressed, LED is turned on (via a pin change interrupt). MY_mistake() written to demonstrate Attach to Target, is commented out, to avoid hanging project unintentionally. From the schematics, it is concluded that: The LED can be turned on by driving PB4 low. SW0 is connected directly to GND and to PB5 through a current limiting resistor. SW0 does not have an external pull-up resistor. SW0 will be read as '0' when pushed and as '1' when released, if the ATtiny817 internal pull-up is enabled. */ #include <avr/io.h> #include <stdbool.h> #include <avr/interrupt.h> void LED_on(); void LED_off(); bool SW_get_state(); void LED_set_state(bool SW_state); int main(void) { PORTB.DIRSET = PIN4_bm; PORTB.OUTSET = PIN4_bm; PORTB.PIN5CTRL |= PORT_PULLUPEN_bm | PORT_ISC_BOTHEDGES_gc; sei(); while (1) { } } #pragma region LED_functions void LED_on() { PORTB.OUTCLR = PIN4_bm; //LED on } void LED_off() { PORTB.OUTSET = PIN4_bm; //LED off } void LED_set_state(bool SW_state) { if (SW_state) { LED_on(); } else { LED_off(); } } #pragma endregion LED_functions bool SW_get_state() { return !(PORTB.IN & PIN5_bm); } /* void My_mistake() { while(1) { asm("nop"); } } */ ISR(PORTB_PORT_vect) { uint8_t intflags = PORTB.INTFLAGS; PORTB.INTFLAGS = intflags; //My_mistake(); bool SW_state = SW_get_state(); LED_set_state(SW_state); }