WHILE/WEND blocking [OSHONSOFT]

Thread Starter

camerart

Joined Feb 25, 2013
3,731
Hi,
I have 2x PICs 'talking' to one another via SPI
Here is one section of CODE that is blockig the program.
Is there an alternative to WAIT/WHILE?
Camerart
Code:
While slave4431_cs = 1  'If 1 then it waits till the CS makes it 0 then it LOOPs
        For spipsn = 0 To 33  'GPS=0to29-$toW INC, QEI=30,31 BATVOLT=32 SPARE DATA=33 'QEI?@
            SSPBUF = s2m(spipsn)  'Transfer S2M(spipsn) into SSPBUF
            While SSPSTAT.BF = 0
            Wend

            m2s(spipsn) = SSPBUF  'Transfer M2S(spipsn) BYTE from SSPBUF into M2S(spipsn)
        Next spipsn
            spi_pin_rdy = 0
    Wend
    spipsn = 0
    yled = 0
    RCSTA.CREN = 1
    PIE1.RCIE = 1
 

Irving

Joined Jan 30, 2016
3,972
Yes, but how easy it is to achieve depends on the way the rest of the software 'loop' is written. Can you publish more of it, or give a wider context to this snippet? If this is already in a separate thread you may need to put something between the while and the wend to force the thread to release its priority. A sleep(0) or delay(0) may work here.

If your peripheral is that slow, such that blocking between byte serialisation is impacting processing overall, I would put this code in a thread of its own with a blocking semaphore at that point and have an interrupt on transmit from the peripheral release the semaphore to transmit the next byte.
 

Thread Starter

camerart

Joined Feb 25, 2013
3,731
Yes, but how easy it is to achieve depends on the way the rest of the software 'loop' is written. Can you publish more of it, or give a wider context to this snippet? If this is already in a separate thread you may need to put something between the while and the wend to force the thread to release its priority. A sleep(0) or delay(0) may work here.

If your peripheral is that slow, such that blocking between byte serialisation is impacting processing overall, I would put this code in a thread of its own with a blocking semaphore at that point and have an interrupt on transmit from the peripheral release the semaphore to transmit the next byte.
Hi I,
I think of the term 'thread' as this is a thread called WEND/WHILE blocking [OSHONSOFT]
What do you mean by 'thread' ?

Here is the CODE section, with the offending WHILE/WENDs
C.


Code:
main_loop:  '/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\

Toggle rled

'Toggle PORTE.0

'Serout PORTB.0, 9600, "TEST", CrLf '<<<<<<<<<<<<<<<<<

If gnrmc_buf_full = 1 Then
    'Break  '<<<<<<<<<<<
    'If spi_pin_rdy = 1 Then  'Tell MASTER SPI ready.<<<<<<<<<<<<<<<<<<<<<<<<<<<<
    Serout PORTB.0, 9600, "SLAVE GNRMC PARSED  ", s2m(0), s2m(1), s2m(2), s2m(3), s2m(4), s2m(5), s2m(6), s2m(7), s2m(8), s2m(9), s2m(10), s2m(11), s2m(12), s2m(13), s2m(14), s2m(15), s2m(16), s2m(17), s2m(18), s2m(19), s2m(20), s2m(21), s2m(22), s2m(23), s2m(24), s2m(25), s2m(26), s2m(27), s2m(28), s2m(29), s2m(30), s2m(31), #s2m(32), #s2m(33), CrLf
    'WaitMs 1  'Lower till not work
    'Gosub irqinitbuf
'Endif
    'If gnrmc_buf_parsed = 0 Then  'parse tried but string was unknown, corrupt or wrong length
        'Hserout "Parse fail ", CrLf
        'Gosub IRQinitBuf  '++++++¦
    'endif
    'Return

    Gosub gnrmc_parse

    'Update the transmit to master buffer with current data
    batvolt = 12
    sparedata = 123
Endif


    yled = 1
    While slave4431_cs = 1  'If 1 then it waits till the CS makes it 0 then it LOOPs
        For spipsn = 0 To 33  'GPS=0to29-$toW INC, QEI=30,31 BATVOLT=32 SPARE DATA=33 'QEI?@
            SSPBUF = s2m(spipsn)  'Transfer S2M(spipsn) into SSPBUF
            While SSPSTAT.BF = 0
            Wend
            m2s(spipsn) = SSPBUF  'Transfer M2S(spipsn) BYTE from SSPBUF into M2S(spipsn)
        Next spipsn
            spi_pin_rdy = 0
    Wend
    spipsn = 0
    yled = 0
    RCSTA.CREN = 1
    PIE1.RCIE = 1
