Logic Analyzer with LPCXpresso Base Board

This project demonstrates how to take advantage of the male and female extension contacts on the LPCXpresso Base Board. With a logic analyzer we will illustrate the I2C and SPI communication between the LPC1343 board and eeprom devices placed on the LPCXpresso Base Board and on a separate breadboard.


Overview
Figure 1 - The LPC1343 Board, LPCXpresso Base Board, Logic Analyzer and Breadboard mounted Eeprom

Introduction to the male and female extension contacts

The LPCXpresso Base Board comes with a lot of diverse components such as accelerometer, joystick and flash memory. It is also fitted with two extension connectors, one female and one male, which makes it ideal for connecting other components to your LPCXpresso Base Board. Both the connectors have 50 pin expansion dual row header with 100 mil pitch. The female contact could be used for connection to external designs (see previous projects) or a breadboard (figure 3a) while the male contact could be used for connection with flat cable (figure 3b) or to a logic analyzer (figure 3c). While using the extension contacts beware that some of the signals from the LPC1343 board are connected to devices on the LPCXpresso base board and that these might need to be disabled using the jumpers on the board. The correct pin configuration is described in the schematics and the user's manual for the LPCXpresso Base Board (download it from Embedded Artists support pages).


Figure 2
Figure 2 - Male and female extension contact on the LPCXpresso Base Board


Figure 3
Figure 3 - Examples of connections a) Female contact with breadboard b) Male contact with flat cable c) Male contact with logic analyzer

Introduction to the logic analyzer

When debugging software and hardware it is often helpful to see the signal on the pins that you want to manipulate. Whether this is just setting a specific pin high or low or inspecting what bits flow over a communication bus the logic analyzer will help to illustrate the behavior of any exposed signal. The logic analyzers come in various shapes and setups, from full mainframe systems to simple USB-connected devices.

The logic analyzer shows relative timing and states, high or low, of the different signals acquired. Note that the logic analyzer does not show an analogue signal so rise time and fringes on the wire will not be visible. It simply indicates whether the signal is high or low, given by a threshold value, and at what relative time it got there. This makes it suitable for analyzing digital signals in a circuit.

A key feature of a logic analyzer is to trigger signal acquisition on a given state. This might be the change on a single wire or on a combination of wires. Performance of a logic analyzer is often measured in speed, resolution, number of channels and memory. Apart from being capable of measuring the signals many logic analyzers can also interpret signals from known communication protocols such as I2C and SPI.

There are several logic analyzers on the market but the one we at Embedded Artists have found to work for us is the Intronix Logicport ( www.pcTestInstruments.com). The software for the PC is downloaded of the Internet and once installed it is just to plug the Logic Analyzer into the USB-contact of your computer and start measuring.

Part A: Analyzing I2C signal on the LPCXpresso Base Board

Introduction

In this demonstration we will use the logic analyzer to view the I2C signal between the LPC1343 placed on the LPCXpresso Base Board and an internal I2C eeprom mounted on the LPCXpresso Base Board. The I2C protocol requires two signal lines; serial data (SDA) and serial clock (SCL). These are the signals that we will monitor.

 

Required Parts

Preparation

Connect the logic analyzer to the corresponding pins on the male extension connector on the baseboard (see picture for correct setup):

  • D0 is connected to GPIO0.4 (SCL)
  • D1 is connected to GPIO0.5 (SDA)

The pin setup for I2C communication
Figure 4 - The pin setup for I2C communication

Setting up the logic analyzer

Add the wires D0 and D1 to the signal view. Trigger the signal on the SDA going from high to low (corresponds to a start bit). Insert an I2C interpreter to the signals (Use "Setup -> Interpreter...-> Create..." or "Right Click" in the wire view). In the interpreter settings use the SDA as the signal and the settings as seen below.


Setting up the Logic Analyzer for I2C interpretation
Figure 5 - Setting up the Logic Analyzer for I2C interpretation

Setting up the microprocessor

The eeprom is organized as four blocks of 256 x 8-bit memory. When writing to the device it is necessary to first specify which block to write to and then specify which of the 256 memory positions to address. Our program will write to the 240th memory location in the first block and then read back the value from the same memory location. Below is the source code for this:

