/** * \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 Atmel Support */ #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; }