Sunday, April 15, 2018

Single Step JTAG debugging an ESP32 Arduino Sketch with VisualGDB

In my last post, I took the latest Preview 1 version of VisualGDB for a test drive. This time around, I am looking at the next increment: Preview 2. Spoiler from the the blog title: it is pretty cool.

TLDR; This is for JTAG debug ESP32 VisualGDB 5.4 Preview 2. This will not work with Preview 1. Preview 3 is now available.

Key is to BOTH: (1) add directories as "Additional Include Directories" and (2) right-click on solution "Add existing files" for these directories:

C:\Users\gojimmypi\Documents\Arduino\libraries\RadioHead
C:\Users\gojimmypi\Documents\Arduino\hardware\espressif\esp32
C:\Users\gojimmypi\Documents\Arduino\hardware\espressif\esp32\cores\esp32
C:\Users\gojimmypi\Documents\Arduino\hardware\espressif\esp32\libraries\SPI
C:\Users\gojimmypi\Documents\Arduino\hardware\espressif\esp32\variants\esp32

This Visual Studio / VisualGDB Arduino project can be downloaded here:

https://github.com/gojimmypi/MyArduinoConversion

(Update 4/28: some final editing still in progress)

First, the Arduino "sketch" is an odd critter, so close to being C/C++ but just enough different that some people have even called Arduino a language of its own. In the Ardunio IDE, File-New creates something like this (note no "main"):

void setup() {
  // put your setup code here, to run once:

}

void loop() {
  // put your main code here, to run repeatedly:

}

The big drawback in the Arduino IDE is there's no debug. (besides - it is really just a text editor, not really an Integrated Development Environment, in my opinion).

Debugging a sketch in Visual Studio takes a few steps. The first part here is based on the Switching Advanced ESP-IDF Projects Between Different IDF Versions and Creating Advanced ESP32 Projects with ESP-IDF tutorials. I should also add that Arduino debugging is not yet officially by VisualGDB.

Note that I am using the PREVIEW 2 version of VisualGDB 5.4 with Visual Studio 2017..

First, we'll need to create a project in Visual Studio with VisualGDB. This example is called myArduinoConversion:


Leave default at "Create a new project based on a sample project". (hopefully the sysprogs folks will fix the black-on-dark-gray color scheme for those of of that choose the dark theme in Visual Studio):


If you don't have the ESP-IDF installed, there will be a prompt:


