Atmega32: SPI SS signal drops prematurely

Go To Last Post
18 posts / 0 new
Author
Message
#1
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Hi,

 

I'm working on some code where I send a command byte to an FPGA and return it's response.  I'm using a typical SPI transfer type function in C:

BYTE SPI_xfer(BYTE b)
{
    
    SPDR = b;
    while (!(SPSR & (1<<SPIF)))
        ;

    return SPDR; 
}

 

And here's the code that sends the command:

BYTE FPGA_send_cmd(BYTE cmd, BYTE wdata)
{
    BYTE rdata;
    
    CLEARPIN(SPI_CMD_CS_N); // select FPGA CMD interface
  
    SPI_xfer(cmd);     
    rdata = SPI_xfer(0); // send NULL byte just so we can receive response in parallel                                   
     
    SETPIN(SPI_CMD_CS_N); // deselect FPGA CMD interface    
    return rdata;
}

What's weird though is that I'm noticing the SS signal drop before the 2nd byte has finished being sent. 

 

 

According to the Atmega datasheet, SPIF should only be set once all bits have been set and so by having the while loop within the SPI_xfer function above, it ensures further instructions won't execute until the entire byte has been sent.  But that's not what I'm seeing here.

Am I missing something?

 

Thanks!

-Adam

-Adam
"Please don't judge my God by my inability to follow him" - Chris Mollins
================
www.onecircuit.com
================

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Above should read "SPIF should only be set once all bits are sent". (Can't seem to edit original post for some reason)

-Adam
"Please don't judge my God by my inability to follow him" - Chris Mollins
================
www.onecircuit.com
================

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

spi_sck is very irregular on your logic analyser trace.

This is probably because the sample rate is a bit low.

I prefer to use a sampl rate that is at least 4x the bit with.

 

Those numbers at the top, are those us ?

What is the clock rate of your AVR (in relation to the SPI data rate)?

For testing / debugging purposes you can also try a much lower bitrate for SPI, Maybe you something obvious then.

 

My best guess is that SPIF is already set.

So the function returns immediately, but with function call overhead etc, the chip release only gets released in the 6th bit.

Try clearing SPIF before a transfer.

 

If that does not work, then try some variants.

For example, send a string of text.

Sometimes this makes it clearer where the error is.

 

Doing magic with a USD 7 Logic Analyser: https://www.avrfreaks.net/comment/2421756#comment-2421756

Bunch of old projects with AVR's: http://www.hoevendesign.com

Last Edited: Thu. Oct 25, 2018 - 12:18 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

It is likely that at point 70, the AVR believes that all the data has been sent.  Therefore it exits from

 rdata = SPI_xfer(0); 

and continues to the next command, which releases the CS line.

 

I suspect that the SPI mode is wrong.  Perhaps it is set on mode 0, which uses the rising edge clock data into the SPI slave device. 

 

Why is the data display so time distorted?   The first zero takes 22 units, and the next bit takes only 3 units.   Or, is the AVR the slave and the FPGA the SPI master?   FPGAs are freaking fast: could it be sending data faster than the AVR can accept it?

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Simonetta wrote:
Why is the data display so time distorted?   The first zero takes 22 units,
No it does not.

Before timestamp 9 the line is just idle.

Then CS_N is cleared, and we get the first SPI_xfer function call.

The first bit is probably set at timestamp 22 -  3 (1 bit time), so around 19.

 

So function call etc overhead seems to be around: 19 - 9 = 10.

If the bug is indeed what I thought in #3, then the software needs "20" (us?) for the assignment and return.

Bit weird that it's twice as much, but it is in the same ballpark.

"20" does not seem enough for any ISR to have fired in between.

My guess is that the AVR is running @ a lowly 1MHz here.

 

Also:

I sometimes use the logic analyser to spit out extra debug data.

I've made some screenposts about that in:

https://www.avrfreaks.net/forum/led-indicator-software-debugging?skey=debugging%20led

But you probably still remember that thread, you started it :)

In post 21 of that thread I attached a little helper header file to spit out extra debug data on a pin.