#include "type.h"
#include "timer32.h"
#include "i2c.h"
#include "gpio.h"
#include "eeprom.h"

#define E_WRITE_LEN 1

int main (void)
{
    uint16_t offset = 240;
    uint8_t b[E_WRITE_LEN];
    uint8_t a[E_WRITE_LEN];
    int16_t len = 0;
    uint16_t addr;

    GPIOInit();
    init_timer32(0, 10);
    I2CInit( (uint32_t)I2CMASTER, 0 );

    // Set the value to write to the eeprom
    b[0]= 0x5A;
    // Set the address position
    addr=offset;			
    // Write to the value b to eeprom addr
    len = eeprom_write(b, addr, E_WRITE_LEN);	
    // Wait for eeprom to finish writing
    delay32Ms(0, 6);
    // Read the value from eeprom addr    
    len = eeprom_read(a, addr, E_WRITE_LEN);	
}
                    

The write and read commands for the eeprom are located in the eeprom.c file:

int16_t eeprom_read(uint8_t* buf, uint16_t offset, uint16_t len)
{
    uint8_t addr = 0;
    int i = 0;

    uint16_t off = offset;

    if (len > EEPROM_TOTAL_SIZE || offset+len > EEPROM_TOTAL_SIZE) {
        return -1;
    }

    addr = EEPROM_I2C_ADDR1 + (offset/EEPROM_BLOCK_SIZE);
    off = offset % EEPROM_BLOCK_SIZE;

    I2CWrite((addr << 1), (uint8_t*)&off, 1);
    for ( i = 0; i < 0x2000; i++);
    I2CRead((addr << 1), buf, len);

    return len;

}

int16_t eeprom_write(uint8_t* buf, uint16_t offset, uint16_t len)
{
    uint8_t addr = 0;
    int16_t written = 0;
    uint16_t wLen = 0;
    uint16_t off = offset;
    uint8_t tmp[17];

    if (len > EEPROM_TOTAL_SIZE || offset+len > EEPROM_TOTAL_SIZE) {
        return -1;
    }

    addr = EEPROM_I2C_ADDR1 + (offset/EEPROM_BLOCK_SIZE);
    off = offset % EEPROM_BLOCK_SIZE;
    wLen = ((((off >> 4) + 1) << 4) - off);
    wLen = MIN(wLen, len);

    while (len) {
        tmp[0] = off;
        memcpy(&tmp[1], (void*)&buf[written], wLen);
        I2CWrite((addr << 1), tmp, wLen+1);

        /* delay to wait for a write cycle */
        eepromDelay();

        len     -= wLen;
        written += wLen;
        off  += wLen;

        wLen = MIN(EEPROM_PAGE_SIZE, len);

        addr += off / EEPROM_BLOCK_SIZE;
        off  = off % EEPROM_BLOCK_SIZE;
    }

    return written;
}
                    

View the results

Start acquiring signals from the logic analyzer. What you will see is a string of bits sent over the wires. If you’re having trouble finding your signal use the view -> Scroll to trigger and Zoom in /out.


The I2C write command captured by the Logic Analyzer
Figure 6 - The I2C write command captured by the Logic Analyzer

The write command consists of three parts:

  • Start bit, block select bits and write bit
    • From the I2C protocol we see that the communication is triggered by a start bit. This is a done by setting the SDA line low while the clock is high. This is followed by four control bits specific to the eeprom and has value (1010). The next three bits are the block select that specify to which block in the eeprom we want to write (please see eeprom data sheet for details) and the last bit (0) is the I2C write command to the slave device. This is followed by an ACK bit, the SDA line is pulled low, sent by the eeprom. The complete command in binary is 0-10100010-0 and in the interpreter this corresponds to S-A0h-A as in figure 6.
  • Memory address
    • Each block consists of 256 addresses. This portion of the protocol sets the memory position in the block that should be written to. The hex number 0xF0 corresponds to the 240th memory position. Once again the instruction is followed by an ACK from the eeprom.
  • Value sent to eeprom and stop bit
    • The 8 bit value that we want to write to the eeprom is sent. This is followed by an ACK from the eeprom and finally the processor sends the stop bit (P), by setting the SDA line high while the clock is high, to finish the transfer.

The I2C read command captured by the logic analyzer
Figure 7 - The I2C read command captured by the logic analyzer

