rt-thread-official/bsp/samd21/sam_d2x_asflib/sam0/drivers/spi/spi.c

867 lines
26 KiB
C
Raw Normal View History

2017-08-30 12:18:28 +08:00
/**
* \file
*
* \brief Serial Peripheral Interface Driver for SAMB11
*
* Copyright (C) 2015-2016 Atmel Corporation. All rights reserved.
*
* \asf_license_start
*
* \page License
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* 3. The name of Atmel may not be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* 4. This software may only be redistributed and used in connection with an
* Atmel microcontroller product.
*
* THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE
* EXPRESSLY AND SPECIFICALLY DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
* \asf_license_stop
*
*/
/*
* Support and FAQ: visit <a href="http://www.atmel.com/design-support/">Atmel Support</a>
*/
#include "spi.h"
/**
* \brief Determines if the SPI module is currently synchronizing to the bus.
*
* This function will check if the underlying hardware peripheral module is
* currently synchronizing across multiple clock domains to the hardware bus.
* This function can be used to delay further operations on the module until it
* is ready.
*
* \param[in] module SPI hardware module
*
* \return Synchronization status of the underlying hardware module
* \retval true Module synchronization is ongoing
* \retval false Module synchronization is not ongoing
*
*/
static bool _spi_is_active(Spi *const spi_module)
{
Assert(spi_module);
return spi_module->SPI_BUS_STATUS.bit.SPI_ACTIVE;
}
/**
* \internal Enable SPI clock.
*
* This function will enable SPI clock.
*
* \param[in] module Pointer to the software instance struct
*/
static void _spi_clock_enable(struct spi_module *const module)
{
Assert(module);
Spi *const spi_module = (module->hw);
if (spi_module == (void *)SPI0) {
system_clock_peripheral_enable(PERIPHERAL_SPI0_SCK_CLK);
system_clock_peripheral_enable(PERIPHERAL_SPI0_SCK_PHASE);
system_clock_peripheral_enable(PERIPHERAL_SPI0_IF);
system_clock_peripheral_enable(PERIPHERAL_SPI0_CORE);
} else if (spi_module == (void *)SPI1) {
system_clock_peripheral_enable(PERIPHERAL_SPI1_SCK_CLK);
system_clock_peripheral_enable(PERIPHERAL_SPI1_SCK_PHASE);
system_clock_peripheral_enable(PERIPHERAL_SPI1_IF);
system_clock_peripheral_enable(PERIPHERAL_SPI1_CORE);
}
}
/**
* \internal Disable SPI clock.
*
* This function will disable SPI clock.
*
* \param[in] module Pointer to the software instance struct
*/
static void _spi_clock_disable(struct spi_module *const module)
{
Assert(module);
Spi *const spi_module = (module->hw);
if (spi_module == (void *)SPI0) {
system_clock_peripheral_disable(PERIPHERAL_SPI0_SCK_CLK);
system_clock_peripheral_disable(PERIPHERAL_SPI0_SCK_PHASE);
system_clock_peripheral_disable(PERIPHERAL_SPI0_IF);
system_clock_peripheral_disable(PERIPHERAL_SPI0_CORE);
} else if (spi_module == (void *)SPI1) {
system_clock_peripheral_disable(PERIPHERAL_SPI1_SCK_CLK);
system_clock_peripheral_disable(PERIPHERAL_SPI1_SCK_PHASE);
system_clock_peripheral_disable(PERIPHERAL_SPI1_IF);
system_clock_peripheral_disable(PERIPHERAL_SPI1_CORE);
}
}
/**
* \internal Writes an SPI configuration to the hardware module.
*
* This function will write out a given configuration to the hardware module.
* Can only be done when the module is disabled.
*
* \param[in] module Pointer to the software instance struct
* \param[in] config Pointer to the configuration struct
*
* \return The status of the configuration
* \retval STATUS_ERR_INVALID_ARG If invalid argument(s) were provided
* \retval STATUS_OK If the configuration was written
*/
static enum status_code _spi_set_config(
struct spi_module *const module,
const struct spi_config *const config)
{
Assert(module);
Assert(config);
Spi *const spi_module = (module->hw);
module->mode = config->mode;
#if CONF_SPI_MASTER_ENABLE == true
/* Find baud value and write it */
if (config->mode == SPI_MODE_MASTER) {
spi_module->SPI_CLK_DIVIDER.reg = config->clock_divider;
}
#endif
/* Set data order */
if (config->data_order == SPI_DATA_ORDER_LSB) {
spi_module->SPI_CONFIGURATION.bit.LSB_FIRST_ENABLE = 0x1;
} else {
spi_module->SPI_CONFIGURATION.bit.LSB_FIRST_ENABLE = 0x0;
}
/* Set clock polarity and clock phase */
switch(config->transfer_mode)
{
case SPI_TRANSFER_MODE_0:
spi_module->SPI_CONFIGURATION.bit.SCK_PHASE = 0x0;
spi_module->SPI_CONFIGURATION.bit.SCK_POLARITY = 0x0;
break;
case SPI_TRANSFER_MODE_1:
spi_module->SPI_CONFIGURATION.bit.SCK_PHASE = 0x1;
spi_module->SPI_CONFIGURATION.bit.SCK_POLARITY = 0x0;
break;
case SPI_TRANSFER_MODE_2:
spi_module->SPI_CONFIGURATION.bit.SCK_PHASE = 0x0;
spi_module->SPI_CONFIGURATION.bit.SCK_POLARITY = 0x1;
break;
case SPI_TRANSFER_MODE_3:
spi_module->SPI_CONFIGURATION.bit.SCK_PHASE = 0x1;
spi_module->SPI_CONFIGURATION.bit.SCK_POLARITY = 0x1;
break;
default:
break;
}
return STATUS_OK;
}
/**
* \brief Checks if the SPI in master mode has shifted out last data, or if the
* master has ended the transfer in slave mode.
*
* This function will check if the SPI master module has shifted out last data,
* or if the slave select pin has been drawn high by the master for the SPI
* slave module.
*
* \param[in] module Pointer to the software instance struct
*
* \return Indication of whether any writes are ongoing
* \retval true If the SPI master module has shifted out data, or slave select
* has been drawn high for SPI slave
* \retval false If the SPI master module has not shifted out data
*/
static inline bool _spi_is_write_complete(
Spi *const spi_module)
{
Assert(spi_module);
/* Check interrupt flag */
return (spi_module->TRANSMIT_STATUS.bit.TX_FIFO_EMPTY);
}
/**
* \brief Checks if the SPI module is ready to write data
*
* This function will check if the SPI module is ready to write data.
*
* \param[in] module Pointer to the software instance struct
*
* \return Indication of whether the module is ready to read data or not
* \retval true If the SPI module is ready to write data
* \retval false If the SPI module is not ready to write data
*/
static inline bool _spi_is_ready_to_write(
Spi *const spi_module)
{
Assert(spi_module);
/* Check interrupt flag */
return (spi_module->TRANSMIT_STATUS.bit.TX_FIFO_NOT_FULL);
}
/**
* \brief Checks if the SPI module is ready to read data
*
* This function will check if the SPI module is ready to read data.
*
* \param[in] module Pointer to the software instance struct
*
* \return Indication of whether the module is ready to read data or not
* \retval true If the SPI module is ready to read data
* \retval false If the SPI module is not ready to read data
*/
static inline bool _spi_is_ready_to_read(
Spi *const spi_module)
{
Assert(spi_module);
/* Check interrupt flag */
return (spi_module->RECEIVE_STATUS.bit.RX_FIFO_NOT_EMPTY);
}
/**
* \brief Initializes an SPI peripheral slave device configuration structure to default values
*
* This function will initialize a given SPI slave device configuration
* structure to a set of known default values. This function should be called
* on any new instance of the configuration structures before being modified by
* the user application.
*
* The default configuration is as follows:
* \li Slave Select on GPIO pin 12
* \li Addressing not enabled
*
* \param[out] config Configuration structure to initialize to default values
*/
void spi_slave_inst_get_config_defaults(
struct spi_slave_inst_config *const config)
{
Assert(config);
config->ss_pin = PIN_LP_GPIO_12;
config->address_enabled = false;
config->address = 0;
}
/**
* \brief Initializes an SPI configuration structure to default values
*
* This function will initialize a given SPI configuration structure to a set
* of known default values. This function should be called on any new
* instance of the configuration structures before being modified by the
* user application.
*
* The default configuration is as follows:
* \li Master mode enabled
* \li MSB of the data is transmitted first
* \li Transfer mode 0
* \li MUX Setting 0
* \li Character size 8 bit
* \li Not enabled in sleep mode
* \li Receiver enabled
* \li Baudrate 50000
* \li Default pinmux settings for all pads
* \li Clock source 0 (26MHz)
* \li Clock divider (Formula: baud_rate = ((clock input freq/clock_divider+1)/2))
* (For Example: if clock source is CLOCK_INPUT_0 then
* ((26000000/(129+1))/2) = 100000 bps)
*
* \param[in,out] config Configuration structure to initialize to default values
*/
void spi_get_config_defaults(
struct spi_config *const config)
{
Assert(config);
config->mode = SPI_MODE_MASTER;
config->data_order = SPI_DATA_ORDER_MSB;
config->transfer_mode = SPI_TRANSFER_MODE_0;
config->clock_source = SPI_CLK_INPUT_0;
config->clock_divider = 129;
config->pin_number_pad[0] = PIN_LP_GPIO_10;
config->pin_number_pad[1] = PIN_LP_GPIO_11;
config->pin_number_pad[2] = PIN_LP_GPIO_12;
config->pin_number_pad[3] = PIN_LP_GPIO_13;
config->pinmux_sel_pad[0] = MUX_LP_GPIO_10_SPI0_SCK;
config->pinmux_sel_pad[1] = MUX_LP_GPIO_11_SPI0_MOSI;
config->pinmux_sel_pad[2] = MUX_LP_GPIO_12_SPI0_SSN;
config->pinmux_sel_pad[3] = MUX_LP_GPIO_13_SPI0_MISO;
};
/**
* \brief Attaches an SPI peripheral slave
*
* This function will initialize the software SPI peripheral slave, based on
* the values of the config struct. The slave can then be selected and
* optionally addressed by the \ref spi_select_slave function.
*
* \param[out] slave Pointer to the software slave instance struct
* \param[in] config Pointer to the config struct
*
*/
void spi_attach_slave(
struct spi_slave_inst *const slave,
struct spi_slave_inst_config *const config)
{
Assert(slave);
Assert(config);
slave->ss_pin = config->ss_pin;
slave->address_enabled = config->address_enabled;
slave->address = config->address;
struct gpio_config config_gpio;
gpio_get_config_defaults(&config_gpio);
config_gpio.direction = GPIO_PIN_DIR_OUTPUT;
gpio_pin_set_config(slave->ss_pin, &config_gpio);
gpio_pin_set_output_level(slave->ss_pin, true);
}
/**
* \brief Resets the SPI module
*
* This function will reset the SPI module to its power on default values and
* disable it.
*
* \param[in,out] module Pointer to the software instance struct
*/
void spi_reset(struct spi_module *const module)
{
/* Sanity check arguments */
Spi *const spi_module = (module->hw);
/* Disable the module */
spi_disable(module);
/* Software reset the module */
if(spi_module == (void *)SPI0) {
system_peripheral_reset(PERIPHERAL_SPI0_CORE);
system_peripheral_reset(PERIPHERAL_SPI0_IF);
} else if (spi_module == (void *)SPI1) {
system_peripheral_reset(PERIPHERAL_SPI1_CORE);
system_peripheral_reset(PERIPHERAL_SPI1_IF);
}
}
/**
* \brief Initializes the SPI module
*
* This function will initialize the SPI module, based on the values
* of the config struct.
*
* \param[out] module Pointer to the software instance struct
* \param[in] hw Pointer to hardware instance
* \param[in] config Pointer to the config struct
*
* \return Status of the initialization
* \retval STATUS_OK Module initiated correctly
* \retval STATUS_ERR_DENIED If module is enabled
* \retval STATUS_BUSY If module is busy resetting
* \retval STATUS_ERR_INVALID_ARG If invalid argument(s) were provided
*/
enum status_code spi_init(
struct spi_module *const module,
Spi *const hw,
const struct spi_config *const config)
{
/* Sanity check arguments */
Assert(module);
Assert(hw);
Assert(config);
uint8_t idx;
/* Initialize device instance */
module->hw = hw;
Spi *const spi_module = (module->hw);
/* Check if module is enabled. */
if (spi_module->SPI_MODULE_ENABLE.reg & SPI_MODULE_ENABLE_MASK) {
spi_module->SPI_MODULE_ENABLE.reg = (0x0ul << SPI_MODULE_ENABLE_ENABLE_Pos);
}
spi_reset(module);
_spi_clock_enable(module);
#if SPI_CALLBACK_MODE == true
if (module->hw == SPI0) {
_spi_instances[0] = module;
system_register_isr(RAM_ISR_TABLE_SPIRX0_INDEX, (uint32_t)spi_rx0_isr_handler);
system_register_isr(RAM_ISR_TABLE_SPITX0_INDEX, (uint32_t)spi_tx0_isr_handler);
} else if (module->hw == SPI1) {
_spi_instances[1] = module;
system_register_isr(RAM_ISR_TABLE_SPIRX1_INDEX, (uint32_t)spi_rx1_isr_handler);
system_register_isr(RAM_ISR_TABLE_SPITX1_INDEX, (uint32_t)spi_tx1_isr_handler);
}
#endif
//Program the pinmux.
struct gpio_config config_gpio;
gpio_get_config_defaults(&config_gpio);
/* Set the pinmux for this spi module. */
for(idx = 0; idx < 4; idx++) {
if (config->pin_number_pad[idx] != PINMUX_UNUSED) {
if (config->mode == SPI_MODE_MASTER) {
config_gpio.direction = GPIO_PIN_DIR_OUTPUT;
} else if (config->mode == SPI_MODE_SLAVE) {
config_gpio.direction = GPIO_PIN_DIR_INPUT;
}
gpio_pin_set_config(config->pin_number_pad[idx], &config_gpio);
gpio_pinmux_cofiguration(config->pin_number_pad[idx], \
(uint16_t)(config->pinmux_sel_pad[idx]));
}
}
/* Set up the input clock for the module */
spi_module->CLOCK_SOURCE_SELECT.reg = config->clock_source;
# if CONF_SPI_MASTER_ENABLE == true
if (config->mode == SPI_MODE_MASTER) {
/* Set the mode in SPI master mode */
spi_module->SPI_MASTER_MODE.reg = SPI_MODE_MASTER;
}
# endif
# if CONF_SPI_SLAVE_ENABLE == true
if (config->mode == SPI_MODE_SLAVE) {
/* Set the mode in SPI slave mode */
spi_module->SPI_MASTER_MODE.reg = SPI_MODE_SLAVE;
}
# endif
#if SPI_CALLBACK_MODE == true
/* Temporary variables */
uint8_t i;
/* Initialize parameters */
for (i = 0; i < SPI_CALLBACK_N; i++) {
module->callback[i] = NULL;
}
module->tx_buffer_ptr = NULL;
module->rx_buffer_ptr = NULL;
module->remaining_tx_buffer_length = 0x0000;
module->remaining_rx_buffer_length = 0x0000;
module->registered_callback = 0x00;
module->enabled_callback = 0x00;
module->status = STATUS_OK;
module->dir = SPI_DIRECTION_IDLE;
module->locked = 0;
#endif
/* Write configuration to module and return status code */
return _spi_set_config(module, config);
}
/**
* \name Enable/Disable
* @{
*/
/**
* \brief Enables the SPI module
*
* This function will enable the SPI module.
*
* \param[in,out] module Pointer to the software instance struct
*/
void spi_enable(struct spi_module *const module)
{
Spi *const spi_module = (module->hw);
#if SPI_CALLBACK_MODE == true
if(spi_module == SPI0) {
NVIC_EnableIRQ(SPI0_RX_IRQn);
NVIC_EnableIRQ(SPI0_TX_IRQn);
} else if(spi_module == SPI1) {
NVIC_EnableIRQ(SPI1_RX_IRQn);
NVIC_EnableIRQ(SPI1_TX_IRQn);
}
#endif
/* Enable SPI */
spi_module->SPI_MODULE_ENABLE.reg = SPI_MODULE_ENABLE_ENABLE;
}
/**
* \brief Disables the SPI module
*
* This function will disable the SPI module.
*
* \param[in,out] module Pointer to the software instance struct
*/
void spi_disable(struct spi_module *const module)
{
Spi *const spi_module = (module->hw);
# if SPI_CALLBACK_MODE == true
if(spi_module == SPI0) {
NVIC_DisableIRQ(SPI0_RX_IRQn);
NVIC_DisableIRQ(SPI0_TX_IRQn);
} else if(spi_module == SPI1) {
NVIC_DisableIRQ(SPI1_RX_IRQn);
NVIC_DisableIRQ(SPI1_TX_IRQn);
}
# endif
/* Disable SPI */
spi_module->SPI_MODULE_ENABLE.reg = (0x0ul << SPI_MODULE_ENABLE_ENABLE_Pos);
_spi_clock_disable(module);
}
/**
* \brief Attempt to get lock on driver instance
*
* This function checks the instance's lock, which indicates whether or not it
* is currently in use, and sets the lock if it was not already set.
*
* The purpose of this is to enable exclusive access to driver instances, so
* that, e.g., transactions by different services will not interfere with each
* other.
*
* \param[in,out] module Pointer to the driver instance to lock
*
* \retval STATUS_OK If the module was locked
* \retval STATUS_BUSY If the module was already locked
*/
enum status_code spi_lock(struct spi_module *const module)
{
enum status_code status;
if (module->locked) {
status = STATUS_BUSY;
} else {
module->locked = true;
status = STATUS_OK;
}
return status;
}
/**
* \brief Unlock driver instance
*
* This function clears the instance lock, indicating that it is available for
* use.
*
* \param[in,out] module Pointer to the driver instance to lock.
*
* \retval STATUS_OK If the module was locked
* \retval STATUS_BUSY If the module was already locked
*/
void spi_unlock(struct spi_module *const module)
{
module->locked = false;
}
/**
* \brief Transfers a single SPI character
*
* This function will send a single SPI character via SPI and ignore any data
* shifted in by the connected device. To both send and receive data, use the
* \ref spi_transceive_wait function or use the \ref spi_read function after
* writing a character.
*
* Note that this function does not handle the SS (Slave Select)
* pin(s) in master mode; this must be handled from the user application.
*
* \note In slave mode, the data will not be transferred before a master
* initiates a transaction.
*
* \param[in] module Pointer to the software instance struct
* \param[in] tx_data Data to transmit
*
* \return Status of the procedure
* \retval STATUS_OK If the data was written
* \retval STATUS_BUSY If the last write was not completed
*/
enum status_code spi_write(struct spi_module *module, uint8_t tx_data)
{
/* Sanity check arguments */
Assert(module);
Assert(module->hw);
Spi *const spi_module = (module->hw);
/* Check if the data register has been copied to the shift register */
if (!_spi_is_ready_to_write(spi_module)) {
/* Data register has not been copied to the shift register, return */
return STATUS_BUSY;
}
/* Write the character to the DATA register */
spi_module->TRANSMIT_DATA.reg = tx_data & SPI_TRANSMIT_DATA_MASK;
return STATUS_OK;
}
/**
* \brief Reads last received SPI character
*
* This function will return the last SPI character shifted into the receive
* register by the \ref spi_write function
*
* \note Receiver must be enabled in the configuration
*
* \param[in] module Pointer to the software instance struct
* \param[out] rx_data Pointer to store the received data
*
* \returns Status of the read operation.
* \retval STATUS_OK If data was read
* \retval STATUS_ERR_IO If no data is available
* \retval STATUS_ERR_OVERFLOW If the data is overflown
*/
enum status_code spi_read(
struct spi_module *const module,
uint8_t *rx_data)
{
/* Sanity check arguments */
Assert(module);
Assert(module->hw);
Spi *const spi_module = (module->hw);
/* Check if data is ready to be read */
if (!_spi_is_ready_to_read(spi_module)) {
/* No data has been received, return */
return STATUS_ERR_IO;
}
/* Return value */
enum status_code retval = STATUS_OK;
/* Check if data is overflown */
if (spi_module->RECEIVE_STATUS.bit.FIFO_OVERRUN) {
retval = STATUS_ERR_OVERFLOW;
}
/* Read the character from the DATA register */
*rx_data = ((uint8_t)spi_module->RECEIVE_DATA.reg & SPI_RECEIVE_DATA_MASK);
return retval;
}
/**
* \brief Transceive requested amount of data to and from the SPI.
*
* This function will return after sending and receiving requested amount of data
*
* \note Receiver must be enabled in the configuration
* \note The \ref spi_select_slave function should be called before calling
* this function.
*
* \param[in] module Pointer to the software instance struct
* \param[in] tx_data Pointer containing the data to be transmitted
* \param[in] length Length of data to be read
* \param[out] rx_data Pointer to store the received data
*
* \returns Status of the read operation.
* \retval STATUS_OK If data was read
* \retval STATUS_ERR_IO If no data is available
* \retval STATUS_ERR_OVERFLOW If the data is overflown
*/
enum status_code spi_transceive_buffer_wait(
struct spi_module *const module,
uint8_t *tx_data,
uint8_t *rx_data,
uint16_t length)
{
Spi *spi_module = module->hw;
uint8_t dummy = 0;
uint8_t skip_mosi = 0;
uint8_t skip_miso = 0;
uint8_t status;
uint16_t transfer_len = 0;
if(spi_module == 0) {
return STATUS_ERR_NOT_INITIALIZED;
}
if(!tx_data) {
tx_data = &dummy;
*tx_data = module->tx_dummy_byte;
skip_mosi = 1;
} else if(!rx_data) {
rx_data = &dummy;
skip_miso = 1;
} else if(length == 0) {
return STATUS_ERR_INVALID_ARG;
}
/* Check for Idle */
do {
status = _spi_is_active(spi_module);
}while(status);
/* Clear all status registers */
spi_module->RECEIVE_STATUS.reg;
spi_module->TRANSMIT_STATUS.reg;
/* Start transfer */
while(transfer_len < length) {
/* Write data to MOSI */
while(!_spi_is_ready_to_write(spi_module));
spi_module->TRANSMIT_DATA.reg = *tx_data;
/* Read data shifted from MISO */
while(!_spi_is_ready_to_read(spi_module));
*rx_data = spi_module->RECEIVE_DATA.reg;
transfer_len++;
if (!skip_mosi) {
tx_data++;
}
if (!skip_miso) {
rx_data++;
}
}
/* check TXFIFO is empty */
do {
status = _spi_is_write_complete(spi_module);
}while(!status);
return STATUS_OK;
}
/**
* \brief Transceive single byte of data to and from the SPI.
*
* This function will return after single byte of data transceived.
*
* \note Receiver must be enabled in the configuration
* \note The \ref spi_select_slave function should be called before calling
* this function.
*
* \param[in] module Pointer to the software instance struct
* \param[in] tx_data Pointer containing the data to be transmitted
* \param[out] rx_data Pointer to store the received data
*
* \returns Status of the read operation.
* \retval STATUS_OK If data was read
* \retval STATUS_ERR_IO If no data is available
* \retval STATUS_ERR_OVERFLOW If the data is overflown
*/
enum status_code spi_transceive_wait(
struct spi_module *const module,
uint8_t *tx_data,
uint8_t *rx_data)
{
return spi_transceive_buffer_wait(module, tx_data, rx_data, 1);
}
/**
* \brief Reads requested amount of data from the SPI.
*
* This function will return after reading requested amount of data
*
* \note Receiver must be enabled in the configuration
* \note The \ref spi_select_slave function should be called before calling
* this function.
*
* \param[in] module Pointer to the software instance struct
* \param[in] length Length of data to be read
* \param[in] dummy Dummy byte to be sent on bus when reading data
* \param[out] rx_data Pointer to store the received data
*
* \returns Status of the read operation.
* \retval STATUS_OK If data was read
* \retval STATUS_ERR_IO If no data is available
* \retval STATUS_ERR_OVERFLOW If the data is overflown
*/
enum status_code spi_read_buffer_wait(
struct spi_module *const module,
uint8_t *rx_data,
uint16_t length,
uint8_t dummy)
{
module->tx_dummy_byte = dummy;
return spi_transceive_buffer_wait(module, NULL, rx_data, length);
}
/**
* \brief Writes requested amount of data to the SPI.
*
* This function will return after writing requested amount of data
*
* \note The \ref spi_select_slave function should be called before calling
* this function.
*
* \param[in] module Pointer to the software instance struct
* \param[in] length length of data to be read
* \param[out] tx_data Pointer to buffer to be transmitted
*
* \returns Status of the read operation.
* \retval STATUS_OK If data was read
* \retval STATUS_ERR_IO If no data is available
* \retval STATUS_ERR_OVERFLOW If the data is overflown
*/
enum status_code spi_write_buffer_wait(
struct spi_module *const module,
uint8_t *tx_data,
uint16_t length)
{
return spi_transceive_buffer_wait(module, tx_data, NULL, length);
}
/**
* \brief Asserting/Deasserting the slave select for the corresponding slave.
*
* This function will assert or deassert the SS of the requested slave device.
*
* \param[in] module Pointer to the software instance struct
* \param[in] slave Pointer containing slave instance
* \param[in] select Bool to select the salve or deselect
*
* \returns Status of the slave select operation.
*
* \retval STATUS_OK If SS pin is a valid one and selected/deselected
* \retval STATUS_ERR_INVALID_ARG Invalid SS pin
*/
enum status_code spi_select_slave(
struct spi_module *const module,
struct spi_slave_inst *const slave,
bool select)
{
uint8_t gpio_num = slave->ss_pin;
if(select) {
/* ASSERT Slave select pin */
gpio_pin_set_output_level(gpio_num, false);
} else {
/* DEASSERT Slave select pin */
gpio_pin_set_output_level(gpio_num, true);
}
return STATUS_OK;
}