Doing magic with a USD 7 Logic Analyser: https://www.avrfreaks.net/comment/2421756#comment-2421756

Bunch of old projects with AVR's: http://www.hoevendesign.com

Last Edited: Thu. Oct 25, 2018 - 12:44 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I don't see how the SPIF can ever be set when I enter the SPI_xfer function. According to the datasheet once I read from SPDR the SPIF bit gets cleared. 

 

"Alternatively, the SPIF bit is cleared by first reading the SPI Status Register with SPIF set, then accessing the SPI Data Register (SPDR)."

So it should always be cleared when I exit the function the 1st time and therefore cleared when I enter the 2nd time.  Also just FYI I have SPI interrupts disabled.

Thanks.

-Adam
"Please don't judge my God by my inability to follow him" - Chris Mollins
================
www.onecircuit.com
================

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Are you talking about the standard AVR gotcha on SPI?

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I'm not familiar with this gotcha. Can you clarify?

-Adam
"Please don't judge my God by my inability to follow him" - Chris Mollins
================
www.onecircuit.com
================

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

ajcrm125 wrote:

I'm not familiar with this gotcha. Can you clarify?


Oh you mean how unless SS is defined as an output the spi mode can switch from a master to a slave? Yes I'm aware of that situation and I don't believe that's the case here. Also just fyi the SS signal above is not actually the SS signal on the avr (that one i have reserved for the sd card on the board). The one above is just an output I have going to the fpga. But let me confirm the avr spi mode and actual SS directional configuration and get back to you.

-Adam
"Please don't judge my God by my inability to follow him" - Chris Mollins
================
www.onecircuit.com
================

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

The point is that the pin on the AVR designated "SS" must be set as output or tied high if an input.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Here's how I'm initializing the SPI. SPI_CS_N is the actual SS pin on the AVR and is fed to the SD card, SPI_CMD_CS_N and SPI_FILE_CS_N are just outputs to the FPGA and both the FPGA and SD card are SPI slaves with the AVR as the master.

<in a header file...>
// Some defines to make the code more readable
#define OUTPUT    1
#define INPUT     0
#define MASTER    1
#define SLAVE     0
#define PULLUP    1
#define NO_PULLUP 0

#define SPI_DDR  DDRB
#define SPI_PORT PORTB
//--------------------

void SPI_init(BYTE mode, BYTE clk_div, BYTE double_speed)
{
	BYTE divisor;

	// Compute divisor based on 2X speed, clk_div
	switch(clk_div)
	{
                case 2  :
		case 4  : divisor = 0x00; break;
                case 8  :
		case 16 : divisor = 0x01; break;
                case 32 :
		case 64 : divisor = 0x02; break;
		default : divisor = 0x03; break; // 64 or 128
	}

        // Set MISO as input, all others as outputs
	SPI_DDR  = (OUTPUT<<PIN(SPI_SCK)) | (INPUT<<PIN(SPI_MISO))  | (OUTPUT<<PIN(SPI_MOSI)) | (OUTPUT<<PIN(SPI_CS_N)) | (OUTPUT<<PIN(SPI_CMD_CS_N)) | (OUTPUT<<PIN(SPI_FILE_CS_N));
	SPI_PORT = (0<<PIN(SPI_SCK))      | (PULLUP<<PIN(SPI_MISO)) | (0<<PIN(SPI_MOSI))      | (1<<PIN(SPI_CS_N))      | (1<<PIN(SPI_CMD_CS_N))      | (1<<PIN(SPI_FILE_CS_N));

        // Enable SPI, set master/slave mode, set clock rate but don't enable interrupts
        SPCR = (1<<SPE) | (mode<<MSTR) | divisor; 

	// Set 2X bit if desired
	SPSR = (double_speed<<SPI2X);
}

And here's how its being called:

// Port B = SPI port
SPI_init(MASTER,64,0); // Master mode, F_CPU/64 = 230.4kHz, single speed mode

 

 

-Adam
"Please don't judge my God by my inability to follow him" - Chris Mollins
================
www.onecircuit.com
================