The read command consists of three parts:

  • Write command and address
    • The eeprom needs to know what memory address it should read from. This is done by sending a write command and an address to the eeprom but without sending a data value. The device is now aimed at the correct memory position.
  • Block select bits and read bit
    • The read command is triggered by a start bit. This is followed by four control bits specific to the eeprom and has value (1010). The next three bits are the block select that specify to which block in the eeprom we want to write to and the last bit (1) is the I2C read command to the slave device. Notice that the exact memory address is not mentioned in the command as we have already set that in the write command above.
  • Value sent from eeprom
    • The eeprom sends the value stored in the memory address requested. Notice the NACK bit sent from the processor after the byte has been sent. This tells the eeprom that it does not need to send any more bytes. After that the stop bit is sent by the processor on the SDA line.

Part B: Analyzing the SPI signal on the LPCXpresso Base Board

Introduction

In this demonstration we will use the logic analyzer to view the SPI signal between the LPC1343 placed on the LPCXpresso Base Board and an external SPI eeprom placed on a breadboard. The SPI protocol requires four signal lines; chip select, clock, line out and line in. These are the signals that we will monitor.

 

Required Parts

Preparation

Connect the eeprom to the female extension connectors on the base board (see picture for correct setup):

  • Chip select (SSL) - p1.11 is connected to p1
  • SSP Clock (CLK) - p2.11 is connected to p2
  • Line Out (MOSI) - p0.09 is connected to p3
  • Line In (MISO) - p0.08 is connected to p4
  • Power (Vcc) - 3V3 is connected to p8
  • Ground (Vss) - GND is connected to p5

Connect the logic analyzer to the corresponding pins on the male extension connectors on the baseboard (see picture for correct setup):

  • D0 is connected to p2.11 (CLK)
  • D1 is connected to p1.11 (SSL)
  • D2 is connected to p0.09 (MOSI)
  • D3 is connected to p0.08 (MISO)

The pin setup for connecting the SPI eeprom
Figure 8 - The pin setup for connecting the SPI eeprom

The pin setup for SPI communication
Figure 9 - The pin setup for SPI communication

Setting up the logic analyzer

Add the wires D0, D1, D2 and D3 to the signal view. Make sure that the trigger is on the chip select (CS) edge up. Insert two SPI interpreters to the signals (Use “Setup -> Interpreter…-> Create…” or "Right-Click" in the wire view). For the first interpreter use the MOSI signal as the data signal and use active high as trigger. For the second interpreter use the MISO signal as the data signal.


Setting up the Logic Analyzer for SPI interpretation
Figure 10 - Setting up the Logic Analyzer for SPI interpretation

Setting up the microprocessor

Our program will set a memory position, write a value to that memory position and then read back the value from the same memory position. Below is the source code for this

#include "type.h"
#include "timer32.h"
#include "ssp.h"
#include "gpio.h"

#define CS_ON()  GPIOSetValue(PORT1, 11, 1 )
#define CS_OFF() GPIOSetValue(PORT1, 11, 0 )
#define SB	 	(0x01)
#define WRITE 	(0x40)
#define READ 	(0x80)

static void writeEnable(void)
{
	uint8_t data[2];
	data[0]= 0x01;
	data[1]= 0x30;
	CS_ON();
	SSPSend(&data, 2);
	CS_OFF();
}

static void writeDisable(void)
{
	uint8_t data[2];
	data[0]= 0x01;
	data[1]= 0x00;
	CS_ON();
	SSPSend(&data, 2);
	CS_OFF();
}

