|
|
View previous topic :: View next topic |
Author |
Message |
kd5uzz
Joined: 28 May 2006 Posts: 56
|
SPI Master/Slave Problems |
Posted: Tue Apr 03, 2007 4:23 pm |
|
|
Hello,
In an attempt to learn SPI and free up some I/Os on my current project I'm trying to build an SPI matrix keypad encoder. I want to be able to perform an SPI read operation from the master and have the keypad encoder send the last key pressed. I understand that the slave must have the data 'ready to go' when the read request is sent. I'm using a 16F88 (using the internal clock @ 8mhz) as the encoder, and a 16F877 (@20mhz) as the master.
F877 -- F88
--------------
SDI to SDO
SDO to SDI
SCK to SCK
D1 to SS
First I press a key on the keypad to put the slave in 'spi_read()', then a read operation is started when I ground D1 on the master. I see the slave display "Reading/Writing SPI...", and after awhile it returns with an invalid result. (usually -1 or 0, not the 65 I expect.). The master returns the same. Thoughts?
SPI Master:
Code: |
#include <16F877a.h>
#fuses HS,NOWDT,NOPROTECT
#use delay(clock=20000000) //clock speed
#use rs232(baud=2400,xmit=PIN_C6,rcv=PIN_C7,ERRORS)
//#include <flex_lcd420.c>
//******************************
// Functions
//******************************
void Startup();
void SecondPassed();
//******************************
// Global Defs
//******************************
int BlinkState,SecondCount;
//******************************
// Code
//******************************
void Main(){
int i;
int KeyCode;
int MenuIndex = 1;
char Key;
Startup();
while(TRUE){
printf("\n\rWaiting on SPI start button...\n\r");
while(input(PIN_D0));
printf("Reading SPI...\n\r");
output_high(PIN_D1);
Key = spi_read(65);
output_low(pin_D1);
printf("SPI Returned %i\n\r",Key);
delay_ms(1000);
}
}
void SecondPassed(){
switch (BlinkState){
case 0:
BlinkState = 1;
output_low(PIN_B5);
break;
default:
BlinkState = 0;
output_high(PIN_B5);
break;
}
}
#INT_TIMER2
void Timer2Int(){
SecondCount++; //counts ints for seconds
if (SecondCount == 250) { //1sec passed
SecondCount = 0;
SecondPassed();
}
}
void Startup(){
enable_interrupts(INT_TIMER2); //used to drive LED display
// setup_timer_2(T2_DIV_BY_4,0,5);
setup_timer_2(T2_DIV_BY_4,253,5); // one int per 4ms
//enable_interrupts(INT_EXT); //INT for B0 (IR or Keypad)
enable_interrupts(GLOBAL); //Actually enable them
setup_spi(SPI_MASTER | SPI_H_TO_L | SPI_CLK_DIV_64);
printf("Keypad and LCD Test\n\r");
}
|
Slave Code:
Code: |
#include <16F88.H>
#fuses INTRC_IO
#fuses NOWDT, BROWNOUT, NOLVP
#use delay(clock=4000000)
#use rs232(baud=2400,xmit=PIN_B3,rcv=PIN_B3,ERRORS)
#include <flex_keypad_4x4.c>
//******************************
// Functions
//******************************
//******************************
// Global Defs
//******************************
//******************************
// Code
//******************************
void main(){
char k,spiinput;
setup_spi(SPI_SLAVE | SPI_H_TO_L);
kbd_init();
delay_ms(5);
printf("\f\rF88 Keypad to SPI encoder\n\r");
while(TRUE){
k=kbd_getc();
if(k!=0){
printf("Key: %c. Reading/Writing SPI...\n\r", k);
spiinput = spi_read(k);
printf("SPI Transaction complete. SPI read returned: %c.\n\r",spiinput);
}
}
}
|
|
|
|
Ttelmah Guest
|
|
Posted: Wed Apr 04, 2007 3:55 am |
|
|
The internal SPI commands, are a little 'too smart' for their own good. With the read, and write, supposedly 'switching modes' according to whether this is a master of slave device. It makes getting the SPI to work harder than it should be. It also means you can't take advantage of the internal hardware buffering quite as easily as should be possible...
I have always used my own code for this reason, and in the slave in particular, it makes things much easier to understand. So:
Code: |
#ifdef __PCH__
#byte SSPBUF = 0xFC9
#byte SSPCON = 0xFC6
#byte SSPSTAT = 0xFC7
#else
#byte SSPBUF = 0x13
#byte SSPCON = 0x14
#byte SSPSTAT = 0x94
#endif
#bit SSPBF = SSTSTAT.0
#DEFINE READ_SSP() (SSPBUF)
#DEFINE SSP_HAS_DATA() (SSPBF)
#DEFINE WAIT_FOR_SSP() while(!SSP_HAS_DATA())
#DEFINE WRITE_SSP(chr) SSPBUF=(chr)
|
Now the 'key' difference, is that these 'split up' the parts involved in the operations, allowing you to load the buffer, and then go and do other things if you want.
For your slave, the code would become:
Code: |
void main(){
char k,spiinput;
setup_spi(SPI_SLAVE | SPI_H_TO_L);
kbd_init();
delay_ms(5);
printf("\f\rF88 Keypad to SPI encoder\n\r");
while(TRUE){
k=kbd_getc();
if(k!=0){
printf("Key: %c. Reading/Writing SPI...\n\r", k);
WRITE_SSP(k); //load the output buffer
while (!SSP_HAS_DATA()) {
//Here you can do anything you want while waiting
}
spiinput = READ_SSP();
printf("SPI Transaction complete. SPI read returned: %c.\n\r",spiinput);
}
}
}
|
Provided your 'trigger'line is working as expected, your source should work.
Obviously remember that the 'SCK' line needs to directly connect between the devices, while SDO, and SDI, need to 'swap over' between the chips. Also, with the 'split' code as I show, you could 'poll' the device every 1/10th second for example, and have it just return '0', unless a key is present, removing the need for the extra wire.
Best Wishes |
|
|
kd5uzz
Joined: 28 May 2006 Posts: 56
|
Undefined Identifier ? |
Posted: Wed Apr 04, 2007 9:50 am |
|
|
After pasting your defines and main() code I get the above error. It seems to be the compiler doesn't like #bit SSPBF = SSTSTAT.0. I'm using compiler 3.222. |
|
|
PCM programmer
Joined: 06 Sep 2003 Posts: 21708
|
|
Posted: Sat Apr 21, 2007 11:54 pm |
|
|
Quote: | It seems to be the compiler doesn't like #bit SSPBF = SSTSTAT.0. |
If you're still interested in trying his code, that was just a typo.
Fix it by changing "SST" to "SSP", as shown below:
Code: | #bit SSPBF = SSPSTAT.0 |
|
|
|
|
|
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
|