drivers/periph: remodeled SPI driver interface
This commit is contained in:
parent
274ab1cd66
commit
37e0482e35
@ -1,9 +1,9 @@
|
||||
/*
|
||||
* Copyright (C) 2014 Freie Universität Berlin
|
||||
* Copyright (C) 2014-2016 Freie Universität Berlin
|
||||
*
|
||||
* This file is subject to the terms and conditions of the GNU Lesser General
|
||||
* Public License v2.1. See the file LICENSE in the top level directory for more
|
||||
* details.
|
||||
* This file is subject to the terms and conditions of the GNU Lesser
|
||||
* General Public License v2.1. See the file LICENSE in the top level
|
||||
* directory for more details.
|
||||
*/
|
||||
|
||||
/**
|
||||
@ -11,258 +11,316 @@
|
||||
* @ingroup drivers_periph
|
||||
* @brief Low-level SPI peripheral driver
|
||||
*
|
||||
* The current design of this interface targets implementations that use the SPI in blocking mode.
|
||||
* This interface defines an abstraction for using a CPU's hardware SPI units.
|
||||
* The interface only supports SPI master mode.
|
||||
*
|
||||
* TODO: add means for asynchronous SPI usage
|
||||
* As SPI buses can have multiple devices connected to them they are to be
|
||||
* considered as shared resources. To reflect this, the SPI interface is based
|
||||
* on a transaction model. This requires that the bus needs to be acquired
|
||||
* before usage and released afterwards, using the `spi_acquire()` and the
|
||||
* `spi_release()` functions.
|
||||
*
|
||||
* This interface supports both software and hardware chip select lines. This is
|
||||
* reflected by the cpi_cs_t type, which overloads the gpio_t type with platform
|
||||
* specific values for defining platform dependent hardware chip select lines.
|
||||
*
|
||||
* Some devices have however very uncommon requirements on the usage and the
|
||||
* timings of their chip select line. For those cases this interface allows to
|
||||
* manage the chip select line manually from the user code (e.g. by calling
|
||||
* gpio_set/clear explicitly) while deactivating the SPI driver internal chip
|
||||
* select handling by passing @ref GPIO_UNDEF as CS parameter.
|
||||
*
|
||||
* In the time, when the SPI bus is not used, the SPI unit should be in
|
||||
* low-power mode to save energy.
|
||||
*
|
||||
* The SPI unit's initialization is split into 3 parts:
|
||||
* 1. `spi_init()` should be called once for each SPI unit defined by a board
|
||||
* during system initialization.
|
||||
* 2. `spi_init_cs()` should be called during device driver initialization, as
|
||||
* each chip select pin/line is used uniquely by a specific device, i.e. chip
|
||||
* select lines are no shared resource.
|
||||
* 3. `spi_aquire()` needs to be called for each new transaction. This function
|
||||
* configures the bus with specific parameters (clock, mode) for the duration
|
||||
* of that transaction.
|
||||
*
|
||||
* @{
|
||||
* @file
|
||||
* @brief Low-level SPI peripheral driver interface definitions
|
||||
* @brief Low-level SPI peripheral driver interface definition
|
||||
*
|
||||
* @author Hauke Petersen <hauke.petersen@fu-berlin.de>
|
||||
*/
|
||||
|
||||
#ifndef SPI_H
|
||||
#define SPI_H
|
||||
#ifndef PERIPH_SPI_H
|
||||
#define PERIPH_SPI_H
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <limits.h>
|
||||
|
||||
#include "periph_cpu.h"
|
||||
#include "periph_conf.h"
|
||||
#include "periph/gpio.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/* add guard for the case that no SPI device is defined */
|
||||
#if SPI_NUMOF
|
||||
/**
|
||||
* @brief Default SPI device access macro
|
||||
*/
|
||||
#ifndef SPI_DEV
|
||||
#define SPI_DEV(x) (x)
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Definition available SPI devices
|
||||
* @brief Define global value for undefined SPI device
|
||||
*/
|
||||
#ifndef SPI_UNDEF
|
||||
#define SPI_UNDEF (UINT_MAX)
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Define value for unused CS line
|
||||
*/
|
||||
#ifndef SPI_CS_UNDEF
|
||||
#define SPI_CS_UNDEF (GPIO_UNDEF)
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Default SPI hardware chip select access macro
|
||||
*
|
||||
* Per default, we map all hardware chip select lines to be not defined. If an
|
||||
* implementation makes use of HW chip select lines, this value needs to be
|
||||
* overridden by the corresponding CPU.
|
||||
*/
|
||||
#ifndef SPI_HWCS
|
||||
#define SPI_HWCS(x) (SPI_CS_UNDEF)
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Default type for SPI devices
|
||||
*/
|
||||
#ifndef HAVE_SPI_T
|
||||
typedef unsigned int spi_t;
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Chip select pin type overlaps with gpio_t so it can be casted to
|
||||
* this
|
||||
*/
|
||||
#ifndef HAVE_SPI_CS_T
|
||||
typedef gpio_t spi_cs_t;
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Status codes used by the SPI driver interface
|
||||
*/
|
||||
enum {
|
||||
SPI_OK = 0, /**< everything went as planned */
|
||||
SPI_NODEV = -1, /**< invalid SPI bus specified */
|
||||
SPI_NOCS = -2, /**< invalid chip select line specified */
|
||||
SPI_NOMODE = -3, /**< selected mode is not supported */
|
||||
SPI_NOCLK = -4 /**< selected clock value is not supported */
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Available SPI modes, defining the configuration of clock polarity
|
||||
* and clock phase
|
||||
*
|
||||
* RIOT is using the mode numbers as commonly defined by most vendors
|
||||
* (https://en.wikipedia.org/wiki/Serial_Peripheral_Interface_Bus#Mode_numbers):
|
||||
*
|
||||
* - MODE_0: CPOL=0, CPHA=0 - The first data bit is sampled by the receiver on
|
||||
* the first SCK rising SCK edge (this mode is used most often).
|
||||
* - MODE_1: CPOL=0, CPHA=1 - The first data bit is sampled by the receiver on
|
||||
* the second rising SCK edge.
|
||||
* - MODE_2: CPOL=1, CPHA=0 - The first data bit is sampled by the receiver on
|
||||
* the first falling SCK edge.
|
||||
* - MODE_3: CPOL=1, CPHA=1 - The first data bit is sampled by the receiver on
|
||||
* the second falling SCK edge.
|
||||
*/
|
||||
#ifndef HAVE_SPI_MODE_T
|
||||
typedef enum {
|
||||
#if SPI_0_EN
|
||||
SPI_0 = 0, /**< SPI device 0 */
|
||||
SPI_MODE_0 = 0, /**< CPOL=0, CPHA=0 */
|
||||
SPI_MODE_1, /**< CPOL=0, CPHA=1 */
|
||||
SPI_MODE_2, /**< CPOL=1, CPHA=0 */
|
||||
SPI_MODE_3 /**< CPOL=1, CPHA=1 */
|
||||
} spi_mode_t;
|
||||
#endif
|
||||
#if SPI_1_EN
|
||||
SPI_1, /**< SPI device 1 */
|
||||
#endif
|
||||
#if SPI_2_EN
|
||||
SPI_2, /**< SPI device 2 */
|
||||
#endif
|
||||
#if SPI_3_EN
|
||||
SPI_3, /**< SPI device 3 */
|
||||
#endif
|
||||
} spi_t;
|
||||
|
||||
/**
|
||||
* @brief The SPI mode is defined by the four possible combinations of clock polarity and
|
||||
* clock phase.
|
||||
* @{
|
||||
* @brief Available SPI clock speeds
|
||||
*
|
||||
* The actual speed of the bus can vary to some extend, as the combination of
|
||||
* CPU clock and available prescaler values on certain platforms may not make
|
||||
* the exact values possible.
|
||||
*/
|
||||
#ifndef HAVE_SPI_CONF_T
|
||||
#ifndef HAVE_SPI_CLK_T
|
||||
typedef enum {
|
||||
/**
|
||||
* The first data bit is sampled by the receiver on the first SCK edge. The
|
||||
* first edge of SCK is rising. This is sometimes also referred to as SPI
|
||||
* mode 0, or (CPOL=0, CPHA=0).
|
||||
*/
|
||||
SPI_CONF_FIRST_RISING = 0,
|
||||
/**
|
||||
* The first data bit is sampled by the receiver on the second SCK edge. The
|
||||
* first edge of SCK is rising, i.e. the sampling edge is falling. This is
|
||||
* sometimes also referred to as SPI mode 1, or (CPOL=0, CPHA=1).
|
||||
*/
|
||||
SPI_CONF_SECOND_RISING = 1,
|
||||
/**
|
||||
* The first data bit is sampled by the receiver on the first SCK edge. The
|
||||
* first edge of SCK is falling. This is sometimes also referred to as SPI
|
||||
* mode 2, or (CPOL=1, CPHA=0).
|
||||
*/
|
||||
SPI_CONF_FIRST_FALLING = 2,
|
||||
/**
|
||||
* The first data bit is sampled by the receiver on the second SCK edge. The
|
||||
* first edge of SCK is falling, i.e. the sampling edge is rising. This is
|
||||
* sometimes also referred to as SPI mode 3, or (CPOL=1, CPHA=1).
|
||||
*/
|
||||
SPI_CONF_SECOND_FALLING = 3
|
||||
} spi_conf_t;
|
||||
SPI_CLK_100KHZ = 0, /**< drive the SPI bus with 100KHz */
|
||||
SPI_CLK_400KHZ, /**< drive the SPI bus with 400KHz */
|
||||
SPI_CLK_1MHZ, /**< drive the SPI bus with 1MHz */
|
||||
SPI_CLK_5MHZ, /**< drive the SPI bus with 5MHz */
|
||||
SPI_CLK_10MHZ /**< drive the SPI bus with 10MHz */
|
||||
} spi_clk_t;
|
||||
#endif
|
||||
/** @} */
|
||||
|
||||
/**
|
||||
* @brief Define a set of pre-defined SPI clock speeds.
|
||||
* @brief Basic initialization of the given SPI bus
|
||||
*
|
||||
* The actual speed of the bus can vary to some extend, as the combination of CPU clock and
|
||||
* available prescale values on certain platforms may not make the exact values possible.
|
||||
* This function does the basic initialization including pin configuration for
|
||||
* MISO, MOSI, and CLK pins. After initialization, the given device should be
|
||||
* in power down state.
|
||||
*
|
||||
* @{
|
||||
* This function is intended to be called by the board initialization code
|
||||
* during system startup to prepare the (shared) SPI device for further usage.
|
||||
* It uses the board specific initialization parameters as defined in the
|
||||
* board's `periph_conf.h`.
|
||||
*
|
||||
* Errors (e.g. invalid @p bus parameter) are not signaled through a return
|
||||
* value, but should be signaled using the assert() function internally.
|
||||
*
|
||||
* @note This function MUST not be called more than once per bus!
|
||||
*
|
||||
* @param[in] bus SPI device to initialize
|
||||
*/
|
||||
#ifndef HAVE_SPI_SPEED_T
|
||||
typedef enum {
|
||||
SPI_SPEED_100KHZ = 0, /**< drive the SPI bus with 100KHz */
|
||||
SPI_SPEED_400KHZ, /**< drive the SPI bus with 400KHz */
|
||||
SPI_SPEED_1MHZ, /**< drive the SPI bus with 1MHz */
|
||||
SPI_SPEED_5MHZ, /**< drive the SPI bus with 5MHz */
|
||||
SPI_SPEED_10MHZ /**< drive the SPI bus with 10MHz */
|
||||
} spi_speed_t;
|
||||
#endif
|
||||
/** @} */
|
||||
void spi_init(spi_t bus);
|
||||
|
||||
/**
|
||||
* @brief Initialize the given SPI device to work in master mode
|
||||
* @brief Initialize the used SPI bus pins, i.e. MISO, MOSI, and CLK
|
||||
*
|
||||
* In master mode the SPI device is configured to control the SPI bus. This means the device
|
||||
* will start and end all communication on the bus and control the CLK line. For transferring
|
||||
* data on the bus the below defined transfer functions should be used.
|
||||
*
|
||||
* @param[in] dev SPI device to initialize
|
||||
* @param[in] conf Mode of clock phase and clock polarity
|
||||
* @param[in] speed desired clock speed for driving the SPI bus
|
||||
* After calling spi_init, the pins must be initialized (i.e. spi_init is
|
||||
* calling) this function internally. In normal cases, this function will not be
|
||||
* used. But there are some devices (e.g. CC110x), that use SPI bus lines also
|
||||
* for other purposes and need the option to dynamically re-configure one or
|
||||
* more of the used pins. So they can take control over certain pins and return
|
||||
* control back to the SPI driver using this function.
|
||||
*
|
||||
* @return 0 on success
|
||||
* @return -1 on unavailable speed value
|
||||
* @return -2 on other errors
|
||||
* The pins used are configured in the board's periph_conf.h.
|
||||
*
|
||||
* @param[in] bus SPI device the pins are configure for
|
||||
*/
|
||||
int spi_init_master(spi_t dev, spi_conf_t conf, spi_speed_t speed);
|
||||
void spi_init_pins(spi_t bus);
|
||||
|
||||
/**
|
||||
* @brief Initialize the given SPI device to work in slave mode
|
||||
* @brief Initialize the given chip select pin
|
||||
*
|
||||
* In slave mode the SPI device is purely reacting to the bus. Transaction will be started and
|
||||
* ended by a connected SPI master. When a byte is received, the callback is called in interrupt
|
||||
* context with this byte as argument. The return byte of the callback is transferred to the
|
||||
* master in the next transmission cycle. This interface enables easy implementation of a register
|
||||
* based access paradigm for the SPI slave.
|
||||
* The chip select can be any generic GPIO pin (e.g. GPIO_PIN(x,y)), or it can
|
||||
* be a hardware chip select line. The existence and number of of hardware chip
|
||||
* select lines depends on the underlying platform and the actual pins used for
|
||||
* hardware chip select lines are defined in the board's `periph_conf.h`.
|
||||
*
|
||||
* @param[in] dev The SPI device to initialize as SPI slave
|
||||
* @param[in] conf Mode of clock phase and polarity
|
||||
* @param[in] cb callback called every time a byte was received
|
||||
* Define the used chip select line using the @ref SPI_HWCS(x) macro for
|
||||
* hardware chip select line `x` or the GPIO_PIN(x,y) macro for using any
|
||||
* GPIO pin for manual chip select.
|
||||
*
|
||||
* @return 0 on success
|
||||
* @return -1 on error
|
||||
* @param[in] bus SPI device that is used with the given CS line
|
||||
* @param[in] cs chip select pin to initialize
|
||||
*
|
||||
* @return SPI_OK on success
|
||||
* @return SPI_NODEV on invalid device
|
||||
* @return SPI_NOCS on invalid CS pin/line
|
||||
*/
|
||||
int spi_init_slave(spi_t dev, spi_conf_t conf, char (*cb)(char data));
|
||||
int spi_init_cs(spi_t bus, spi_cs_t cs);
|
||||
|
||||
/**
|
||||
* @brief Configure SCK, MISO and MOSI pins for the given SPI device
|
||||
* @brief Start a new SPI transaction
|
||||
*
|
||||
* @param[in] dev SPI device to use
|
||||
* Starting a new SPI transaction will get exclusive access to the SPI bus
|
||||
* and configure it according to the given values. If another SPI transaction
|
||||
* is active when this function is called, this function will block until the
|
||||
* other transaction is complete (spi_relase was called).
|
||||
*
|
||||
* @return 0 on success
|
||||
* @return -1 on error
|
||||
* @note This function expects the @p bus and the @p cs parameters to be
|
||||
* valid (they are checked in spi_init and spi_init_cs before)
|
||||
*
|
||||
* @param[in] bus SPI device to access
|
||||
* @param[in] cs chip select pin/line to use, set to SPI_CS_UNDEF if chip
|
||||
* select should not be handled by the SPI driver
|
||||
* @param[in] mode mode to use for the new transaction
|
||||
* @param[in] clk bus clock speed to use for the transaction
|
||||
*
|
||||
* @return SPI_OK on success
|
||||
* @return SPI_NOMODE if given mode is not supported
|
||||
* @return SPI_NOCLK if given clock speed is not supported
|
||||
*/
|
||||
int spi_conf_pins(spi_t dev);
|
||||
int spi_acquire(spi_t bus, spi_cs_t cs, spi_mode_t mode, spi_clk_t clk);
|
||||
|
||||
/**
|
||||
* @brief Get mutually exclusive access to the given SPI bus
|
||||
* @brief Finish an ongoing SPI transaction by releasing the given SPI bus
|
||||
*
|
||||
* In case the SPI device is busy, this function will block until the bus is free again.
|
||||
* After release, the given SPI bus should be fully powered down until acquired
|
||||
* again.
|
||||
*
|
||||
* @param[in] dev SPI device to access
|
||||
*
|
||||
* @return 0 on success
|
||||
* @return -1 on error
|
||||
* @param[in] bus SPI device to release
|
||||
*/
|
||||
int spi_acquire(spi_t dev);
|
||||
|
||||
/**
|
||||
* @brief Release the given SPI device to be used by others
|
||||
*
|
||||
* @param[in] dev SPI device to release
|
||||
*
|
||||
* @return 0 on success
|
||||
* @return -1 on error
|
||||
*/
|
||||
int spi_release(spi_t dev);
|
||||
void spi_release(spi_t bus);
|
||||
|
||||
/**
|
||||
* @brief Transfer one byte on the given SPI bus
|
||||
*
|
||||
* @param[in] dev SPI device to use
|
||||
* @param[in] out Byte to send out, set NULL if only receiving
|
||||
* @param[out] in Byte to read, set NULL if only sending
|
||||
* @param[in] bus SPI device to use
|
||||
* @param[in] cs chip select pin/line to use, set to SPI_CS_UNDEF if chip
|
||||
* select should not be handled by the SPI driver
|
||||
* @param[in] cont if true, keep device selected after transfer
|
||||
* @param[in] out byte to send out, set NULL if only receiving
|
||||
*
|
||||
* @return Number of bytes that were transfered
|
||||
* @return -1 on error
|
||||
* @return the received byte
|
||||
*/
|
||||
int spi_transfer_byte(spi_t dev, char out, char *in);
|
||||
uint8_t spi_transfer_byte(spi_t bus, spi_cs_t cs, bool cont, uint8_t out);
|
||||
|
||||
/**
|
||||
* @brief Transfer a number bytes on the given SPI bus
|
||||
* @brief Transfer a number bytes using the given SPI bus
|
||||
*
|
||||
* @param[in] dev SPI device to use
|
||||
* @param[in] out Array of bytes to send, set NULL if only receiving
|
||||
* @param[out] in Buffer to receive bytes to, set NULL if only sending
|
||||
* @param[in] length Number of bytes to transfer
|
||||
*
|
||||
* @return Number of bytes that were transfered
|
||||
* @return -1 on error
|
||||
* @param[in] bus SPI device to use
|
||||
* @param[in] cs chip select pin/line to use, set to SPI_CS_UNDEF if chip
|
||||
* select should not be handled by the SPI driver
|
||||
* @param[in] cont if true, keep device selected after transfer
|
||||
* @param[in] out buffer to send data from, set NULL if only receiving
|
||||
* @param[out] in buffer to read into, set NULL if only sending
|
||||
* @param[in] len number of bytes to transfer
|
||||
*/
|
||||
int spi_transfer_bytes(spi_t dev, char *out, char *in, unsigned int length);
|
||||
void spi_transfer_bytes(spi_t bus, spi_cs_t cs, bool cont,
|
||||
const void *out, void *in, size_t len);
|
||||
|
||||
/**
|
||||
* @brief Transfer one byte to/from a given register address
|
||||
* @brief Transfer one byte to/from a given register address
|
||||
*
|
||||
* This function is a shortcut function for easier handling of register based SPI devices. As
|
||||
* many SPI devices use a register based addressing scheme, this function is a convenient short-
|
||||
* cut for interfacing with such devices.
|
||||
* This function is a shortcut function for easier handling of SPI devices that
|
||||
* implement a register based access scheme.
|
||||
*
|
||||
* @param[in] dev SPI device to use
|
||||
* @param[in] reg Register address to transfer data to/from
|
||||
* @param[in] out Byte to send, set NULL if only receiving data
|
||||
* @param[out] in Byte to read, set NULL if only sending
|
||||
* @param[in] bus SPI device to use
|
||||
* @param[in] cs chip select pin/line to use, set to SPI_CS_UNDEF if chip
|
||||
* select should not be handled by the SPI driver
|
||||
* @param[in] reg register address to transfer data to/from
|
||||
* @param[in] out byte to send, set NULL if only receiving data
|
||||
*
|
||||
* @return Number of bytes that were transfered
|
||||
* @return -1 on error
|
||||
* @return value that was read from the given register address
|
||||
*/
|
||||
int spi_transfer_reg(spi_t dev, uint8_t reg, char out, char *in);
|
||||
uint8_t spi_transfer_reg(spi_t bus, spi_cs_t cs, uint8_t reg, uint8_t out);
|
||||
|
||||
/**
|
||||
* @brief Transfer a number of bytes from/to a given register address
|
||||
* @brief Transfer a number of bytes to/from a given register address
|
||||
*
|
||||
* This function is a shortcut function for easier handling of register based SPI devices. As
|
||||
* many SPI devices use a register based addressing scheme, this function is a convenient short-
|
||||
* cut for interfacing with such devices.
|
||||
* This function is a shortcut function for easier handling of SPI devices that
|
||||
* implement a register based access scheme.
|
||||
*
|
||||
* @param[in] dev SPI device to use
|
||||
* @param[in] reg Register address to transfer data to/from
|
||||
* @param[in] out Byte array to send data from, set NULL if only receiving
|
||||
* @param[out] in Byte buffer to read into, set NULL if only sending
|
||||
* @param[in] length Number of bytes to transfer
|
||||
*
|
||||
* @return Number of bytes that were transfered
|
||||
* @return -1 on error
|
||||
* @param[in] bus SPI device to use
|
||||
* @param[in] cs chip select pin/line to use, set to SPI_CS_UNDEF if chip
|
||||
* select should not be handled by the SPI driver
|
||||
* @param[in] reg register address to transfer data to/from
|
||||
* @param[in] out buffer to send data from, set NULL if only receiving
|
||||
* @param[out] in buffer to read into, set NULL if only sending
|
||||
* @param[in] len number of bytes to transfer
|
||||
*/
|
||||
int spi_transfer_regs(spi_t dev, uint8_t reg, char *out, char *in, unsigned int length);
|
||||
|
||||
/**
|
||||
* @brief Tell the SPI driver that a new transaction was started. Call only when SPI in slave mode!
|
||||
*
|
||||
* @param[in] dev SPI device that is active
|
||||
* @param[in] reset_val The byte that is send to the master as first byte
|
||||
*/
|
||||
void spi_transmission_begin(spi_t dev, char reset_val);
|
||||
|
||||
/**
|
||||
* @brief Power on the given SPI device
|
||||
*
|
||||
* @param[in] dev SPI device to power on
|
||||
*/
|
||||
void spi_poweron(spi_t dev);
|
||||
|
||||
/**
|
||||
* @brief Power off the given SPI device
|
||||
*
|
||||
* @param[in] dev SPI device to power off
|
||||
*/
|
||||
void spi_poweroff(spi_t dev);
|
||||
|
||||
#endif /* SPI_NUMOF */
|
||||
void spi_transfer_regs(spi_t bus, spi_cs_t cs, uint8_t reg,
|
||||
const void *out, void *in, size_t len);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* SPI_H */
|
||||
#endif /* PERIPH_SPI_H */
|
||||
/** @} */
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user