![](templates/subSilver/images/CCSLogo.jpg) |
![CCS C Software and Maintenance Offers](templates/subSilver/images/forumAd6.jpg) |
View previous topic :: View next topic |
Author |
Message |
Backfire
Joined: 12 Oct 2020 Posts: 46
|
SPI Rx ISR and Timer ISRs |
Posted: Thu Jul 25, 2024 4:41 am |
|
|
Hi all,
I'm hoping someone can help me find an issue with some SPI code I'm trying to get working.
I have a system of 4 PIC's (all PIC16F15356); One acting as an SPI master, three acting as slaves running the same code. I have successfully transferred data over the SPI from the master -> slaves, previously I had the code send three bytes of data, and the slaves would build these bytes into a message, then respond accordingly. This message building and further data manipulation was implemented in the main loop, after the SPI ISR had set a flag indicating data was available.
Sometimes the slaves seemed to miss a message, so I have decided to transfer 32 bits of data, (with the MSB being a dummy 0x00 value). I have also moved the data manipulation into the ISR, as it's essentially just copying values received over the SPI into data structures.
These values do seem to be collected correctly, but now another element of my code (stepper motor movement) no longer seems to run. This was called by a timer-controlled ISR again serviced in the main loop.
I'm thinking my two ISR processes might be getting in the way of each other. I know that ISRs are supposed to contain the absolute minimum of processing (I previously made calls to make8(...) from my ISR), but all I'm doing now is bitwise operations and shifts, surely that's not "too much" to do in an ISR?
I didn't think the timer ISR could be called while I was in the SPI Rx ISR, or can that happen with this PIC?
Many thanks in advance for taking the time to read this. |
|
![](templates/subSilver/images/spacer.gif) |
temtronic
Joined: 01 Jul 2010 Posts: 9174 Location: Greensville,Ontario
|
|
Posted: Thu Jul 25, 2024 8:19 am |
|
|
others will KNOW, but is there a 'priority' sequence you may need to configure for which interrupt is '#1'.
Have seen reference to it here several times. CCS creates a 'table' ? with ALL of the possibles...maybe you need to create your own (3 or 4 ?) necessary. That will save time... |
|
![](templates/subSilver/images/spacer.gif) |
Ttelmah
Joined: 11 Mar 2010 Posts: 19369
|
|
Posted: Thu Jul 25, 2024 8:23 am |
|
|
The PIC16 only supports one level of interrupts. So while you are inside an
interrupt another cannot be serviced. The PIC18 does support two levels,
while the DsPIC's support unlimited levels. This though gets more complex
with 'levels' of interrupt priority, and only a higher level interrupt can
interrupt a low level one. Interrupts on the same level cannot interrupt
each other.
However a second interrupt should be serviced as soon as you leave the
handler for the first interrupt. This at heart is why the interrupt handling
needs to be kept quick.
Now it is difficult to know what is going on without seeing your code, but
I'm slightly suspicious with your talk of a 32bit message, that your SPI
handler may be staying inside the handler for more than one byte. If so,
this will be a problem. An SPI interrupt means _one_ byte has been
received, and just one byte should be read. Your SPI, should simply buffer
the data, nothing else. Look at how ex_sisr handles serial receive.
Bit shifts are quite slow, depending on how many bits are involved.
However 'bitwise operations' may be slow or fast, depending on what
you actually are doing. So a simple 'set a single predefined bit' operation
uses just one instruction, but an operation saying 'set a bit using a
variable to define which bit', uses dozens of operations. The PIC itself
does not have an operator to do this, so a mask has to be rotated to
select the right bit, and then logical operations are used to perform the
actual operation. You can speed this type of thing massively by
instead of using a counter, using your own mask. So (very much only an
example):
Code: |
void routine(void)
{
static int8 counter=1;
static int8 value=0;
if (something)
bit_set(value,counter);
counter++;
if (counter==8)
counter=1;
}
//Now compare with:
void alternative(void)
{
static int8 mask=1;
static int8 value=0;
if (something)
value|=mask;
if (mask==128)
mask=1;
else
mask>>=1
}
|
The former is about 20* slower than the latter!.....
In the former, the compiler has to read the counter, then move a bit the
number of locations specified by this, then OR this with the value.
In the latter, there is a mask containing the required bit. This is simply
OR'ed with the value. Then if it is not in the top bit, this is rotated once.
Since this is a fixed one bit rotation, just uses one instruction. If it is
in the top bit a new value replaces this in the low bit.
Several big questions apply. How fast is your timer interrupt?. How fast
is the SPI?. Are you possibly staying in the SPI handler for more than one
byte?.
Make8 is a fast instruction. However I cannot see why you would use this
unless you have a larger value. If you are using something like make32,
how fast this is again depends on whether it is involving fixed locations
or variables.
Things that are slow:
Maths - particularly of higher types.
Pointer accesses.
Bit accesses involving variables.
Waiting for things to arrive.
Things that are quick.
Reading a single byte.
Basic logic.
Integer addition and logic.
Fixed rotations.
The interrupt priority on the PIC16 only effects the order the interrupts
are polled when an interrupt triggers. |
|
![](templates/subSilver/images/spacer.gif) |
Backfire
Joined: 12 Oct 2020 Posts: 46
|
|
Posted: Fri Jul 26, 2024 6:37 am |
|
|
Hi Ttelmah, and thanks for such detailed advice.
I was right in thinking that there was only the most basic of interrupt handling on this device then, I've not yet used a PIC with multiple interrupt priorities!
I was also apparently right to initially be transferring data a 'byte at a time', which I have reverted to, and seen my issue be solved.
I must profess, the compiler documentation making reference to a default 'size' of 32 bits, in my mind (at least) made it seem as though an interrupt would only trigger when 32 bits had been received... A quick read of the datasheet shows this is not the case, and the SPI peripheral of course only works on 8 bit-wide data...
I'll chalk this one up to odd examples in the compiler documentation. |
|
![](templates/subSilver/images/spacer.gif) |
Ttelmah
Joined: 11 Mar 2010 Posts: 19369
|
|
Posted: Fri Jul 26, 2024 10:26 am |
|
|
Aaargh.
So you are using spi_xfer. OK. The 32bit thing is part of spi_xfer, not the
interrupt. spi_xfer can transfer up to 32bit is a single spi transaction, but
what is available to transfer is dependant on the hardware. Now using
spi_xfer without interrupts, you can say 'transfer 32 bits', and it
encapsulates the four byte transfers into a single operation. On the
32bit PIC's these have multi byte SPI buffers and can support sending
these bytes as one operation, but on the more basic PIC's the buffer is
only one byte. Now you can tell spi_xfer to only transfer one byte, by
simply telling it the number of bits in the transfer:
byte_value=spi_xfer(YOUR_SPI, byte_to_send, 8);
So no make8 needed.
Key is that you always need to look at the hardware details for your PIC
and understand that though the compiler can try to help, it is constrained
by what the hardware supports. |
|
![](templates/subSilver/images/spacer.gif) |
|
|
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
|