Recommended SPI Library?

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

Greetings -

 

SPI is relatively simple compared to TWI/I2C. But, there are still a few subtleties. So, is there a recommended library? I searched and found a couple including one on GitHub, but no developer names that I recognized. 

 

Suggestions?

 

Thanks, 

Jim

Jim Wagner Oregon Research Electronics, Consulting Div. Tangent, OR, USA http://www.orelectronics.net

Last Edited: Wed. Aug 7, 2019 - 08:59 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

There is only 1 line of code to set it up and a couple more to use it. (Unless of course one tries to use the ASF)

 

Do you have any specifications or device you need to talk to? That's about the only "hard part" of SPI, understanding clock phase and polarity and if one doesn't understand that part than no "library" is going to help.

 

// SPI driver

#include "spi.h"

void spi_write (uint8_t temp)
{
	SPDR = temp;
	while(!(SPSR & (1<<SPIF)));
}

void spi_init (void)
{
//Set up SPI, MSB first, Master, mode 0, clock fck/64
	SPCR |= (1<<SPE | 1<<MSTR );	// | 1<<SPR1 );	//| 1<<SPR0);
}
// SPI driver header file

#include <avr/io.h>

void spi_write(uint8_t temp);
void spi_init (void);

 

John Samperi

Ampertronics Pty. Ltd.

www.ampertronics.com.au

* Electronic Design * Custom Products * Contract Assembly

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

I agree with Jim that a good SPI library would be nice. My reason is that I would like a library that automatically use hardware SPI or hardware USI (for the tinys) depending on which MCU is used, plus a bitbang option in case arbitrary pin assignment is more important that shaving a handful of bytes from the code or having fast data transfer. I've written a hardware SPI + hardware USI library that uses the appropriate hardware but sadly the code looks like overcooked stew with all the ifdef's used.

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

Ignore John's 'function'.   You only need one:  e.g.

uint8_t spi(uint8_t temp)
{
	SPDR = temp;
	while(!(SPSR & (1<<SPIF)));
	return SPDR;
}

Since you get a reply for every write,    you don't want to think of spi_write() and spi_read() as separate operations.

 

Regarding the initialisation:    All that is important is to make SS an output if you are Master.    And set the DDRs accordingly for SCK, MOSI, MISO.

 

If you omit the SS output step,    your AVR will go haywire.   

 

All the same,   the whole procedure is only a few lines.

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

Hard to think of a library for something so simple.

If you don't know my whole story, keep your mouth shut.

If you know my whole story, you're an accomplice. Keep your mouth shut. 

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

AFAIK the *only* complexity to SPI is getting the right one of the four CPOL/CPHA combinations. I don't see the need for a "library" otherwise. There's one line of init to set SPCR then the three lines David showed for the tx/rx routine. And that's it. 

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

SPI gets complex when there are 2+ devices competing to use the SPI. Especially if an ISR uses the SPI assuming there's no competition for the hardware SPI.

There is a proposed semi-solution for this in the Arduino beta world, and Teensy 3 world.

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

I2C might use "General Call Address" or 1-Wire might use "Search ROM"

but SPI has a dedicated /CS pin for each Slave.

 

So you never have a conflict.

 

David.
 

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

I thought that it would be handy to have some mode constants that could be loaded into the appropriate register, named according to the common SPI mode names.

 

And, yes, a read and a write function. Read would automatically load data register with something benign and return a byte at the end. Write would send the function argument and ignore what ever came back. 

 

So, yes, all these are pretty simple, just the principle of not reinventing the wheel, yadda yadda. 

 

OK, I turn the magic code generating crank and be done with it. After I figure out what mode it needs to be.

 

BUT - a couple of questions:

 

(a) This is an SPI master with TWO slaves. One slave will use /SS as chip select. Is there anything to watch out for on the other chip select? Can it be any GPIO pin?

 

(b) I don't know, yet, if this is the case, but.... what do you do if the two devices use different modes? Do you just hold both chip selects high and change it? Anything that should be flushed after the change?

 

(c) This is on a Mega328 that shares SPI with ISP. I have added two very weak pull-ups (100K) to hold the chip selects inactive while the chip is in reset. Is this a recommended strategy or am I obsessing about nothing?

 

Thanks

Jim

Jim Wagner Oregon Research Electronics, Consulting Div. Tangent, OR, USA http://www.orelectronics.net

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

