12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265 |
- /**
- * \file
- *
- * \brief SAM Serial Peripheral Interface Driver
- *
- * Copyright (C) 2012-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 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)
- 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)
- 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;
- }
|