'Endif

    While ServoCount = 0  'wait for 20mS to pass (=1/50th of a second)
    Wend
    While ServoCount <> 0
    Wend
    For i = 0 To 7
        dir = servoDir(i)  'doesn't matter if ISR happens as servoPos isn't changed by ISR
        pos = ServoPos(i)
        If dir = 1 Then
            pos = pos + 40  'add 1/50th to the servo position
        Else
            pos = pos - 40  'subtract it
        Endif
        If pos < 2000 Then  'have we gone past the end?
            pos = 2000  'yes so make it the end stop
            dir = 1  'and turn it around
        Endif
        If pos > 4000 Then  'same for other end
            pos = 4000
            dir = 0
        Endif
        'INTCON.GIE = 0
        Disable High
        ServoPos(i) = pos
        'INTCON.GIE = 1
        Enable High
        servoDir(i) = dir  'servoDir not used by ISR
    Next i

Goto main_loop

End
 

Thread Starter

camerart

Joined Feb 25, 2013
3,731
Hi C,
Why are you using ?
While slave4431_cs = 1 ....

when
If slave4431_cs = 1 then do......
E
Hi E,
I'm not sure how While slave4431_cs = 1 was written, I'll try If slave4431_cs = 0

Why is WHILE/WEND used instead of IF ENDIF?
Thanks, E.
 

ericgibbs

Joined Jan 29, 2010
18,984
Hi C,
Compare the definition of a While .... Wend.

You say : While slave4431_cs = 1 'If 1 then it waits till the CS makes it 0 then it LOOPs

Clip:
This example uses the While...Wend statement to increment a counter variable.
The statements in the loop are executed as long as the condition evaluates to True.


VB
Dim Counter
Counter = 0 ' Initialize variable.
While Counter < 20 ' Test value of Counter. // While the count is less than 20 it performs the Code in the Loop
Counter = Counter + 1 ' Increment Counter.
Wend ' End While loop when Counter > 19.
Debug.Print Counter ' Prints 20 in the Immediate window.
 

Irving

Joined Jan 30, 2016
3,972
Hi I,
I think of the term 'thread' as this is a thread called WEND/WHILE blocking [OSHONSOFT]
What do you mean by 'thread' ?

Here is the CODE section, with the offending WHILE/WENDs
C.
The 'WHILE' issue Eric noticed might be your culprit, as that will block your main thread's servo activity until a CS=0 is received from the slave. Replacing it with If/Endif means it won't wait forever for a CS=0 but the downside is it will only transfer data every 20mS as the main loop only runs every 20mS as this:
Code:
   While ServoCount = 0  'wait for 20mS to pass (=1/50th of a second)
    Wend
    While ServoCount <> 0
    Wend
is bad code because if your servo processing time + data transfer time is > 20mS you will have problems.

Anyway, to follow up my point....