Can it be any GPIO pin?

Yes, as long as the /SS pin is an output.

 

what do you do if the two devices use different modes? Do you just hold both chip selects high and change it?

That makes sense but I have never needed to do this as I only ever had one slave.

am I obsessing about nothing?

No, quite correct. The other way would be to have a 1K (or a little higher)  on the MISO line, but keeping the slaves disabled during reset is best.

John Samperi

Ampertronics Pty. Ltd.

www.ampertronics.com.au

* Electronic Design * Custom Products * Contract Assembly

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

 

I'd like to run the clock rate near the upper limit with Fcpu = 8MHz so a series resistor does not sound very attractive. I think that keeping the slaves disabled is a better choice at this point.

 

Thanks

Jim

Jim Wagner Oregon Research Electronics, Consulting Div. Tangent, OR, USA http://www.orelectronics.net

Last Edited: Mon. Oct 6, 2014 - 03:25 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0
// macros for SPI display.   mode#3,   CS on PB2
#define SPCRVAL ((1<<SPE)|(1<<MSTR)|(0<<CPHA)|(0<<SPR0))
#define CSHI    { PORTB |= (1<<2); }
#define CSLO  { SPCR = SPCRVAL; SPDR; PORTB &= ~(1<<2); }

You can use macros, inline functions, or regular functions.    The idea is the same.     You simply set the relevant SPCR value every time that you 'enable' a device.

 

Note that I do a dummy read when changing device.    It is not strictly necessary but only costs 1 or 2 cycles.

 

This is what FATfs does.    i.e. sets its SPI for every access.    And leaves SPI in mode #0 afterwards.

So you must change it back to mode#3 before writing to the display.

 

In practice,    you always use the fastest SCK speed but might use different mode# and very occasionally LSB first.

If you look at the FATfs code,    it probably has a SDSLOW and a SDFAST setting.     (you always initialise a SDCard at slow speed)

e.g.

// macros for SDCard.   mode#0,   CS on PB1
#define SPCRFAST ((1<<SPE)|(1<<MSTR)|(0<<CPHA)|(0<<SPR0))
#define SPCRSLOW ((1<<SPE)|(1<<MSTR)|(0<<CPHA)|(7<<SPR0))
#define SDHI    { PORTB |= (1<<1); }
#define SDSLOW  { SPCR = SPCRSLOW; SPDR; PORTB &= ~(1<<1); }
#define SDFAST  { SPCR = SPCRFAST; SPDR; PORTB &= ~(1<<1); }

If you are using FCPU/2,   your initalisation will have a SPSR=(1<<SPI2X) as well as the DDRx settings.

 

Regarding efficiency.    the 'enable' sequence is going to cost about 7 cycles on a mega328P.    5 cycles on a mega32.

 

Regarding views in the Simulator.     I can only suggest that you learn to live with 'obvious' optimisations.

Even with GCC -O1,   I can't see your csvstr variables.

 

But these sort of basic C exercises are better done on a PC Simulator.

Or perhaps try Codevision (with minimal optimisation)

 

David.

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

David I deleted your other 2 duplicate posts. I there still a problem with the user deleting his own posts? I think someone else mentioned this.

John Samperi

Ampertronics Pty. Ltd.

www.ampertronics.com.au

* Electronic Design * Custom Products * Contract Assembly

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

And, yes, a read and a write function.

Err no. This is the most common mistake people make about SPI. You never just read and you never just write. You always do both. So it doesn't really make a lot of sense to have separate routines. Just have a single spiTranfer() or whatever you prefer to call it:

uint8_t spiTransfer(uint8_t data) {
  SPDR = data;
  while (!(SPSR & (1 << SPIF)));
  return SPDR;
}

If you really insist on having read/write then just base them on this:

#define spiRead() spiTransfer(0xff /*stuffing byte*/)
#define spiWrite(x) spiTransfer(x)

then you can use as something like:

spiWrite(spiRead() + 1);

though the .i file shows this is really:

spiTransfer(spiTransfer(0xff ) + 1);

 

Last Edited: Mon. Oct 6, 2014 - 09:15 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

IME/IMO --

 

1)  In a real AVR app, you definitely want to use a weak pullup/pulldown on each chip select, to keep the device unselected when the AVR is in reset.  That is when ISP is done which uses SPI pins on most AVR8.  Note that it is a pullup for /CS and a pulldown for CS.

 

