USART as SPI clock problems

Go To Last Post
7 posts / 0 new
Author
Message
#1
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I am using an ATtiny1634 on the STK-600 to talk to a coupon that has the spi pins broken out for the ST micro LIS3DSH accelerometer. I have been able to communicate to the device with no issue for a while now using a bit bang code, I just recently wanted to attempt to use the 1634's usart as an spi, and I am having a hell of a time.

 

It seems like it should be relatively simple from what I have seen on the internet and in examples from the data sheet.

 

I currently have the USART transmitting just fine, but when it comes recieving the USART never sends any clock pulses so the accelerometer can never communicate back. After I send the address (who_am_I) using the USART I can see that the accelerometer starts to communicate, because it pulls the data line low to start the byte that should be returned (0x3F), but then no clock pulse occurs and the accelerometer gives up when chip select goes back high. I have tried all of the different spi modes through UPCHA and UCPOL and that hasn't been the issue as well as various baud rates as the accelerometer should be able to do anything under 2 MHz

 

I am pretty sure this has nothing to do with the accelerometer because a simple bit bang code will communicate just fine. I can even transmit the address with the USART and recieve the actual byte using bit bang code.

 

My USART code is below, which hopefully has some mistake.

 

void INIT_SPI(void)
{
	DATA_OUT; //enable outputs
	LE_OUT;
	LE_HI;
	CLK_OUT;
	
	UBRR1 = 0; 
	UCSR1C |= (1<<UMSEL11)|(1<<UMSEL10); //select SPIM mode
	UCSR1C &= ~(1<<UDORD1); //set byte polarity
	UCSR1C &= ~(1<<UCPHA1); //set spi mode 0
	UCSR1C &= ~(1<<UCPOL1); 
	UCSR1B |= (1<<RXEN1)|(1<<TXEN1); //enable TX and RX on USART1
	UBRR1 = 50; //set baud rate
}
char RECIEVE(char address)
{
    
    LE_LO;//Latch to begin communication
    while (!(UCSR1A&(1<<UDRE1))); //wait for empty buffer
    UDR1 = address|0x80; //send address with read byte
    while(!(UCSR1A&(1<<RXC1)));  //wait for data to be recieved
    char read=UDR1; //read data
    LE_HI; //Latch to end communication
}

 

The code is pretty much a copy of the example code from datasheet.

 

Basically it seems like the USART doesn't want to recieve because it the clock pulses never occur, even though the accelerometer wants to talk.

 

Thanks for the help!

 

 

This topic has a solution.
This reply has been marked as the solution. 
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

SPI is full-duplex.  The master generates the clocks.  Data bits are shifted in both directions for each clock pulse.

 

A typical master-slave sequence, from the master point of view, might go:

-- Master selects slave

-- Master sends query, ignoring returned bits as the slave isn't responding with anything.

-- Master sends dummy bytes to force clocks, gathering the slave's response bits.

-- Master de-selects slave.

 

(So the short answer is that you need to send dummy bytes.)

 

 A shortened sequence from a DS1305 RTC read:

    SELECT_RTC = 1;				// select RTC

>> 0x00 us a real command.
>> Note returned value from slave is ignored
    spi(0x00);					// send command "read starting at 0"

>> Now each register is read in turn, sending a dummy value, and the 
>>	returned value captured and processed.
// Seconds
	bcdwork = spi(0x00);
...
// Minutes
	bcdwork = spi(0x00);
...
// Hours (24-hour format)
	bcdwork = spi(0x00);
...
...
    SELECT_RTC = 0;			// de-select RTC

USART-as-SPI-master should probably also be implemented with a general write-read primitive, equivalent to spi().

You can put lipstick on a pig, but it is still a pig.

I've never met a pig I didn't like, as long as you have some salt and pepper.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Thank you, that solved my problem... that is surprisingly not obvious from what I saw in the data sheet and online. Especially for an application as simple as mine.

 

Here is the code I made that is a simple 8 bit address accessing code if anyone else later has this issue.

 