Last Edited: Thu. Oct 25, 2018 - 01:22 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Simonetta wrote:
I suspect that the SPI mode is wrong. Perhaps it is set on mode 0, which uses the rising edge clock data into the SPI slave device.

        // Enable SPI, set master/slave mode, set clock rate but don't enable interrupts
        SPCR = (1<<SPE) | (mode<<MSTR) | divisor; 

The mode may not be wrong, but curious that with all of your other annotated statements there is no mention of data order or idle clock state or data/clock edge DORD CPOL CPHA

You can put lipstick on a pig, but it is still a pig.

I've never met a pig I didn't like, as long as you have some salt and pepper.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

theusch wrote:

The mode may not be wrong, but curious that with all of your other annotated statements there is no mention of data order or idle clock state or data/clock edge DORD CPOL CPHA

 

Good point. I'm just using the default values from those bits based on the datasheet which is:

CPOL = 0, CPHA = 0, DORD = 0

 

since those are what I want anyway.  But it seems the enable goes high a good 2 cycles before the end of the transfer which I don't think should happen regardless of those settings.

 

-Adam
"Please don't judge my God by my inability to follow him" - Chris Mollins
================
www.onecircuit.com
================

Last Edited: Thu. Oct 25, 2018 - 03:33 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I strongly advise you to use regular 1 and 0 in your bitmasks.    Your macros are confusing (to me).

 

All the same,   your code "looks" ok except for the mode setting.   It should be (mode << 2) for regular SPI peripheral. 

You seem to make SS output high.    So this should avoid the SPI Haywire feature.

 

I suggest that you just run your code through the Simulator.    Check that the SPI special function registers are correct.

 

David.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

david.prentice wrote:

All the same,   your code "looks" ok except for the mode setting.   It should be (mode << 2) for regular SPI peripheral. 

 

Oh sorry if I didn't mention but I'm using an Atmega32. The datasheet says the MSTR bit is bit 4 and I'm using the 'MSTR' defines as it is from ion32a.h

#define MSTR    4

 

-Adam
"Please don't judge my God by my inability to follow him" - Chris Mollins
================
www.onecircuit.com
================

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

My apologies.

 

I presumed that your mode was SPI mode e.g. #0, #1, #2, #3

If you meant master/slave you need to alter DDR bits too.

 

David.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

In cases such as this, I try to conduct a sanity check.  You are pretty close already...

 

Create a complete small test program.  Most of what is needed is already posted.  Then, have a small SPI transmit loop with a delay between, such that the logic analyzer only picks up >>one<< transmitted byte.  I might suggest 'U' as that is 01010101 and bit widths can easily be seen.  Forget the destination and the response and such.  It appears you have a /CS active-low select.

 

So, with my toolchain and primitive:

while (1)
{
    delay_ms(1000);

    // Select the device, whatever pin is being used
    PORTB.2 = 0;
    
    spi ('U');
    
    // De-select the device
    PORTB.2 = 1;
}

Don't even have your target connected.  What do you get on your logic analyzer?  Are all the samples consistent?

You can put lipstick on a pig, but it is still a pig.

I've never met a pig I didn't like, as long as you have some salt and pepper.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

theusch wrote:

In cases such as this, I try to conduct a sanity check.  You are pretty close already...

 

Create a complete small test program.  Most of what is needed is already posted.  Then, have a small SPI transmit loop with a delay between, such that the logic analyzer only picks up >>one<< transmitted byte.  I might suggest 'U' as that is 01010101 and bit widths can easily be seen.  Forget the destination and the response and such.  It appears you have a /CS active-low select.

 

So, with my toolchain and primitive:

while (1)
{
    delay_ms(1000);

    // Select the device, whatever pin is being used
    PORTB.2 = 0;
    
    spi ('U');
    
    // De-select the device
    PORTB.2 = 1;
}

Don't even have your target connected.  What do you get on your logic analyzer?  Are all the samples consistent?

 

Thanks for all the feadback/ideas so far from everyone.  I'll try this little experiment and report back with my findings. 

-Adam
"Please don't judge my God by my inability to follow him" - Chris Mollins
================
www.onecircuit.com
================