2)  I see no need for a "driver" for SPI.  I also see no need for three primitive routines (tx, rx, tx+rx).

 

3)  In (my) real world, it isn't unusual to have several SPI devices in one app.  It also isn't unusual (IME) for them to have different settings.  My usual approach is to have one be the "default".  The others set the parameters, do the transaction, and then return to the default after the transaction.  It might well be more straightforward to just set the parameters before each transaction.  It is only one write to SPCR, after all.

 

// SPI initialization
// SPI Type: Master
// SPI Clock Rate: 1843.200 kHz
// SPI Clock Phase: Cycle Start
// SPI Clock Polarity: Low
// SPI Data Order: MSB First

//	CPOL = 0, CPHA = 0 Sample (Rising) Setup (Falling) 0
//	CPOL = 0, CPHA = 1 Setup (Rising) Sample (Falling) 1
//	CPOL = 1, CPHA = 0 Sample (Falling) Setup (Rising) 2
//	CPOL = 1, CPHA = 1 Setup (Falling) Sample (Rising) 3

// Low bits, without SPI2X (low bit of SPSR) set:
//
//	0	0	/4		1843kHz with 7.3728MHz crystal
//	0	1	/16		460kHz
//	1	0	/64		115kHz
//	1	1	/128	58kHz

#define	SPCR_DS1305		((1 << SPE) | (1 << MSTR) | (1 << CPHA))
#define	SPCR_SD_INIT	((1 << SPE) | (1 << MSTR) | (1 << SPR1))
#define	SPCR_SD_RUN		((1 << SPE) | (1 << MSTR))

SPCR = SPCR_DS1305; // for DS1305,
//SPCR = 0x54; // for DS1305,
SPSR=0x00;

 

SPCR = SPCR_DS1305;

// Start the RTC
SELECT_RTC = 1;	// select RTC
spi (0x8f);		// select the Control Register Write
spi (0x00);		// enable oscillator /EOSC, enable write WP, disable alarms
//spi (0x01);		// enable oscillator /EOSC, enable write WP, enable /INT0
SELECT_RTC = 0;	// de-select RTC

if (eecfg.ee_restart_count == 0)
	{
	// Newly programmed.
	// Reset the cycle counter
	// Put the cycle counter
	put_rtc (NVRAM_CYCLES_OFFSET, (unsigned char *)&cycle_counter, NVRAM_CYCLES_WIDTH);
	}

// Get the cycle counter
get_rtc (NVRAM_CYCLES_OFFSET, (unsigned char *)&cycle_counter, NVRAM_CYCLES_WIDTH);
SPCR = SPCR_SD_RUN;	// return to SD card setup as the default

eecfg.ee_restart_count++;

 

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.

Last Edited: Mon. Oct 6, 2014 - 01:52 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

HI HOW ARE YOU?

HOW CAN I HAVE YOUR LIBRARY FOR SPI TO USE IN ATMEL STUDIO IN A PROJECT BY ATMEGA32?

HOW CAN I LEARN IT AND DOWNLOAD IT ?

HOW CAN I AEE A EXAMPLE BY THIS SPI.H LIBRARY?

 

Aasshh

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

Aashkanpasha wrote:
HOW CAN I HAVE YOUR LIBRARY FOR SPI TO USE IN ATMEL STUDIO IN A PROJECT BY ATMEGA32?

Just copy and paste the code above into your project.

 

 

 

 

 

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

Aashkanpasha wrote:
HOW CAN I LEARN IT AND DOWNLOAD IT ?

 

Stop digging up every old thread for starters and stick with one of your own.  Add links to your thread of the old threads you searched for.

 

Jim - moderator

I would rather attempt something great and fail, than attempt nothing and succeed - Fortune Cookie

 

"The critical shortage here is not stuff, but time." - Johan Ekdahl

 

"Step N is required before you can do step N+1!" - ka7ehk

 

"If you want a career with a known path - become an undertaker. Dead people don't sue!" - Kartman

"Why is there a "Highway to Hell" and only a "Stairway to Heaven"? A prediction of the expected traffic load?"  - Lee "theusch"

 

Speak sweetly. It makes your words easier to digest when at a later date you have to eat them ;-)  - Source Unknown

Please Read: Code-of-Conduct

Atmel Studio6.2/AS7, DipTrace, Quartus, MPLAB, RSLogix user

Topic locked