Debugging 2: Conditional- and Action-Breakpoints

This section covers more advanced debugging topics with Studio 7 both as video (linked below) and hands-on document. The main topics are how to modify variables in the code, conditional- and action-breakpoints, as well as memory view.

Getting Started Topics

Video: Debugging - 2

Todo: Use Atmel Studio to inspect and modify the contents of variables in the code.
  1. 1.The code (see below) used is the same as the one developed in section Editor: Writing and Re-Factoring Code (Visual Assist). The SW_get_state() function has just been replaced with the following code (note also the change in return value type):
    uint8_t SW_get_state(void)
    {
        static uint8_t SW0_prv_state  = 0;
        static uint8_t SW0_edge_count = 0;
    
        uint8_t SW0_cur_state = !(PORTB.IN & PIN5_bm); /* Read the current SW0 state  */
        if (SW0_cur_state != SW0_prv_state)            /* Check for edges             */
        {
            SW0_edge_count++;
        }
        SW0_prv_state = SW0_cur_state;                /* Keep track of previous state */
         
        /*
         * Report the switch as pushed when it is pushed or the edge counter is a 
         * multiple of 3
         */
        return SW0_cur_state || !(SW0_edge_count % 3);
    }
    
    Info: This code will count how many times the SW0 push button has been pressed or released. The return statement has also been modified to always report the button as pushed if the SW0_edge_count variable is a multiple of three.
  2. 2.Go to Debug → Disable All Breakpoints to disable all breakpoints. This should be reflected by all the checkboxes becoming unchecked in the Breakpoints window.
  3. 3.Launch a new debug session by clicking the Start Debugging button .
  4. 4.Push SW0 on the kit several times and observe how the changes to the code have affected the LED's behavior.
  5. 5.Break execution by placing a breakpoint at the return line of the SW_get_state function.
  6. 6.Hover over the SW0_edge_count variable to observe the current value, as indicated in Figure 1.
    Figure 1. Hover Over Variable to See Current Value
    Info: When the cursor hovers over a variable that is in scope at the point where execution is halted, Atmel Studio will present the content of the variable in a pop-up.
  7. 7.Right-click the SW0_edge_count variable and select Add Watch from the context menu to add the variable to the data Watch window. The Watch window should appear, with the SW0_edge_count variable listed, with the variable value, data type, and memory address, as in Figure 2.
    Figure 2. Add Variable to Watch Window
  8. 8.Modify the contents of a Watch Window variable, using the process described below. Assign the value '3' to the SW0_edge_count variable. The value will reflect as updated by turning red, as indicated in Figure 3.
    • Double-click a variable value in the Watch window
    • Type in the desired new value of the variable
    • Press Enter to confirm
    Figure 3. Newly Updated Variable Value in the Watch Window
    Info: The Value column in the Watch window can be displayed in hex by right-clicking in the Watch window and selecting Hexadecimal Display from the context menu.
  9. 9.To have the device evaluate the new value of SW0_edge_count, disable all breakpoints and continue the debug session by clicking or pressing F5. Observe how the LED stays ON as a result of the change made to SW0_edge_count.
Info:

A variable can also be added to the Watch window by clicking on an empty field name and typing the variable name. This way, it is even possible to cast a variable to a different data type for better readability in the Watch window. This is especially useful if it is required to look at an array that is passed to a function as a pointer.

For example, if an array is passed to a function, it will be passed to the function as a pointer. This makes it impossible for Atmel Studio to know the length of the array. If the length of the array is known, and it needs to be examined in the Watch window, the pointer can be cast to an array using the following cast:
*(uint8_t (*)[<n>])<name_of_array_pointer>
Where <n> is the number of elements in the array and <name_of_array_pointer> is the name of the array to be examined.
This can be tested on the SW0_edge_count variable by entering the following in an empty name field in the Watch window:
*(uint8_t (*)[5])&SW0_edge_count
Note that the '&' symbol must be used in this case to obtain a pointer to the variable.
Result: Atmel Studio has now been used to inspect and modify the contents of variables in the code.

Conditional Breakpoints

This section is a guide to using Atmel Studio to place conditional breakpoints.

