/** * \file * * \brief SAM Serial Peripheral Interface Driver * * Copyright (c) 2012-2018 Microchip Technology Inc. and its subsidiaries. * * \asf_license_start * * \page License * * Subject to your compliance with these terms, you may use Microchip * software and any derivatives exclusively with Microchip products. * It is your responsibility to comply with third party license terms applicable * to your use of third party software (including open source software) that * may accompany Microchip software. * * THIS SOFTWARE IS SUPPLIED BY MICROCHIP "AS IS". NO WARRANTIES, * WHETHER EXPRESS, IMPLIED OR STATUTORY, APPLY TO THIS SOFTWARE, * INCLUDING ANY IMPLIED WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY, * AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT WILL MICROCHIP BE * LIABLE FOR ANY INDIRECT, SPECIAL, PUNITIVE, INCIDENTAL OR CONSEQUENTIAL * LOSS, DAMAGE, COST OR EXPENSE OF ANY KIND WHATSOEVER RELATED TO THE * SOFTWARE, HOWEVER CAUSED, EVEN IF MICROCHIP HAS BEEN ADVISED OF THE * POSSIBILITY OR THE DAMAGES ARE FORESEEABLE. TO THE FULLEST EXTENT * ALLOWED BY LAW, MICROCHIP'S TOTAL LIABILITY ON ALL CLAIMS IN ANY WAY * RELATED TO THIS SOFTWARE WILL NOT EXCEED THE AMOUNT OF FEES, IF ANY, * THAT YOU HAVE PAID DIRECTLY TO MICROCHIP FOR THIS SOFTWARE. * * \asf_license_stop * */ /* * Support and FAQ: visit Microchip Support */ #include "spi.h" /** * \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 */ Assert(module); Assert(module->hw); SercomSpi *const spi_module = &(module->hw->SPI); /* Disable the module */ spi_disable(module); while (spi_is_syncing(module)) { /* Wait until the synchronization is complete */ } /* Software reset the module */ spi_module->CTRLA.reg |= SERCOM_SPI_CTRLA_SWRST; } /** * \brief Set the baudrate of the SPI module * * This function will set the baudrate of the SPI module. * * \param[in] module Pointer to the software instance struct * \param[in] baudrate The baudrate wanted * * \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 */ enum status_code spi_set_baudrate( struct spi_module *const module, uint32_t baudrate) { /* Sanity check arguments */ Assert(module); Assert(baudrate); Assert(module->hw); /* Value to write to BAUD register */ uint16_t baud = 0; SercomSpi *const spi_module = &(module->hw->SPI); /* Disable the module */ spi_disable(module); while (spi_is_syncing(module)) { /* Wait until the synchronization is complete */ } /* Find frequency of the internal SERCOMi_GCLK_ID_CORE */ uint32_t sercom_index = _sercom_get_sercom_inst_index(module->hw); uint32_t gclk_index = sercom_index + SERCOM0_GCLK_ID_CORE; uint32_t internal_clock = system_gclk_chan_get_hz(gclk_index); /* Get baud value, based on baudrate and the internal clock frequency */ enum status_code error_code = _sercom_get_sync_baud_val( baudrate, internal_clock, &baud); if (error_code != STATUS_OK) { /* Baud rate calculation error, return status code */ return STATUS_ERR_INVALID_ARG; } spi_module->BAUD.reg = (uint8_t)baud; while (spi_is_syncing(module)) { /* Wait until the synchronization is complete */ } /* Enable the module */ spi_enable(module); while (spi_is_syncing(module)) { /* Wait until the synchronization is complete */ } return STATUS_OK; } # if CONF_SPI_SLAVE_ENABLE == true /** * \internal Clears the Transmit Complete interrupt flag. * * \param[in] module Pointer to the software instance struct */ static void _spi_clear_tx_complete_flag( struct spi_module *const module) { /* Sanity check arguments */ Assert(module); Assert(module->hw); SercomSpi *const spi_module = &(module->hw->SPI); /* Clear interrupt flag */ spi_module->INTFLAG.reg = SPI_INTERRUPT_FLAG_TX_COMPLETE; } # endif /** * \internal Writes an SPI SERCOM 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) { /* Sanity check arguments */ Assert(module); Assert(config); Assert(module->hw); SercomSpi *const spi_module = &(module->hw->SPI); Sercom *const hw = module->hw; struct system_pinmux_config pin_conf; system_pinmux_get_config_defaults(&pin_conf); pin_conf.direction = SYSTEM_PINMUX_PIN_DIR_INPUT; if(config->mode == SPI_MODE_SLAVE) { pin_conf.input_pull = SYSTEM_PINMUX_PIN_PULL_NONE; } uint32_t pad_pinmuxes[] = { config->pinmux_pad0, config->pinmux_pad1, config->pinmux_pad2, config->pinmux_pad3 }; /* Configure the SERCOM pins according to the user configuration */ for (uint8_t pad = 0; pad < 4; pad++) { uint32_t current_pinmux = pad_pinmuxes[pad]; if (current_pinmux == PINMUX_DEFAULT) { current_pinmux = _sercom_get_default_pad(hw, pad); } if (current_pinmux != PINMUX_UNUSED) { pin_conf.mux_position = current_pinmux & 0xFFFF; system_pinmux_pin_set_config(current_pinmux >> 16, &pin_conf); } } module->mode = config->mode; module->character_size = config->character_size; module->receiver_enabled = config->receiver_enable; # ifdef FEATURE_SPI_HARDWARE_SLAVE_SELECT module->master_slave_select_enable = config->master_slave_select_enable; # endif # if CONF_SPI_MASTER_ENABLE == true /* Value to write to BAUD register */ uint16_t baud = 0; # endif /* Value to write to CTRLA register */ uint32_t ctrla = 0; /* Value to write to CTRLB register */ uint32_t ctrlb = 0; # if CONF_SPI_MASTER_ENABLE == true /* Find baud value and write it */ if (config->mode == SPI_MODE_MASTER) { /* Find frequency of the internal SERCOMi_GCLK_ID_CORE */ uint32_t sercom_index = _sercom_get_sercom_inst_index(module->hw); uint32_t gclk_index = sercom_index + SERCOM0_GCLK_ID_CORE; uint32_t internal_clock = system_gclk_chan_get_hz(gclk_index); /* Get baud value, based on baudrate and the internal clock frequency */ enum status_code error_code = _sercom_get_sync_baud_val( config->mode_specific.master.baudrate, internal_clock, &baud); if (error_code != STATUS_OK) { /* Baud rate calculation error, return status code */ return STATUS_ERR_INVALID_ARG; } spi_module->BAUD.reg = (uint8_t)baud; } # endif # if CONF_SPI_SLAVE_ENABLE == true if (config->mode == SPI_MODE_SLAVE) { /* Set frame format */ ctrla = config->mode_specific.slave.frame_format; /* Set address mode */ ctrlb = config->mode_specific.slave.address_mode; /* Set address and address mask*/ spi_module->ADDR.reg |= (config->mode_specific.slave.address << SERCOM_SPI_ADDR_ADDR_Pos) | (config->mode_specific.slave.address_mask << SERCOM_SPI_ADDR_ADDRMASK_Pos); if (config->mode_specific.slave.preload_enable) { /* Enable pre-loading of shift register */ ctrlb |= SERCOM_SPI_CTRLB_PLOADEN; } } # endif /* Set data order */ ctrla |= config->data_order; /* Set clock polarity and clock phase */ ctrla |= config->transfer_mode; /* Set MUX setting */ ctrla |= config->mux_setting; /* Set SPI character size */ ctrlb |= config->character_size; /* Set whether module should run in standby. */ if (config->run_in_standby || system_is_debugger_present()) { ctrla |= SERCOM_SPI_CTRLA_RUNSTDBY; } if (config->receiver_enable) { /* Enable receiver */ ctrlb |= SERCOM_SPI_CTRLB_RXEN; } # ifdef FEATURE_SPI_SLAVE_SELECT_LOW_DETECT if (config->select_slave_low_detect_enable) { /* Enable Slave Select Low Detect */ ctrlb |= SERCOM_SPI_CTRLB_SSDE; } # endif # ifdef FEATURE_SPI_HARDWARE_SLAVE_SELECT if (config->master_slave_select_enable) { /* Enable Master Slave Select */ ctrlb |= SERCOM_SPI_CTRLB_MSSEN; } # endif /* Write CTRLA register */ spi_module->CTRLA.reg |= ctrla; /* Write CTRLB register */ spi_module->CTRLB.reg |= ctrlb; return STATUS_OK; } #if SPI_CALLBACK_MODE == false /** * \internal Checks an SPI config against current set config * * This function will check that the config does not alter the * configuration of the module. If the new config changes any * setting, the initialization will be discarded. * * \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_ERR_DENIED If configuration was different from previous * \retval STATUS_OK If the configuration was written */ static enum status_code _spi_check_config( struct spi_module *const module, const struct spi_config *const config) { /* Sanity check arguments */ Assert(module); Assert(config); Assert(module->hw); SercomSpi *const spi_module = &(module->hw->SPI); Sercom *const hw = module->hw; uint32_t pad_pinmuxes[] = { config->pinmux_pad0, config->pinmux_pad1, config->pinmux_pad2, config->pinmux_pad3 }; /* Compare the current SERCOM pins against the user configuration */ for (uint8_t pad = 0; pad < 4; pad++) { uint32_t current_pinmux = pad_pinmuxes[pad]; if (current_pinmux == PINMUX_DEFAULT) { current_pinmux = _sercom_get_default_pad(hw, pad); } if (current_pinmux == PINMUX_UNUSED) { continue; } if ((current_pinmux & 0xFFFF) != system_pinmux_pin_get_mux_position(current_pinmux >> 16)) { module->hw = NULL; return STATUS_ERR_DENIED; } } # if CONF_SPI_MASTER_ENABLE == true /* Value to read BAUD register */ uint16_t baud; uint32_t external_clock = system_gclk_chan_get_hz(SERCOM_GCLK_ID); # endif /* Value to read CTRLA, CTRLB and ADDR register */ uint32_t ctrla = 0; uint32_t ctrlb = 0; # if CONF_SPI_SLAVE_ENABLE == true uint32_t addr = 0; # endif # if CONF_SPI_MASTER_ENABLE == true /* Find baud value and compare it */ if (config->mode == SPI_MODE_MASTER) { enum status_code error_code = _sercom_get_sync_baud_val( config->mode_specific.master.baudrate, external_clock, &baud); if (error_code != STATUS_OK) { /* Baud rate calculation error, return status code */ return STATUS_ERR_INVALID_ARG; } if (spi_module->BAUD.reg != (uint8_t)baud) { return STATUS_ERR_DENIED; } ctrla |= SERCOM_SPI_CTRLA_MODE(0x3); } # endif # if CONF_SPI_SLAVE_ENABLE == true if (config->mode == SPI_MODE_SLAVE) { /* Set frame format */ ctrla |= config->mode_specific.slave.frame_format; /* Set address mode */ ctrlb |= config->mode_specific.slave.address_mode; /* Set address and address mask*/ addr |= (config->mode_specific.slave.address << SERCOM_SPI_ADDR_ADDR_Pos) | (config->mode_specific.slave.address_mask << SERCOM_SPI_ADDR_ADDRMASK_Pos); if (spi_module->CTRLA.reg != addr) { return STATUS_ERR_DENIED; } if (config->mode_specific.slave.preload_enable) { /* Enable pre-loading of shift register */ ctrlb |= SERCOM_SPI_CTRLB_PLOADEN; } ctrla |= SERCOM_SPI_CTRLA_MODE(0x2); } # endif /* Set data order */ ctrla |= config->data_order; /* Set clock polarity and clock phase */ ctrla |= config->transfer_mode; /* Set MUX setting */ ctrla |= config->mux_setting; /* Set SPI character size */ ctrlb |= config->character_size; if (config->run_in_standby) { /* Enable in sleep mode */ ctrla |= SERCOM_SPI_CTRLA_RUNSTDBY; } if (config->receiver_enable) { /* Enable receiver */ ctrlb |= SERCOM_SPI_CTRLB_RXEN; } # ifdef FEATURE_SPI_SLAVE_SELECT_LOW_DETECT if (config->select_slave_low_detect_enable) { /* Enable Slave Select Low Detect */ ctrlb |= SERCOM_SPI_CTRLB_SSDE; } # endif # ifdef FEATURE_SPI_HARDWARE_SLAVE_SELECT if (config->master_slave_select_enable) { /* Enable Master Slave Select */ ctrlb |= SERCOM_SPI_CTRLB_MSSEN; } # endif ctrla |= SERCOM_SPI_CTRLA_ENABLE; /* Check that same config is set */ if (spi_module->CTRLA.reg == ctrla && spi_module->CTRLB.reg == ctrlb) { module->mode = config->mode; module->character_size = config->character_size; return STATUS_OK; } /* Not same config, wipe module pointer and return */ module->hw = NULL; return STATUS_ERR_DENIED; } #endif /** * \brief Initializes the SERCOM SPI module * * This function will initialize the SERCOM 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, Sercom *const hw, const struct spi_config *const config) { /* Sanity check arguments */ Assert(module); Assert(hw); Assert(config); /* Initialize device instance */ module->hw = hw; SercomSpi *const spi_module = &(module->hw->SPI); /* Check if module is enabled. */ if (spi_module->CTRLA.reg & SERCOM_SPI_CTRLA_ENABLE) { # if SPI_CALLBACK_MODE == false /* Check if config is valid */ return _spi_check_config(module, config); # else return STATUS_ERR_DENIED; # endif } /* Check if reset is in progress. */ if (spi_module->CTRLA.reg & SERCOM_SPI_CTRLA_SWRST){ return STATUS_BUSY; } uint32_t sercom_index = _sercom_get_sercom_inst_index(module->hw); uint32_t pm_index, gclk_index; #if (SAML21) || (SAMR30) || (SAMR34) || (SAMR35) if (sercom_index == 5) { # ifdef ID_SERCOM5 pm_index = MCLK_APBDMASK_SERCOM5_Pos; gclk_index = SERCOM5_GCLK_ID_CORE; # else return STATUS_ERR_INVALID_ARG; # endif } else { pm_index = sercom_index + MCLK_APBCMASK_SERCOM0_Pos; gclk_index = sercom_index + SERCOM0_GCLK_ID_CORE; } #elif (SAMC21) if (sercom_index == 5) { # ifdef ID_SERCOM5 pm_index = sercom_index + MCLK_APBCMASK_SERCOM0_Pos; gclk_index = SERCOM5_GCLK_ID_CORE; # else return STATUS_ERR_INVALID_ARG; # endif } else { pm_index = sercom_index + MCLK_APBCMASK_SERCOM0_Pos; gclk_index = sercom_index + SERCOM0_GCLK_ID_CORE; } #elif (SAMC20) || (SAML22) pm_index = sercom_index + MCLK_APBCMASK_SERCOM0_Pos; gclk_index = sercom_index + SERCOM0_GCLK_ID_CORE; #else pm_index = sercom_index + PM_APBCMASK_SERCOM0_Pos; gclk_index = sercom_index + SERCOM0_GCLK_ID_CORE; #endif /* Turn on module in PM */ #if (SAML21) || (SAMR30) || (SAMR34) || (SAMR35) if (sercom_index == 5) { # ifdef ID_SERCOM5 system_apb_clock_set_mask(SYSTEM_CLOCK_APB_APBD, 1 << pm_index); # else return STATUS_ERR_INVALID_ARG; # endif } else { system_apb_clock_set_mask(SYSTEM_CLOCK_APB_APBC, 1 << pm_index); } #else system_apb_clock_set_mask(SYSTEM_CLOCK_APB_APBC, 1 << pm_index); #endif /* Set up the GCLK for the module */ struct system_gclk_chan_config gclk_chan_conf; system_gclk_chan_get_config_defaults(&gclk_chan_conf); gclk_chan_conf.source_generator = config->generator_source; system_gclk_chan_set_config(gclk_index, &gclk_chan_conf); system_gclk_chan_enable(gclk_index); sercom_set_gclk_generator(config->generator_source, false); # if CONF_SPI_MASTER_ENABLE == true if (config->mode == SPI_MODE_MASTER) { /* Set the SERCOM in SPI master mode */ spi_module->CTRLA.reg |= SERCOM_SPI_CTRLA_MODE(0x3); } # endif # if CONF_SPI_SLAVE_ENABLE == true if (config->mode == SPI_MODE_SLAVE) { /* Set the SERCOM in SPI slave mode */ spi_module->CTRLA.reg |= SERCOM_SPI_CTRLA_MODE(0x2); } # endif #if SPI_CALLBACK_MODE == true /* Temporary variables */ uint8_t i; uint8_t instance_index; /* 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 = false; /* * Set interrupt handler and register SPI software module struct in * look-up table */ instance_index = _sercom_get_sercom_inst_index(module->hw); _sercom_set_handler(instance_index, _spi_interrupt_handler); _sercom_instances[instance_index] = module; #endif /* Write configuration to module and return status code */ return _spi_set_config(module, config); } /** * \brief Reads buffer of \c length SPI characters * * This function will read a buffer of data from an SPI peripheral by sending * dummy SPI character if in master mode, or by waiting for data in slave mode. * * \note If address matching is enabled for the slave, the first character * received and placed in the buffer will be the address. * * \param[in] module Pointer to the software instance struct * \param[out] rx_data Data buffer for received data * \param[in] length Length of data to receive * \param[in] dummy 8- or 9-bit dummy byte to shift out in master mode * * \return Status of the read operation. * \retval STATUS_OK If the read was completed * \retval STATUS_ABORTED If transaction was ended by master before * the entire buffer was transferred * \retval STATUS_ERR_INVALID_ARG If invalid argument(s) were provided * \retval STATUS_ERR_TIMEOUT If the operation was not completed within the * timeout in slave mode * \retval STATUS_ERR_DENIED If the receiver is not enabled * \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, uint16_t dummy) { /* Sanity check arguments */ Assert(module); Assert(module->hw); # if SPI_CALLBACK_MODE == true if (module->status == STATUS_BUSY) { /* Check if the SPI module is busy with a job */ return STATUS_BUSY; } # endif /* Sanity check arguments */ if (length == 0) { return STATUS_ERR_INVALID_ARG; } if (!(module->receiver_enabled)) { return STATUS_ERR_DENIED; } # if CONF_SPI_SLAVE_ENABLE == true if ((module->mode == SPI_MODE_SLAVE) && (spi_is_write_complete(module))) { /* Clear TX complete flag */ _spi_clear_tx_complete_flag(module); } # endif uint16_t rx_pos = 0; while (length--) { # if CONF_SPI_MASTER_ENABLE == true if (module->mode == SPI_MODE_MASTER) { /* Wait until the module is ready to write a character */ while (!spi_is_ready_to_write(module)) { } /* Send dummy SPI character to read in master mode */ spi_write(module, dummy); } # endif # if CONF_SPI_SLAVE_ENABLE == true /* Start timeout period for slave */ if (module->mode == SPI_MODE_SLAVE) { for (uint32_t i = 0; i <= SPI_TIMEOUT; i++) { if (spi_is_ready_to_read(module)) { break; } } /* Check if master has ended the transaction */ if (spi_is_write_complete(module)) { _spi_clear_tx_complete_flag(module); return STATUS_ABORTED; } if (!spi_is_ready_to_read(module)) { /* Not ready to read data within timeout period */ return STATUS_ERR_TIMEOUT; } } # endif /* Wait until the module is ready to read a character */ while (!spi_is_ready_to_read(module)) { } uint16_t received_data = 0; enum status_code retval = spi_read(module, &received_data); if (retval != STATUS_OK) { /* Overflow, abort */ return retval; } /* Read value will be at least 8-bits long */ rx_data[rx_pos++] = received_data; /* If 9-bit data, write next received byte to the buffer */ if (module->character_size == SPI_CHARACTER_SIZE_9BIT) { rx_data[rx_pos++] = (received_data >> 8); } } return STATUS_OK; } /** * \brief Sends and reads a single SPI character * * This function will transfer a single SPI character via SPI and return the * SPI character that is shifted into the shift register. * * In master mode the SPI character will be sent immediately and the received * SPI character will be read as soon as the shifting of the data is * complete. * * In slave mode this function will place the data to be sent into the transmit * buffer. It will then block until an SPI master has shifted a complete * SPI character, and the received data is available. * * \note The data to be sent might not be sent before the next transfer, as * loading of the shift register is dependent on SCK. * \note If address matching is enabled for the slave, the first character * received and placed in the buffer will be the address. * * \param[in] module Pointer to the software instance struct * \param[in] tx_data SPI character to transmit * \param[out] rx_data Pointer to store the received SPI character * * \return Status of the operation. * \retval STATUS_OK If the operation was completed * \retval STATUS_ERR_TIMEOUT If the operation was not completed within the * timeout in slave mode * \retval STATUS_ERR_DENIED If the receiver is not enabled * \retval STATUS_ERR_OVERFLOW If the incoming data is overflown */ enum status_code spi_transceive_wait( struct spi_module *const module, uint16_t tx_data, uint16_t *rx_data) { /* Sanity check arguments */ Assert(module); if (!(module->receiver_enabled)) { return STATUS_ERR_DENIED; } # if SPI_CALLBACK_MODE == true if (module->status == STATUS_BUSY) { /* Check if the SPI module is busy with a job */ return STATUS_BUSY; } # endif # if CONF_SPI_SLAVE_ENABLE == true uint16_t j; # endif enum status_code retval = STATUS_OK; # if CONF_SPI_SLAVE_ENABLE == true /* Start timeout period for slave */ if (module->mode == SPI_MODE_SLAVE) { for (j = 0; j <= SPI_TIMEOUT; j++) { if (spi_is_ready_to_write(module)) { break; } else if (j == SPI_TIMEOUT) { /* Not ready to write data within timeout period */ return STATUS_ERR_TIMEOUT; } } } # endif /* Wait until the module is ready to write the character */ while (!spi_is_ready_to_write(module)) { } /* Write data */ spi_write(module, tx_data); # if CONF_SPI_SLAVE_ENABLE == true /* Start timeout period for slave */ if (module->mode == SPI_MODE_SLAVE) { for (j = 0; j <= SPI_TIMEOUT; j++) { if (spi_is_ready_to_read(module)) { break; } else if (j == SPI_TIMEOUT) { /* Not ready to read data within timeout period */ return STATUS_ERR_TIMEOUT; } } } # endif /* Wait until the module is ready to read the character */ while (!spi_is_ready_to_read(module)) { } /* Read data */ retval = spi_read(module, rx_data); return retval; } /** * \brief Selects slave device * * This function will drive the slave select pin of the selected device low or * high depending on the select Boolean. * If slave address recognition is enabled, the address will be sent to the * slave when selecting it. * * \param[in] module Pointer to the software module struct * \param[in] slave Pointer to the attached slave * \param[in] select Boolean stating if the slave should be selected or * deselected * * \return Status of the operation. * \retval STATUS_OK If the slave device was selected * \retval STATUS_ERR_UNSUPPORTED_DEV If the SPI module is operating in slave * mode * \retval STATUS_BUSY If the SPI module is not ready to write * the slave address */ enum status_code spi_select_slave( struct spi_module *const module, struct spi_slave_inst *const slave, const bool select) { /* Sanity check arguments */ Assert(module); Assert(module->hw); Assert(slave); /* Check that the SPI module is operating in master mode */ if (module->mode != SPI_MODE_MASTER) { return STATUS_ERR_UNSUPPORTED_DEV; } # ifdef FEATURE_SPI_HARDWARE_SLAVE_SELECT if(!(module->master_slave_select_enable)) # endif { if (select) { /* Check if address recognition is enabled */ if (slave->address_enabled) { /* Check if the module is ready to write the address */ if (!spi_is_ready_to_write(module)) { /* Not ready, do not select slave and return */ port_pin_set_output_level(slave->ss_pin, true); return STATUS_BUSY; } /* Drive Slave Select low */ port_pin_set_output_level(slave->ss_pin, false); /* Write address to slave */ spi_write(module, slave->address); if (!(module->receiver_enabled)) { /* Flush contents of shift register shifted back from slave */ while (!spi_is_ready_to_read(module)) { } uint16_t flush = 0; spi_read(module, &flush); } } else { /* Drive Slave Select low */ port_pin_set_output_level(slave->ss_pin, false); } } else { /* Drive Slave Select high */ port_pin_set_output_level(slave->ss_pin, true); } } return STATUS_OK; } /** * \brief Sends a buffer of \c length SPI characters * * This function will send a buffer of SPI characters via the SPI * and discard any data that is received. To both send and receive a buffer of * data, use the \ref spi_transceive_buffer_wait function. * * Note that this function does not handle the _SS (slave select) pin(s) in * master mode; this must be handled by the user application. * * \param[in] module Pointer to the software instance struct * \param[in] tx_data Pointer to the buffer to transmit * \param[in] length Number of SPI characters to transfer * * \return Status of the write operation. * \retval STATUS_OK If the write was completed * \retval STATUS_ABORTED If transaction was ended by master before * entire buffer was transferred * \retval STATUS_ERR_INVALID_ARG If invalid argument(s) were provided * \retval STATUS_ERR_TIMEOUT If the operation was not completed within the * timeout in slave mode */ enum status_code spi_write_buffer_wait( struct spi_module *const module, const uint8_t *tx_data, uint16_t length) { /* Sanity check arguments */ Assert(module); # if SPI_CALLBACK_MODE == true if (module->status == STATUS_BUSY) { /* Check if the SPI module is busy with a job */ return STATUS_BUSY; } # endif if (length == 0) { return STATUS_ERR_INVALID_ARG; } # if CONF_SPI_SLAVE_ENABLE == true if ((module->mode == SPI_MODE_SLAVE) && (spi_is_write_complete(module))) { /* Clear TX complete flag */ _spi_clear_tx_complete_flag(module); } # endif uint16_t tx_pos = 0; uint16_t flush_length = length; /* Write block */ while (length--) { # if CONF_SPI_SLAVE_ENABLE == true /* Start timeout period for slave */ if (module->mode == SPI_MODE_SLAVE) { for (uint32_t i = 0; i <= SPI_TIMEOUT; i++) { if (spi_is_ready_to_write(module)) { break; } } /* Check if master has ended the transaction */ if (spi_is_write_complete(module)) { _spi_clear_tx_complete_flag(module); return STATUS_ABORTED; } if (!spi_is_ready_to_write(module)) { /* Not ready to write data within timeout period */ return STATUS_ERR_TIMEOUT; } } # endif /* Wait until the module is ready to write a character */ while (!spi_is_ready_to_write(module)) { } /* Write value will be at least 8-bits long */ uint16_t data_to_send = tx_data[tx_pos++]; /* If 9-bit data, get next byte to send from the buffer */ if (module->character_size == SPI_CHARACTER_SIZE_9BIT) { data_to_send |= (tx_data[tx_pos++] << 8); } /* Write the data to send */ spi_write(module, data_to_send); if (module->receiver_enabled) { # if CONF_SPI_SLAVE_ENABLE == true /* Start timeout period for slave */ if (module->mode == SPI_MODE_SLAVE) { for (uint32_t i = 0; i <= SPI_TIMEOUT; i++) { if (length && spi_is_ready_to_write(module)) { data_to_send = tx_data[tx_pos++]; /* If 9-bit data, get next byte to send from the buffer */ if (module->character_size == SPI_CHARACTER_SIZE_9BIT) { data_to_send |= (tx_data[tx_pos++] << 8); } /* Write the data to send */ spi_write(module, data_to_send); length--; } if (spi_is_ready_to_read(module)) { break; } } /* Check if master has ended the transaction */ if (spi_is_write_complete(module)) { _spi_clear_tx_complete_flag(module); return STATUS_ABORTED; } if (!spi_is_ready_to_read(module)) { /* Not ready to read data within timeout period */ return STATUS_ERR_TIMEOUT; } } # endif while (!spi_is_ready_to_read(module)) { } /* Flush read buffer */ uint16_t flush; spi_read(module, &flush); flush_length--; } } # if CONF_SPI_MASTER_ENABLE == true if (module->mode == SPI_MODE_MASTER) { /* Wait for last byte to be transferred */ while (!spi_is_write_complete(module)) { } } # endif # if CONF_SPI_SLAVE_ENABLE == true if (module->mode == SPI_MODE_SLAVE) { if (module->receiver_enabled) { while (flush_length) { /* Start timeout period for slave */ for (uint32_t i = 0; i <= SPI_TIMEOUT; i++) { if (spi_is_ready_to_read(module)) { break; } } if (!spi_is_ready_to_read(module)) { /* Not ready to read data within timeout period */ return STATUS_ERR_TIMEOUT; } /* Flush read buffer */ uint16_t flush; spi_read(module, &flush); flush_length--; } } } # endif return STATUS_OK; } /** * \brief Sends and receives a buffer of \c length SPI characters * * This function will send and receive a buffer of data via the SPI. * * In master mode the SPI characters will be sent immediately and the * received SPI character will be read as soon as the shifting of the SPI * character is complete. * * In slave mode this function will place the data to be sent into the transmit * buffer. It will then block until an SPI master has shifted the complete * buffer and the received data is available. * * \param[in] module Pointer to the software instance struct * \param[in] tx_data Pointer to the buffer to transmit * \param[out] rx_data Pointer to the buffer where received data will be stored * \param[in] length Number of SPI characters to transfer * * \return Status of the operation. * \retval STATUS_OK If the operation was completed * \retval STATUS_ERR_INVALID_ARG If invalid argument(s) were provided * \retval STATUS_ERR_TIMEOUT If the operation was not completed within the * timeout in slave mode * \retval STATUS_ERR_DENIED If the receiver is not enabled * \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) { /* Sanity check arguments */ Assert(module); # if SPI_CALLBACK_MODE == true if (module->status == STATUS_BUSY) { /* Check if the SPI module is busy with a job */ return STATUS_BUSY; } # endif /* Sanity check arguments */ if (length == 0) { return STATUS_ERR_INVALID_ARG; } if (!(module->receiver_enabled)) { return STATUS_ERR_DENIED; } # if CONF_SPI_SLAVE_ENABLE == true if ((module->mode == SPI_MODE_SLAVE) && (spi_is_write_complete(module))) { /* Clear TX complete flag */ _spi_clear_tx_complete_flag(module); } # endif uint16_t tx_pos = 0; uint16_t rx_pos = 0; uint16_t rx_length = length; /* Send and receive buffer */ while (length--) { # if CONF_SPI_SLAVE_ENABLE == true /* Start timeout period for slave */ if (module->mode == SPI_MODE_SLAVE) { for (uint32_t i = 0; i <= SPI_TIMEOUT; i++) { if (spi_is_ready_to_write(module)) { break; } } /* Check if master has ended the transaction */ if (spi_is_write_complete(module)) { _spi_clear_tx_complete_flag(module); return STATUS_ABORTED; } if (!spi_is_ready_to_write(module)) { /* Not ready to write data within timeout period */ return STATUS_ERR_TIMEOUT; } } # endif /* Wait until the module is ready to write a character */ while (!spi_is_ready_to_write(module)) { } /* Write value will be at least 8-bits long */ uint16_t data_to_send = tx_data[tx_pos++]; /* If 9-bit data, get next byte to send from the buffer */ if (module->character_size == SPI_CHARACTER_SIZE_9BIT) { data_to_send |= (tx_data[tx_pos++] << 8); } /* Write the data to send */ spi_write(module, data_to_send); # if CONF_SPI_SLAVE_ENABLE == true /* Start timeout period for slave */ if (module->mode == SPI_MODE_SLAVE) { for (uint32_t i = 0; i <= SPI_TIMEOUT; i++) { if (spi_is_ready_to_write(module)) { data_to_send = tx_data[tx_pos++]; /* If 9-bit data, get next byte to send from the buffer */ if (module->character_size == SPI_CHARACTER_SIZE_9BIT) { data_to_send |= (tx_data[tx_pos++] << 8); } /* Write the data to send */ spi_write(module, data_to_send); length--; } if (spi_is_ready_to_read(module)) { break; } } /* Check if master has ended the transaction */ if (spi_is_write_complete(module)) { _spi_clear_tx_complete_flag(module); return STATUS_ABORTED; } if (!spi_is_ready_to_read(module)) { /* Not ready to read data within timeout period */ return STATUS_ERR_TIMEOUT; } } # endif /* Wait until the module is ready to read a character */ while (!spi_is_ready_to_read(module)) { } enum status_code retval; uint16_t received_data = 0; rx_length--; retval = spi_read(module, &received_data); if (retval != STATUS_OK) { /* Overflow, abort */ return retval; } /* Read value will be at least 8-bits long */ rx_data[rx_pos++] = received_data; /* If 9-bit data, write next received byte to the buffer */ if (module->character_size == SPI_CHARACTER_SIZE_9BIT) { rx_data[rx_pos++] = (received_data >> 8); } } # if CONF_SPI_MASTER_ENABLE == true if (module->mode == SPI_MODE_MASTER) { /* Wait for last byte to be transferred */ while (!spi_is_write_complete(module)) { } } # endif # if CONF_SPI_SLAVE_ENABLE == true if (module->mode == SPI_MODE_SLAVE) { while (rx_length) { /* Start timeout period for slave */ for (uint32_t i = 0; i <= SPI_TIMEOUT; i++) { if (spi_is_ready_to_read(module)) { break; } } if (!spi_is_ready_to_read(module)) { /* Not ready to read data within timeout period */ return STATUS_ERR_TIMEOUT; } enum status_code retval; uint16_t received_data = 0; rx_length--; retval = spi_read(module, &received_data); if (retval != STATUS_OK) { /* Overflow, abort */ return retval; } /* Read value will be at least 8-bits long */ rx_data[rx_pos++] = received_data; /* If 9-bit data, write next received byte to the buffer */ if (module->character_size == SPI_CHARACTER_SIZE_9BIT) { rx_data[rx_pos++] = (received_data >> 8); } } } # endif return STATUS_OK; }