In my case, there was a version error. (despite the version warning, my C:\SysGCC\esp32\esp-idf\master-VisualGDB didn't even exist!

Click Clone an ESP-IDF from GitHub link to download:


VisualGDB may give an error:


So I manually downloaded the toolchain. Unfortunately the error message does now show the actual command being attempted:



mkdir C:\SysGCC\esp32\esp-idf\master
cd C:\SysGCC\esp32\esp-idf\master
git clone --recursive https://github.com/espressif/esp-idf.git
If you had to download your own manually, VisualGDB will not find it, even if in the specified directory. Choose the option on the right "Locate and existing ESP-IDF checkout. I purposely keep the VisualGDB stuff in this directory, as I have another ESP-IDF in my:
Documents\Arduino\hardware\espressif\esp32
... that is used for other Arduino / VisualMicro projects. Visual GDB will me looking for the README.md file in the root of the clone project. with the above command, I would need to use the ESP-IDF checkout here:

C:\SysGCC\esp32\esp-idf\master\esp-idf


Next, ensure the proper ESP32 toolchain is selected:



Next, choose a sample project. Here we use the get-started / blink template:


Next is the JTAG driver. In my case, I am using the ESP32-WROVER-KIT V3, and I chose the interface/ftdi/esp32-devkitj_v1,cfg file:


Here we can optionally press the "test" button, for a result like this if everything is working properly:



Although there's a "next" button there, it does not do anything; click "finish".

WAIT. (yes, it takes a surprisingly long time) It appears nothing is happening. I think most of the time is spent setting up the project ESP-IDF container.  Eventually a new "blink.c" file shows up:



/* Blink Example

   This example code is in the Public Domain (or CC0 licensed, at your option.)

   Unless required by applicable law or agreed to in writing, this
   software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
   CONDITIONS OF ANY KIND, either express or implied.
*/
#include <stdio.h&gt
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "driver/gpio.h"
#include "sdkconfig.h"

/* Can run 'make menuconfig' to choose the GPIO to blink,
   or you can edit the following line and set a number here.
*/
#define BLINK_GPIO CONFIG_BLINK_GPIO

void blink_task(void *pvParameter)
{
    /* Configure the IOMUX register for pad BLINK_GPIO (some pads are
       muxed to GPIO on reset already, but some default to other
       functions and need to be switched to GPIO. Consult the
       Technical Reference for a list of pads and their default
       functions.)
    */
    gpio_pad_select_gpio(BLINK_GPIO);
    /* Set the GPIO as a push/pull output */
    gpio_set_direction(BLINK_GPIO, GPIO_MODE_OUTPUT);
    while(1) {
        /* Blink off (output low) */
        gpio_set_level(BLINK_GPIO, 0);
        vTaskDelay(1000 / portTICK_PERIOD_MS);
        /* Blink on (output high) */
        gpio_set_level(BLINK_GPIO, 1);
        vTaskDelay(1000 / portTICK_PERIOD_MS);
    }
}

void app_main()
{
    xTaskCreate(&blink_task, "blink_task", configMINIMAL_STACK_SIZE, NULL, 5, NULL);
}


The first thing we'll need to do is take that sketch_mmmdd.ino file and rename it to something like main.cpp and make a few other changes.  (hopefully in a future release, sysprogs will give the option of using C or C++ sample templates).

In the case of this blink app, BLINK_GPIO is simply defined as "2" with no type. The gpio_set_level expects a gpio_num_t like this:
  gpio_set_level(static_cast<gpio_num_t>(BLINK_GPIO), 0);

The blink.c will also need to be renamed blink.cpp (apparently to ensure the compiler knows we are doing C++ and not just C). Preview 2 does not support right-click to rename, but you can click on the file in Solution Explorer and press F2 to enable the rename.

Only other minor tweak is needed; add extern "C" before the void main() like this:
extern "C" void app_main()

The revised code now looks like this:

/* Blink Example

   This example code is in the Public Domain (or CC0 licensed, at your option.)

   Unless required by applicable law or agreed to in writing, this
   software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
   CONDITIONS OF ANY KIND, either express or implied.
*/
#include <stdio.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "driver/gpio.h"
#include "sdkconfig.h"

/* Can run 'make menuconfig' to choose the GPIO to blink,
   or you can edit the following line and set a number here.
*/
#define BLINK_GPIO 2

void blink_task(void *pvParameter)
{
    /* Configure the IOMUX register for pad BLINK_GPIO (some pads are
       muxed to GPIO on reset already, but some default to other
       functions and need to be switched to GPIO. Consult the
       Technical Reference for a list of pads and their default
       functions.)
    */
    gpio_pad_select_gpio(BLINK_GPIO);
    /* Set the GPIO as a push/pull output */
 gpio_set_direction(static_cast<gpio_num_t>(BLINK_GPIO), GPIO_MODE_OUTPUT);
    while(1) {
        /* Blink off (output low) */
     gpio_set_level(static_cast<gpio_num_t>(BLINK_GPIO), 0);
        vTaskDelay(1000 / portTICK_PERIOD_MS);
        /* Blink on (output high) */
     gpio_set_level(static_cast<gpio_num_t>(BLINK_GPIO), 1);
        vTaskDelay(1000 / portTICK_PERIOD_MS);
    }
}

extern "C" void app_main()
{
    xTaskCreate(&blink_task, "blink_task", configMINIMAL_STACK_SIZE, NULL, 5, NULL);
}


So that all is your basic VisualGDB project. Not a whole lot new. Yet.

I've been tinkering with LoRa stuff, and being able to single-step debug the code will be helpful. I'm using the RadioHead library and my local copy is git-cloned into \Documents\libraries\RadioHead. The interesting thing here is that the library is what I call "Arduino-style". I've never been able to get VisualGDB to play well with them, so I do a lot of development with VisualMicro instead. It is less expensive and fully supports the Arduino libraries. Alas I have a tons of "if debugging serial print" statements, as the JTAGsupport is weak. (there is however a VisualMicro GDB tutorial I've been meaning to check out). The reality is the sysprogs VisualGDB is simply a more extensive and robust implementation with JTAG support (with a price to show for it). The problem with the sysprogs folks however, is they never had much of an interest in Arduino.  (Yes, I want my cake and JTAG it too!)

So a single include statement turns out project into something considerably more interesting.

#include "RH_RF95.h"

Actually we'll do a few more things to instantiate the RadioHead drivers:


#include "RH_RF95.h"

#define RFM95_CS 5   // LORA_CS_PIN
#define RFM95_RST 36 // LORA_RST_PIN is 36, TODO but it is read-only! so we'll need to short to another pin
#define RFM95_INT 26 // M5 LORA_IRQ_PIN 36 (jumper to 16)
RH_RF95 rf95(RFM95_CS, RFM95_INT);

Ok, so the code is actually from my M5Stack project, and I don't yet have LoRa hooked up to my lastest ESP32-WROVER, but that's beside the point...

After adding the code, the IDE will complain:


So simply go into Project-Properties and add the path the the "Additional Include Directories". In my case that's:

C:\Users\gojimmypi\Documents\Arduino\libraries\RadioHead

NOTE: If your cursor is in the source code panel when you click Project-Properties, you'll get this modal dialog box. It is NOT the one to use to enter paths for include files (it didn't work for me):


Be sure to click on the project name in the solution explorer, and THEN click Project - Properties:


...for this dialog box to enter the Additional Include File Directory paths:


(note I like to make a habit of always pressing "Apply" before pressing ok, anytime that option is available)

That will take care of finding the code, but trying to compile will give an error about platform not defined:


The first compile will take some time. There's a shockingly large amount of code that gets compiled for such a tiny target. If when building/cleaning/rebuilding, nothing happens, simply exit Visual Studio and relaunch (see below).

This is another one of those Arduino-style nuances. You'll notice this code in RadioHead.h header and shown here:

#ifndef RH_PLATFORM
 #if (MPIDE>=150 && defined(ARDUINO))
  // Using ChipKIT Core on Arduino IDE
  #define RH_PLATFORM RH_PLATFORM_CHIPKIT_CORE
 #elif defined(MPIDE)
  // Uno32 under old MPIDE, which has been discontinued:
  #define RH_PLATFORM RH_PLATFORM_UNO32
#elif defined(NRF51)
  #define RH_PLATFORM RH_PLATFORM_NRF51
#elif defined(NRF52)
  #define RH_PLATFORM RH_PLATFORM_NRF52
 #elif defined(ESP8266)
  #define RH_PLATFORM RH_PLATFORM_ESP8266
 #elif defined(ESP32)
  #define RH_PLATFORM RH_PLATFORM_ESP32
 #elif defined(ARDUINO)
  #define RH_PLATFORM RH_PLATFORM_ARDUINO
 #elif defined(__MSP430G2452__) || defined(__MSP430G2553__)
  #define RH_PLATFORM RH_PLATFORM_MSP430
 #elif defined(MCU_STM32F103RE)
  #define RH_PLATFORM RH_PLATFORM_STM32
 #elif defined(STM32F2XX)
  #define RH_PLATFORM RH_PLATFORM_STM32F2
 #elif defined(USE_STDPERIPH_DRIVER)
  #define RH_PLATFORM RH_PLATFORM_STM32STD
 #elif defined(RASPBERRY_PI)
  #define RH_PLATFORM RH_PLATFORM_RASPI
#elif defined(__unix__) // Linux
  #define RH_PLATFORM RH_PLATFORM_UNIX
#elif defined(__APPLE__) // OSX
  #define RH_PLATFORM RH_PLATFORM_UNIX
 #else
  #error Platform not defined!  
 #endif
#endif

#if defined(__AVR_ATtiny84__) || defined(__AVR_ATtiny85__) || defined(__AVR_ATtiny24__) || defined(__AVR_ATtiny44__) || defined(__AVR_ATtiny45__) || defined(__AVR_ATtinyX4__) || defined(__AVR_ATtinyX5__) || defined(__AVR_ATtiny2313__) || defined(__AVR_ATtiny4313__) || defined(__AVR_ATtinyX313__)
 #define RH_PLATFORM_ATTINY
#endif

To fix, simply add "ESP32" as a Preprocessor definition:


Rebuild again. There's a subtle change in the error message:


So this starts looking a bit more intimidating: Arduino.h missing for our ESP32 project. I took the brute-force approach and searched from the root of my C:\ drive:



C:
cd\
dir Arduino.h /s

I had a 32 matches in various locations. Some are obviously not of interest if found in an .\Arduino\ directory. In my case, I have the Espressif Arduino Core git-cloned in my .\Documents\Hardware\Espressif directory. It can be installed like this:


cd %USERPROFILE%\documents
mkdir hardware
git clone --recursive https://github.com/espressif/arduino-esp32.git
cd arduino-esp32
dir Arduino.h /s
dir SPI.h /s
dir pins_arduino.h /s




Put that directory in the include file list. Compile again and find another missing header file. Repeat. Note the pins_arduino.h is found in multiple directories. I chose the generic ESP32 one. After doing this a few times, VisualGDB often will complain the the settings are corrupt:


I simply exited Visual Studio and restarted. Upon restart, it complained out "line endings not being consistent". VisualGDB probably did this to itself; simple answer yes to fix:


Upon doing a rebuild, I found more files missing. Repeat the procedure for finding files.

Mine were found in:

C:\Users\gojimmypi\Documents\Arduino\libraries\RadioHead
C:\Users\gojimmypi\Documents\Arduino\hardware\espressif\esp32
C:\Users\gojimmypi\Documents\Arduino\hardware\espressif\esp32\cores\esp32
C:\Users\gojimmypi\Documents\Arduino\hardware\espressif\esp32\libraries\SPI\src
C:\Users\gojimmypi\Documents\Arduino\hardware\espressif\esp32\variants\esp32


May also need:
C:\Users\gojimmypi\Documents\Arduino\hardware\espressif\esp32\variants\doitESP32devkitV1

Note that the include file locations is not the only place for this fix; The intellisense will be happy, however the compiler does not seem to "find" there files. There is where things get a little wonky. Those same include files paths need to be added to the project. Right-click on the project, add, existing item (for each of the directories listed above).


