⏱ Updated on January 10, 2018 to cover addition of debugging support in Visual Studio 2017 15.6
In Visual Studio 2017 15.5 we are introduced support for cross compilation targeting ARM microcontrollers. The 15.6 Preview 2 release adds debugging support. To enable this in the installation choose the Linux development with C++ workload and select the option for Embedded and IoT Development. This adds the ARM GCC cross compilation tools and Make to your installation.
Our cross compilation support uses our Open Folder capabilities so there is no project system involved. We are using the same JSON configuration files from other Open Folder scenarios and have added additional options to support the toolchains introduced here. We hope that this provides flexibility for many styles of embedded development. The best way to get started with this and understand the capabilities is with a project exported from the ARM mbed online compiler. We’ll cover the basics here, to learn more about the online compiler see ARM’s tutorials, and you can sign up for an account here.
ARM’s online compiler lets you select your target platform and configures your project accordingly. I’m using an ST Nucleo-F411RE, but any board supported by the compiler should be fine. Click New, and in the dialog that opens choose a template to get started with. I chose “Blinky LED test for ST Nucleo boards”.
Then in the Program Workspace select the program, right click and choose Export Program.
In the Export program dialog choose GCC (ARM Embedded) for the Export Target.
This will download a zip file to your computer. Extract the contents of that file to where you like and open Visual Studio. Now open it with File > Open > Folder. We need to inform VS of what type of C++ code this is, so go the Project menu and select Edit Settings > CppProperties.json. Modify the contents so there is only one entry that matches the below.
{
"configurations": [
{
"inheritEnvironments": [
"gcc-arm"
],
"name": "gccarm",
"includePath": [
"${env.INCLUDE}"
],
"defines": [
]
}
]
}
The environment definition for gcc-arm (gcc_arm in 15.5) sets the path to the ARM GCC toolchain that VS installed. It also triggers scanning of the subfolder named mbed to properly set include paths for use in IntelliSense. You will see this when you open main.cpp. Everything you expect to work will, auto completion, go to definition, peek definition, etc. You can specify additional include directories, but note that subdirectories when present must also be explicitly declared at this time.
Open folder also supports things like syntax highlighting for makefiles, so go ahead and open it to see that. To compile the project right click the Makefile and select build.
You’ll see the build output in the output window.
You will now see a new BUILD output folder in the Solution Explorer. Within that is your output binary which is your program name .bin you can flash onto your board.
One of the great things about mbed boards is how easy they are to flash with that binary. When connected to your PC they show up as a USB mass storage device, so you simply drag and drop the bin file to the drive, the board resets and your program runs. If you want to control this from Visual Studio you can use tasks. Select the binary, right click and choose Configure Tasks. This will create a file called tasks.vs.json where you can configure tasks that will show up in the context menu for the file it is configured for. The example task shown below will copy the binary specified to the D drive, you can modify this to match your system. The command ${env.COMSPEC} provides a command prompt environment that the arguments are passed to.
{
"version": "0.2.1",
"tasks": [
{
"taskName": "flash",
"appliesTo": "BUILD/Nucleo_blink_led.bin",
"type": "launch",
"command": "${env.COMSPEC}",
"args": [
"copy BUILD\\Nucleo_blink_led.bin D:"
]
}
]
}
Debugging
Debugging is supported as of the Visual Studio 2017 15.6 Preview 2 release.
First it is important to ensure your output has debugging symbols. In the case of the GCC projects exported from the ARM online compiler they do not. To add them edit the makefile under the tools and flags section and add the -g flag for the GCC and G++ commands like the below.
CC = 'arm-none-eabi-gcc' '-g' ... CPP = 'arm-none-eabi-g++' '-g' ...
Now after you have built your binary and flashed the device, right click on the binary output and select Debug and Launch Settings.
In the dialog that pops up select C/C++ Debug microcontroller (gdbserver).
This will create a launch.vs.json that has many options expressed that are relevant for embedded debugging. There are many ways to debug these kinds of devices so what you fill in here will be specific to your board, the hardware debugger and its associated software that provides a gdbserver interface. We are providing as many defaults and hints as we can to help you. In this preview some of the environment variables emitted are not working yet, you will need to substitute these with the values needed.
- ${workspaceRootFolderName}, your folder name
- ${env.gccpath}, your VS installation path followed by Linux\gcc_arm\bin
- ${debugInfo.linuxNatvisPath}, path to a Natvis file if you have one. This is fine to remove as it is for specific scenarios.
I will walk through configuring this with the ST Nucleo-F411RE using OpenOCD. The process is similar for most boards.
First, change the program name in the output to point to your .elf file.
"program": "${workspaceRoot}\\BUILD\\Nucleo_blink_led.elf",
Change the miDebuggerPath to point to the full path to arm-none-eabi-gdb.exe.
"miDebuggerPath": "C:\\Program Files (x86)\\Microsoft Visual Studio\\Internal\\Enterprise\\Linux\\gcc_arm\\bin\\arm-none-eabi-gdb.exe",
In the setupCommands you can remove the documentation link section (leaving it is not harmful). Change the symbol load to point to your .elf file.
"text": "-file-exec-and-symbols Nucleo_blink_led.elf",
You can execute additional commands to get your board into a proper state for debugging, often you need to halt and then reset the board at the beginning of a session. To do so add those command as part of the setup commands array as follows.
{
"text": "monitor reset halt",
"ignoreFailures": true
},
{
"text": "monitor reset init",
"ignoreFailures": true
}
Make sure the miDebuggerServerAddress matches the location your hardware debugger will provide. This is the default for OpenOCD.
"miDebuggerServerAddress": "localhost:3333",
If you want to launch your hardware interface yourself manually you can omit these lines. If you want VS to start the software to interface with your hardware debugger this is an example for launching OpenOCD with a config file. You should check this command by running it manually to make sure you have the correct config file and that the text used to validate the server has started is correct.
"debugServerPath": "D:\\openocd-0.10.0\\bin-x64\\openocd.exe", "debugServerArgs": "-f d:/openocd-0.10.0/scripts/board/st_nucleo_f4.cfg", "serverStarted": "Info : stm32f4x.cpu: hardware has 6 breakpoints, 4 watchpoints",
Now with our configuration done use F5 to start debugging your embedded ARM application on your device from Visual Studio.
What’s next
Download the Visual Studio 2017 Preview, install the Linux C++ Workload, select the option for Embedded and IoT Development and give it a try with your projects.
We’re actively working on additional support for embedded scenarios. Your feedback here is very important to us. We look forward to hearing from you and seeing the things you make.
The best way to reach us is via our GitHub hosted issue list, directly via mail at vcpplinux-support@microsoft.com or find me on Twitter @robotdad.










