The title “simulated debugging” seems a bit mysterious. After reading, embedded firmware developers may have cognitive dissonance; But believe me, it will make sense in the future. The title implies the task of processing signals processed in the microcontroller. Many tasks involving small MCUs are related to processing raw signals from sensors such as microphones, hydrophones, and pressure sensors. Some of these signals need to be cleaned up and then processed.

This processing can use a variety of digital signal processing (DSP) firmware technologies, such as fir and IIR filters, mixers, and FFT. As the signal passes through micro transmission, the data we hope to verify through debugging may be very extensive. For example, what the signal looks like after passing the filter, or what the output of the correlator is when the signal passes. This is where simulation debugging comes in. It allows you to observe signals in real time.

Smaller microcontrollers may lack some of the powerful debugging tools that larger processors have, such as BDM, J-TAG, and SWD. Smaller MCU can also operate as base metal without operating system, which means that any debugging tools available in the operating system will be lost. The lack of such tools and the complexity of real-time signal processing will cause problems in debugging code. However, debugging requires an in-depth understanding of what happens to the data inside the microprocessor, and when processing stream analog signals, you may want to see the actual situation in the analog domain.

Usually, when debugging firmware, the engineer will use the serial port on the microcontroller (if any) to print out the variable value or indicator of the code being executed. There are many problems here. First, in a small MCU, there may not be enough space for printing routines because memory may be scarce. Second, speed can be a problem. In DSP type processing, we usually process input signals in real time one by one. We can’t stop to process long print calls. Third, print routines often use interrupts, which can cause problems with real-time systems. Finally, dumping data to the serial port does not provide you with a direct mock view of the data being processed.

For example, suppose you use an analog-to-digital converter (ADC) to receive signals from a sensor. You can hang the oscilloscope on the output of the sensor and view the signal and noise in the analog view. However, if you view the same signal through the serial port, you will see a pile of numbers when the ADC is read by MCU and sent to the serial port. Now you can put these numbers into a spreadsheet and chart them, or set up another device with digital and analog converters and a display to view the data again. But it seems a bit slow and tedious, and certainly not real-time.

Analog debugging with DAC

The best place is to connect the oscilloscope probe. We can quickly dump the processed samples in the firmware. So, what can we use? The first idea is to connect a digital to analog converter (DAC) to an MCU, or better, to use a converter that can be used as a peripheral device on the MCU.

To try this technique, I connected the ad7801 (an 8-bit DAC) of analog devices to the Arduino nano design I was studying. The core of nano is microchip atmega328, which has no onboard DAC. The ad7801 uses parallel input of 8 data lines, which are synchronously input by another line. The writing speed is very fast. Note that we can use this setting to view 8-bit data, but 10 bit, 12 bit, or other sizes can be used with other DACs, or can be scaled to fit an 8-bit DAC. I connect 8 data cables to the port DAC on the Arduino, and connect the WR cable to the D13 on the Arduino,As shown in Figure 1

Figure 1The DAC is connected to Arduino through 8 data cables.

Now, to send data to the DAC, you only need 3 lines of Arduino ide C code:

Portd = data// Place data bytes between d0 and D7

PORTB = PORTB & B11011111; // Lower D13 to latch data to ad7801

Port B = port B | b00100000// Pull up D13

On 16 MHz Arduino, this code takes about 5 cycles or about 312 ns, and the DAC setup time is 1.2 us. Therefore, you can see that this data display method can be completed quickly without interruption or too much code. You can insert this code into the appropriate location in the firmware to view the data of interest. It might be simpler to put three lines of code in a macro or function. If you create a function for this, you should compile it using the “always_inline” compilation instructions to ensure that it runs quickly.

Now that the DAC is connected, let’s look at some debugging examples. have a lookFigure 2

Figure 2The oscilloscope snapshot shows how analog debugging works in the setting of enabling DAC.

This is an oscilloscope snapshot of the incoming sensor signal – the marking has been deleted for clarity. The bottom trace (Pink / purple) is the original signal because it is entering the ADC pin on the atmega328. You can see the obvious noise on this line. The above trace (yellow) is the same signal after some filtering and other processing in MCU firmware.

The DAC write debug code is inserted in this process, so the sampling timing in the DAC is the same as that in the ADC. If necessary, you can also extract signals from MCU. Ignoring the “spikes” in the signal for the time being, we can see that the processing process has eliminated most of the noise. We now have a clean signal that can be evaluated. It should be noted that the DAC output is a continuous signal stream, not just some transient memory buffer capture.

But what is “spike”? These are some debugging features that I intentionally put into the code to see how the process works. The signal you see is actually a proprietary digital signal corrupted by the signal medium. The task of this code is to read digital packets by:

  • “Found preamble” “package start” “symbol sequence”
  • Track the sampling time so that we can slice the sample at the appropriate time
  • Continue to collect samples until the end of the packet

Now, let’s take a lookFigure 3

Figure 3Display the processed signal and add comments.

This is a view of the processed signal annotated. What I did in the code was to scale the signal from a minimum of 50 to a maximum of 200. This allows some space in the 256 available values to add “spikes” above and below the signal. The first thing we see is the “spike” marked with “preamble detected”. It is created when the code verifies that a preamble (b00000011) has been found, and can be easily generated using the following Arduino ide Code:

Port = 255// Place 255 between d0 and D7

PORTB = PORTB & B11011111; // Lower D13 to latch data to ad7801

Port B = port B | b00100000// Pull up D13

It creates a 312ns wide mark on the oscilloscope track with an amplitude equal to the maximum voltage of the DAC.

