SPIF never set to 1 (ATMega644p)

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

Hi mates!

 

I'm working with an ATMega644P and a CS5460A measurement unit. The goal is to communicate both microcontrollers but i have not got it yet. The problem is that my ATMega644P gets stuck in the following line:

 

while (!(SPSR0 & (1<<SPIF0))){};    

 

My 'Spi send funtions is as follows':

 

void cs5460_send(unsigned char byte_to_send){
  
    unsigned char received_byte = 0;
  
    DisableSPIInterrupt();                             // I use SPI communication to serve 2 slaves. One is interrupt driven while the one i'm currently talking about is polled driven   
    CLRBIT(CS5460A_SS);                           // Set CS line low
    SPDR0 = byte_to_send;                         // Send byte
    TOGBIT(LED);                                         // Debug led which allows me to know where the program stops
    while (!(SPSR0 & (1<<SPIF0))){};         //Line which causes trouble
    
    received_byte = SPDR0;                        //Make a dummy read as I only want to send data
    
    SETBIT(CS5460A_SS);                          // Set CS line high
    EnableSPIInterrupt();                            // Once transmission is done re-enable interrupts

 

My SPI initialization is as follows (CS5460A clock is 3.6864Mhz):

 

/**
* Initialize and configure the SPI
* SPI Type: Master
* SPI Clock Rate: 20MHz/16 --> 1.25Mhz
* SPI Clock Phase: Cycle Half
* SPI Clock Polarity: Low
* SPI Data Order: MSB First
* Enable interrupt
*/
__C_task void InitSPI(void)
{
    SPCR0=0xD5;
    SPSR0=0x00;

 

Really don't know what could be going on. I just know that my program stops in the line i mentioned. Do you have any idea?

 

Thanks in advance.

This topic has a solution.
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

There is one big gotcha for AVR SPI that is seen in probably 1,000 threads here.

 

The SPI has a "feature" that even if you set MSTR, if the pin designated as "SS" is pulled low (because it's left as a floating input) then the SPI switches from Master to Slave and writing SPDR no longer generates SCK's and SPIF never gets set to show the end of a transfer.

 

So what are you doing with the "SS" pin in the 644?

 

BTW SPI generally relies on the slave having some kind of "chip select", "Slave select" that just enables the device while a transfer is made (and the edge on CS/SS reset the shift latch) so your SPI device probably needs CS to be driven anyway and if you are going to do that then what better pin than the one called "SS" anwyay? If you are actually using it as an outgoing chip select you cannot be caught by the gotcha I just mentioned.

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

clawson wrote:

There is one big gotcha for AVR SPI that is seen in probably 1,000 threads here.

 

The SPI has a "feature" that even if you set MSTR, if the pin designated as "SS" is pulled low (because it's left as a floating input) then the SPI switches from Master to Slave and writing SPDR no longer generates SCK's and SPIF never gets set to show the end of a transfer.

 

So what are you doing with the "SS" pin in the 644?

 

BTW SPI generally relies on the slave having some kind of "chip select", "Slave select" that just enables the device while a transfer is made (and the edge on CS/SS reset the shift latch) so your SPI device probably needs CS to be driven anyway and if you are going to do that then what better pin than the one called "SS" anwyay? If you are actually using it as an outgoing chip select you cannot be caught by the gotcha I just mentioned.

 

Hi mate, first of all thank you for the quick reply. 

 

SS pin is an I/O pin connected to the SS of the slave device and to a pull up resistor. As you mentioned, one of the test I made was to set Master Mode just before starting the transmission. I mean this:

 

void cs5460_send(unsigned char byte_to_send){
  
    unsigned char received_byte = 0;
  
    DisableSPIInterrupt();
    SetMasterMode();
    CLRBIT(CS5460A_SS);
    SPDR0 = byte_to_send;      
    TOGBIT(LED);
    while (!(SPSR0 & (1<<SPIF0))){};                                    //Do nothing until transfer is completed
    
    received_byte = SPDR0;                        //Receives command
    
    SETBIT(CS5460A_SS);  
    EnableSPIInterrupt();
}

And my function SetMasterMode looks as follows:

 

__C_task void SetMasterMode(void)
{
   SPCR |= (1<<4);
}

 The reason why I don't use the SS pin for that purpose is because i'm actually using it to drive another slave. 

Thank you again, mate.

Last Edited: Wed. May 20, 2015 - 08:59 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

So just to be clear, SS *is* an output? That is the bit for that pin in a DDR register is set to 1, all the time?

 

It just seems a little coincidental that you have "all the usual symptoms" of a very common problem.

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

SS is an output. Actually I did the following test:

 

Inside the 1sec timer I just placed a function which i use to toggle bits or ports. I did "TOGBIT(SS_pin)" and with an oscilloscope i was able to see the SS pin changing from 1 to 0 and from 0 to 1 so from this test i assume SS is correctly configured. 

 

I'm using PA3 of ATMega644p and the way I initialize that port is this:

 

/* Port A initialisation
    Func7=In Func6=In Func5=In Func4=OUT Func3=OUT Func2=In Func1=In Func0=OUT
    State7=T State6=T State5=T State4=T State3=T State2=T State1=T State0=T*/
   

    PORTA=0x00;
    DDRA=0x19;

Thank you mate. 

Last Edited: Wed. May 20, 2015 - 09:12 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I'm using PA3 of ATMega644p and the way I initialize that port is this:

...

You aren't quite getting it...

 

-- /SS pin on a '644 is PB4

-- PB4 must be an output, or if an input always high, for the SPI to act as a master

 

18.3 SS pin functionality
18.3.1 Slave mode
When the SPI is configured as a Slave, the Slave Select (SS) pin is always input. When SS is
held low, the SPI is activated, and MISO becomes an output if configured so by the user. All
other pins are inputs. When SS is driven high, all pins are inputs, and the SPI is passive, which
means that it will not receive incoming data. Note that the SPI logic will be reset once the SS pin
is driven high.
The SS pin is useful for packet/byte synchronization to keep the slave bit counter synchronous
with the master clock generator. When the SS pin is driven high, the SPI slave will immediately
reset the send and receive logic, and drop any partially received data in the Shift Register.
18.3.2 Master mode
When the SPI is configured as a Master (MSTR in SPCR is set), the user can determine the
direction of the SS pin.
If SS is configured as an output, the pin is a general output pin which does not affect the SPI
system. Typically, the pin will be driving the SS pin of the SPI Slave.
If SS is configured as an input, it must be held high to ensure Master SPI operation. If the SS pin
is driven low by peripheral circuitry when the SPI is configured as a Master with the SS pin
defined as an input, the SPI system interprets this as another master selecting the SPI as a
slave and starting to send data to it. To avoid bus contention, the SPI system takes the following
actions:
1. The MSTR bit in SPCR is cleared and the SPI system becomes a Slave. As a result of
the SPI becoming a Slave, the MOSI and SCK pins become inputs.

 

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

Thanks for the reply. As I said in the first post I have two slaves. One is driven by PB4 and the other uses PA3. I don't understand what you mean..

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

But we haven't seen any code snippet that shows PB4 being made an output; only the PA3 snippet.

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.

Last Edited: Wed. May 20, 2015 - 01:18 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Sorry for that, my fault. Here's the initialization of all ports:

 

__C_task void InitPorts(void)
{
    /* Port A initialisation
    Func7=In Func6=In Func5=In Func4=OUT Func3=OUT Func2=In Func1=In Func0=OUT
    State7=T State6=T State5=T State4=T State3=T State2=T State1=T State0=T*/
    PORTA=0x00;
    DDRA=0x19;
    
    /* Port B initialisation
    Func7=Out Func6=In Func5=Out Func4=Out Func3=Out Func2=In Func1=Out Func0=IN
    State7=0 State6=T State5=0 State4=1 State3=0 State2=T State1=0 State0=0*/
    PORTB=0x10;
    DDRB=0xBA;
    
    /* Port C initialisation
    Func7=In Func6=Out Func5=In Func4=In Func3=In Func2=In Func1=In Func0=In
    State7=T State6=0 State5=T State4=T State3=T State2=T State1=T State0=T*/
    PORTC=0x00;
    DDRC=0x40;
    
    /* Port D initialisation
    Func7=Out Func6=IN Func5=Out Func4=Out Func3=Out Func2=In Func1=Out Func0=In
    State7=0 State6=0 State5=0 State4=0 State3=0 State2=T State1=0 State0=T*/
    PORTD=0x00;
    DDRD=0xBA;
}

Last Edited: Wed. May 20, 2015 - 01:24 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

My SPI initialization is as follows (CS5460A clock is 3.6864Mhz):

 

/**
* Initialize and configure the SPI
* SPI Type: Master
* SPI Clock Rate: 20MHz/16 --> 1.25Mhz
* SPI Clock Phase: Cycle Half
* SPI Clock Polarity: Low
* SPI Data Order: MSB First
* Enable interrupt
*/
__C_task void InitSPI(void)
{
    SPCR0=0xD5;
    SPSR0=0x00;

 

Really don't know what could be going on. I just know that my program stops in the line i mentioned. Do you have any idea?

 

Yes, I have an idea (or several)...

 

One idea is to make a simple complete test program in "vanilla" C.  I see the __C_task stuff, and it makes me wonder.

 

Another idea is to be consistent in using bit names.  E.g. SetMasterMode uses (1<<4) instead of the bit name, so we all (and you also in 6 months when updating the code) need to pull up the datasheet and double-check.

 

Now, I also will use hex constants when e.g. setting up the entire DDR/PORT.  And sometimes on other registers as well.  Let's look above at the SPCR0 init to 0xD5.  Note that master is already set there, so no need for SetMasterMode, right?

 

Anyway, 0xD5 sets SPIE as noted in the comment block as well.  With that set, any polling loop where your program "stops" will never see SPIF set.  With SPIE set, the ISR will be invoked before you can do the test, and that invocation will clear the bit.

 

Depending on your toolchain and options and such, if there is no ISR you may get different actions, such as a soft reset of the AVR.

 

NB:  That's where a complete, simple, test program is very helpful to us, and if nothing else serves as a sanity check.

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

Thanks for the answer mate. I think I found the issue..not sure yet. The "send" funcion i was trying to call is inside a 1sec timer. Polling inside a timer might not be the best option, what do you think?

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

IF YOU ENABLE AN INTERRUPT SOURCE THEN YOU MUST HAVE AN ISR TO HANDLE THE EVENT.

 

IF YOU ENABLE AN INTERRUPT SOURCE AND DO NOT HAVE AN ISR TO HANDLE THE EVENT, THEN THE DEFAULT ACTION OF MANY/MOST/ALL AVR8 C TOOLCHAINS IS A JUMP TO ADDRESS 0 AND CODE RESTART.

 

IF YOU ENABLE AN INTERRUPT SOURCE AND HAVE AN ISR TO HANDLE THE EVENT, THEN ANY POLLING CODE WILL NEVER SEE THE ___IF FLAG BIT SET.

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

Remember I have 2 SPI slaves. One is interrupt driven and the other is polled driven. When I want to use polled driven slave i disable spi interrupts and when I want to use interrupts I reenable the SPI interrupt. There's no need of speaking like that. 

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

According to Google __C_task is IAR-speak for the equivalent of GCC's __attribute__((noreturn))

 

It would therefore seem a little odd to use it on a function you most definitely hope to return like InitPorts() ?!?

 

Presumably the implication of __C_task is "don't bother to put register saving/restoring prologue/epilogue around this as it won't be coming back". (such as each of the "main()" tasks in an RTOS or something? Or just the single main() in a "normal" program?)

 

Maybe OP can confirm this is IAR and tell us what the "__C_task" is there for?

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

clawson wrote:

According to Google __C_task is IAR-speak for the equivalent of GCC's __attribute__((noreturn))

 

It would therefore seem a little odd to use it on a function you most definitely hope to return like InitPorts() ?!?

 

Presumably the implication of __C_task is "don't bother to put register saving/restoring prologue/epilogue around this as it won't be coming back". (such as each of the "main()" tasks in an RTOS or something? Or just the single main() in a "normal" program?)

 

Maybe OP can confirm this is IAR and tell us what the "__C_task" is there for?

 

I think you hit the nail on the head! The project I'm working on is a former-worker's work so i received his code and need to make some changes. I certainly didn't know what was __C_task for. As you mentioned I'm working with IAR. 

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

There's no need of speaking like that. 

Well, you are not giving the complete picture.  I certainly outlined a very probable cause for the "any ideas", once we finally wormed out PB4 set as an output.

 

Now I'm again going to try to pull teeth:  Did any of your posted code snippets indicate SPIE being turned off, or global interrupts disabled?  Yes, we see an invocation for DisableSPIInterrupt() but no code. (BTW, I've never justified a case to use SPI interrupts for master work.  The overhead is just too high unless the SPI clock rate is very very slow.)

 

As I said, make a complete small test program for analysis, and post that.  If that works, then it is the sanity check and you (or we) should be able to spot when it starts failing, as ancillary code is added chunk by chunk.

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

Thank you again. 

 

I have worked with SPI before so more or less I have an idea about how to deal with it. As I have a really big project in which I have to make some changes, a small test program won't be helpful as it has nothing to do with my project. My error seems to be "interrupt related". Remember that this code is inside a 1sec timer. Without the line "while(!(SPSR & (1<<SPIF)));" I can see data travelling through MOSI.

 

If I try using interrupts everything works fine so I'm really confused because I can't understand what's going on.

This reply has been marked as the solution. 
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Finally I solved the problem. It was a "timing" issue. I used a 1 sec timer to use SPI and due to the lenght of the project, the SPI initialization was not done until aprox 2~3 seconds later. Now everything is fine. Thank you all of you who posted in this thread as I've learnt a lot from you.