Continue to Site

Welcome to our site!

Electro Tech is an online community (with over 170,000 members) who enjoy talking about and building electronic circuits, projects and gadgets. To participate you need to register. Registration is free. Click here to register now.

  • Welcome to our site! Electro Tech is an online community (with over 170,000 members) who enjoy talking about and building electronic circuits, projects and gadgets. To participate you need to register. Registration is free. Click here to register now.

Spi Sspbuf

Status
Not open for further replies.

richb

New Member
Hi

I'm trying to write a putString function so I can write a string to the SPI port on the PIC16F877A.

The problem I'm having is that the first char is being sent fine, but the rest are not being placed into the SSPBUF.

I'm not sure how to check when the SSPBUF is empty, so i can push out another character to it. When using the I2C, you can check the 'BF' register, but this is not available for TX mode on SPI. Looked through the PIC datasheet, and tried several things but still no luck. Any ideas what register I need to check before flushing another char at it?

Cheers
Rich
 
I've never used the SPI port, but looking at the data sheet, it looks like you have to write a byte and check if a collision occurred (WCOL). If it did cause a collision then rewrite it. It also looks like when writing a byte a byte will also be received and so the BF flag will get set and an IRQ generated.

Mike.
 
Hi,

I believe the 'BF' flag IS used with SPI. In the datasheet for the 16F819 I have, it clearly states that the 'BF' bit is the only part of the SSPSTAT register when used in SPI mode.

I have some code I wrote ages ago, generic routine for sending bytes over SPI:

SEND

movwf SSPBUF ; write to the buffer, so it send it.
banksel SSPSTAT


busy

BTFSS SSPSTAT,BF ; check to see if old data is complete
goto busy ; not, continue to wait
bcf SSPSTAT,BF ; disable flag
return

This is assuming you have setup the SPI perihperal correctly, if its getting the first byte ok, then I guess it is. Try here for detailed info:

https://www.electro-tech-online.com/custompdfs/2007/02/spi.pdf

Regards,

Blueteeth

Ps. Excuse the formatting of the code, I just pasted it and it looks awful, I'm sure you can work out the subroutine format.
 
Last edited:
Pommie said:
I've never used the SPI port, but looking at the data sheet, it looks like you have to write a byte and check if a collision occurred (WCOL). If it did cause a collision then rewrite it. It also looks like when writing a byte a byte will also be received and so the BF flag will get set and an IRQ generated.

Mike.
This is nonsense. In a properly configured Master Slave system there is only one master and the possibility of a collision is essentially nil. The only way to do things is to make sure that the transmit shift register is EMPTY and that it can accept another byte before you write that byte to the data register.
 
I forgot to mention: interupts.

I tend to turn off the peripheral interupt for the SSP module, as SPI sends it so fast, it generally doesn't require a long delay between sending bytes (ie: the PIC does nothing, so interupts are used to increase efficiency). If you've got the interupt set, turn it off.

Blueteeth
 
Papabravo said:
This is nonsense. In a properly configured Master Slave system there is only one master and the possibility of a collision is essentially nil. The only way to do things is to make sure that the transmit shift register is EMPTY and that it can accept another byte before you write that byte to the data register.

From the data sheet,
Any write to the SSPBUF register during transmission/reception of data
will be ignored and the write collision detect bit, WCOL (SSPCON<7>), will be set. User software must clear the WCOL bit so that it can be determined if the following write(s) to the SSPBUF register completed successfully.

So, why don't you answer the OP's question - how do you know when the transmit shift register is empty?

Mike.
 
Sure. Regardless of its use as an interrupt flag SSPIF in PIR1 is essentially a transaction complete flag. Assuming you are not using this particular interrupt. If it is SET during your initialization sequence before ANY other interrupts are enabled then it will be SET when you go to transmit your first byte. This is essentially a fake completion before the first SPI transaction. If it is SET then write the data byte SSPBUF. This write will clear the SSPIF bit and you can check it for completion by checking for it to return to one.

Says me if you do this you will never write a byte at the wrong time causing a collision.

One other thing is that since SPI peripherals are normally run at high data rates, the time between bytes can be as short as a few microseconds, and there is a temptation to loop on a single condition waiting for it to happen. This would be a bad thing to do; there should ALWAYS be a second condition to break a loop to avoid having an embedded brick.
 
