View previous topic :: View next topic |
Author |
Message |
javick82
Joined: 12 Jul 2007 Posts: 43
|
SPI timing issue??? |
Posted: Thu Mar 13, 2008 9:39 am |
|
|
We are working on a fluid dispensing machine that has valves open for so many milliseconds then closes them. This needs to be as accurate as possible, so we are using the timer 0 on the PIC18F97J60-based PICDEM.net 2 board. The issue we are seeing is that every so often, an entire valve block (on one serial driver chip, L9822E) will stay open for longer than it needs to, often causing an overflow.
Code "skeleton":
Code: |
max_time = //maximum time any valve needs to be open;
elapsed_time = 0;
while(elapsed_time <= max_time)
{
set_timer0();
for(all instances of index)
{
if(valve_time[instance of index] == elapsed time)
valve_command_changed = 1;
}
if(valve_command_changed)
{
send_valve_command(); //function that generates an SPI signal for all 16 daisy-chained driver chips
}
while(get_timer0()<3125); //25 MHz clock
elapsed_time++;
}
|
The timer is setup with the correct values, the SPI signal is as slow as possible to ensure proper data transmission. The code mostly works (probably 99.9% of the time). I have examined the incoming data (written from ethernet to EEPROM to chip memory) to ensure it is correct when the error occurs.
I assume it is a timing issue since that data is all correct (the valve_time[instance of index] are correct as are the elapsed time and max time values. I have increased the "idle" time at the end of the code 10-fold and added the additional code to check if the valve needs to be off with little success. It seems to help in that there are fewer errors but that will kill accuracy.
Let me know if you have any thoughts on the matter. Thanks. |
|
|
RLScott
Joined: 10 Jul 2007 Posts: 465
|
Re: SPI timing issue??? |
Posted: Thu Mar 13, 2008 10:09 am |
|
|
Your method of timing from Timer 0 looks a little unreliable. I assume that while(get_timer0()<3125); is supposed to wait for the rest of a specific period to expire. But that assumes you can always get through everything in that loop (including the SPI stuff) before 3125 ticks go by. Are you sure you can? Since you did not post the code for send_valve_command(), there is no way I can tell. Really, your skeleton is too abstract to be of much use. The problem is probably in some detail that you are omitting.
Robert Scott
Real-Time Specialties |
|
|
javick82
Joined: 12 Jul 2007 Posts: 43
|
|
Posted: Thu Mar 13, 2008 11:23 am |
|
|
I apologize for the abstractness, but I want to avoid divulging all of my code over the 'net.
The send valve command writes a 16-byte wide "valve command" to the driver chips. The bits of the valve command are toggled appropriately and the function works. The bits in each element of valve_command are toggled appropriately.
Code: |
void send_valve_command(void)
{
int i;
output_low(valve_driver_EN);
delay_us(200);
for(i = 0; i<16; i++)
{
spi_write2(valve_command[i]);
}
output_high(valve_driver_EN);
}
|
The timer0 was also my guess. I have increased the 3125 to 31250 and added the code to look for times that have elapsed in the 10 ms interval (instead of equality I use 2 inequalities). I am now implementing a longer timer tick (was using system timer/2, now am using system timer/8). I just can't think of any reason (there is no feedback in the "send_valve_command" function) that the MC would seemingly "hang-up" during this loop unless it passes over the timer. But I would think if it works once it would work every time because it always sends the same sized valve command.
The simplified loop is really all that I have, the index is just a 2-D array and there is one more check for another valve block's timing.
The time seems to be much greater than any of the times involved. |
|
|
RLScott
Joined: 10 Jul 2007 Posts: 465
|
|
Posted: Fri Mar 14, 2008 8:15 am |
|
|
So each instance of send_valve_command() is essentially sending 16 bytes over the SPI. Your code shows setting valve_command_changed = 1, but I don't see where you are clearing it. If you don't clear it, you will continue to send unnecessarily forever.
Why do you say "all instances of index" instead of something definite like:
for(index=0; index<16; index++)
Is there something going on there that might be a problem?
Are you sure that all that set_timer0() does is set Timer 0 to 0x0000?
Is there any possible way that valve_time[0...15] could be changed within your main while() loop?
Something still doesn't make sense. You set valve_command_changed if any one of the valve_time[] entries equals elapsed time. But then you go and call send_valve_command(), which seems to do the same thing regardless of which valve_time[] ran out. Shouldn't your response to a valve_time running out depend on which valve_time it was that ran out? If not, then what is the harm in calling send_valve_command() all the time, whether things changed or not? That would simplify your loop for analysis, and perhaps point the way to where it is hanging up.
Robert Scott
Real-Time Specialties |
|
|
javick82
Joined: 12 Jul 2007 Posts: 43
|
|
Posted: Fri Mar 14, 2008 8:50 am |
|
|
Sorry, I was just copying the code from memory. I am working on it from a different machine.
The valve_command_changed bit is reset at the top of each millisecond loop.
The 16-byte valve command is a global variable. Say I want to turn all valves on the 2nd daisy chained SPI chip on, I would leave all bits high except for the 15th of 16 bytes and "send_valve_command()", so commands are only sent as they need to be.
I have changed the scaling on my timer from 2 to 8 and modified the code to look at 4 ms windows which seems to, at the very least, mitigate the problem.
Is there another way, instead of the "while(get_timer0() <= 3125);" I can wait for the 1 ms (or whatever time period) to elaspe? |
|
|
RLScott
Joined: 10 Jul 2007 Posts: 465
|
|
Posted: Fri Mar 14, 2008 9:14 am |
|
|
javick82 wrote: | ...Is there another way, instead of the "while(get_timer0() <= 3125);" I can wait for the 1 ms (or whatever time period) to elaspe? |
Assuming that is your only problem, a more reliable way to detect 1 ms going by is to use the Timer 0 interrupt flag that gets set when Timer 0 rolls over from 0xFFFF to 0x0000. So your check becomes:
Code: |
while( ! INTCON.T0IF ) ;
|
and instead of setting Timer 0 to 0, you should
Code: |
TIMER0 = -3125;
INTCON.T0IF = 0;
|
at the top of the loop. The exact form of INTCON.T0IF is probably different for your PIC18. I don't have the docs handy, but you can look it up.
Robert Scott
Real-Time Specialties |
|
|
|