Tutorial: Using a Logic Analyzer for Fun and Data

Tutorial: Using a Logic Analyzer for Fun and Data

Introduction

Designing software and hardware on Raspberry Pi and Arduino computer can be pretty easy and clear.  However, when things don’t work, and you aren’t sure where the problem is, it can be very frustrating.   Many of us software engineers are used to finding bugs in software by using source code debuggers, print statements and working to duplicate and isolate the problem.  And we are pretty good at it after awhile.  However, in small computer systems, we add another layer in, and that is the hardware.  Trying to figure out why your I2C device isn’t responding can be very difficult when you have no idea what is actually going out on the I2C bus.  Why is your stepper motor not turning?  Why can’t you get your Raspberry Pi to talk to your Arduino across a serial interface?  Often, the answer is either in the wiring, the grounds, or you just aren’t writing the correct data out.

It is hard to find when you can’t see the signals and the data coming across the lines.  This is when a Logic Analyzer can be worth it’s weight in gold.

What is a Logic Analyzer

First of all, don’t panic.  Decent logic analyzers aren’t very expensive any more.  The 8-Channel USB Logic Analyzer set me back $150 at Adafruit.  You can find other brands for less and a number of models for a lot more.  

 A logic analyzer is a test instrument that captures and displays multiple signals from a digital circuit. A logic analyzer converts the captured digital data  (versus analog data – see the below section on oscilloscopes) into timing diagrams, decoded protocols, state machine traces, and sometimes even assembly language. Logic Analyzers have triggering capabilities, which can help you locate a problem, and are useful when a user needs to see the timing relationships between many signals in a digital system.  A USB based logic analyzer tends to be cheaper because you use your computer screen, keyboard and the your own computer to crunch the data unlike units that include all of the above.  Typical USB brands include:

  • Saleae
  • Intronix
  • USBee
  • Knockoff Saleae brands (emulating the Saleae API) all over E-Bay
  • BitScope
  • ChronoVu

SwitchDoc Note:  When you are debugging hardware, remember the golden rule:  “You can always trust your mother, but you can never trust your ground.”  Make sure you have connected a common ground between your devices.  Things act really funny when your ground is loose or not connect.  Make sure it is solid.

The Logic Analyzer We are using

A few years ago, we bought a Saleae Logic – 8-Channel USB Logic Analyzer.  It currently is about $400 on Amazon .  Excellent device and value.  Really excellent.

Logic 8 (Red) – Saleae 8-Channel Logic Analyzer – Compatible With Windows, Mac, or Linux($399)

A couple of other Logic Analyzers (Lower quality and speed, but significantly cheaper):

A logic analyzer is a great tool, and when you need it, you need it.   We are pleased with the performance of even a low end logic analyzer like Saleae.  

 

20 years ago when Dr. Shovic, our CTO, started designing VLSI (Very Large Scale Integrated) circuits, he spent many hours looking at the outputs of my chip to make sure things were working properly.  After a period of bench testing, we would take the simulation data that was generated during a design and put it on a VLSI Tester which can easily cost $2MM and up.  And yet, the results needed to match (functionally anyway) his results on the bench.  A $2MM VLSI tester can do a lot more than a logic analyzer.  It can check parametric like rise and fall times, voltage levels, switch loads to require more current and vary the power supplies (even change the ripple) to make sure that your ICs work.  We can’t do that with this $150 logic analyzer, but we can see if the digital signals are coming out at the right time in the right sequence.  

What are the Specs and what do they mean?

The Saleae Logic-8 logic analyzer plugs into either a PC or Mac computer and has an included pretty comprehensive software.   We found it works pretty well, although every hour or so it would lock up and we would have to restart it.  After we found a loose ground (see SwitchDoc Note above) it became much more solid.  Since it supports protocol analysis (see section below),  if you ever have to to debug SPI, I2C, Serial, CAN, 1-wire, Manchester, or other digital protocols, this tool will save you lots of time.

This logic analyzer samples each channel at up to 24 Million times per second (24MHz). A large fraction of hobbiest applications run at less than 10MHz, and so this device is great for these kind of circuits.   Since the rule of thumb is that you have to sample 4 times faster than your highest speed signal to accurately reproduce the digital signal, a 24MHz sampling rate means that your best accurate signal speed will be about 6MHz.  Higher sampling speeds requires low USB latency on all of these USB Logic Analyzers.  So depending on your computer, achieving the maximum rate may require you to remove other USB devices, use a different USB host controller or increase the logic analyzer software priority on the CPU.

