jenswilly.dk


SPI on LPC1347 vs capacitance, part II

6. February 2013 22:35 by Jens Willy Johannsen
Categories: ARM

Aha! Gotcha!

(This is a follow-up to my last post about GPIO delay and capacitance.)

Looking again at the scope captures, I thought that the stray capacitance from boards and wires couldn't possibly account for the huge 18 microsecond delay. And my colleague, who has an actual education in these things, agreed. Thanks, Niels :)

Then we took a look at the base board schematic. And lo and behold:

The culprit: C42

The culprit: C42

A 100 nF capacitor right on the PIO1.13 line. Yes, it's called GPIO_21 in the schematic – it is slightly confusing but it is, in fact, PIO1.13. Removing the J33 jumper (thereby disconnecting PIO1.13 from the capacitor) and removing the 48 cycles delay it looked like this on the scope:

Nice steep transitions on the CS line

Nice steep transitions on the CS line

and like this on the logic analyzer.

And no unnecessary delays

And no unnecessary delays

Everyone is happy. Now off to cobble together some RS-485 code.

No comments No comments »

SPI on LPC1347 vs capacitance

5. February 2013 22:07 by Jens Willy Johannsen
Categories: ARM

Or "Capacitance matters – who'd've thunk it"

I am using SPI to talk to a MAX7456 video overlay IC from an LPC1347 MCU in my ROV project.

That should be easy enough since the LPC1347 contains twp SPI compatible synchronous serial ports ("SSP" for short). Perusing the manual and some code examples I came up with code to initialize the SSP for SPI comms and to send SPI data.

But, alas, it didn't work :(

Connecting my trusty Saleae Logic analyzer I saw that the CS line was toggled low and then high again for every byte. Which the MAX7456 apparently doesn't like – it wants the CS line to be low for the entire command (which consists of several bytes) – just as described in the datasheet.

So I switched to manually toggling a GPIO line low before transmitting SPI data and then high again when transmission is done. Like this:

#define MAX7456_CS_ON LPC_GPIO->CLR[ 1 ] = (1<< 13)
#define MAX7456_CS_OFF LPC_GPIO->SET[ 1 ] = (1<< 13)

MAX7456_CS_ON;

spi_transfer(VM0_reg);
spi_transfer(MAX7456_reset);

MAX7456_CS_OFF;

And, for reference, the spi_transfer() function looks like this:

void spi_transfer( uint8_t data )
{
    // Wait until SSP is not busy and TX FIFO is not full
    while ( (LPC_SSP0->SR & (SSPSR_TNF|SSPSR_BSY)) != SSPSR_TNF )
        ;

    // Send data
    LPC_SSP0->DR = data;
}

But that didn't work either. Looking at the logic analyzer it turned out that CS came low several microseconds after the SPI transmission started. Weird, since the the GPIO pin is set low before I start the SPI. I also tried using the byte pin and word pin registers but the result was the same.

So it just looks like the GPIO is just really, really slow. What I finally came up with that works was this:

#define MAX7456_CS_ON { uint8_t i; LPC_GPIO->CLR[ 1 ] = (1<< 13); for( i=0; i<48; i++ ) __NOP(); }
#define MAX7456_CS_OFF LPC_GPIO->SET[ 1 ] = (1<< 13)

In other words: let the CPU wait for 48 clock cycles after setting the pin low before trying to do something else.

On the logic analyzer, it looks like this:

Logic analyzer capture. With delay.

Logic analyzer capture. With delay.

Still weird though. Until I connected a scope instead of a logic analyzer. Then it looked like this without the delay:

Scope capture. Without delay.

Scope capture. Without delay.

And this with the delay:

Scope capture. With delay.

Scope capture. With delay.

So it looks like the delay is, in fact, not the MCU's GPIO that is slow but rather capacitance. So even though the pin was toggled low the capacitance caused the voltage to decay slowly (relatively speaking) instead of instantly. So without the delay, the voltage never dropped low enough for the MAX7456 to consider the CS low. Now bear in mind that the pin is connected to the MAX7456 though the LPCXpresso board, the LPCXpresso Base Board, a jumper wire and then the MAX7456 breakout board. All of which have non-zero capacitance.

In other words: when working with high-speed (in this case 4 MHz) signals, make sure you design the board with that in mind and minimize capacitance (and inductance).

Unfortunately, I have no experience with that kind of semi-advanced PCB layout. Fortunately I can fix it in software and the delay I introduce will not pose a problem in this case.

Comments 2 comments »

SPI and USI

6. December 2010 13:36 by Jens Willy Johannsen
Categories: ATtiny

The Max 7219 chip communicates using SPI. The last time I used it I had apparently written my own bit-banging SPI routine in the Arduino IDE. Which works just fine (the SPI protocol being so simple) but it seems clunky. And since the ATTiny2313 has a built-in USI (Universal Serial Interface) capable of doing SPI I thought I might as well use that.

After a bit of Googling and reading of the datasheet, this is what I came up with:

#define PIN_LOAD PB4

// Utitlity method to shift 8 bits out the USI pins in 3-wire (SPI) mode
static unsigned char spiByte(unsigned char val) 
{ 
    USIDR = val;            // Set data byte to send
    USISR = (1<<USIOIF);    // Clear Counter Overflow Interrupt Flag (by writing 1 to the USIOIF bit)
    do
    { 
        USICR = (1<<USIWM0)|(1<<USICS1)|(1<<USICLK)|(1<<USITC);    // 3-wire mode; shift reg. clock source: external pos. edge; 4-bit counter source: USITC; Toggle USICK pin 0->1 or 1->0 (shift register will only clock on 0->1)
    } while ((USISR & (1<<USIOIF)) == 0);                        // Check for OIF set which will happen after 16 cycles. I.e. 8 bits since the clock toggles twice per bit.
    return USIDR;            // Return data value
}

void sendToMax7219( char reg, char value )
{
    PORTB &= ~(1 << PIN_LOAD);    // LOAD low
    spiByte( reg );
    spiByte( value );
    PORTB |= (1<<PIN_LOAD;    // LOAD high
}
No comments No comments »