Quickstart Guides: PlatformIO
The following two quickstart guides provide an easy entrance into the the wonderful land of debugging using PlatformIO.
Quickstart guide: Atmel-ICE & ATmega1284
This quickstart guide shows you how to set up a PlatformIO project for debugging a JTAG target using an EDBG-based debug probe.
Required hardware
We will use an ATmega1284 (but any other AVR JTAG Mega will do) and the debug probe Atmel-ICE (any other EDBG-based debug probe is also OK). In addition, we will use the DIP-40 Arduino-compatible development board to demonstrate basic debugging, but again, any board with a JTAG and ISP connector would do.
In the following, I will assume that PlatformIO, as an extension of VSCode, has been installed already and that you are somewhat familiar with it.
Step 1: Installing PyAvrOCD
Go to the PyAvrOCD GitHub repo and click on the Latest button below Releases on the right side of the page.
This will open the latest release page with all its assets.
Here you can download the debug server executables for your platform. If there is nothing for you, there is an alternative way to install PyAvrOCD described below. After downloading the archive, extract the files (for Windows, I assume, you use the Windows PowerShell):
> tar xvzf avrocd-tools-X.Y.Z-architecture.tar.gz
Once you have downloaded the archive and uncompressed it, you need to decide where to place the files contained in it. I prefer the folder ~/.local/bin/ (where ~ signifies the user folder). Move the three items that you find in the tools folder of the uncompressed archive to this folder (or any other place you have chosen):
pyavrocd[.exe] - the debug server executable,pyavrocd-util- a folder with auxiliary code and data that should always be stored alongside the executable, andavr-gdb[.exe] - a current version (16.3) of the GDB debugger for AVR chips with a minimal amount of dependencies on dynamic libraries.
> mkdir ~/.local
> mkdir ~/.local/bin
> rm -rf ~/.local/bin/pyavrocd-util
> mv tools/* ~/.local/bin/
Windows
On Windows, use rmdir instead of rm -rf above.
Linux
Under Linux, you need to install udev rules so that the debug probes can communicate with the host. Download https://pyavrocd.io/99-edbg-debuggers.rules, edit if you want, and copy to /etc/udev/rules.d/.
macOS
On a Mac, files downloaded through a browser or from an email are marked as potentially dangerous, and the system may not allow them to be executed. In this case, use the command xattr -d com.apple.quarantine FILE in order to remove the extended attribute com.apple.quarantine from the binary executable FILE.
Step 1a: An alternative way of installing PyAvrOCD
When you are not happy with the choices provided to you above, when the PyAvrOCD binaries will not start because of incompatibilities, or when you have a compatible Python installation that you do not want to duplicate, it is also possible to install PyAvrOCD from PyPI, the Python Package Index. The preferred way of doing that is using pipx, which will install an executable in the folder ~/.local/bin/ and a virtual Python environment with all the required modules in ~/.local/pipx/. Although this is, in theory, platform agnostic, you may encounter problems when USB and HIDAPI libraries have to be built from source. Note that Linux users need to install udev rules (see above).
Assuming that you have been successful in installing PyAvrOCD using pipx, we also want to download SVD files that we will need during our IDE debugging sessions. Create the folder pyavrocd-util in the folder ~/.local/bin , download the SVD archive into it, and extract the folder:
> cd ~/.local/bin
> mkdir pyavrocd-util
> cd pyavrocd-util
> wget https://github.com/felias-fogg/PyAvrOCD/releases/latest/download/svd.tar.gz -O svd.tar.gz
> tar xvzf svd.tar.gz
Step 2: Replace the outdated avr-gdb client
The avr-gdb client in the AVR toolchain, as delivered by PlatformIO, is seriously outdated (version 7.8 from 2014). Even worse, under recent versions of macOS and Ubuntu, it will not start because of incompatibilities. For this reason, it makes sense to employ a more recent version of avr-gdb. Either use the one obtainable from your OS or make use of the binary downloaded in Step 1. The folder you need to copy the client to is ~/.platformio/packages/toolchain-atmelavr/bin:
cp ~/.local/bin/avr-gdb .platformio/packages/toolchain-atmelavr/bin/
Step 3: Set up the example project
Since PyAvrOCD is a custom debug solution, a number of things have to be specified in the PlatformIO configuration file platformio.ini, too long to present here. You can clone a project containing this file, together with a small program, from
https://github.com/felias-fogg/pio-atmega1284p-example
After opening a new window, click on Clone Git Repository... and type the above line into the input field. Perhaps VS Code asks you to log in to GitHub first.
The repo will be cloned, and you have to provide a destination for it.
Step 4: Prepare the board for debugging
Before debugging can take place, you need to make sure that the JTAG pins are enabled. On an ATmega1284P, these are the pins PC2—PC5. Fresh from the factory, the JTAG pins are enabled. However, on Arduino boards, they are by default disabled. Since the state is probably unknown, we will set them anyway. In order to activate them, we need to connect the Atmel-ICE to the board using the ISP connection, as shown in the following photo. The key or marker of the ISP plug should be oriented towards the MCU.
In order to set the correct fuses, we now select the debug environment by first clicking on the environment symbol in the bottom line and then choosing the right environment at the top.
After that, we request to set the fuses as specified in the debug section in the configuration file by clicking on Set Fuses. The result of this action is displayed in the Terminal window and should be as shown in the picture below.
Before we can start debugging, we need to change the connection between the debug probe and the target board from ISP to JTAG, as shown in the following picture. As with the ISP plug, the keying or marker should be oriented towards the MCU.
Step 5: Debug the program
If you have not activated the debug environment, now is the time to do it (as shown in the previous step). And then we are ready to go into business seriously. First, click the debug symbol (bug in front of the triangle) in the left side bar, which will bring up the debug panes on the left side. Then, click the green triangle at the top.
This will start the compilation process, and after that, the debug server. The code will be uploaded, and execution will begin. As requested by the configuration in platformio.ini, a first temporary stop is made in the setup function. The yellow triangle and the highlighted line signify this. The most important control panel is now the one shown on the right side at the top. It enables you (from left to right) to
- continuing/suspending execution,
- stepping-over, i.e., making a step to the beginning of the next source line in the same function,
- stepping-in, that is, making a step to the next source line (entering perhaps a new function),
- stepping-out, that is, executing the current function until it returns to the calling function,
- resetting the MCU, and
- terminating the debugging session.
Before we click the Continue button, let us make some amendments. Place a breakpoint in line 19 by clicking to the left of the line number (1). Then let us require to make local and global variables visible for inspection by clicking on the markers left at the top (2 & 3). In addition, let us have a look at the peripheral register PORTB (4). Finally, click on the continue button (5) to continue execution.
After a short while, execution will stop in line 19. As one can see on the left, a local variable local_ontime came to life and has a value now. Similarly, all the global variables have values now. And one can see that bit 0 of PORTB, which is the port bit controlling the LED, is now also 1. Unfortunately, not all register values that should be displayed are actually displayed. This seems to be an error of the IDE.
I believe that from here on, you will be able to use the debugger productively.
Step 6: Start over or terminate the debugging session
If you have found the bug you were hunting, you can now leave the editor (red square), edit the program, and start again at step 5. Note that you always have to restart the debugger before any changes you made to the program are effective. In fact, changing the source text while you are debugging is not a good idea, because the correspondence between the compiled code and the source code will be lost.
Instead of starting a new edit/compile/debug cycle, you may want to call it a day and end debugging. In this case, you may wish to disable the JTAG pins, perhaps. For this purpose, you first need to switch back to the ISP connection. Then switch to the release environment and click Set Fuses again. Possibly, you even want to restore the bootloader, which was deleted when starting the debugger. In this case, you need to click Burn Bootloader.
Potential Problems
There is always the chance that something goes south, either debugging does not start at all, or something funny happens while debugging. If so, it is a good idea to have a look at the output in the DEBUG CONSOLE. Messages with the prefix [CRITICAL] often tell what went wrong. It may also be a good idea to consult the Troubleshooting and the Limitations sections of the PyAvrOCD manual.
One common problem is forgetting to change from ISP to JTAG or back. In this case, the debug probe complains that there is no device.
Quickstart guide: dw-link & ATtiny85
This quickstart guide demonstrates how to set up a PlatformIO project for debugging on an ATtiny85 without requiring you to invest in a commercial debug probe. Instead, we will build our own debug probe.
It explains
-
how to install the PyAvrOCD GDB server,
-
how to turn an Arduino UNO R3 into a debugWIRE debug probe using the dw-link firmware,
- how to set up the breadboard with the ATtiny85 on it, and
- how to use PlatformIO for debugging a program on the ATtiny.
Required hardware
- Arduino Uno (will become the debug probe)
- USB cable
- ATtiny85 as the target
- In order to connect the debug probe to the target, you need:
- a breadboard together with
- 11 Jumper wires (male-to-male)
- 2 LEDs
- 3 Resistors (10 kΩ, 220Ω, 220Ω)
- 2 Capacitors (100 nF, 10 µF)
Step 1: Installing PyAvrOCD
Go to the PyAvrOCD GitHub repo and click on the Latest button below Releases on the right side of the page.
This will open the latest release page with all its assets.
Here you can download the debug server executables for your platform. If there is nothing for you, there is an alternative way to install PyAvrOCD described below. After downloading the archive, extract the files (for Windows, I assume, you use the Windows PowerShell):
> tar xvzf avrocd-tools-X.Y.Z-architecture.tar.gz
Once you have downloaded the archive and uncompressed it, you need to decide where to place the files contained in it. I prefer the folder ~/.local/bin/ (where ~ signifies the user folder). Move the three items that you find in the tools folder of the uncompressed archive to this folder (or any other place you have chosen):
pyavrocd[.exe] - the debug server executable,pyavrocd-util- a folder with auxiliary code and data that should always be stored alongside the executable, andavr-gdb[.exe] - a current version (16.3) of the GDB debugger for AVR chips with a minimal amount of dependencies on dynamic libraries.
> mkdir ~/.local
> mkdir ~/.local/bin
> rm -rf ~/.local/bin/pyavrocd-util
> mv tools/* ~/.local/bin/
Windows
On Windows, use rmdir instead of rm -rf above.
Linux
Under Linux, you need to install udev rules so that the debug probes can communicate with the host. Download https://pyavrocd.io/99-edbg-debuggers.rules, edit if you want, and copy to /etc/udev/rules.d/.
macOS
On a Mac, files downloaded through a browser or from an email are marked as potentially dangerous, and the system may not allow them to be executed. In this case, use the command xattr -d com.apple.quarantine FILE in order to remove the extended attribute com.apple.quarantine from the binary executable FILE.
Step 1a: An alternative way of installing PyAvrOCD
When you are not happy with the choices provided to you above, when the PyAvrOCD binaries will not start because of incompatibilities, or when you have a compatible Python installation that you do not want to duplicate, it is also possible to install PyAvrOCD from PyPI, the Python Package Index. The preferred way of doing that is using pipx, which will install an executable in the folder ~/.local/bin/ and a virtual Python environment with all the required modules in ~/.local/pipx/. Although this is, in theory, platform agnostic, you may encounter problems when USB and HIDAPI libraries have to be built from source. Note that Linux users need to install udev rules (see above).
Assuming that you have been successful in installing PyAvrOCD using pipx, we also want to download SVD files that we will need during our IDE debugging sessions. Create the folder pyavrocd-util in the folder ~/.local/bin , download the SVD archive into it, and extract the folder:
> cd ~/.local/bin
> mkdir pyavrocd-util
> cd pyavrocd-util
> wget https://github.com/felias-fogg/PyAvrOCD/releases/latest/download/svd.tar.gz -O svd.tar.gz
> tar xvzf svd.tar.gz
Step 2: Replace the outdated avr-gdb client
The avr-gdb client in the AVR toolchain, as delivered by PlatformIO, is seriously outdated (version 7.8 from 2014). Even worse, under recent versions of macOS and Ubuntu, it will not start because of incompatibilities. For this reason, it makes sense to employ a more recent version of avr-gdb. Either use the one obtainable from your OS or make use of the binary downloaded in Step 1. The folder you need to copy the client to is ~/.platformio/packages/toolchain-atmelavr/bin:
cp ~/.local/bin/avr-gdb .platformio/packages/toolchain-atmelavr/bin/
Step 3: Turn an UNO into a debug probe
First, connect the UNO to your computer using the USB cable. Make sure that you have the permission to access the serial interface (under Linux).
The simplest way to install the firmware is to download an uploader from the Release assets of the GitHub repo. The uploader should fit your architecture, e.g., dw-uploader-windows-intel64 for Windows. Under Linux and macOS, open a terminal window, go to the download folder, and set the executable permission using chmod +x. Afterward, execute the program. Under Windows, it is enough to start the program after downloading by double-clicking on it.
Alternatively, you can download or clone the dw-link repository and then compile and upload the dw-link Arduino sketch using PlatformIO.
The Uno now acts as a debug probe providing a GDB RSP interface. If you configured the serial line to the Uno as 115200 baud, and click on Monitor in the PROJECT TASK menu, select the Terminal window, and then type a minus sign into this window, you should get the response "$#00". If you type Ctrl-E, the probe should respond with "dw-link".
Step 4: Set up the example project
Since PyAvrOCD is a custom debug solution, a number of things have to be specified in the PlatformIO configuration file platformio.ini, too long to present here. You can clone a project containing this file, together with a small program, from
https://github.com/felias-fogg/pio-attiny85-example
After opening a new window, click on Clone Git Repository... and type the above line into the input field. Perhaps VS Code asks you to log in to GitHub first.
The repo will be cloned, and you have to provide a destination for it.
Step 5: Set up the hardware
You need to set up the hardware on a breadboard and use six wires to connect the ATtiny to your Uno, turned into a hardware debugger. Note that the notch or dot on the ATtiny is oriented towards the left.
In reality, this could be like in the following photo.
Here is a table of all the connections so that you can check that you have made all the connections.
| ATtiny pin# | Arduino Uno pin | component |
|---|---|---|
| 1 (Reset) | D8 | 10k resistor to Vcc |
| 2 (D3) | ||
| 3 (D4) | 220 Ω resistor to target (red) LED (+) | |
| 4 (GND) | GND | red and yellow LED (-), decoupling cap 100 nF, RESET blocking cap of 10µF (-) |
| 5 (D0, MOSI) | D11 | |
| 6 (D1, MISO) | D12 | |
| 7 (D2, SCK) | D13 | |
| 8 (Vcc) | 5V | 10k resistor, decoupling cap 100 nF |
| RESET | RESET blocking cap of 10 µF (+) | |
| D7 | 220 Ω to system (yellow) LED (+) |
The yellow LED is the system LED, and the red one is the ATtiny-LED. The system LED gives you information about the internal state of the debugger:
- debugWIRE mode disabled (LED is off),
- waiting for power-cycling the target (LED flashes every second for 0.1 sec),3.
- debugWIRE mode enabled (LED is on),
- ISP programming (LED is blinking slowly),
- error state, i.e., not possible to connect to target or internal error (LED blinks furiously every 0.1 sec).
Step 6: Debug the program
If you have not activated the debug environment, now is the time to do it.
And then we are ready to go into business seriously. First, click the debug symbol (bug in front of the triangle) in the left side bar, which will bring up the debug panes on the left side. Then, click the green triangle at the top.
This will start the debugging process and open the TERMINAL window below the editor window. Click on the DEBUG CONSOLE label, so that this console will be opened. There, you will probably see that you should power cycle the target.
After having done that, the ATtiny is in debugWIRE mode, the executable will be loaded, and after that, execution is started. As required, execution will stop in the setup function, which is signified by the yellow triangle and the highlighted line (A). The most crucial control panel (B) is now the one shown on the right side at the top. It enables you (from left to right) to
- continuing/suspending execution,
- stepping-over, i.e., making a step to the beginning of the following source line in the same function,
- stepping-in, that is, making a step to the following source line (entering perhaps a new function),
- stepping-out, that is, executing the current function until it returns to the calling function,
- resetting the MCU, and
- terminating the debugging session.
From here on, I believe, you know your way around. Otherwise, consult, e.g., the debugging section of another quickstart guide.
Step 7: Start over or terminate the debugging session
If you have found the bug you were hunting, you can now leave the editor (red square), edit the program, and start again at step 6. Note that you always have to restart the debugger before any changes you made to the program are effective. In fact, changing the source text while you are debugging is not a good idea, because the correspondence between the compiled code and the source code will be lost.
Instead of starting a new edit/compile/debug cycle, you may want to call it a day and end debugging. In this case, you may wish to switch the MCU back into normal mode, in which ordinary SPI programming is possible. This can be accomplished by typing the command monitor debugwire disable into the input line of the DEBUG CONSOLE window (1) just before terminating the debugger (2).
Potential Problems
There is always the chance that something goes south, either debugging does not start at all, or something funny happens while debugging. If so, it is a good idea to have a look at the output in the DEBUG CONSOLE. Messages with the prefix [CRITICAL] often tell what went wrong. It may also be a good idea to consult the Troubleshooting and the Limitations sections.