The Saleae Logic analyzer that we are using has 8 inputs — it can monitor 8 different digital signals at once.

This Saleae logic analyzer can save as many as 10B samples, letting you capture even the rare events.   This raises a question.  How can my computer, which has only 8GB of RAM store 10B samples?  The answer is compression.  The logic analyzer stores the data in a compressed mode which saves a bunch of space.

The inputs are protected against overvoltage conditions (+/- 15V, but not for continuous operation!) via current-limiting resistors and ultra-low-capacitance diode clamps.  A resettable fuse also protects the USB ground return line to protect your computer.

The last big thing to care about is how the logic analyzer can be triggered.  See setting a trigger below.

Logic Analyzers versus Oscilloscopes

We have both an oscilloscope and a logic analyzer.  Different tools for different problems.  A logic analyzer is designed to look for digital signals and for long patterns of digital signals, while a oscilloscope is designed for looking at a constantly varying signal voltage over time.  The observed waveform can be analyzed for properties such as rise time, 

 amplitude, distortion and others.  A storage oscilloscope can be used to detect single  events and display them for a long time allowing events to be observed that are happening far too fast to be directly perceptible.

SwitchDoc Note:  OK, things don’t work.  You can’t see the signals.  Remember the first rule of Electrical Engineering:  “It works better if you plug it in.”  Make sure your devices have power, your test equipment has power and that you have all the lines wired correctly and secure.  If you soldered your board, check closely for cold solder joints.  Oh, and make sure you don’t have your board touching a conductive surface.  Dr. Shovic once spent hours trying to figure out why a board wasn’t working and it was because it was sitting on conductive foam.

Looking at a Signal Trace

 