char RECIEVE(char address)
{
	LE_LO;//Latch to begin communication
	while (!(UCSR1A&(1<<UDRE1)));//wait for buffer to open
	UDR1 = address|0x80;//send address with read byte
	while(!(UCSR1A&(1<<RXC1)));//wait for recieved data
	char read=UDR1;//read empty data to keep buffer synced
	while (!(UCSR1A&(1<<UDRE1)));//wait for buffer to open
	UDR1 = 0x00;//send empty bytes to create clock for byte at address to return
	while(!(UCSR1A&(1<<RXC1)));//wait for recieved data
	LE_HI;//Latch to end communication
	return UDR1;//return byte
}

 

thanks again for the help.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

that is surprisingly not obvious from what I saw in the data sheet and online.

Expand more on that.  The datasheet for the AVR, or the device?  Indeed when doing first SPI app, the concepts of device select, master/slave/ and full-duplex need to be grasped.

 

nearly all target device datasheets will have a timing diagram for a "transaction".  IME most common is indeed a command from the master on MOSI with the master driving SCK, and usually during that time the MISO is n/a as the slave is decoding how to respond.  Then the master drives SCK with MOSI being n/a and gathers the slave's response on MISO.

 

From the mentioned LIS3DSH datasheet, "Figure 6. Read and write protocol" , "Figure 7. SPI read protocol", ... outline this.  I guess you need to read between the lines to know that a "flat line" is what I called n/a above.

 

1)  "char" may not be the best data type.  Is it signed or unsigned in your particular configuration?  Or androgynous? Eliminate that doubt; call it "unsigned char" or "uint8_t".

2)  I would create a routine, call it "frog()", that is the analogy of the spi() primitive in my code fragment.  Even with your device, there are different message formats.  IMO/IME you will soon find the advantage to hiding the repeated detail.

// Send+receive primitive
unsigned char	frog	(unsigned char outdata)
{    
	while (!(UCSR1A&(1<<UDRE1)));	//wait for buffer to open
	UDR1 = outdata;					//load send info
	while(!(UCSR1A&(1<<RXC1)));		//wait for recieved data
	return UDR1;					//read data to keep buffer synced
}	

// One-byte read transaction
unsigned char RECIEVE(unsigned char address)
{
unsigned char result;

	LE_LO;//Latch to begin communication
	frog (address|0x80);	// send command; ignore reply
	result = frog (0x00);	// get response byte
	
	LE_HI;//Latch to end communication
	return result;//return byte
}

Now you can use real SPI or USART-as-SPI-master just by substituting primitives.

You can put lipstick on a pig, but it is still a pig.

I've never met a pig I didn't like, as long as you have some salt and pepper.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

theusch wrote:

Expand more on that.

 

I was mostly confused by the 1634 datasheet. There example shows a c++ code called "USART_Receive" where it sends out a byte  and then waits for the data to be received, and then reads the data. With only having used simple bit bang SPI and not knowing that SPI is full duplex the example code looks (to an amateur like me) like it would operate two seperate 8 byte operations, like sending an address and then receiving a byte.

 

Now that I have that understanding of it, it all makes sense and seems quite easy.

 

Thanks for the help

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

The datasheet does say: "After initialization the USART is ready for doing data transfers. A data transfer is initiated by writing to UDRn. This is the case for both sending and receiving data since the transmitter controls the transfer clock. "

 

Note that the datasheet example is identical to my frog() routine. ;)

You can put lipstick on a pig, but it is still a pig.

I've never met a pig I didn't like, as long as you have some salt and pepper.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

As theusch has suggested,  you should look at every SPI operation as a write-and-read.   e.g. call it spi() or spi_transfer()

 

Whether you use SPI, USI, USART or bit-bash,   the action is the same.

 

If you treat it as a transfer()  that does not return until it has completed the read,   you are always in step.

 

Once you have this working,  you can take advantage of the write-buffer in the USART if you have a lot of SPI comms.   e.g.  painting a 240x320 full colour graphics display takes 153600 8-bit transfers.

 

David.