Driver for SPI basic functionality.
v0.0.0.1 Initial Commit
Transmission of a byte or block of bytes
Reception of a byte or block of bytes
Optional support for open()/close() interface to allow multiple processes share one SPI bus
Master polled mode
Slave polled mode
Master interrupt-driven mode
Slave interrupt-driven mode
The SPI Basic driver is configured using START. The user can choose from the four modes of operation (master/slave, polled/IRQ). START also allows the user to select SCK frequency, data order, data polarity and data phase.
START allows the user to select whether or not to include an open()/close() interface for the SPI. If this interface is included, the SPI must be opened by calling <component_name>_open() before it can be used. After use, the SPI is closed by calling <component_name>_close(). A call to <component_name>_open() for a SPI which is already open will return a false value, indicating that the SPI is already busy on behalf of another process.
If several SPI hardware instances are available on the device, the user can select which SPI instance the driver shall use.
When in master mode, the SPI generates the SCK clock. Data is driven out on the MOSI I/O pin according to the configured phase and polarity.
When in slave mode, the SPI receives the SCK clock from the master and uses this to sample data on the MOSI I/O pin. The phase and polarity must be configured according to the values used by the master.
transmitting and receiving one byte of data
transmitting and receiving one block of data. The same buffer holds both data to be transmitted abd received data. The received bytes overwrites the transmitted bytes after they have been transmitted.
transmitting one block of data. Received bytes are discarded.
receiving one block of data. A number of bytes identical to the block size is shifted out of the SPI. These bytes have the value 0x00.
All these functions are blocking: The functions will not return before the operations have completed.
transmitting and receiving one byte of data
transmitting and receiving one block of data. The same buffer holds both data to be transmitted abd received data. The received bytes overwrites the transmitted bytes after they have been transmitted.
transmitting one block of data. Received bytes are discarded.
receiving one block of data. A number of bytes identical to the block size is shifted out of the SPI. These bytes have the value 0x00.
check if SPI bus is IDLE.
check if SPI bus is BUSY.
check if SPI bus is DONE.
The functions checking the SPI bus status are included in the interrupt driven driver to allow the application to know the state of the SPI transfer. They are not needed in the polled driver since this driver blocks until the operation has completed.
The SPI bus is in one of the states listed in spi_transfer_status_t typedef. The bus state is used by the exchange one byte function: This function will wait until the SPI bus is no longer BUSY before exchanging a byte. This allows the exchange byte function to work seamlessly with the exchange, receive and transmit block functions.
In IRQ mode, the ISR can be configured to call a callback function before exiting. This is done by calling the function <component_name>_register_callback() with a pointer to the callback function as parameter. Registering a NULL pointer as callback causes no callback function to be called. NULL is the default value of the callback function pointer.
In master mode: Release SS after transfer as finished.
In slave mode: Implement a chosen SPI protocol, setting up next transfer after the previous one (i.e. data transfer phase after control/command phase).
A SPI master usually controls a Slave Select pin to choose the slave to communicate with. The SPI driver does not manipulate the Slave Select (SS_bar) I/O pin. If the user wants to control SS_bar before and after a transfer, this has to be done using normal I/O port operations.
The <component_name>_open()-function takes a parameter, namely the name of the configuration to use when opening the SPI. A configuration is a set of SPI-related parameters such as SCK frequency, SPI phase and polarity etc. The parameters controlled by a configuration depends on the underlying hardware the SPI Basic driver uses.
Different configurations allows the SPI to use one setup when communicating with one SPI slave, and another configuration when communicating with another slave. As an example, assume a SPI system with one master and two slaves SLAVE_A and SLAVE_B.
SLAVE_A is slow, so requires a slow SCK rate for communication
SLAVE_B is fast, so can use a fast SCK rate for communication
To open a connection to SLAVE_A, the master would call <component_name>_open(SLAVE_A), to open a connection to SLAVE_B, <component_name>_open(SLAVE_B) would be called. These calls would reconfigure the SPI hardware appropriately.
The SPI Basic driver provides one configuration named DEFAULT. This is the configuration that the user configured in START. The user can provide additional configurations by modifying SPI__BASIC_8C.html and SPI__BASIC_8H.html appropriately.
The SPI Basic driver usually uses some sort of hardware that implements SPI functionality, even though it is possible to implement a software SPI implemented by bit-banging I/O pins.
Different MCUs have SPI hardware with different names and functionalities, such as UART, SPI, USI etc. In START, the user selects a device and adds the SPI Basic driver. A device may have several possible hardware resources available for the driver, such as SPI0, SPI1 etc. In this case the user must select which one to use.
The configuration options in START displays options that are dependent on the hardware used to implement the SPI driver. For example, an option may allow changing the baud rate used to drive the underlying SPI hardware.
The interrupt-driven configurations use the interrupt functionality of the underlying SPI hardware. Make sure that global interrupts are enabled (using sei()) and that the Interrupt Controller, if present, is configured so that any interrupts are serviced correctly.
#include <stdio.h>
#include <string.h>
#include <atmel_start.h>
uint8_t buffer[16] = "data";
volatile int8_t status = 0; // Just to keep pass() and fail() from being optimized away
void fail(void);
void drive_slave_select(void);
void release_slave_select(void);
void fail(void);
void pass(void);
void drive_slave_select_low(){
// Control GPIO to drive SS_bar low
}
void drive_slave_select_high(){
// Control GPIO to drive SS_bar high
}
void fail(){
status = -1;
while(1);
}
void pass(){
status = 1;
while(1);
}
int main(void)
{
/* Initializes MCU, drivers and middleware */
atmel_start_init();
// Register callback function releasing SS when transfer is complete
SPI_0_register_callback(drive_slave_select_high);
// If SPI Basic driver is in IRQ-mode, enable global interrupts.
sei();
// Test driver functions, assumes that the SPI MISO and MOSI pins
// have been looped back, and that the open/close interface was enabled
// during configuration in START
if(!SPI_0_open(SPI_0_DEFAULT))
// Not able to open SPI, call fail() or optionally do something
// else waiting for the SPI to become free
fail();
drive_slave_select_low();
SPI_0_exchange_block(buffer, sizeof(buffer));
while(SPI_0_status_busy()); // Wait for the transfer to complete
SPI_0_close();
// Check that the correct data was received
if (strncmp((char*)buffer, "data", strlen("data")))
fail();
// If we get here, everything was OK
pass();
/* Replace with your application code */
while (1) {
}
}