int main (void){
	GPIOInit();
	init_timer32(0, 10);
	SSPInit();

	GPIOSetDir( PORT1, 11, 1 );
	CS_OFF();

	uint8_t send[4], data[2];
	uint8_t addr =0x26;

	send[0] = SB;    // give the send vector the start bit
	send[1] = WRITE; // give the send vector the write command
	send[1] |= addr; // give the send vector the address 
                   // to write to
	send[2] = 0xAA;
	send[3] = 0xAA;

	writeEnable();   // enable write to the eeprom
	CS_ON();         // turn on chip select
	SSPSend(send,4); // send the command for writing
	CS_OFF();        // turn off chip select
	writeDisable();  // disable write to the eeprom

	delay32Ms(0, 10); // 10ms delay for eeprom to erase and 
                    // write
	send[1]=0x00;     // set the send vector to 0x0100xxxx
	send[2]=0x00;     // set the send vector to 0x010000xx
	send[3]=0x00;     // set the send vector to 0x01000000
	send[1]|=READ;    // give send vector read command
	send[1]|=addr;    // give send vector addr to read from

	CS_ON();             // turn on chip select
	SSPSend(send, 2);    // send command for reading from 
                       // the eeeprom
	SSPReceive(data, 2); // read the 16 bit value that the 
                       // eeprom sends
	CS_OFF();            // turn off chip select
	return 0;
}

                    

Settings for the SSP port on the LPC1343 are controlled in the function SSPInit() created in the ssp.c (seen below). There are a few things to take note of. The frequency of the SPI communication is controlled through a number of dividers. First the system clock is divided to the SSP peripheral clock (SSP_PCLK). The second division is the Prescale Clock Divider (CPSDVSR) that reduces the SSP peripheral clock. Third is the Serial Clock Rate (SCR) that sets the number of prescaled clocks per bit on the bus. The final clock rate of the SSP communication is (System Clock)/((SSP_PCLK divider)*(Prescale Clock Divider)*(Serial Clock Rate +1)). Please see LPC1343 user's manual (page 236) for details of the SSP clock setup. The values we have used are:

  • Serial Clock Rate (SCR): LPC_SSP->CR0 = 0x0F07 = 15
  • Prescale Clock Divider (CPSDVSR): LPC_SYSCON->SSPCLKDIV = 0x04 = 4
  • SSP_PCLK divider: LPC_SSP->CPSR = 0x2 = 2
  • System clock: 72 MHz

From the data sheet we can see that the max frequency of the eeprom is 2MHz. With current setup, the SPI clock frequency is 72 MHz/(4*2*(15+1))= 562 KHz and well below the maximum value.

void SSPInit( void )
{
  uint8_t i, Dummy=Dummy;

  LPC_SYSCON->PRESETCTRL |= (0x1<<0);
  LPC_SYSCON->SYSAHBCLKCTRL |= (1<<11);
  LPC_SYSCON->SSPCLKDIV = 0x04;	/* Divided by 4 */
  LPC_IOCON->PIO0_8 &= ~0x07; /*  SSP I/O config */
  LPC_IOCON->PIO0_8 |= 0x01;  /* SSP MISO */
  LPC_IOCON->PIO0_9 &= ~0x07;	
  LPC_IOCON->PIO0_9 |= 0x01;  /* SSP MOSI */
#ifdef __JTAG_DISABLED
  LPC_IOCON->SCKLOC = 0x00;
  LPC_IOCON->JTAG_TCK_PIO0_10 &= ~0x07;
  LPC_IOCON->JTAG_TCK_PIO0_10 |= 0x02; /* SSP CLK */
#endif
 
#if 1
  /* On HummingBird 1(HB1), SSP CLK can be routed to different pins,
  other than JTAG TCK, it's either P2.11 func. 1 or P0.6 func. 2. */
  LPC_IOCON->SCKLOC = 0x01;
  /* P2.11 function 1 is SSP clock, need to combined
		with IOCONSCKLOC register setting */  
  LPC_IOCON->PIO2_11 = 0x01;
#else
  LPC_IOCON->SCKLOC = 0x02;
  /* P0.6 function 2 is SSP clock, need to combined
		with IOCONSCKLOC register setting */  
  LPC_IOCON->PIO0_6 = 0x02;	
#endif

#if USE_CS
  LPC_IOCON->PIO0_2 &= ~0x07;	
  LPC_IOCON->PIO0_2 |= 0x01; /* SSP SSEL */
#else
  LPC_IOCON->PIO0_2 &= ~0x07; /* SSP SSEL is a GPIO pin */
  /* port0, bit 2 is set to GPIO output and high */
  GPIOSetDir( PORT0, 2, 1 );
  GPIOSetValue( PORT0, 2, 1 );
#endif
		
  /* Set DSS data to 8-bit, Frame format SPI, CPOL = 0, 
     CPHA = 0, and SCR is 15 */
  LPC_SSP->CR0 = 0x0F07;

  /* SSPCPSR clock prescale register, master mode, 
     minimum divisor is 0x02 */
  LPC_SSP->CPSR = 0x2;

  for ( i = 0; i < FIFOSIZE; i++ )
  {
	Dummy = LPC_SSP->DR;		/* clear the RxFIFO */
  }

  /* Enable the SSP Interrupt */
  NVIC_EnableIRQ(SSP_IRQn);
	
  /* Device select as master, SSP Enabled */
#if LOOPBACK_MODE
  LPC_SSP->CR1 = SSPCR1_LBM | SSPCR1_SSE;
#else
#if SSP_SLAVE
  /* Slave mode */
  if ( LPC_SSP->CR1 & SSPCR1_SSE )
  {
	/* The slave bit can't be set until SSE bit is zero. */
	LPC_SSP->CR1 &= ~SSPCR1_SSE;
  }
  LPC_SSP->CR1 = SSPCR1_MS;		/* Enable slave bit first */
  LPC_SSP->CR1 |= SSPCR1_SSE;	/* Enable SSP */
#else
  /* Master mode */
  LPC_SSP->CR1 = SSPCR1_SSE;
#endif
#endif
  /* Set SSPINMS registers to enable interrupts */
  /* enable all error related interrupts */
  LPC_SSP->IMSC = SSPIMSC_RORIM | SSPIMSC_RTIM;
  return;
}
                    

