Chapter 7

Buffered SPI

This module provides a buffered interface to the microcontroller SPI in master mode. It utilizes an interrupt service routine to clock out the SPI data asynchronously.

Each data transfer request can include execution of a user-defined function just before the transmission begins and just after the transmission completes. The user-defined functions can do things like enable and disable chip-select lines and change SPI clocking frequencies to allow you to buffer SPI transmissions for multiple SPI devices.

7.1  Usage

To use the SPI module, place the following in your source files:

  #include <spi3.h>

7.2  Functions

void spi3_init(void)   function

This function places the SPI into “master” mode, enables the SPI, and enables the SPI interrupt. The library provides the required interrupt service routine.

bool spi3_clock_data(uint8_t* data, uint8_t size, void (*pre)(void), void (*post)(void), volatile bool* done)   function

This function buffers an SPI data transfer request. A data transfer request consists of:

data
Pointer to the data to exchange.
size
Number of bytes to exchange.
pre
Thunk called by the interrupt service routine before the exchange begins.
post
Thunk called by the interrupt service routine after the exchange ends.
done
Pointer to a Boolean set by the interrupt service routine to true once the exchange ends and the call to post completes.

If there is room in the buffer, the request is buffered and spi3_clock_data() returns true. Otherwise, spi3_clock_data() returns false. The data is clocked onto the SPI bus once all previously buffered requests are completed. Any pending requests placed in the buffer by calls to spi3_pend_data() will no longer be considered pending and they will all eventually be clocked onto the SPI.

The arguments pre, post, and done are optional; passing a NULL causes spi3_clock_data() to ignore that argument.

Note that the memory pointed to by data and done must remain valid until the exchange is complete. For example, it would be an error if data were to point to a location on the stack and that location were to go out of scope while the exchange was taking place.

Note also that the pre and post thunks are called directory from the interrupt service routine.

bool spi3_pend_data(uint8_t* data, uint8_t size, void (*pre)(void), void (*post)(void), volatile bool* done)   function

This functions is similar to spi3_clock_data(), except that the data transfer request is held pending in the buffer without being clocked onto the SPI bus. This function can be called repeatedly to buffer more requests. Pending transfer requests will begin being clocked onto the SPI the next time spi3_clock_data() is called.

This function is useful when a multi-byte exchange needs to take place without interruption, but when it is more convenient to build the multi-byte exchange in pieces.

void spi3_clear_pending(void)   function

This function clears any pending requests that have been placed in the buffer by spi3_pend_data().

7.3  Unbuffered Interface

There is also an unbuffered version of this interface that does not use an interrupt service routine. To use the unbuffered version, place the following in your source files:

  #include <spi3-no-isr.h>

Every function from the buffered interface is available, but each function is prefixed with spi3_no_isr instead of spi3.

Because the data is not buffered, spi3_no_isr_clock_data() and spi3_no_isr_pend_data() both immediately clock out the data to the SPI bus.

void spi3_no_isr_wait(void)   user-defined function

This user defined function is called by spi3_no_isr_clock_data() and spi3_no_isr_pend_data() before placing the next byte into SPDR. At the very least, it should wait for the SPIFflag in SPSRto be set, like this:

void spi3_no_isr_wait(void)
{
    while (! (SPSR & _BV(SPIF)))
        ;
}

By having the wait function be a user-defined function, you can program in an escape or a timeout check, something like this:

void spi3_no_isr_wait(void)
{
    while (! (SPSR & _BV(SPIF)))
        check_timeout();
}