Rob's Technical Article: Debugging in a Real Time Scenario

If you're working on a microcontroller project where you need to maintain real-time constraints, analyze timings or handle events dynamically while debugging, using a breakpoint debugger can be a showstopper. If you hit one breakpoint, the processor ceases to handle events, devices that need to be serviced go unserviced, dogs and cats will start living together in sin and all hell will break loose. In such a case, your handy logic analyzer can help you debug your program without hitting breakpoints.

I'm wondering why it's taking so long for the packet that's coming in to process. I thought that what's taking the most amount of time was my hamming code engine, however, D7 Trigger 2 shows the time when I loop through the data that's received and find what ADC produced that data, then socks the data away in a queue for the USB to transfer at the end of the processing cycle. As you can see, it's not the gorrilla in the room, something else is really eating my CPU. By shuffling around when I raise and lower various GPIOs, I've established that my error correcting code is doing it.

As you can see, I'm re-using the Trigger 3 pin to mark different areas. This is OK to do as long as you don't lose context of what's going on where and when. It can be a real time saver with compile-flash-inspect cycles.

So now that I know what is causing the problem, I can work on potential solutions. This code is very bit-count heavy, so the best way I can get away with dealing with this is by simply counting bits in a more efficient manner.

Although not too much better, this is a whole lot better than what we had before.

Here's another issue I've noticed. There's a stutter in the operation of my microcontroller. This stutter causes me to sometimes miss ADC data ready when pulling in high sample rates. Sometimes this stutter happens in the middle of my SPI communications phase. I am doing this because I want to pump words until nDONE triggers. The "D6 Trigger 1" line shows where the CPU is actually working; I'm 'manually' pumping the SPI rather than use DMA in this case. Because of this, the interrupt that's causing the stutter can get in the way of my communications. After the communications completes, I process the data. I show the data processing for each device in the ADC chain using the Trigger 2 line. I am using Trigger 3 to show where the USB interrupt is happening by simply raising the GPIO at the beginning of the USB ISR, and then dropping it at the end. It turns out that this USB interrupt is what's causing my stutter. I actually knew this from the beginning because it's the only interrupt in the software at the moment, but I wanted to show this because using GPIOs to establish why there is jitter in your timing can be quite useful.

Now that you get the idea that we can tighten timings with GPIOs, you can imagine other things that are useful. Take for example, lets say you want to know when the program goes into a certain conditional. You can raise and lower the GPIO within the conditional statement. Likewise, you can raise and lower the GPIO in a loop with another GPIO bracketing the loop to establish how many times you go through that loop.

There are plenty of cases where using GPIOs won't help you debug, for example, if you're wondering why you're getting memory corruption or hitting the deadly FAULT-ISR. However, you can use GPIOs to count out how many iterations through a loop you've gone before you hit that fault, if it's something that takes a while. Likewise, you can use a GPIO as an interrupt to hit a breakpoint in your source code, should you want to be able to do so, although whatever real-time events pass after you hit that breakpoint will be lost and whatever state you were maintaining will be completely hosed.

There's no end to the things you can do with a GPIO or a handful of GPIOs to help you troubleshoot your microcontroller. A bit of creativity is all it takes to get through just about any problem.

By: Rob Stoddard