Paste one of those include file paths into the File Name box. I choose to select header files (some directories have only headers!). Pick one of them. VisualGDB will recognize that there are others and prompt you to add them all. Click ok.

If multiple files are found, there will be a prompt asking if they all should be added (yes, they should):



Rebuild the project. (sometimes it can help to clean, and then do a full rebuild).

Tada! A fully compiled Arduino-style library with VisualGDB. For me - this was really quite cool: something I've been wanting to do for a long time.



There are still a few weird things. The include files are presented in the Solution Explorer in a pretty bizarre fashion. As these same include directories are found in the Project Properties, I really think it would be best of the directory scanning and file additions happened transparently to the developer.



Also, It seems that breakpoint cannot be placed directly on those included files. However you can step-into them. Unfortunately the only option is step into. Step-over does not seem to work. Nor does step out. Alas this is only Preview 2, and still in development.

The Solution Explorer also looks a bit odd:



There's also some wonkiness with OpenOCD when code is paused too long. I think there's some sort of panic watchdog that is not fed while single-step debug is paused:



And no sooner do I polish this Preview 2 blog... the sysprogs folks have already released Preview 3! (And yes, it DOES appear to work in Preview 3 as well!)

* July 2018 edit:

Over the course of time, my Windows 10 drivers were changed, resulting in the dreaded message:

Error: libusb_open() failed with LIBUSB_ERROR_ACCESS
Error: no device found
Error: unable to open ftdi device with vid 0403, pid 6010, description '*', serial '*' at bus location '*'


Zadig to the rescue once again! Change both drivers on Interface 0 and Interface 1 to libusbk.


I am using the config file found in:

C:\sysgcc\esp32\esp32-bsp\OpenOCD\share\openocd\scripts\interface\ftdi\esp32_devkitj_v1.cfg

That specifically looks like this:


#
# Driver for the FT2232H JTAG chip on the Espressif DevkitJ board
#
 

interface ftdi
ftdi_vid_pid 0x0403 0x6010

# interface 1 is the uart
ftdi_channel 0
# just TCK TDI TDO TMS, no reset
ftdi_layout_init 0x0008 0x000b
reset_config none



Saturday, March 31, 2018

VisualGDB 5.4 Preview 1 with support for Advanced ESP-IDF

Recently there was an exciting announcement from the clever folks at sysprogs: Support for the Espressif ESP32 is finally coming to VisualGDB!! (specifically the preview Try It Now link)

In some previous posts, I attempted to get ESP32 JTAG debugging working in Visual Studio with the VisualGDB add-in. I later went on to try out the amazing Visual Studio Code JTAG debugging. In the end it was relatively complex and clumsy. I ended up returning to VisualMicro coding in Visual Studio. Why? Well, simply put there are way more open source libraries that follow the "Arduino Style" libraries. I was never able to get them to work in a VisualGDB project. As of last year, it didn't seem to be very high on the priority list, either.

Why my obsession with Visual Studio? Well, for one, I've been programming in this environment since the beta arrived on my desk on a single 3.5" floppy disk. Yes, even before the days of "dot net" when it was just experimental plain ASP on IIS for Windows NT. Today Visual Studio is an amazing development environment for many target platforms. The robust intellisense and auto-complete features are incredibly helpful.

I find it quite surprising that the Arduino IDE has been so successful, despite having no intelllisense, no auto-complete, and even more: no debugging. What the Arduino IDE does well: get code onto embedded hardware easily. 

The tricky thing with embedded code is that to effectively do single step debugging - a JTAG adapter is needed. This is a crazy world of semi-standards between a variety of vendors supporting different micro-controllers. Most vendors target "mainstream" processors: ARM, STM32, Atmel, etc. The thing with Espressif is they use Tensilica processors. I had never even heard of them until the ESP8266 came along. So it was not a surprise that using existing JTAG debuggers with them was such a bumpy road.

Here I have some comments and observations with the preview version of VisualGDB for the ESP32

Bugs in VisualGDB 5.4 Preview 1

Right-click on "Components". Add - New Item.


Add MyComponent:


What happens? Nothing. There's a quick blink, but no folder appears there. Try adding another. Same result. The directories *were* created. Right-click, add existing item:


The directories are there... even drilling down and adding the component.mk does not make them appear in Solution Explorer.

Another issue is when assigning Preprocessor Definitions:


The compiler will not "see" this change until Visual Studio exit and restart. For some external libraries (in my case RadioHead) - this directive is *never* seen.

Next, is attempting to include external libraries (in this case Arduino style; yes I know it is not officially supported, but it should in fact act just like any other library, no?)

This first example should be pretty self-explanatory: despite being listed in the "components" of the project, the #include "pins_arduino.h" in the arduino.h file cannot actually find the file:


Despite also being listed in the "additional include directories:"


The file is definitely there:


I've been unable to compile. So close, with only "missing" files... so close.  :)

VisualGDB Wish List:

1) Include more Windows Environment Variables in the VisualGDB Build variables. In particular the USERPROFILE, in my case:

USERPROFILE=C:\Users\gojimmypi

Why? Well Arduino-style libraries are typically installed in:

($USERPROFILE)\Documents\Arduino


In my case, libraries such as:
($USERPROFILE)\Documents\Arduino\hardware\espressif\esp32
($USERPROFILE)\Documents\Arduino\libraries\Adafruit_GFX_Library
($USERPROFILE)\Documents\Arduino\libraries\RadioHead
($USERPROFILE)\Documents\Arduino\libraries\M5Stack


2) Right-click on source file name should include the ability to rename. In particular to change from blink.c to blink.cpp.  Otherwise I needed to rename from Windows Explorer. Visual Studio did not "see" the change. I even after exit and restart. Curiously double-clicking on the file forces the re-scan, but only *after* exiting from Visual Studio.


Curiously, only after renaming a source file (such as blink.c to blink.cpp) - and double-clicking on it- does the re-scan occur, causing Visual Studio to then also see those new Component directories added above. (the "Reload Project" does not seem to work consistently)

3) Right-click on file names and components should also allow "Delete" (purge from disk) and "Remove" (exclude from project)


My version of Visual Studio for this exercise:

Microsoft Visual Studio Enterprise 2017
Version 15.6.4
VisualStudio.15.Release/15.6.4+27428.2015
Microsoft .NET Framework
Version 4.7.02556

Installed Version: Enterprise

Visual C++ 2017   00369-90250-38212-AA522
Microsoft Visual C++ 2017

* July 2018 edit:

Over the course of time, my Windows 10 drivers were changed, resulting in the dreaded message:

Error: libusb_open() failed with LIBUSB_ERROR_ACCESS
Error: no device found
Error: unable to open ftdi device with vid 0403, pid 6010, description '*', serial '*' at bus location '*'


Zadig to the rescue once again! Change both drivers on Interface 0 and Interface 1 to libusbk.


I am using the config file found in:

C:\sysgcc\esp32\esp32-bsp\OpenOCD\share\openocd\scripts\interface\ftdi\esp32_devkitj_v1.cfg

That specifically looks like this:


#
# Driver for the FT2232H JTAG chip on the Espressif DevkitJ board
#
 

interface ftdi
ftdi_vid_pid 0x0403 0x6010

# interface 1 is the uart
ftdi_channel 0
# just TCK TDI TDO TMS, no reset
ftdi_layout_init 0x0008 0x000b
reset_config none



Tuesday, March 13, 2018

The Magic of Visual Studio and GitHub Integration

The more I use Visual Studio 2017 and the integration with GitHub, the more I am amazed at just how well this all works. I'll use a real-world walk-through with some Espressif ESP32 Arduino code that I am using on my M5Stack to show how really useful this is.

First, note that I have a couple of add-ins installed (Tools - Extensions and Updates). In particular the Arduino IDE for Visual Studio and the GitHub Extension for Visual Studio.


I was reading this thingpulse blog entry on reducing ESP32 power when I noticed the line:
WiFi.forceSleepBegin();

Hmm... interesting. But when I tried to use that, it seems to not be implemented for me:


So here we can right click on WiFi and Go To Definition:


And sure enough.. the WiFi.h file opens... but as a "soft" open tab on the right, instead of the regular solution file on the left; hmm. there's no forceSleepBegin() in the header. Let's see if we can find out more.


It gets more interesting, as now we can right click on the WiFi.h tab name and select Open Containing Folder:



So here I can see which of the several (in my case Arduino or ESP32) WiFi libraries are actually being used in this project. In the path we see the ESP32 library is being referenced:



Now.. this directory was installed via a git clone command some months ago.

Even more cool, is we can right-click on Windows Explorer and Open in Visual Studio. (yes, going back!) Be sure to click on white page in the Explorer file list, and not on a specific file. Otherwise only that single file will be opened.



Note how the ~\src directory is opened in the solution. Looking closely, there are little lock symbols next to each file! I didn't do anything to set that up. It just works!


Yes, those little locks say "this is a file under source control and has not been checked out nor edited".

Next, to really see the magic, click on the Team Explorer tab (or View - Team Explorer).



Look! A Welcome to GitHub for Visual Studio message. It just knows that this subdirectory has a parent directory somewhere above that has the git clone repository information. How cool is that!?!

Click on the Sync Button/Tab and we can see the incoming commits, LIVE from GitHub!

Here we can press the Fetch link to just bring in the historical information, but not change our local files.


Want something else? Click on the master Branch text/link, to view the other branches:


Double-click to select a different branch. All the usual git commands apply, so git stash is probably your friend here. (unfortunately manually from command line, as far as I know)

Sadly, the "Search Work Items" does not seem to work for me; I cannot type anything there. That will soon be a Very Cool feature.

We can however, right-click on commit items and compare & see what changes by pulling each commit:


All from the comfort of the Visual Studio UI. How really awesome is that!?! :)