View the results

Start acquiring signals from the logic analyzer. What you will see is a string of bits being sent over the wires. (If you're having trouble finding your signal use the view -> Scroll to trigger and Zoom in /out). There should be two sets of signals, one at the beginning of acquisition containing the write command and one 10 ms after containing the read command. The LPC1343 sends the information one byte at the time and therefore the start bit (SB) will be sent in a first byte and the following byte will contain the rest of the instructions (see table below for instruction set for the 93LC46C).


SPI commands for eeprom. Reference: Microchip Data Sheet for 93LC46B
Table 1 - SPI commands for eeprom. Reference: Microchip Data Sheet for 93LC46B

The SPI write command captured by the Logic Analyzer
Figure 11 - The SPI write command captured by the Logic Analyzer

The write command consists of three parts:

  • EWEN
    • From the data sheet, see table 1, for the eeprom we know that we have to do "write enable" before we can write to the device. This is as an overwrite protection for the memory. The EWEN command is a nine bit instruction starting with a start bit (1), followed by the two bit OP code (00) and the two first of the six address bits set high (11). The four remaining address bits can have any value but in our code they are low (0000). Since the LPC1343 send SPI commands one byte at the time the full EWEN command is needs two bytes where we have set the first 7 bits low and the rest of the nine bits contain the 9 bit EWEN command. This corresponds to the hex code: 0x01 0x30.
  • Write
    • This is done with a start bit and an operation command followed by the address and the value. As can be seen in table 1 the write command requires 25 bits to be complete. We have used 4 bytes to send the command and set the 7 first none used bits low. The memory address we want to write to is number 0x26 (38 decimal) and combined with the write command (01 binary) the hex command is 0x66. The value we want in that position is 0xAAAA which is split into two bytes.
  • EWDS
    • The "write disable" locks the memory so that it cannot be overwritten. Once again the 9 nine bit command is writen using two bytes and the 7 first none used bits set as low.

The SPI read command captured by the Logic Analyzer
Figure 12 - The SPI read command captured by the Logic Analyzer

The read command consists of two parts:

  • Read Command
    • The bytes representing the read command contains a start bit, a two bit operation command and is followed by the memory address to read from. Notice that the address is the same as in the write command (0x26) but with the read command the hex command result in the value 0xA6.
  • Eeprom reply
    • At this point the eeprom sends a string of bits on the MISO wire. Notice that we wrote 0xAAAA but received 0x5555. This is not same value but if we look at the sequences in binary format 0xAAAA = 0b10101010 and 0x5555 = 0b01010101 we see that the read value is in fact the written value right shifted one position. The explanation for this is found in the data sheet for the eeprom where we read: "A dummy zero bit precedes the ... 16-bit ... output string." To find the actual value of the memory position we would need to read 17 bits and discard the first dummy bit.