Yes, your main loop is a thread, but with a slow peripheral you could give it its own thread so it can block while waiting for things to transmit. This only applies if the main loop is servicing other aspects, such as the User Interface which you want to be responsive at all times. In your case the only other thing running is the servo processing so as long as your servo processing time + data transfer time is < 20mS then your code is OK (though I'd still kick the servo processing off on a timer interrupt rather than an inline blocking while/wend. That guarantees the consistency of the servo updates.

If the data transfer code is in a separate thread then you can use:
Code:
Main setup
   CreateThread slave4431Thread ' create & start peripheral thread

main_loop:

'do your servo stuff

'this is bad code... much better to use a timer interrupt
   While ServoCount = 0  'wait for 20mS to pass (=1/50th of a second)
    Wend
    While ServoCount <> 0
    Wend

   For i = 0 To 7
.
.
.
.
    Next i

Goto main_loop
and in the slave Thread:

Code:
While true ' do for ever
    
    While slave4431_cs = 1 
        delay(0) ' force thread to release slot, allow main to run
    Wend

    'now cs = 0 so can do data transfer

    yled = 1
    For spipsn = 0 To 33  'GPS=0to29-$toW INC, QEI=30,31 BATVOLT=32 SPARE DATA=33 'QEI?@
        SSPBUF = s2m(spipsn)  'Transfer S2M(spipsn) into SSPBUF
        While SSPSTAT.BF = 0 'this loop assumes SPI clock rate is high so 33byte transfer is << compared to 20mS main loop
        Wend
        m2s(spipsn) = SSPBUF  'Transfer M2S(spipsn) BYTE from SSPBUF into M2S(spipsn)
    Next spipsn
    spi_pin_rdy = 0

    spipsn = 0
    yled = 0
    RCSTA.CREN = 1
    PIE1.RCIE = 1
Wend
' end of slave thread

If your PIC doesn't support threads then I'd put your data transfer code in an ISR kicked off by slave4431_cs going low and put the servo code into an interrupt routine kicked off by a 20mS timer eg

Code:
Main setup
    Link  slave4431ISR to slave4431_cs pin edgetriggered, low going ' setup peripheral ISR
    Setup 20mS timer on timer1
    timer1.start

main_loop:

if timer1.expired
    timer1.start  'restart timer

' now do your servo stuff

   For i = 0 To 7
.
.
.
.
    Next i

Endif   ' timer1 expired

Goto main_loop
and in the slave ISR:

Code:
    yled = 1
    For spipsn = 0 To 33  'GPS=0to29-$toW INC, QEI=30,31 BATVOLT=32 SPARE DATA=33 'QEI?@
        SSPBUF = s2m(spipsn)  'Transfer S2M(spipsn) into SSPBUF
        While SSPSTAT.BF = 0 'this loop assumes SPI clock rate is high so 33byte transfer is << compared to 20mS main loop
        Wend
        m2s(spipsn) = SSPBUF  'Transfer M2S(spipsn) BYTE from SSPBUF into M2S(spipsn)
    Next spipsn
    spi_pin_rdy = 0

    spipsn = 0
    yled = 0
    RCSTA.CREN = 1
    PIE1.RCIE = 1

' end of slave ISR
 

Thread Starter

camerart

Joined Feb 25, 2013
3,731
The 'WHILE' issue Eric noticed might be your culprit, as that will block your main thread's servo activity until a CS=0 is received from the slave. Replacing it with If/Endif means it won't wait forever for a CS=0 but the downside is it will only transfer data every 20mS as the main loop only runs every 20mS as this:
Code:
   While ServoCount = 0  'wait for 20mS to pass (=1/50th of a second)
    Wend
    While ServoCount <> 0
    Wend
is bad code because if your servo processing time + data transfer time is > 20mS you will have problems.

Anyway, to follow up my point....

Yes, your main loop is a thread, but with a slow peripheral you could give it its own thread so it can block while waiting for things to transmit. This only applies if the main loop is servicing other aspects, such as the User Interface which you want to be responsive at all times. In your case the only other thing running is the servo processing so as long as your servo processing time + data transfer time is < 20mS then your code is OK (though I'd still kick the servo processing off on a timer interrupt rather than an inline blocking while/wend. That guarantees the consistency of the servo updates.

If the data transfer code is in a separate thread then you can use:
Code:
Main setup
   CreateThread slave4431Thread ' create & start peripheral thread

main_loop:

'do your servo stuff

'this is bad code... much better to use a timer interrupt
   While ServoCount = 0  'wait for 20mS to pass (=1/50th of a second)
    Wend
    While ServoCount <> 0
    Wend

   For i = 0 To 7
.
.
.
.
    Next i

Goto main_loop
and in the slave Thread:

Code:
While true ' do for ever
   
    While slave4431_cs = 1
        delay(0) ' force thread to release slot, allow main to run
    Wend

    'now cs = 0 so can do data transfer

    yled = 1
    For spipsn = 0 To 33  'GPS=0to29-$toW INC, QEI=30,31 BATVOLT=32 SPARE DATA=33 'QEI?@
        SSPBUF = s2m(spipsn)  'Transfer S2M(spipsn) into SSPBUF
        While SSPSTAT.BF = 0 'this loop assumes SPI clock rate is high so 33byte transfer is << compared to 20mS main loop
        Wend
        m2s(spipsn) = SSPBUF  'Transfer M2S(spipsn) BYTE from SSPBUF into M2S(spipsn)
    Next spipsn
    spi_pin_rdy = 0

    spipsn = 0
    yled = 0
    RCSTA.CREN = 1
    PIE1.RCIE = 1
Wend
' end of slave thread

If your PIC doesn't support threads then I'd put your data transfer code in an ISR kicked off by slave4431_cs going low and put the servo code into an interrupt routine kicked off by a 20mS timer eg

Code:
Main setup
    Link  slave4431ISR to slave4431_cs pin edgetriggered, low going ' setup peripheral ISR
    Setup 20mS timer on timer1
    timer1.start

main_loop:

if timer1.expired
    timer1.start  'restart timer

' now do your servo stuff

   For i = 0 To 7
.
.
.
.
    Next i

Endif   ' timer1 expired

Goto main_loop
and in the slave ISR:

Code:
    yled = 1
    For spipsn = 0 To 33  'GPS=0to29-$toW INC, QEI=30,31 BATVOLT=32 SPARE DATA=33 'QEI?@
        SSPBUF = s2m(spipsn)  'Transfer S2M(spipsn) into SSPBUF
        While SSPSTAT.BF = 0 'this loop assumes SPI clock rate is high so 33byte transfer is << compared to 20mS main loop
        Wend
        m2s(spipsn) = SSPBUF  'Transfer M2S(spipsn) BYTE from SSPBUF into M2S(spipsn)
    Next spipsn
    spi_pin_rdy = 0

    spipsn = 0
    yled = 0
    RCSTA.CREN = 1
    PIE1.RCIE = 1

' end of slave ISR
Hi I,
Thanks for your comprehensive reply!
I'm not fast, so it will take a while to understand all of your points, I'll let you know how I get on, thanks.
C
 

Thread Starter

camerart

Joined Feb 25, 2013
3,731
Hi C,
Compare the definition of a While .... Wend.

You say : While slave4431_cs = 1 'If 1 then it waits till the CS makes it 0 then it LOOPs

Clip:
This example uses the While...Wend statement to increment a counter variable.
The statements in the loop are executed as long as the condition evaluates to True.


VB
Dim Counter
Counter = 0 ' Initialize variable.
While Counter < 20 ' Test value of Counter. // While the count is less than 20 it performs the Code in the Loop
Counter = Counter + 1 ' Increment Counter.
Wend ' End While loop when Counter > 19.
Debug.Print Counter ' Prints 20 in the Immediate window.

Hi E,
Does this do the same thing as COUNTER?
-----------------------------------------------------------
If it is necessary to count the number of pulses that come to one of the micrcontroller's pins during a certain period of time, there is COUNT statement available for that purpose. It has three arguments. The first one is the pin that is connected to the source of pulses. COUNT statement will setup the pin as an input pin. The second argument defines the duration of the observation expressed in milliseconds and it must be a numeric constant in the range 1-10000. The last argument of this statement is a Byte or Word variable where the counted number of pulses will be stored after its execution. COUNT statement uses internal Timer0 peripheral module. There is COUNT_MODE parameter available that can be setup with #define directive. If it is set to value 1 (default value) COUNT statement will count the number of rising pulse edges. If COUNT_MODE = 2, the number of falling edges will be counted.
#define COUNT_MODE = 1
Dim num_of_pulses As Word
Count PORTB.0, 1000, num_of_pulses
----------------------------------------------------------
I'll have to experiment a bit.

EDIT: I have only found COUNT_MODE so far, and it looks like it checks a PIN not a BIT/FLAG
I haven't found COUNTER
Thanks.
C
 
Last edited:

Thread Starter

camerart

Joined Feb 25, 2013
3,731
Hi C,
Compare the definition of a While .... Wend.

You say : While slave4431_cs = 1 'If 1 then it waits till the CS makes it 0 then it LOOPs

Clip:
This example uses the While...Wend statement to increment a counter variable.
The statements in the loop are executed as long as the condition evaluates to True.


VB
Dim Counter
Counter = 0 ' Initialize variable.
While Counter < 20 ' Test value of Counter. // While the count is less than 20 it performs the Code in the Loop
Counter = Counter + 1 ' Increment Counter.
Wend ' End While loop when Counter > 19.
Debug.Print Counter ' Prints 20 in the Immediate window.
Hi E,
EDIT: I have only found COUNT_MODE so far, and it looks like it checks a PIN not a BIT/FLAG
I haven't found COUNTER
Thanks. C.
 

Thread Starter

camerart

Joined Feb 25, 2013
3,731
The 'WHILE' issue Eric noticed might be your culprit, as that will block your main thread's servo activity until a CS=0 is received from the slave. Replacing it with If/Endif means it won't wait forever for a CS=0 but the downside is it will only transfer data every 20mS as the main loop only runs every 20mS as this:
Code:
   While ServoCount = 0  'wait for 20mS to pass (=1/50th of a second)
    Wend
    While ServoCount <> 0
    Wend
is bad code because if your servo processing time + data transfer time is > 20mS you will have problems.

Anyway, to follow up my point....

Yes, your main loop is a thread, but with a slow peripheral you could give it its own thread so it can block while waiting for things to transmit. This only applies if the main loop is servicing other aspects, such as the User Interface which you want to be responsive at all times. In your case the only other thing running is the servo processing so as long as your servo processing time + data transfer time is < 20mS then your code is OK (though I'd still kick the servo processing off on a timer interrupt rather than an inline blocking while/wend. That guarantees the consistency of the servo updates.

If the data transfer code is in a separate thread then you can use:
Code:
Main setup
   CreateThread slave4431Thread ' create & start peripheral thread

main_loop:

'do your servo stuff

'this is bad code... much better to use a timer interrupt
   While ServoCount = 0  'wait for 20mS to pass (=1/50th of a second)
    Wend
    While ServoCount <> 0
    Wend

   For i = 0 To 7
.
.
.
.
    Next i

Goto main_loop
and in the slave Thread:

Code:
While true ' do for ever
 
    While slave4431_cs = 1
        delay(0) ' force thread to release slot, allow main to run
    Wend

    'now cs = 0 so can do data transfer

    yled = 1
    For spipsn = 0 To 33  'GPS=0to29-$toW INC, QEI=30,31 BATVOLT=32 SPARE DATA=33 'QEI?@
        SSPBUF = s2m(spipsn)  'Transfer S2M(spipsn) into SSPBUF
        While SSPSTAT.BF = 0 'this loop assumes SPI clock rate is high so 33byte transfer is << compared to 20mS main loop
        Wend
        m2s(spipsn) = SSPBUF  'Transfer M2S(spipsn) BYTE from SSPBUF into M2S(spipsn)
    Next spipsn
    spi_pin_rdy = 0

    spipsn = 0
    yled = 0
    RCSTA.CREN = 1
    PIE1.RCIE = 1
Wend
' end of slave thread

If your PIC doesn't support threads then I'd put your data transfer code in an ISR kicked off by slave4431_cs going low and put the servo code into an interrupt routine kicked off by a 20mS timer eg

Code:
Main setup
    Link  slave4431ISR to slave4431_cs pin edgetriggered, low going ' setup peripheral ISR
    Setup 20mS timer on timer1
    timer1.start

main_loop:

if timer1.expired
    timer1.start  'restart timer

' now do your servo stuff

   For i = 0 To 7
.
.
.
.
    Next i

Endif   ' timer1 expired

Goto main_loop
and in the slave ISR:

Code:
    yled = 1
    For spipsn = 0 To 33  'GPS=0to29-$toW INC, QEI=30,31 BATVOLT=32 SPARE DATA=33 'QEI?@
        SSPBUF = s2m(spipsn)  'Transfer S2M(spipsn) into SSPBUF
        While SSPSTAT.BF = 0 'this loop assumes SPI clock rate is high so 33byte transfer is << compared to 20mS main loop
        Wend
        m2s(spipsn) = SSPBUF  'Transfer M2S(spipsn) BYTE from SSPBUF into M2S(spipsn)
    Next spipsn
    spi_pin_rdy = 0

    spipsn = 0
    yled = 0
    RCSTA.CREN = 1
    PIE1.RCIE = 1

' end of slave ISR
Hi I,
This SERVO CODE was written by a forum member, not knowing that it would be added to a longer program, this is why I think there are e,g, WHILE/WENDS that perhaps affect the whole.

For me this is complicated, and needs careful following!
Would you be good enough to edit your last reply please? You use the term SLAVE, and I'm not sure if it is a word you use like the previously used 'THREAD'. I use SLAVE for this PIC which has the SERVO CODE on it, and is SLAVE to the MASTER PIC, so [slave4431_cs] is set by MASTER.
C
 
Last edited:

ericgibbs

Joined Jan 29, 2010
18,984
Hi C,
My post with the Counter was to show that the While Loop is executed while the test is True.
You had it reversed in your Comment.
Do you follow?
E
 

Thread Starter

camerart

Joined Feb 25, 2013
3,731
Hi C,
My post with the Counter was to show that the While Loop is executed while the test is True.
You had it reversed in your Comment.
Do you follow?
E
Hi E,
I think you mean in #5?
Yes I had it backwards. I usually figure stuff out by results, and saw it while testing,
Thanks.
C.
 

Thread Starter

camerart

Joined Feb 25, 2013
3,731
The 'WHILE' issue Eric noticed might be your culprit, as that will block your main thread's servo activity until a CS=0 is received from the slave. Replacing it with If/Endif means it won't wait forever for a CS=0 but the downside is it will only transfer data every 20mS as the main loop only runs every 20mS as this:
Code:
   While ServoCount = 0  'wait for 20mS to pass (=1/50th of a second)
    Wend
    While ServoCount <> 0
    Wend
is bad code because if your servo processing time + data transfer time is > 20mS you will have problems.

Anyway, to follow up my point....

Yes, your main loop is a thread, but with a slow peripheral you could give it its own thread so it can block while waiting for things to transmit. This only applies if the main loop is servicing other aspects, such as the User Interface which you want to be responsive at all times. In your case the only other thing running is the servo processing so as long as your servo processing time + data transfer time is < 20mS then your code is OK (though I'd still kick the servo processing off on a timer interrupt rather than an inline blocking while/wend. That guarantees the consistency of the servo updates.

If the data transfer code is in a separate thread then you can use:
Code:
Main setup
   CreateThread slave4431Thread ' create & start peripheral thread

main_loop:

'do your servo stuff

'this is bad code... much better to use a timer interrupt
   While ServoCount = 0  'wait for 20mS to pass (=1/50th of a second)
    Wend
    While ServoCount <> 0
    Wend

   For i = 0 To 7
.
.
.
.
    Next i

Goto main_loop
and in the slave Thread:

Code:
While true ' do for ever

    While slave4431_cs = 1
        delay(0) ' force thread to release slot, allow main to run
    Wend

    'now cs = 0 so can do data transfer

    yled = 1
    For spipsn = 0 To 33  'GPS=0to29-$toW INC, QEI=30,31 BATVOLT=32 SPARE DATA=33 'QEI?@
        SSPBUF = s2m(spipsn)  'Transfer S2M(spipsn) into SSPBUF
        While SSPSTAT.BF = 0 'this loop assumes SPI clock rate is high so 33byte transfer is << compared to 20mS main loop
        Wend
        m2s(spipsn) = SSPBUF  'Transfer M2S(spipsn) BYTE from SSPBUF into M2S(spipsn)
    Next spipsn
    spi_pin_rdy = 0

    spipsn = 0
    yled = 0
    RCSTA.CREN = 1
    PIE1.RCIE = 1
Wend
' end of slave thread

If your PIC doesn't support threads then I'd put your data transfer code in an ISR kicked off by slave4431_cs going low and put the servo code into an interrupt routine kicked off by a 20mS timer eg

Code:
Main setup
    Link  slave4431ISR to slave4431_cs pin edgetriggered, low going ' setup peripheral ISR
    Setup 20mS timer on timer1
    timer1.start

main_loop:

if timer1.expired
    timer1.start  'restart timer

' now do your servo stuff

   For i = 0 To 7
.
.
.
.
    Next i

Endif   ' timer1 expired

Goto main_loop
and in the slave ISR:

Code:
    yled = 1
    For spipsn = 0 To 33  'GPS=0to29-$toW INC, QEI=30,31 BATVOLT=32 SPARE DATA=33 'QEI?@
        SSPBUF = s2m(spipsn)  'Transfer S2M(spipsn) into SSPBUF
        While SSPSTAT.BF = 0 'this loop assumes SPI clock rate is high so 33byte transfer is << compared to 20mS main loop
        Wend
        m2s(spipsn) = SSPBUF  'Transfer M2S(spipsn) BYTE from SSPBUF into M2S(spipsn)
    Next spipsn
    spi_pin_rdy = 0

    spipsn = 0
    yled = 0
    RCSTA.CREN = 1
    PIE1.RCIE = 1

' end of slave ISR
Hi I,
EDIT: The 'WHILE' issue Eric noticed might be your culprit, as that will block your main thread's servo activity until a CS=0 is received from the slave.
The SS-CS comes from the MASTER.

This has scambled my brain!!
After looking though this reply, I see that you suggest a TMR1 setup. TMR1 is alread setup, and uses CCP1 Capture compare, does this do the same thing?
C
 
Last edited:

Irving

Joined Jan 30, 2016
3,972
Hi I,
This SERVO CODE was written by a forum member, not knowing that it would be added to a longer program, this is why I think there are e,g, WHILE/WENDS that perhaps affect the whole.

For me this is complicated, and needs careful following!
Would you be good enough to edit your last reply please? You use the term SLAVE, and I'm not sure if it is a word you use like the previously used 'THREAD'. I use SLAVE for this PIC which has the SERVO CODE on it, and is SLAVE to the MASTER PIC, so [slave4431_cs] is set by MASTER.
C
Ahh, that makes sense... however I don't think that changes what I wrote. The slave ISR is on the SLAVE PIC, and the [slave4431_cs] signal is an input pin on the SLAVE, pulled low by an output pin on the MASTER, which initiates the interchange between SLAVE & MASTER? In any case, the techniques I describe are appropriate to best practice real-time coding.

Based on that, am I right that all the code you've presented runs on the SLAVE PIC, and there's no code here that runs on the MASTER?

Hi I,
This has scambled my brain!!
After looking though this reply, I see that you suggest a TMR1 setup. TMR1 is already setup, and uses CCP1 Capture compare, does this do the same thing?
C
I'm guessing you're setting up TMR1 with an overflow count in CCPR1 of, maybe, 1mS and trigger special event mode CCP1CON = b‘00001010’, and in the TMR1 ISR, resetting the count to zero when it reaches 20, and setting ServoCount to 0, before setting it back to 1 on the next interrupt.

So when your main loop runs it hits While ServoCount = 0 Wend block and either blocks there or passes through that and then blocks on While ServoCount <> 0 Wend until ServoCount is set to 0 in the ISR which releases it to run the Servo increment/decrement code, by which time ServoCount has moved off zero. so it blocks again waiting for the next 0. The upshot of that is that the main loop only executes after the transition of ServoCount from >0 to 0; most of the time its blocked in one or other of the "While ServoCount" blocks. This means that any code in the main-loop outside the Servo processing is constrained to run in 20mS snatches - which may be OK for your needs.

A better solution, IMHO, is to set a flag in the TMR1 ISR when the end count is reached and have the Servo processing reset that when the processing is completed. eg:
Code:
ServoRunFlag = 0

main_loop:

'do other stuff here...


if ServoRunFlag = 1 ' do servo processing

    for i = 0 to 7
.
.
.
.
.
    next i
    ServoRunFlag  = 0 ' signal weve finished processing
endif

'do other stuff here...

goto main_loop

End
and in the TMR1 ISR something like:

Code:
if TMR1 = finalCount
    if ServoRunFlag = 1 'hasn't completed processing since last flag was set - error/over-run  situation
        handle_error
    endif
    ServoRunFlag = 1
    TMR1 = 0 ' reset timer
endif
Now the main loop runs as fast as the cumulative run time of all processing but no process blocks any other (unless there is a reason to do so). I the other processes need to run on specific intervals then its easy to add without breaking the existing timing. eg if i wanted to flash an LED on and off at 500mS intervals I could simply add:

in main loop:
Code:
if LEDFlashFlag = 1
   LED = !LED ' toggle LED on/off
   LEDFlashFlag = 0
endif
and in TMR ISR I add a tick counter for each processing block - here I assume a 1mS tick
Code:
ServoTickCount = ServoTickCount + 1
LEDTickCount = LEDTickCount+1

if ServoTickCount = finalServoCount
    if ServoRunFlag = 1 'hasn't completed processing since last flag was set - error/over-run  situation
        handle_error
    endif
    ServoRunFlag = 1
    ServoTickCount  = 0 ' reset servo timer
endif

if LEDTickCount = 500
    LEDTickCount = 0  ' no need to check for error here, just rest timer
    LEDFlashFlag = 1
endif

Hope this helps (or a least is starting to make sense)
 

Thread Starter

camerart

Joined Feb 25, 2013
3,731
Hi I,
I'll answer each bit as I read it, for my clarity.

Ahh, that makes sense... however I don't think that changes what I wrote. The slave ISR is on the SLAVE PIC, and the [slave4431_cs] signal is an input pin on the SLAVE, pulled low by an output pin on the MASTER, which initiates the interchange between SLAVE & MASTER? In any case, the techniques I describe are appropriate to best practice real-time coding.

Based on that, am I right that all the code you've presented runs on the SLAVE PIC, and there's no code here that runs on the MASTER?


Yes, this is only on the REMOTE-SLAVE PIC.

The If slave4431_cs is from the REMOTE-MASTER and only for the SPI exchange section not the SERVO section.
C.
 

Thread Starter

camerart

Joined Feb 25, 2013
3,731
I'm guessing you're setting up TMR1 with an overflow count in CCPR1 of, maybe, 1mS and trigger special event mode CCP1CON = b‘00001010’, and in the TMR1 ISR, resetting the count to zero when it reaches 20, and setting ServoCount to 0, before setting it back to 1 on the next interrupt.

So when your main loop runs it hits While ServoCount = 0 Wend block and either blocks there or passes through that and then blocks on While ServoCount <> 0 Wend until ServoCount is set to 0 in the ISR which releases it to run the Servo increment/decrement code, by which time ServoCount has moved off zero. so it blocks again waiting for the next 0. The upshot of that is that the main loop only executes after the transition of ServoCount from >0 to 0; most of the time its blocked in one or other of the "While ServoCount" blocks. This means that any code in the main-loop outside the Servo processing is constrained to run in 20mS snatches - which may be OK for your needs.

A better solution, IMHO, is to set a flag in the TMR1 ISR when the end count is reached and have the Servo processing reset that when the processing is completed. eg:
Code:
ServoRunFlag = 0

main_loop:

'do other stuff here...


if ServoRunFlag = 1 ' do servo processing

    for i = 0 to 7
.
.
.
.
.
    next i
    ServoRunFlag  = 0 ' signal weve finished processing
endif

'do other stuff here...

goto main_loop

End
Hi I,
I'm pretty sure this is correct.
I have noted your FLAG suggestion and agree! I've been told many times to keep INTERRUPTs short, and they do have a habit of growing.
C
 

Thread Starter

camerart

Joined Feb 25, 2013
3,731
Hi I,
Below the:
and in the TMR1 ISR something like:
is a bit confusing to me. It looks like your doing away with CCP, and using a TIMER.

You mention ERRORS, there are OVERRUN and FRAMING errors in other parts of the program, I'm not sure if these will do for your suggestion?

NOTE: I've used SEROUTs in the program, so I can see what goes on. They affect the timings, and are 'outed' for proper testing. I may set up a BUTTON to 'in' them?

Also other minor WAITS, that I know should be checked, but were added , to get things working.
C
 

Irving

Joined Jan 30, 2016
3,972
elow the:
and in the TMR1 ISR something like:
is a bit confusing to me. It looks like your doing away with CCP, and using a TIMER
Not at all, I just haven't written a fully fleshed out ISR. You still use CCP but for a smaller interval, say 1mS and then count ISR calls to determine various timeouts. I'm guessing currently your CCP is set for 20mS and you get one ISR hit per 20mS period.
 

Thread Starter

camerart

Joined Feb 25, 2013
3,731
Not at all, I just haven't written a fully fleshed out ISR. You still use CCP but for a smaller interval, say 1mS and then count ISR calls to determine various timeouts. I'm guessing currently your CCP is set for 20mS and you get one ISR hit per 20mS period.
Hi I,
I think you're correct with 20mS period?

This suggestion, is a bit beyond me, but is it possible to have 'say' a 1mS period, and cycle through the UART section, and the SERVO section, and the SPI section, only using FLAGS to interact with each of them?
C.
 
Top