123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866 |
- /**
- * \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;
- }
|