Close the yellow welcome message banner and we can see exactly where this repository lives on GitHub. Press the little home icon any time in Team Explorer to return to this view:


Click on the repository link and the GitHub web page for the repository (in this case Espressif Aduino repository on GitHub) opens in your default browser.

As a suggestion, there are probably some files that should be added to your .gitignore file. Simply going though this exercise creates some files that you probably don't want to push back to the repository and may interfere with pulls. Click the Changes tab/button on the Team Explorer home to see what's been added:


These will of course interfere with selecting other branches, etc.

Overall this is really quite cool. The developers creating Visual Studio simply rock. :)

There's a ton of other cool stuff going on. Be sure to check out The Visual Studio Blog and the Visual Studio conversation in the Gitter community.


Other links:

Sunday, March 11, 2018

Serial Port Debugging

Today I'm trying to use the AVR serial port on a custom ATMEGA328 board.

TL;DR

  • Know your fuse settings! Incorrect settings can result in timing problems, including incorrect UART baud rates.
  • RS232 Serial port UART decoding using Rigol DS1054z Oscilloscope
  • How to fix bad baud rate timing with software.
  • Programming AVR devices from Atmel Studio and Arduino IDE with Atmel ICE


Try as I might, I simply could not get it to work properly. The LED on the USB/TTL adapter would blink during data transfer, but no data shows up in Putty from the AVR.

After exhausting all obvious possibilities, it was time to get serious about looking at what's going on. Rigol oscilloscope debugging to the rescue!

I had not performed any signal decoding any time recently and needed to refresh my memory.  I tried reading the fine manual, but alas still could not get it to work quite the way I wanted. I found this really quite excellent YouTube video. (I have the DS1054z, but close enough)




In short, the most important thing is that the oscilloscope needs to be "zoomed out" (horizontal scale) when doing single trigger. The full capture is apparently the scale of the visible screen. I might have implemented that a bit differently but it is ok once understood.

Trigger: Menu Button (CH1, falling edge, single sweep). Adjust trigger level to halfway point, in my case 1.6V.

Decode: Math Button, (Decode 1, Decoder = RS232, Decode ON, Tx = CH1, Baud Rate, etc)

First, an example of a properly working H (from "Hello World") as sent by Putty:


Fairly straightforward serial port setup: 9600 8N1. I connected my oscilloscope probe to GND and Tx of a typical USB TTL adapter. Pressing the "H" on the keyboard when Putty has focus, sends a nice, clean RS232 digital sequence to the TTL adapter as shown:


Reminder that UART/TTL RS232 idle is normally high, transmission begins with low start bit, then (in our case) 8 data bits, least-significant-bit (LSB) first, then followed by stop bit (aka mark) which is high.

Note the scale here is 500 μs (0.5 ms) per division. We're seeing about 100 μs per bit. Actually at 9,600 baud, the expected time per bit is (1 bit / (9600 bits / sec)) = 0.0001041667 seconds or about 104 μs per bit.

The ASCII value of "H" is 72 = = 0x4c = = 0b01001000