Good news.
3 years ago i use arm-gcc with simple bat file for compile projects under VS 2015.
But this way easy for developers.
I can’t wait for debuging option. FInally I can use VS for developing mbed on my K64F.
Congratulations, Mark (and team) for getting it working.
I meant…congratulations Marc (and team) :P
Can 15.5 generate ARM EXE to be run directly under arm version of Windows 10?
Using normal Windows projects that have ARM targeting yes, not with this. This is for low power micro controllers that are not running Windows.
Excellent work (as usual!). Bravo!!
Can’t wait for the debug….
This and WSL is pretty awesome.
Any plans to allow other cross compilers to be used, such as for MIPS64?
Thanks! Not specifically yet, but we are thinking about how make it easier to use other compilers. Hopefully I’ll get something together soon on how you can roll your own for things we haven’t gotten to yet. Open folder is very flexible, we just need to provide some more guidance around it.
Interesting to see how this one comes out, could be a great additional feature – and if the debugging is great then a lot of “less mature” IDEs may lose a lot of market share.
Great!
Just added support for debugging (via launch.vs.json and external OpenOCD process) and it works!
I have one question: how to, in CppProperties.json, point out which gcc compiler should be used? I have two mcu (ESP and STM) and they have diffrent toolchains.
In json file is only: “inheritEnvironments”: [ “gcc_arm” ].
“gcc_arm” value is hardcoded? I found, that it refers to ‘gcc_arm’ or ‘GNU MCU Eclipse’ folder in Microsoft Visual Studio instalation folder. It would be great, to simply add another configuration ;)
Hi Marc,
This is a very interesting development and a welcome enhancement to VS. However, for my organisation we will need the ability to add and specify our own toolchains (including GDB) and simply select them from the project properties – will this be possible? I couldn’t see how to do this with the preview version.
Good work though. :)
About a year ago I evaluated the VisualGDB plugin for doing ARM cross compiling. It did a good job, and supported any old cross compiler, although now I can’t remember whether IntelliSense still worked. How does MS’s approach to cross compiling compare to what VisualGDB does? For example, from a user perspective would it look similar or will there be other advantages – like better integration with the debugger? VisualGDB also provides startup code for various chipsets, but that sort of functionality I’m less concerned about since those files can be manually added to a project. It’ll be a great day when VS natively supports arbitrary processor cross compilers derived from the gcc toolchain!
I’ve followed your example, but I can’t reproduce it. I have just installed “Microsoft Visual Studio Community 2017 Preview”, it’s “Version 15.6.0 Preview 5.0”. I only selected “Embedded development and IoT” (or something like that, my VS installer is in French), and its dependency “Visual C++ for Linux development” (here again not exact English title) and the English linguistic module.
When I right click on the Makefile, there is no “Build” entry in the context menu. What did I do wrong?
I had that problem initially. The CppProperties.json has to be changed to have the entry “gccarm” as specified. The environment selector in the toolbar did not update until I restarted Visual Studio, and opened the folder again.. It should display “gccarm”.
The toolbar doesn’t have that combo box at all here.
Hi, did you ever figure this out? Having the exact same problem.
I configured and tested OpenOCD from command line to confirm it attaches to an STM32F7 Discovery board. When attempt to start debugging (specifying .bin as the startup file), all I get is a dialog with “Unable to start debugging. No process is associated with this object”. Is there any way to get output from Visual Studio on what it is attempting to do during the startup that fails?
Found that the problem was due to spaces within the file path for the board configuration file supplied in “debugServerArgs”. Even though overall args string is in quotes, \” is needed around the file path itself if there spaces. To find this problem, turn on the debug log in the VS command window with command Debug.MIDebugLog /On:C:\dev\test1.log. (described in https://github.com/Microsoft/VSLinux/issues/262).
Debugging works great once you get past configuration issues!
I have nearly the same problem. I have the VS 15.6 stable version and try to debug the ST Nucleo F446RE. The CPU is like in the example. The build works fine and I can execute the bin file. But the debuger stops with the same message.
2: (149867) LaunchOptions{
2: (149867) LaunchOptions “target”: “D:\\Development\\Arm\\Nucleo_blink_led_gcc_arm_nucleo_f446re\\Nucleo_blink_led\\mbed”,
2: (149867) LaunchOptions “type”: “cppdbg”,
2: (149867) LaunchOptions “name”: “mbed”,
2: (149867) LaunchOptions “project”: “mbed”,
2: (149867) LaunchOptions “cwd”: “D:\\Development\\Arm\\Nucleo_blink_led_gcc_arm_nucleo_f446re\\Nucleo_blink_led”,
2: (149867) LaunchOptions “program”: “D:\\Development\\Arm\\Nucleo_blink_led_gcc_arm_nucleo_f446re\\Nucleo_blink_led\\BUILD\\Nucleo_blink_led.elf”,
2: (149867) LaunchOptions “MIMode”: “gdb”,
2: (149867) LaunchOptions “externalConsole”: true,
2: (149867) LaunchOptions “inheritEnvironments”: [
2: (149867) LaunchOptions “gcc_arm”
2: (149867) LaunchOptions ],
2: (149867) LaunchOptions “miDebuggerPath”: “C:\\Program Files (x86)\\Microsoft Visual Studio\\2017\\Enterprise\\Linux\\gcc_arm\\\\bin\\\\arm-none-eabi-gdb.exe”,
2: (149867) LaunchOptions “setupCommands”: [
2: (149867) LaunchOptions {
2: (149867) LaunchOptions “text”: “Documentation: Learn how to configure embedded debugging”,
2: (149867) LaunchOptions “description”: “See here for more info http://aka.ms/vsembeddeddebug“,
2: (149867) LaunchOptions “ignoreFailures”: true
2: (149867) LaunchOptions },
2: (149867) LaunchOptions {
2: (149867) LaunchOptions “text”: “-environment-cd D:\\Development\\Arm\\Nucleo_blink_led_gcc_arm_nucleo_f446re\\Nucleo_blink_led/BUILD”
2: (149867) LaunchOptions },
2: (149867) LaunchOptions {
2: (149867) LaunchOptions “text”: “-file-exec-and-symbols Nucleo_blink_led.elf”,
2: (149867) LaunchOptions “description”: “load file”,
2: (149867) LaunchOptions “ignoreFailures”: false
2: (149867) LaunchOptions },
2: (149867) LaunchOptions {
2: (149867) LaunchOptions “text”: “-enable-pretty-printing”,
2: (149867) LaunchOptions “ignoreFailures”: true
2: (149867) LaunchOptions },
2: (149867) LaunchOptions {
2: (149867) LaunchOptions “text”: “monitor reset halt”,
2: (149867) LaunchOptions “ignoreFailures”: true
2: (149867) LaunchOptions },
2: (149867) LaunchOptions {
2: (149867) LaunchOptions “text”: “monitor reset init”,
2: (149867) LaunchOptions “ignoreFailures”: true
2: (149867) LaunchOptions }
2: (149867) LaunchOptions ],
2: (149867) LaunchOptions “visualizerFile”: “C:\\Program Files (x86)\\Microsoft Visual Studio\\2017\\Enterprise\\Common7\\IDE\\VC\\..\\CommonExtensions\\Microsoft\\Linux\\Linux\\stl.natvis”,
2: (149867) LaunchOptions “showDisplayString”: true,
2: (149867) LaunchOptions “miDebuggerServerAddress”: “localhost:3333”,
2: (149867) LaunchOptions “launchCompleteCommand”: “None”,
2: (149867) LaunchOptions “debugServerPath”: “C:\\mbed\\openocd-0.10.0\\bin-x64\\openocd.exe”,
2: (149867) LaunchOptions “debugServerArgs”: “-f C:\\mbed\\openocd-0.10.0\\scripts\\board\\st_nucleo_f4.cfg”,
2: (149867) LaunchOptions “serverStarted”: “Info : stm32f4x.cpu: hardware has 6 breakpoints, 4 watchpoints”,
2: (149867) LaunchOptions “filterStderr”: true,
2: (149867) LaunchOptions “filterStdout”: true
2: (149867) LaunchOptions}
2: (149870) Starting: “C:\mbed\openocd-0.10.0\bin-x64\openocd.exe” -f C:\mbed\openocd-0.10.0\scripts\board\st_nucleo_f4.cfg
2: (150058) “C:\mbed\openocd-0.10.0\bin-x64\openocd.exe” exited with code 1 (0x1).
2: (150058) <-logout
Marian Grzesik: Try running the exact same openocd command on the command line.
Michael Pear: An excellent tips. I tried so many things in order to get this to work and with the debug logging enabled I could see that the monitor commands are fed to gdb before the remote session is entered.
Does anybody know how to change so some commands are postponed until after the remote session has been established?
Hi Stig,
this is the output in a command window:
C:\Program Files (x86)\Microsoft Visual Studio\2017\Enterprise>C:\\mbed\\openocd-0.10.0\\bin-x64\\openocd.exe -f C:\\mbed\\openocd-0.10.0\\scripts\\board\\st_nucleo_f4.cfg
Open On-Chip Debugger 0.10.0
Licensed under GNU GPL v2
For bug reports, read
http://openocd.org/doc/doxygen/bugs.html
Info : The selected transport took over low-level target control. The results might differ compared to plain JTAG/SWD
adapter speed: 2000 kHz
adapter_nsrst_delay: 100
none separate
srst_only separate srst_nogate srst_open_drain connect_deassert_srst
Info : Unable to match requested speed 2000 kHz, using 1800 kHz
Info : Unable to match requested speed 2000 kHz, using 1800 kHz
Info : clock speed 1800 kHz
Error: libusb_open() failed with LIBUSB_ERROR_NOT_FOUND
Error: open failed
in procedure ‘init’
in procedure ‘ocd_bouncer’
For some reason there is no response button for your post…
It seems like you either miss a driver for the usb-stlink connection or that your board is wrongly strapped.
I installed en.stsw-link009 before connecting the card (search STs homepage).
On my board there was a picture showing how the straps should be set to enable the st-link.
Hi Stig,
you were right, I updated the driver for my board NUCLEO F446RE and it seems to work better. Unfortunately the debugger try to starts, but it cannot hit any breakpoint. I think it doesn’t start so really….
From the console:
openocd: Open On-Chip Debugger 0.10.0
openocd: Licensed under GNU GPL v2
openocd: For bug reports, read
openocd: http://openocd.org/doc/doxygen/bugs.html
openocd: Info : The selected transport took over low-level target control. The results might differ compared to plain JTAG/SWD
openocd: adapter speed: 2000 kHz
openocd: adapter_nsrst_delay: 100
openocd: none separate
openocd: srst_only separate srst_nogate srst_open_drain connect_deassert_srst
openocd: Info : Unable to match requested speed 2000 kHz, using 1800 kHz
openocd: Info : Unable to match requested speed 2000 kHz, using 1800 kHz
openocd: Info : clock speed 1800 kHz
openocd: Info : STLINK v2 JTAG v28 API v2 SWIM v17 VID 0x0483 PID 0x374B
openocd: Info : using stlink api v2
openocd: Info : Target voltage: 3.255911
openocd: Info : stm32f4x.cpu: hardware has 6 breakpoints, 4 watchpoints
=thread-group-added,id=”i1″
GNU gdb (GNU Tools for ARM Embedded Processors 6-2017-q2-update) 7.12.1.20170417-git
Copyright (C) 2017 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law. Type “show copying”
and “show warranty” for details.
This GDB was configured as “–host=i686-w64-mingw32 –target=arm-none-eabi”.
Type “show configuration” for configuration details.
For bug reporting instructions, please see:
.
Find the GDB manual and other documentation resources online at:
.
For help, type “help”.
Type “apropos word” to search for commands related to “word”.
=cmd-param-changed,param=”pagination”,value=”off”
openocd: Info : accepting ‘gdb’ connection on tcp/3333
openocd: Info : device id = 0x10006421
openocd: Info : flash size = 512kbytes
0x0800239a in update_present_time ()
Note: automatically using hardware breakpoints for read-only addresses.
Loaded ‘shared libraries loaded at this time.’. Cannot find or open the symbol file.
Hi Stig,
once again, it woks :-)
The problem was at the end (after update the usb driver for the board), that the debugger doesn’t hit any breakpoint, only a specific one. In my case in the blinky example is it the last line. I’ve rebuild the example many times, but it didn’t help.
Thanks a lot for help!!!
I really appreciate the effort of enabling embedded arm gcc compilation from Visual Studio, in addition to the improved CMake integration – great stuff!
As others have commented, being able to specify the toolchain is important. For me, this would first be useful when combining with CMake and toolchain files. I cannot see myself maintaining makefiles when this is already handled by CMake.
Just to point out, my main use-case is dual targeting embedded arm (bare metal) and native desktop. With CMake on windows I can currently:
– Command line build with arm gcc for embedded (using CMake toolchain file)
– Command line build native with MSVC (or clang)
– In QtCreator open, edit, build, deploy and debug for both native desktop (MSVC) and embedded arm gcc (using the same CMake toolchain file) in the same QtCreator instance.
– Visual Studio: Native desktop build
Being able to reuse the same CMake setup and toolchain file in Visual studio would be very powerful. I hope this kind of use-case will be considered for future development of Visual Studio.
Thanks again
Hi Marc,
Will this work under VS2017 professional v15.7.3?
I got to the stage of not seeing the build option on the makefile.
Thanks so much!!!
Apologies for the delay in responding, been trying to figure out what happened here. It looks like we had a regression that removed the build option. This is fixed in 15.8 Preview 3.
Hi All
Does anybody know how to get the build option on the makefile to show ?
I have tried all the VS2017 v15.7.3 builds including the latest VS preview but still no build option on the makefile.
Is there a version of VS available that works?
Apologies for the delay in responding, been trying to figure out what happened here. It looks like we had a regression that removed the build option. This is fixed in 15.8 Preview 3.
Cross-compilation either never worked, or it stopped working since this article was written. I have the same problem everyone complains about in comments: After editing CppProperties.json, there is no “Build” command in the context menu when right-clicking the makefile. I tried it on my main computer and also on a brand new VS install in a virtual machine (both running 15.7.4). Tried restarting VS, restarting the computer, reinstalling the GCC tools in the VS Installer, all to no avail.
Apologies for the delay in responding, been trying to figure out what happened here. It looks like we had a regression that removed the build option. This is fixed in 15.8 Preview 3.
Thanks for letting us know.
Having no luck with the debugging has it broken in later visual studios?
A dialog appears “Unable to start debugging. Debug server process failed to initialize” and then “Operation aborted (Exception from HRESULT: 0x80004004 (E_ABORT))”
The log file ends with this:
16: (3228465) Starting: “C:\Projects\Test\Firmware\tools\openocd\bin\openocd.exe” -f C:\Projects\Test\Firmware\TestDevice.cfg
16: (3238473) <-logout
if i run openocd manually in cmd prompt with the above it works ok and is waiting for gdb to connect.
If i turn of stdout/stderr filter in the cfg I see the openocd outputing the same into the log, but straight after it has the logout.