Conditional breakpoints are those which will only halt code execution if a specified condition is met, and can be useful if it is required to break if certain variables have given values. Conditional breakpoints can also be used to halt code execution according to the number of times a breakpoint has been hit.
Todo: Place a conditional breakpoint inside SW_get_state() to halt execution for debugging at every 5th edge count, but only if the edge was rising, and check its functionality.
  1. 1.Clear all breakpoints from the project using the Breakpoints window.
  2. 2.Place a breakpoint at the return line of SW_get_state(), as in Figure 4.
  3. 3.Right-click the breakpoint and select Conditions... from the context menu.
  4. 4.Enter the following in the condition textbox:
    ((SW0_edge_count % 5) == 0) && SW0_cur_state
    Figure 4. Conditional Breakpoint Expression Example

  5. 5.Press Enter to confirm the break condition.
  6. 6.Continue/Start a new debug session by clicking the button or pressing F5.
  7. 7.Push SW0 on the kit several times and observe how code execution is halted when the condition is fulfilled.
  8. 8.Verify that the condition is met by double-checking the variable values in the Watch window.
Warning: Even though code execution is completely halted only if the specified break condition is met, Atmel Studio temporarily breaks code execution each time the breakpoint is hit to read the variable content and determine if the break condition is met. Conditional breakpoints will, therefore, have an impact on execution timing, even if the actual break condition is never met.
Tip: Use the Hit Count condition if execution needs to break based on how many times a breakpoint has been hit.
Result: Atmel Studio has been used to halt execution when the specified break condition is satisfied.

Action Breakpoints

This section is a guide to using Atmel Studio to place action breakpoints.

Action breakpoints can be useful if variable contents or execution flow needs to be logged without having to halt code execution and manually record the required data.
Todo: Place an action breakpoint to log SW0_cur_state, SW0_prv_state and SW0_edge_count, and check the output for the relevant variable states.
  1. 1.Stop the ongoing debug session and clear all the breakpoints from the Breakpoints window.
  2. 2.Place a breakpoint at the SW0_prv_state = SW0_cur_state; line, as in Figure 5.
  3. 3.Right-click the breakpoint and select Actions... from the context menu.
  4. 4.Enter the following in the output message text box:
    Prv state:{SW0_prv_state}, Cur_state:{SW0_cur_state}, Edge count:{SW0_edge_count}
    Figure 5. Action Breakpoint Example
  5. 5.Press Enter to confirm.
  6. 6.Start a debug session.
  7. 7.Open the Debug Output window by going to Debug → Windows → Output. It should list the variable contents as in Figure 6. If SW0 is pushed on the kit, the content is updated.
    Figure 6. Debug Output Window Showing Variable Contents
Warning: When using action breakpoints, Atmel Studio will temporarily halt code execution in order to read out variable content. As a result, execution timing will be affected. A less intrusive approach would be to place the action breakpoint at the SW0_edge_count++ line, which is only executed upon SW0 edge detection. This will cause a temporary halt only when SW0 is pressed, but will also cause the debug window output to be delayed by one line of code.
Tip: Action and Conditional breakpoints can be used together in order to log data only if a condition is satisfied.
Result: Atmel Studio has been used to log variable data using an action breakpoint.

Code used (for ATtiny817 Xplained Pro)

Code used for conditional- and action-breakpoints.

#include <avr/io.h>
#include <avr/interrupt.h>

void LED_on();
void LED_off();
uint8_t SW_get_state();
void LED_set_state(uint8_t 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(uint8_t SW_state)
{
	if (SW_state)
	{
		LED_on();	
	}
	else
	{
		LED_off();
	}
}
#pragma endregion LED_functions


uint8_t SW_get_state(void)
{
    static uint8_t SW0_prv_state  = 0;
    static uint8_t SW0_edge_count = 0;

    uint8_t SW0_cur_state = !(PORTB.IN & PIN5_bm); /* Read the current SW0 state  */
    if (SW0_cur_state != SW0_prv_state)            /* Check for edges             */
    {
        SW0_edge_count++;
    }
    SW0_prv_state = SW0_cur_state;                /* Keep track of previous state */
     
    /*
     * Report the switch as pushed when it is pushed or the edge counter is a 
     * multiple of 3
     */
    return SW0_cur_state || !(SW0_edge_count % 3);
}

ISR(PORTB_PORT_vect)
{
	uint8_t intflags = PORTB.INTFLAGS;
	PORTB.INTFLAGS = intflags;
	
	uint8_t SW_state = SW_get_state();
	
	LED_set_state(SW_state);
}