The up and down “spikes” in the signal trace are marks indicating the position where the code determines the symbol boundary. It is important to slice symbols at the right time, which becomes critical when a long 0 or 1 occurs. This is because no conversion from 0 to 1 or 1 to 0 was found. It is very useful to check these “spikes” on the oscilloscope, because it allows us to verify the actual timing and confirm that there are no omissions. These symbol boundary “spikes” are created by sending 127 to the DAC using the following Arduino ide code, which is inserted into the appropriate location of the symbol timing code:

Port = 127// Place 127 on d0 to D7

PORTB = PORTB & B11011111; // Lower D13 to latch data to ad7801

Port B = port B | b00100000// Pull up D13

Symbol conversion is marked as a “spike” by sending a 0 to the DAC using the following code, which is inserted into the code that observes the conversion of symbols from 0 to 1 or 1 to 0:

Port = 0// Place 0 on d0 to D7

PORTB = PORTB & B11011111; // Lower D13 to latch data to ad7801

Port B = port B | b00100000// Pull up D13

As you can see, using the DAC to view debugging information that overrides the actual processing trace can greatly help debug various parts of the code. It is many times more powerful than using LED, i/o cable and oscilloscope. It is also more useful than serial port sending data as timing information.

People with sharp eyes may have noticed that at the right edge of Figure 3, the probe attenuation is not X1 or X10, but x53.5. This is a technique that can be done on many newer oscilloscopes, sometimes referred to as custom attenuation settings. The reason for setting it to 53.5 is that it allows you to directly read the 8-bit input value of the DAC using the cursor of the oscilloscope. That is, if I slide the cursor up to the top of the preamble detection “spike”, the oscilloscope cursor reading is 255. If I move the cursor to the end of the symbol boundary “spike”, it is 127.

When using an 8-bit DAC, the formula for this setting is 255/maxvolts, that is, the output voltage of the DAC when inputting the maximum binary input, which is 255 in this example. Therefore, for the 5 V power rail, the custom setting is 51.0 – my power rail has only 4.77 V, so my number is 53.5. When using a 10:1 probe, you may need to multiply this number by 10 before entering it into the oscilloscope.

It is very convenient because you can directly read the number of DAC settings; In other words, the value that an internal variable has when calling the DAC. Think about that. Essentially, you can read variables “in real time” in this way, almost as good as printing statements, but faster and non intrusive. Please note that the noise and resolution of the vertical scale of the oscilloscope will reduce the accuracy, so you may only get ± 1 or 2 counts of the actual value, which is still quite good.

In addition to streaming signals, using this technique, 8-bit DAC can also represent the status of 8 binary flags or the current value of 8-bit variables in the program at the same time. In other words, using an 8-bit DAC provides eight times more information than monitoring a single i/o line.

Analog debugging with PWM

Now, what if you don’t have a DAC available? You can use a pulse width modulator (PWM) peripheral on the microcontroller to perform similar operations. Many small MCU have PWM. When they have PWM, they usually have multiple – usually 6. One of the differences between PWM and DAC is that the PWM output needs to be filtered with a low-pass filter to convert the output to a voltage level. Therefore, when you send a signal sample to PWM, the voltage level will recreate the signal that can be displayed on the oscilloscope, just as you do with the DAC. A simple RC filter can be used for filtering.

But here are some caveats; Low pass filter means that only low-frequency components of the signal can be displayed, and the response speed is slow. Therefore, you should initialize the frequency of PWM to the highest frequency available. On the 16 MHz atmega328, PWM can be set to a maximum frequency of about 31 kHz, so a low-pass signal can be designed for a frequency content of about 3-4 kHz.

Arduino ide code using PWM is even simpler than DAC code after initialization. The code to write an 8-bit value to PWM is simple:

Analog write (PIN number, data)

“Data” here is an 8-bit sampling value, and “pinnumber” is the pin number of PWM output.

Although PWM may be less accurate or unable to display higher frequency signals, it has an interesting function. Some MCU have up to 6 PWM, which means that up to 6 outputs can provide real-time data. You can have a 4 trace oscilloscope to display 4 variables at the same time, leaving 2 standby PWM outputs. In addition, with 2 outputs (PWM or DAC), you can provide I & Q data, which is usually used for DSP signal processing, allowing you to explore negative frequencies. It should be noted that, like DAC code, PWM code does not need to be interrupted.

Other commissioning tools

Another powerful tool that can be used for signals transmitted by DAC or PWM is the spectrum.Figure 4The oscilloscope screenshot in shows an example. The trace above shows the waveform generated in the microcontroller. The signal is actually a mixture or multiplication of two frequencies (F1 = 165 Hz and F2 = 135 Hz) sampled one by one and then sent to the DAC when generated. In frequency mixing, the result is the sum of frequencies and the frequency difference. The hybrid operation suppresses the original generation frequency, as shown by the FFT in the lower half of the oscilloscope trace. Most oscilloscopes, even amateur oscilloscopes, provide FFT as one of the mathematical operations.

Figure 4The oscilloscope screenshot shows how the spectrum mixes or multiplies frequencies.

If your system does not have a DAC or PWM, you can still use something to get some information about the signals in the running firmware. For example, you can write code to bit Bang PWM signals. Although it is likely to be useful for low-frequency signals or slowly changing variables.

I hope the concept of simulation debugging is clearer now. The main concept of streaming data from firmware and displaying it on the oscilloscope is a powerful tool that can speed up the debugging of your signal processing firmware. If possible, it may be useful to select an MCU with DAC peripherals or add a DAC to your first prototype PCB. It can always be deleted later or made as no-pop in the bill of materials (BOM).

Damian bonicatto is a consulting engineer with decades of experience in embedded hardware, firmware and system design. He has 30 patents.

The article is reproduced from planetanalog. Phoenix bonicato is a freelance writer.

Leave a Reply

Your email address will not be published.