View previous topic :: View next topic |
Author |
Message |
alan
Joined: 12 Nov 2012 Posts: 357 Location: South Africa
|
SPI Slave weird behaviour |
Posted: Mon Aug 19, 2013 1:48 am |
|
|
Goodday All
I am stump by the code below exiting a routine when it should not.
Hope somebody can spot my flaw.
PIC16F1518
CCS 5.010 and 4.141
http://imageshack.us/photo/my-images/826/e08w.jpg/
Code: |
#include <16F1518.h>
#device ADC=10
#case
#FUSES NOWDT //No Watch Dog Timer
#FUSES PUT //Power Up Timer
#FUSES NOIESO //Internal External Switch Over mode disabled
#FUSES NOFCMEN //Fail-safe clock monitor disabled
#FUSES BORV25 //Brownout reset at 2.5V
#FUSES NOLVP //No low voltage prgming, B3(PIC16) or B5(PIC18) used for I/O
#use delay(internal=16MHz)
#include <stdint.H>
#define RELAY1 PIN_A5
#define RELAY2 PIN_C5
#define SLAVE_SELECT PIN_B4
#define SPI_CLK PIN_B7
#define SPI_MOSI PIN_B5
#define SPI_MISO PIN_B6
#ZERO_RAM
void ReceiveSPI(void) {
uint8_t i;
while (!input_state(SLAVE_SELECT)) {
for (i=0; i<8; i++) {
//Wait for SPI CLK to go low
while (input_state(SPI_CLK)) {
/*If SS high terminate and return*/
if (input_state(SLAVE_SELECT)) {
output_toggle(RELAY2);
return;
}
}
//Wait for SPI CLK to go high
while (!input_state(SPI_CLK)) {
//If SS high terminate and return
if (input_state(SLAVE_SELECT)) {
output_toggle(RELAY2);
return;
}
}
}
}
}
void main() {
set_tris_a(0x0C);
set_tris_b(0xB9);
set_tris_c(0x44);
while(TRUE){
//Monitor the SS line and enable SPI reception when low
if (!input_state(SLAVE_SELECT)) { //Slave select is low
ReceiveSPI();
output_toggle(RELAY1);
}
}
}
|
What is happening is that the ReceiveSPI() should only exit when SLAVE_SELECT is high but as can be seen on the oscilloscope trace, that does not always work like that.
Ch 1 (yellow) is SCK from master
Ch 2 (blue) is CS from master
Ch 4 (green) is the output_toggle(RELAY1) which toggles every time I exit ReceiveSPI().
It can be seen that on the picture it exits while SLAVE_SELECT is still low, then re-enters and exit correctly when SLAVE_SELECT high.
Now the question is why does the code do this. PIC is running at 5V and input signals are at 5V as measured on the PIC pin in question. A LED is connected to 12V via a NPN transistor to ground, tha's why that is 12V.
Regards
Alan |
|
![](templates/subSilver/images/spacer.gif) |
Ttelmah
Joined: 11 Mar 2010 Posts: 19359
|
|
Posted: Mon Aug 19, 2013 2:07 am |
|
|
You are testing the same pins, at multiple places in the loop. Giving multiple exit routes.
The main loop in 'ReceiveSPI', will exit if SLAVE_SELECT is high at the moment it loops, doing nothing when it does. However It'll also exit if SLAVE_SELECT happens to go high in the moment after it tests the clock, this time toggling the relay. The behaviour is indeterminate, depending on when exactly relative to the loop, events happen.....
Best Wishes |
|
![](templates/subSilver/images/spacer.gif) |
alan
Joined: 12 Nov 2012 Posts: 357 Location: South Africa
|
|
Posted: Mon Aug 19, 2013 2:26 am |
|
|
Thanks Ttelmah.
The reason for multiple testing are if the SLAVE_SELECT goes high when waiting for a CLK change to exit the routine.
I want to refer to the attached oscilloscope trace again. It exits the loop after about 3.5ms with the SLAVE_SELECT pin still low. The pin goes high 2ms after this event.
Regards |
|
![](templates/subSilver/images/spacer.gif) |
alan
Joined: 12 Nov 2012 Posts: 357 Location: South Africa
|
|
Posted: Mon Aug 19, 2013 2:35 am |
|
|
Ttelmah
Changed the code to have only one exit out of the routine. Still the same behaviour. Does not happen every time, difficult to determine the timing but a guess would be once every 5 sec on average.
Code: |
#include <16F1518.h>
#device ADC=10
#case
#FUSES NOWDT //No Watch Dog Timer
#FUSES PUT //Power Up Timer
#FUSES NOIESO //Internal External Switch Over mode disabled
#FUSES NOFCMEN //Fail-safe clock monitor disabled
#FUSES BORV25 //Brownout reset at 2.5V
#FUSES NOLVP //No low voltage prgming, B3(PIC16) or B5(PIC18) used for I/O
#use delay(internal=16MHz)
#include <stdint.H>
#define RELAY1 PIN_A5
#define RELAY2 PIN_C5
#define SLAVE_SELECT PIN_B4
#define SPI_CLK PIN_B7
#define SPI_MOSI PIN_B5
#define SPI_MISO PIN_B6
#ZERO_RAM
void ReceiveSPI(void) {
uint8_t i;
while (TRUE) { //!input_state(SLAVE_SELECT)) {
for (i=0; i<8; i++) {
//Wait for SPI CLK to go low
while (input_state(SPI_CLK)) {
/*If SS high terminate and return*/
if (input_state(SLAVE_SELECT)) {
output_toggle(RELAY2);
return;
}
}
//Wait for SPI CLK to go high
while (!input_state(SPI_CLK));
}
}
}
void main() {
set_tris_a(0x0C);
set_tris_b(0xB9);
set_tris_c(0x44);
while(TRUE){
//Monitor the SS line and enable SPI reception when low
if (!input_state(SLAVE_SELECT)) { //Slave select is low
ReceiveSPI();
output_toggle(RELAY1);
}
}
}
|
Regards
Alan |
|
![](templates/subSilver/images/spacer.gif) |
Ttelmah
Joined: 11 Mar 2010 Posts: 19359
|
|
Posted: Mon Aug 19, 2013 3:21 am |
|
|
Point is that the trace, does not show it exiting with SS low. It looks as it it may be, but it you realise that your timing is 2mSec/division, I think the SS may actually be high at the time of exit.
You'd need to be running the scope with a much higher scan resolution to see what is happening.
Obvious possibilities are things like ringing on the SS line etc....
Also are you absolutely sure you have the connections the right way round?. Obvious 'pattern' to the exit's, is that the SCK is high at the time.
The new code looks much more likely to work right.
Best Wishes |
|
![](templates/subSilver/images/spacer.gif) |
alan
Joined: 12 Nov 2012 Posts: 357 Location: South Africa
|
|
Posted: Mon Aug 19, 2013 7:51 am |
|
|
Well I managed to capture at 100us per div and SLAVE_SELECT still low.
[img]
http://imageshack.us/photo/my-images/89/mtny.jpg/
[/img]
Changed the code to detect the SLAVE_SELECT line high for 50 x 7 instructions and that seems to solve the problem, which seems to point to ringing that a 5GS/s scope could not detect.
Thanks Ttelmah appreciate your input.
Code: |
void ReceiveSPI(void) {
uint8_t i,SS_cnt;
SS_cnt = 0;
while (TRUE) { //!input_state(SLAVE_SELECT)) {
for (i=0; i<8; i++) {
//Wait for SPI CLK to go low
while (input_state(SPI_CLK)) {
/*If SS high terminate and return*/
if (input_state(SLAVE_SELECT)) {
//SS must be high for 50 counts
if (SS_cnt++ > 50) return;
}
else SS_cnt = 0;
}
//Wait for SPI CLK to go high
while (!input_state(SPI_CLK));
}
}
}
|
Regards
Alan |
|
![](templates/subSilver/images/spacer.gif) |
|