Indeed we see about 400 μs of zero (1 start bit, and then 3 LSB's of the "H"), a one, 2 more zeros, followed by a one, zero, then return to high for stop.

So ok, the TTL adapter is working. Actually, I already knew that, as I used it to login to the serial port of my Raspberry Pi just to confirm. Ok, so that's the easy one. Now on to the data coming from the AVR. Much more interesting:

Here's the source code in Atmel Studio. Also pretty straightforward:


Here too, 9600, (default 8N1). But with a much different result:


The first curious thing I observed was that the delay(milliseconds) function was off by an order of 16 (as measured by eye).  Note the scale here is 5mS per division; 10x slower that the trace above!

A coding delay(1000) actually waited for approximately 16 seconds. So the first thing I thought was perhaps the timescale was simply off, so I tried scaling the baud rate by 16. No luck.

Ok, this certainly explains why nothing is showing up in putty! First: notice there seems to be a duplicate character! (there's no echo; nothing connected to the Tx pin other than the oscilloscope probe) Next: notice the scale on the properly decoded character above on CH2 is 500 μs/div.  The scale on this AVR data is 5 ms/div.  The first low pulse on CH1 is 400 μs, but on CH2 more like nearly 7 ms (7,000 μs!)

Quick note on the math, assuming we're really off by exactly 16x:

400 μs * 16 = 6400 (or 6.4 ms); more precisely (4 * 104.17) * 16 = 6,666.67 μs (or 6.67 ms)

Unfortunately 9600 * 16 =  153,600 is not a valid baud rate. However 2400 * 16 = 38,400 baud is!

So I tried changing the source code to send a character at 2400 * 16 baud (38,400). However it too could not be decoded.

The AVR serial port does indeed look like it is approximately 16x slower. Zooming in on that first low pulse to confirm:


It appears that that first 4 bit pulse is actually 6.3 ms long. Interestingly it is not 6.4 ms (or more precisely not 6.67 ms). That makes a difference of  (6,300 μS / (4 * 100 μS) = 15.75x, not the nice round 16.  More precisely: (6,300 μS / (4 * 104.17 μS) = 15.12x.

So ok, the good old Microsoft Paint program (glad to hear it will not be retired!)... a quick copy / paste / scale of the non-working (10x scale; recall 500μs/dev vs 5ms/div ) waveform on top of the properly decoded signal shows we're not going to get from here to there with a simple baud rate scale. The data is not just duplicated at the wrong speed, something weird is going on with a lot more data than just a single character:


So it would seem my AVR is not going to be doing any useful Serial Port Communication anytime soon. Perhaps there's something weird going on in Atmel Studio. But of all the people that would know how to properly program an ATMEGA328, one would think it would be the ones that built Atmel Studio, eh? Time to go back to bed, just in time for the sunrise.

UPDATE: Ok, so after getting some sleep I thought more about this problem. I was wrong about the math! I needed a target that is 15.75 (more precisely 15.12) times faster than the oscilloscope is capturing. So I simply scaled the baud rate in the app:


And setting the decode frequency to the un-scaled value... TADA! Perfectly decoded. <Happy dance>.

I've kept the estimated and precise numbers above, as the initial estimate of 15.75x worked, even though 15.12x is the more precise answer. Somewhere buried in the RS232/UART spec is probably a timing tolerance range.



Oh and look at that "extra" data: I'm doing a Serial.println() that includes extra carriage return and line feed chars! <sigh>

Here's a lesson I've repeatedly learned: walking away from a problem can sometimes solve it ten times faster as compared to attempting to brute-force it non-stop. (particularly at 4:00 in the morning, time change day)

Still it is curious that I needed to scale the baud rate by an odd value of 15.75 (?)

So a little bit of googling (ok, imagine being an embedded programmer before the internet, say in the 80's. crazy)... I found this YouTube video on ISP programming problems due to wrong clock frequency:



But that's not really my problem - as I can program it using ISP or debugWIRE, but the clock is still wrong by a factor of 15.12x.

There's still the ISP Clock Frequency setting in Atmel Studio:


But that's just the clock speed at which the ISP is doing the programming; this is not the clock speed of the processor. Eventually I found this interesting clip (starting at about the 1:50 mark):



This does seem like a reasonable solution. Perhaps I should have known there's a frequency setting. How else would the code "know" how long a millisecond is?

But I've never needed to manually set the processor speed ever before and never had any serial problem quite like this. In any case - first time for everything. So I tried values of 16000000UL and 1600000UL, and others. No values of #define F_CPU seemed to help. Ok, at this point I'm glad it was not something so obvious.

I'm think it might be something with the fuse settings, but even with the online fuse calculator, nothing jumps out as the obvious answer.

Stay tuned for an update if I figure this one out. In the meantime to set a baud rate, I'll simply multiple by 15.12 (certainly makes for some not-very-portable code  :|  meh)


/*Begining of Auto generated code by Atmel studio */
#include 
/*End of auto generated code by Atmel studio */

#define  F_CPU 16000000UL # does this actually do anything?

//Beginning of Auto generated function prototypes by Atmel Studio
//End of Auto generated function prototypes by Atmel Studio

void setup() {
  // put your setup code here, to run once:
  //pinMode(2, OUTPUT);
  delay(100);
  Serial.begin(2400 * 15.12); // originally estimated at 15.75
}

void loop() {
  // put your main code here, to run repeatedly:
  Serial.println("H");       // Hello World. (just the "H" to keep things simple)
  //digitalWrite(2, HIGH);   // turn the LED on (HIGH is the voltage level)
  //delay(10);               // wait  
  //digitalWrite(2, LOW);    // turn the LED off by making the voltage LOW
  delay(200);                // wait  HH
}

I've posted my entire Atmel Studio Solution here. (note that it is in the M5-RadioHead branch of my LoRa-GPIO project)

Thanks to this post on the AVR Freaks forum, I did find this setting in Atmel Studio for F_CPU:


Although this certainly does not clarify why the problems exists in the first place. Perhaps if I had found the value something like 1,058,236 here, it would have been more of an AHA moment.  (1.0582 * 15.1195 = 16).  At this point I am thinking perhaps all this may be caused by a bad crystal oscillator. (?)

It was suggested that I use the Arduino IDE to program the M5Stack AVR chip instead of Atmel Studio. I found this forum thread that has the solution for using the Atmel ICE with the Arduino IDE. In particular, the input device, data gateway, and composite devices with hex Id's: 03EB and 2141 need to be selected with the install-filter-win.exe tool:


The Arduino IDE uses avrdude to program the ATmega328; the resultant output looks like this:


avrdude: Version 6.3, compiled on Jan 17 2017 at 12:00:53
         Copyright (c) 2000-2005 Brian Dean, http://www.bdmicro.com/
         Copyright (c) 2007-2014 Joerg Wunsch

         System wide configuration file is "C:\Users\gojimmypi\AppData\Local\Arduino15\packages\arduino\tools\avrdude\6.3.0-arduino9/etc/avrdude.conf"

         Using Port                    : usb
         Using Programmer              : atmelice_isp
avrdude: usbdev_open(): Found Atmel-ICE CMSIS-DAP, serno: J4180007xxxx
avrdude: Found CMSIS-DAP compliant device, using EDBG protocol
         AVR Part                      : ATmega328P
         Chip Erase delay              : 9000 us
         PAGEL                         : PD7
         BS2                           : PC2
         RESET disposition             : dedicated
         RETRY pulse                   : SCK
         serial program mode           : yes
         parallel program mode         : yes
         Timeout                       : 200
         StabDelay                     : 100
         CmdexeDelay                   : 25
         SyncLoops                     : 32
         ByteDelay                     : 0
         PollIndex                     : 3
         PollValue                     : 0x53
         Memory Detail                 :

                                  Block Poll               Page                       Polled
           Memory Type Mode Delay Size  Indx Paged  Size   Size #Pages MinW  MaxW   ReadBack
           ----------- ---- ----- ----- ---- ------ ------ ---- ------ ----- ----- ---------
           eeprom        65    20     4    0 no       1024    4      0  3600  3600 0xff 0xff
           flash         65     6   128    0 yes     32768  128    256  4500  4500 0xff 0xff
           lfuse          0     0     0    0 no          1    0      0  4500  4500 0x00 0x00
           hfuse          0     0     0    0 no          1    0      0  4500  4500 0x00 0x00
           efuse          0     0     0    0 no          1    0      0  4500  4500 0x00 0x00
           lock           0     0     0    0 no          1    0      0  4500  4500 0x00 0x00
           calibration    0     0     0    0 no          1    0      0     0     0 0x00 0x00
           signature      0     0     0    0 no          3    0      0     0     0 0x00 0x00

         Programmer Type : JTAG3_ISP
         Description     : Atmel-ICE (ARM/AVR) in ISP mode
         Vtarget         : 3.3 V
         SCK period      : 125.00 us

avrdude: AVR device initialized and ready to accept instructions

Reading | ################################################## | 100% 0.02s

avrdude: Device signature = 0x1e950f (probably m328p)
avrdude: NOTE: "flash" memory has been specified, an erase cycle will be performed
         To disable this feature, specify the -D option.
avrdude: erasing chip
avrdude: reading input file "C:\Users\GOJIMM~1\AppData\Local\Temp\arduino_build_159166/M5Stack_LoRa_AVR.ino.ino.hex"
avrdude: writing flash (1694 bytes):

Writing | ################################################## | 100% 6.94s

avrdude: 1694 bytes of flash written
avrdude: verifying flash memory against C:\Users\GOJIMM~1\AppData\Local\Temp\arduino_build_159166/M5Stack_LoRa_AVR.ino.ino.hex:
avrdude: load data flash data from input file C:\Users\GOJIMM~1\AppData\Local\Temp\arduino_build_159166/M5Stack_LoRa_AVR.ino.ino.hex:
avrdude: input file C:\Users\GOJIMM~1\AppData\Local\Temp\arduino_build_159166/M5Stack_LoRa_AVR.ino.ino.hex contains 1694 bytes
avrdude: reading on-chip flash data:

Reading | ################################################## | 100% 7.33s

avrdude: verifying ...
avrdude: 1694 bytes of flash verified

avrdude done.  Thank you.


However I had the same result in needing the baud rate to be scaled by 15x in order for it to work properly. So this was still not the solution.

Many thanks to Jimmy Lai at M5Stack for sending me fuse selection setting pics (there are many settings to choose from!):




In the second and third pic, it is important to note that the DIVCK8 needs to be left unchecked, despite the value here showing 4E in that first pic. The desired values are: CE D9 FF

My fuse settings before:




Notice LOW.SUT_CKSEL! It is set to an internal clock! There's actually an external clock on the board:

Ok, one would think that even with an external clock, if the fuse settings say to use an internal clock - and this is in fact the default value, that it would work properly with the ATmega328p, eh? Apparently not. As seen above, the clock was wildly off.

When changing the fuses, there's a nice indication of what has been changed, but not written to the chip:


When pressing the Program button, there's a warning that if you don't know what you are doing, you might brick the target:


Braving the ominous warning dialog box... My final fuses look like this:



TADA! <big Happy Dance> Serial.println("Hello world") at 2400 baud actually streams out the UART at 2400 baud. Go figure. This was a tremendous amount of time spent on a couple of fuse bit settings. However it was also an awesome learning opportunity.

More coming soon.... now to get those RadioHead drivers onto the AVR...


Resources, Inspiration, Credits, and Other Links::









Find gojimmypi at gojimmypi.github.io

I'm currently working on my new blog home at  gojimmypi.github.io After implementing a variety of features such as dark mode , syntax hi...