CCS C Software and Maintenance Offers
FAQFAQ   FAQForum Help   FAQOfficial CCS Support   SearchSearch  RegisterRegister 

ProfileProfile   Log in to check your private messagesLog in to check your private messages   Log inLog in 

CCS does not monitor this forum on a regular basis.

Please do not post bug reports on this forum. Send them to CCS Technical Support

SPI slave with timeout?
Goto page 1, 2  Next
 
Post new topic   Reply to topic    CCS Forum Index -> General CCS C Discussion
View previous topic :: View next topic  
Author Message
rcooke



Joined: 23 Feb 2011
Posts: 21
Location: Oceanside, CA USA

View user's profile Send private message

SPI slave with timeout?
PostPosted: Tue Aug 16, 2011 4:11 pm     Reply with quote

Hi Folks,

In our application we have a PIC18F24K22 set up as the slave SPI. Our code is working fine but there is a small possibility that if the master controller (our customer's responsibility) hangs during a transmission our board waits and waits and then won't be able to tickle the hardware WDT and the system resets. The WDT gets tickled in the main loop.

Is there a way to do slave SPI comms with a timeout? I'm using the hardware SPI module and the SPI interrupt.

Thanks,

Richard
asmallri



Joined: 12 Aug 2004
Posts: 1635
Location: Perth, Australia

View user's profile Send private message Send e-mail Visit poster's website

PostPosted: Tue Aug 16, 2011 10:46 pm     Reply with quote

If your SPI slave was interrupt driven then there would be no waiting on the master.

Seeing as it is not interrupt driven (or you would not have asked) you could add a timer and modify the SPI operations to include a check on the timer flag.

Personally I think the interrupt driven SPI is cleaner.
_________________
Regards, Andrew

http://www.brushelectronics.com/software
Home of Ethernet, SD card and Encrypted Serial Bootloaders for PICs!!
rcooke



Joined: 23 Feb 2011
Posts: 21
Location: Oceanside, CA USA

View user's profile Send private message

PostPosted: Wed Aug 17, 2011 8:59 am     Reply with quote

Actually I am using the SPI interrupt. Our issue is what happens if the master stops the transmission before all 8 bits are received? If the master (customer's computer) hangs during a transmission we'll be stuck waiting and if we don't tickle the hardware watchdog within 500ms our system resets.

Is there a way to make the SPI interrupt only wait for a specified time before returning to the main program?

Thanks,

Richard
Ttelmah



Joined: 11 Mar 2010
Posts: 19538

View user's profile Send private message

PostPosted: Wed Aug 17, 2011 9:22 am     Reply with quote

The SPI interrupt only occurs _when 8 bits _have_ been received_. If the master stops transmitting after (say) 5 bits, you won't get an SPI interrupt at all.
The SPI interrupt never 'waits'. It only happens _after_ the byte is already in the input shift register, and transferred to the latch.

Now the _problem_ is resynchronisation when the master starts again. Not a problem if you are using the slave select (this going high resets the 8 bit counter), but otherwise difficult. What you need is something like monitor code on the slave device, which if a character is _not_ received in the timescale expected, checks the clock line, and if it is in the idle state, then resets the SPI (turn the peripheral off, and on again - just clear the SSPEN bit, and set it again).

Best Wishes
rcooke



Joined: 23 Feb 2011
Posts: 21
Location: Oceanside, CA USA

View user's profile Send private message

PostPosted: Wed Aug 17, 2011 9:36 am     Reply with quote

Thanks Telmah. I think you have made the issue more understandable. I'm now going to have to figure out how to implement the monitor code.

Thanks again,

Richard
John P



Joined: 17 Sep 2003
Posts: 331

View user's profile Send private message

PostPosted: Fri Aug 19, 2011 6:26 am     Reply with quote

I've never had this problem, but it sounds interesting. Is there any flag that the processor sets which says "SPI buffer is partly full"? Then you could check that a few times and if it keeps being seen, you can flush the buffer. If all you can do is check whether a full character has come in, you'd risk doing the reset when there had been a pause, but with Mr Murphy constantly on duty, you could get to the timeout just in the middle of an incoming character.

Or is there any kind of "incomplete character timeout" function? Maybe there should be.
rcooke



Joined: 23 Feb 2011
Posts: 21
Location: Oceanside, CA USA

View user's profile Send private message

PostPosted: Fri Aug 19, 2011 8:36 am     Reply with quote

John,

The problem we're having is if the customer's computer (master) hangs during the 3 byte transmission to our slave. If we don't tickle the WDT within 1 sec it will reset our system. I'm going to need some sort of timeout mechanism that forces the ISR to exit if a TBD time interval has elasped. The master will re-send if we don't return the proper response. It's not ideal but the chance of the master hanging during a transmission (@1MHz SPI clock) should be pretty slim.

Thanks,

Richard
Ttelmah



Joined: 11 Mar 2010
Posts: 19538

View user's profile Send private message

PostPosted: Fri Aug 19, 2011 2:49 pm     Reply with quote

You should not be in the ISR.

The SPI interrupt, occurs when there is _one_ character waiting to be received. Just one, and already received.
It sounds as if you are trying to handle three bytes in the ISR, which is simply 'wrong'.

The basic system should be:
Code:

ISR - should just receive one character, and put it into a buffer. Nothing else.

Timer ISR - ticking at some useful time for your system - typically perhaps 50Hz. Use for your clocks in the system etc. Have a 'countdown' counter available here, with code like:
if (countdown) --countdown;

Main code. When it sees a first character arrive, load 'countdown' with a value greater than the worst case longest time for the whole message.

Then sit pulling characters from the buffer, and _watching_ countdown.
If countdown gets to zero, before the message is complete - implies a problem, force the error recovery.


Best Wishes
rcooke



Joined: 23 Feb 2011
Posts: 21
Location: Oceanside, CA USA

View user's profile Send private message

PostPosted: Fri Aug 19, 2011 3:17 pm     Reply with quote

Ttelmah,

I agree that my scheme isn't pretty or "proper" but it is working. We need to receive 3 bytes. We need to send 0x5A back to the master as the 1st byte is received, call this one the Command byte.

Byte#2 then comes in (call it the Data byte) and we echo back the Command byte at the same time.

Byte#3 comes in (Dummy byte) and we have to parse the Command byte. Is it a read or a write? Then depending on the Command byte we either send back the value of the register pointed to by the Data byte or we write a new value of the register.

I preload the value 0x5A into SSP1BUF so it is ready to return to the master when the Command byte comes in.

I'm able to keep up with the test system which is using the Pickit3 serial pod at a SPI speed of 1.25MHz. The customer spec requires a speed of 1MHz so for now we are able to meet their requirements.

I will take a good long look at modifying our code to make it more proper. I know that ISRs are supposed to be a short as possible but in this application, I don't know how else to make it work as fast.

By the way our chip is the PIC18F24K22 running with a 32MHz crystal.

Thanks,

Richard
John P



Joined: 17 Sep 2003
Posts: 331

View user's profile Send private message

PostPosted: Fri Aug 19, 2011 4:18 pm     Reply with quote

If I were doing this I'd want to put the incoming data into packets where each packet would start with some unique character. If there's absolutely no way to avoid having the start byte appear as data occasionally, the solution would be to send it twice when it's data, so a lone <start> always introduces a new packet.

Then there would be a timeout which the master has to use between the start of a packet and the start of the next.

If this is done, the slave knows when a packet starts. Then once a packet begins, the slave starts a timeout of its own, which is slightly shorter than the master's inter-packet timeout. If the timeout elapses before the packet is complete, the slave assumes that the master choked while sending the packet, and declares it void, and resets its SPI port even if the last character is incomplete. The slave then waits for the start of the next packet.

This might error-proof the transmission line, but it does it at the cost of slowing things down. So maybe it fails for that reason.
rcooke



Joined: 23 Feb 2011
Posts: 21
Location: Oceanside, CA USA

View user's profile Send private message

PostPosted: Fri Aug 19, 2011 4:45 pm     Reply with quote

John P,

Unfortunately we don't (or can't) influence the customer and their comm scheme. We're just a small part of their overall system. They won't even tell us what the inter-byte delay can be.

In other words, it's what it is and we can't change it even if it would make a "better" system.

Thanks,

Richard
John P



Joined: 17 Sep 2003
Posts: 331

View user's profile Send private message

PostPosted: Fri Aug 19, 2011 6:59 pm     Reply with quote

All right, another idea.

Send the incoming clock line to the SPI slave, and also to the input to a counter. If all is well, the count will either be divisible by 8 (if an integral number of SPI characters have been seen) or it will be changing rapidly. A number not divisible by 8 that doesn't change is an indicator that communication has stalled. Could you pry loose from the customer any information on what the recovery from a stall looks like? As in, how long it will be before the line becomes active again? That would determine how long you'd want to allow the non-divisible-by-8 state to exist before you declare an error condition. Or if it turns out that any perturbation in the clock rate is going to be the result of an error, maybe that's all you need. Get the 8 bits in 8 microseconds and you're fine, but if you're still waiting at 9 microsec, reset and wait for the next byte to start.
rcooke



Joined: 23 Feb 2011
Posts: 21
Location: Oceanside, CA USA

View user's profile Send private message

PostPosted: Mon Aug 22, 2011 5:49 pm     Reply with quote

John P,

That might work but as is usual for one of our projects - we've run out of free inputs. I do like your idea though.

Richard
Ttelmah



Joined: 11 Mar 2010
Posts: 19538

View user's profile Send private message

PostPosted: Tue Aug 23, 2011 2:03 am     Reply with quote

I think even given your rather strange circumstances, a timer is your answer.
What you need to do is basically (pseudo code):
Code:


    //Start of SPI interrupt handler
    //set a hardware timer to a particular value, and clear it's interrupt flag

    get the first character from the SPI (since the interrupt has triggered).

    timeout = false;

    //Now wait for your subsequent characters but use:
    do {
       if (SPI_DATA_IS_IN()) {
           //here read the SPI character (singular) and reset the timer.
           //perform your parsing on the input 'packet' here.
           //set flag 'packet_complete' when all the data has been received.
       }
       else {
          //If timer interrupt flag is set, then a character has not arrived
          //disable the SPI, re-enable it, and set the 'timeout' flag to leave the
          //interrupt.
       }
    } while (!timeout && !packet_complete);

The point is that you never read a character, without first checking that one is already in the input buffer (using spi_data_is_in), and while waiting for the character(s) you have a hardware timer counting. If it overflows, it's interrupt flag will become set, and you use this to say 'something wrong'.
You chose the timer divider (set in the main), and preset value, depending on how quickly characters should arrive. plus a margin.

Hopefully you can follow the logic, and see how the SPI handler will have to work.

Best Wishes
rcooke



Joined: 23 Feb 2011
Posts: 21
Location: Oceanside, CA USA

View user's profile Send private message

PostPosted: Wed Aug 24, 2011 8:51 pm     Reply with quote

Ttelmah,

Thanks for the info. I appreciate all the help folks give on this forum. This is a great resource.

I'm still running into a real can of worms with this SPI routine. If I use this for the SPI ISR:
Code:

#INT_ssp
void ssp_isr(void)
{
 unsigned int8 dummy;
 unsigned int8 packet_complete = 0;
 Command_Byte = spi_read();   // slave must send the sync byte as 1st byte
 Data_Byte = spi_read(Command_Byte); // slave must echo the command byte back to master

...parse code is here....
}


This works every time. I'm able to catch the three bytes and parse and return the proper values to the master. The issue with this version is if the master starts a transfer but hangs up before the 8th bit is received our code hangs.

If I use this version of the ISR:
Code:

#INT_ssp
void ssp_isr(void)
{
 unsigned int8 dummy;
 unsigned int8 packet_complete = 0;
 Command_Byte = spi_read();         // slave must send the sync byte as 1st byte
 Tick = 2;                                // set the timer - 16.66ms per tick

 do {
    if(spi_data_is_in()) {
       Data_Byte = spi_read(Command_Byte); // slave must echo the command byte back to master
       packet_complete = 1;
    }
    else {
       packet_complete = 0;
    }
 } while(Tick == 2 && !packet_complete);
...parse code is here....
}



This version won't read all three bytes. It ends up reading the 2nd byte twice. If the master sends 0xA0, 0x01, 0x00 the slave code above will return 0x5A (sync byte), 0xA0 (echos back the command byte), but then it also gets 0xA0 again. The proper operation is the slave sends 0x5A, then echos the command byte and then echos the data byte.

Can anybody see what I've screwed up?

Thanks again,

Richard
Display posts from previous:   
Post new topic   Reply to topic    CCS Forum Index -> General CCS C Discussion All times are GMT - 6 Hours
Goto page 1, 2  Next
Page 1 of 2

 
Jump to:  
You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot vote in polls in this forum


Powered by phpBB © 2001, 2005 phpBB Group