Now let us set up a simple test to show what the logic analyzer can do.  Here is a simple test circuit, one LED and one resistor hooked up to a Raspberry Pi (for instructions on how to do this, check out:  [https://projects.drogon.net/raspberry-pi/gpio-examples/tux-crossing/gpio-examples-1-a-single-led/].   Make sure you hook up the LED to the right pin (GPIO 17 in our case).  Put the following code in “blinkLED.py” using your favorite text editor on the Pi.

import RPi.GPIO as GPIO

import time
# to use Pi board pin numbers
GPIO.setmode(GPIO.BCM)
pin = 17

# set up GPIO output channel

GPIO.setup(pin, GPIO.OUT, pull_up_down=GPIO.PUD_DOWN)
GPIO.setup(pin, GPIO.OUT)

# blink GPIO17 50 times

for i in range(0,50):

        GPIO.output(pin,GPIO.HIGH)
        time.sleep(0.01)
        GPIO.output(pin,GPIO.LOW)
        time.sleep(0.01)
        GPIO.output(pin,GPIO.HIGH)
        time.sleep(0.02)
        GPIO.output(pin,GPIO.LOW)
        time.sleep(0.02)
        time.sleep(0.5) # sleep 500msec

GPIO.cleanup()

Execute the code by typing:

sudo python BlinkLED.py

This code blinks an LED on GPIO17 100 times (first for 10ms and then for 20ms).

Here is the resulting trace from the Logic Analyzer. You can clearly see the first 10ms blink followed by the 20ms second blink.

While this is a simple example, it really shows the power of using a Logic Analyzer.  These signals are impossible to see with just a multimeter.

Setting a trigger

A logic analyzer needs to be told when to start storing state data.  All logic analyzers have a maximum limit on the amount of data that can be stored (10 Billion samples for our logic analyzer – a lot of data).  The trigger also tells us where to start looking for a specific event (going through 10 Billion samples can be a real problem.  You need to be smarter than just capturing tons of data).

You can set a trigger condition to start showing the data.  A trigger condition can range from simple (such as triggering on a rising or falling edge of a single signal) to the very complex (such as configuring the analyzer to decode the higher levels of the TCP/IP stack and triggering on a certain HTTP packet or on a type of I2C packet ).

At this point, the user sets the analyzer to “run” mode, either triggering once, or repeatedly triggering.

Once the data are captured, they can be displayed several ways, from the simple (showing waveforms or state listings) to the complex (showing decoded I2C protocol traffic as shown below). Some more complex analyzers can also operate in a “compare” mode, where they compare each captured data set to a previously recorded data set, and halt capture or visually notify the operator when this data set is either matched or not.   This is useful for long-term specialized testing.

With the Saleae logic analyzer 8, you can trigger on the following conditions on one channel:

    • Rising edge of a signal
    • Falling edge of a signal
    • width of a pulse either high or low

You can set the other channels to require the channels to be either high or low during a the previously selected edge. After selecting an edge, note that other channels will display a button with an ‘X’. ‘X’ indicates ‘don’t care’.  You can only set one channel to an edge condition.  The rest of the channels can only to be set to level conditions.

Note this provides a lot of flexibility in capturing signals.  You can set a control signal in software to feed into one channel and then capture the rest of the channels or you can set a trigger to look for all ones on the other channels anytime you see a rising edge of channel 0 for example.

The logic analyzer will typically store a lot of data before the trigger, so say if you had an I2C module that resets itself, you could trigger on the reset and then look back in the pre-trigger buffer to find the offensive I2C command.

Looking at signals on I2C on WeatherPiArduino

SwitchDoc Labs produced a board called the  WeatherPiArduino (Now replaced by the more sophisticated Weatherboard), an interface board for both the Raspberry Pi and Arduino to talk to weather instruments including a new Lightning Detector.  It uses a bunch of I2C devices as well as connecting to an anemometer / wind vane and rain bucket.  We thought we would use the logic analyzer to start looking at the action on the I2C bus.   

One of the most powerful features of this logic analyzer is the ability for the analyzer to record and then display protocol information.   Take a look at this trace below.  It shows a trace of the I2C bus when the computer is writing to the WeatherPiArduino BMP180 Barometer / Temperature I2C device.  The protocol analyzer tells us that we are writing to I2C address 0x77 (the address for the BMP180), setting up a write to address 0xF4 (Measurement Control) and then writing out the data 0x2E, which sets up the BMP180 to start a temperature measurement.

 

Setting up a Temperature Read on an I2C BMP180 Barometer 

The trace below shows a read of the DS3231 Real Time Clock that is also connected to the WeatherPiArduino board.  This trace shows a much more complex sequence, all nicely decoded by the logic analyzer.  Look how hard it would be to read these sequences by hand.  This shows first a write to the I2C Address 0x68 which is the address for the DS3231, then a command to read out the time from the DS3231 (0x00) and then the read from the DS3231 address 0x68 consisting of seconds, minutes, hours, day, date, month and year in 7 single byte read transfers. 

Stepper motor drive from Arduino the SunTracker System

 

The trace above is from a SunAir board talking to the MouseAir motor controller board driving a stepper motor in the Sun Tracker System.  A stepper motor is a brushless DC electric motor that divides a full rotation into a number of equal steps. The motor’s position can then be commanded to move and hold at one of these steps without any feedback sensor (an open-loop controller), as long as the motor is carefully sized to the application.

To make a stepper turn, you send the two coils of the motor specific signals to attract the gears teeth with an electromagnet.  With the right sequence one coil is turned off and then the second coil is turned on, making the gear rotates slightly to align with the second one.  Repeating this sequence makes the motor move step by step, hence the name.  This allows the motor to be turned by a precise angle.

 

Here is the main part of the Arduino code generating this trace.

 

// Stepper Motor Test for Logic Analyzer
// SwitchDoc Labs 1/14/2014



#define LEFTSTEP 0
#define RIGHTSTEP 1
int Motor1A = 8;   // GP2 on SunAir
int Motor2A = 13;   // Servo on SunAir
int Motor3A = 12;   // Limit0 on SunAir
int Motor4A = 11;   // Limit1 on SunAir
int EN12 =7;   // Not on on SunAir / on SunAirPlus 
int EN34 = 6;   // Not on  on SunAir / on SunAirPlus 


// stepper controls

void enable_motor()
{
      digitalWrite(EN12, 1); 
     
      digitalWrite(EN34, 1); 
  
}

void disable_motor()
{
      digitalWrite(EN12, 0); 
      digitalWrite(EN34, 0); 
  
  
}
void multipleStep(int count, int direction)
{
  
  enable_motor();
  int i;
  Serial.print("multipleStep:  Count=");
  Serial.print(count);
  Serial.print(" direction = ");
  if (direction == LEFTSTEP)
    Serial.println("LEFTSTEP");
  else
    Serial.println("RIGHTSTEP");

  
  for (i= 0; i < count; i++)
  {
    if (direction == LEFTSTEP)
      forwardStep();
     else 
      reverseStep();
    
  }
  disable_motor();
  
}



void forwardStep()
{
   float StepDelay = 50;

  // step 1
  digitalWrite(Motor1A, 1);  // coil 1a  
  digitalWrite(Motor3A, 1);  // coil 2a
  digitalWrite(Motor2A, 0);  // coil 1b
  digitalWrite(Motor4A, 0);  // coil 2b  
  delay(StepDelay);


  // step 2
  digitalWrite(Motor1A, 0);  // coil 1a  
  digitalWrite(Motor3A, 1);  // coil 2a
  digitalWrite(Motor2A, 1);  // coil 1b
  digitalWrite(Motor4A, 0);  // coil 2b 
  delay(StepDelay);



  // step 3
  digitalWrite(Motor1A, 0);  // coil 1a  
  digitalWrite(Motor3A, 0);  // coil 2a
  digitalWrite(Motor2A, 1);  // coil 1b
  digitalWrite(Motor4A, 1);  // coil 2b  
  delay(StepDelay);

  // step 4
  digitalWrite(Motor1A, 1);  // coil 1a  
  digitalWrite(Motor3A, 0);  // coil 2a
  digitalWrite(Motor2A, 0);  // coil 1b
  digitalWrite(Motor4A, 1);  // coil 2b  
  delay(StepDelay);

}

void reverseStep()
{
  
     float StepDelay = 50;
  // step 4
  digitalWrite(Motor1A, 1);  // coil 1a  
  digitalWrite(Motor3A, 0);  // coil 2a
  digitalWrite(Motor2A, 0);  // coil 1b
  digitalWrite(Motor4A, 1);  // coil 2b  
  delay(StepDelay);

 
  // step 3
  digitalWrite(Motor1A, 0);  // coil 1a  
  digitalWrite(Motor3A, 0);  // coil 2a
  digitalWrite(Motor2A, 1);  // coil 1b
  digitalWrite(Motor4A, 1);  // coil 2b  
  delay(StepDelay);

  // step 2
  digitalWrite(Motor1A, 0);  // coil 1a  
  digitalWrite(Motor3A, 1);  // coil 2a
  digitalWrite(Motor2A, 1);  // coil 1b
  digitalWrite(Motor4A, 0);  // coil 2b 
  delay(StepDelay);




 // step 1
  digitalWrite(Motor1A, 1);  // coil 1a  
  digitalWrite(Motor3A, 1);  // coil 2a
  digitalWrite(Motor2A, 0);  // coil 1b
  digitalWrite(Motor4A, 0);  // coil 2b  
  delay(StepDelay);

  
}


// the setup routine runs once when you press reset:
void setup() {
  // initialize serial communication at 9600 bits per second:
  Serial.begin(57600);
  Serial.println("");
  Serial.println("----------start of SunAir SunTracker script---------");
  Serial.println("");
  
  pinMode(Motor1A, OUTPUT);
  pinMode(Motor2A, OUTPUT);
  pinMode(Motor3A, OUTPUT);  
  pinMode(Motor4A, OUTPUT);
  pinMode(EN12, OUTPUT);
  pinMode(EN34, OUTPUT);
  
  
}

void loop() {

  
  
   multipleStep(5, LEFTSTEP);
 

   delay(100);
 
   multipleStep(5, RIGHTSTEP);
   delay(1000);

}

Conclusion

A Logic Analyzer is an excellent, inexpensive tool to help debug complex designs.   Sometimes, it is absolutely essential to see what is going on your signal lines to figure out how to fix a problem.  In the next column, we will be discussing Oscilloscopes and reviewing the new Saleae Logic 16 Logic Analyzer that includes an analog “oscilloscope” type of trace.