Last edited:
OK reading the spec sheet it does say that BF is for received data. However, shifting out transmit data and shifting in received data is synchronous. So transmit buffer empty/receive Buffer Full should be the same thing and the spec sheet alludes to that in several places.

Now if you disabled the receiver, that's a grey area in the spec sheet because it specifically said "receive". Logically looking at the problem, I suspect the BF flag will still work. They'd have to go out of their way to disable it and it would make no sense to do so. Try it out and see! Actually MPSIM should accurately tell you what it will do too without ever setting up a circuit.

And of course the interrupt is mentioned, that's another good way to do it.
 
Yeah, the BF flag is used for both transmit and recieve....it simply indicates if the SSPBUF is full...ie: you've recieved a new byte, which is the same (in SPI terms) as just finished trasmitting a byte. Its a data exchange protocol, so discard the 'recieved data' if you're just concerned with transmitting. Otherwise, none of the programs I've written for SPI would work :D

I don't think its 'hidden' in the datasheet, it even states to check the BF flag in that SPI app note I linked.

Sorry if I've repeated what others have said, you just have to rememver that, when you send a byte in SPI, you are also automatically receiving one at the same time, from the SDI pin, that is loaded into the same shift register, whether you need to or not. The BF flag should probably be called 'transfer complete' as opposed to 'buffer full'.

Blueteeth
 
I agree that the BF flag should also work. The real issue here is how to avoid creating a collision or even having to test the WCOL bit. I think there are at least two ways documented in this thread to do that.
 
I haven't experienced that problem in any SPI-based program I've written, on any PIC, and I don't think many would....it only occurs if you try to write to the SSPBUF when a transfer is in progress - why would you try to do that? All it requires is a simple loop to check the BF flag to see if the transfer is complete, once it is, return from subroutine. Or...use a delay. And even if you do try to write a byte to it while its busy, it won't stop the transfer, just set that WCOL flag, you clear it in software, jobs a goodun, no harm done.

I guess I'm over simplifying things.

Blueteeth
 
The only fly in the ointment is that the BF flag does not indicate a complete transaction BEFORE the first transaction has taken place. There can be no received byte if there has never been a byte transmitted. Also the BF flag is Read Only and initialized to zero. Therefore, any loop on BF==1 alone, will be infinite anytime before the first write SSPBUF.

My original point was, that in a correctly designed system, you would never have occasion to write a second byte while the first byte was being shifted.
 
Last edited:
Thanks for your replys, unfortunately I'm still having issues with my code.

I am trying to utilise the MAX3100 with SPI.

Basically I'm sending this:

Code:
spi_puts("TEST\r\n");

in the main routine, but unfortunately I only receive the first and second characters, hence 'TS'.

I've checked everything, but can't seem to work out why my spi_puts() string function is not working. My code is in C, but the definitions and logic is very similar to ASM.

Code:
void spi_write(unsigned char txdata1, unsigned char txdata2)
{
	unsigned char rxdata;
	CS	= 0;
	
	rxdata	= SSPBUF;
	SSPBUF	= txdata1;
	while(!STAT_BF);
	
	rxdata	= SSPBUF;
	SSPBUF	= txdata2;
	while(!STAT_BF);

	CS	= 1;
}

void spi_puts(const char *str)
{	
	while(*str){
		spi_write(0b10000000, *str);
	//	putch(*str);
		str++;
	}
}

void spi_init()
{
	STAT_CKE = 1;
	spi_write(0b11000000, 0b00001011);
}

void setup_spi()
{
	sdo_dir = 0; 			// make SDO output
	sck_dir = 0; 			// make SCK input(slave) or output
	sdi_dir = 1; 			// make SDI input
			
	SSPEN = FALSE; 			// disable ssp
	SSPCON 	= 0x21; 		// set ssp mode
	SSPEN = TRUE; 			// re-enable ssp
}

If you could take a look that would be great.

Thanks
Rich
 
Last edited:
What exactly is your setup? Are you trying to send characters, from one PIC to another via SPI? Transmission, or 'master mode' on a PIC is easy, but sometimes a slave can be a bit tricky, as it should always just be based on interupts.

My C is not just rusty, but down right crap (still learning) but I can follow that code. The 'BF' flag is set and cleared by the PIC, if you don't read the byte from the SSBUF (the recieved byte after transfer is complete) when the BF flag is set after transfer, you'll set the overflow flag. I assume you've turned off all interupts like I said before?

If that code is the master, ie: it controls when bytes are to be sent, I would send the byte, THEN wait for the BF flag, then read it:

****************************

CS = 0;


SSPBUF = txdata1;
while(!STAT_BF);
rxdata = SSPBUF;

CS = 1;

<wait a bit here>

CS = 0;

SSPBUF = txdata2;
while(!STAT_BF);
rxdata = SSPBUF;

****************************

The problem with high level languages is, you don't know exactly whats going on :D Perhaps if you could post some of the generated assembly, it would highlight any problems.

Also, it would be best to send bytes seperately. In most cases it doesn't matter, but I tihnk the PIC's PIS in slave mode likes to have CS pulled low for every byte, with ti being high for a period inbetween bytes. After all, its the 'SS/CS' line in the slave that initiates communication. Without a gap between bytes, it will probably over write the contents of the previous transfer, and then get really arsey.

So perhaps, rather than sending two bytes in the routine, you just send one, and have another routine that calls it twice.

Try here for C examples:

https://www.microchipc.com/sourcecode/#interface,

worst case scenario, you could add in a small assembly snippet to control the transfer, the code I posted works, at least for sending bytes continuously.
 
Hi

Ok i'm not sure i've made my last two posts overly clear. What i'm trying to achieve is connect my PIC16F877A to a MAX3100 which is an external USART via SPI.

The MAX3100 takes 16 bits, 8 of them are for control bits, then the next 8 bits are for data. When i want to send data I need to bring the CS line low, send 8 bits, then send another 8 data bits before bringing the CS line high.

The issue I'm having is when you push 16 bits to the device, you get 16 bits back (information from the USART).

I need to have SSPIF enabled so when i receive information I can throw and interrupt and collect it. Really don't want to utilise pooling if I can help it.

The way i've been told to write is using this approach:-

Code:
spi_out(chat ch)
{
  SSPIF = 0;
  SSPBUGG = ch;
  while(SSPIF == 0);
}

Unfortunately this method does not seem to work.
 
Last edited:
Richb,

If you look carefully at the MAX3100 datasheet you will notice that you cannot just send characters to the UART over the SPI port. Each character that you transmit and receive is part of a larger two byte transaction. Along with the bits for the characters you have to send some bits to tell the MAX3100 what to do. There are also status bits which the 3100 returns to you as part of the data exchange.
 
Ahh max3100, totally different ball game :D The PIC's SPI isn't really 'true' SPI, as many devices require multiple bytes per frame, whereas the PIC geneally likes to do a single byte. Although,I generally ignore the incoming data, and use a generic I/O for the CS line, so I can send as many bytes in a frame as I wish.

Think papabravo has it though, make sure you're providing exactly what the max3100 needs.

Blueteeth
 
Not to worry, the PIC implementation is a true enough SPI. When you need a sixteen bit transaction you do it as two bytes. The SPI peripherals don't care if there is a very LONG delay between clock pulse number 8 and clock pulse number 9. In fact they could care less if each clock pulse was a different length. For the nth time though you need to read the datasheets very carefully. For my money Maxim writes some of the very BEST SPI peripheral datasheets.
 
Sure its a fine peripheral to work with, I wasn't disputed that, but the 'CS' line is only used for single byte transfers. Of course, as I always do, and thats do the CS manually, its still not part of the automatic peripheral itself. Not putting microchip down here, I only use PIC's and SPI most of the time, but I nearly always haved to add extra code for 16/24/32 bit transfers, of course i do, PIC's are 8-bit devices!

Sure, the synchronous nature of it means you could send two bytes seperately, or three, but if you just connected up the PIC to the maxim device, and put a byte in the buffer, you'd onyl send 8 bits, and get 8-bits back, unless you manually control the CS line.

I'll agree that maxim are pretty damn good with their datasheets/appnotes, I based my uni project on one of theirs :D

Blueteeth
 
I don't think you need to toggle CS between the first and second bytes of a transaction on the MAX3100. If you look at the timing diagram in Figure 4. of the data sheet it clearly shows 16 consecutive clocks from CS falling to CS rising. On a PIC or indeed any 8 bit microprocessor such as an 8051, or a 68HC11 , the sixteen bits have to be sent out as 2 bytes. You must also be certain that the first byte has been completely sent before writing the data buffer with the second byte or you will create a collision. In fact if CS rising occurs before the sixteenth clock the MAX3100 will be very badly confused.
 
Status
Not open for further replies.

Latest threads

New Articles From Microcontroller Tips

Back
Top