/** * \file * * \brief SAM SERCOM USART Driver * * Copyright (c) 2012-2019 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 "usart.h" #include #if USART_CALLBACK_MODE == true # include "usart_interrupt.h" #endif /** * \internal * Set Configuration of the USART module */ static enum status_code _usart_set_config( struct usart_module *const module, const struct usart_config *const config) { /* Sanity check arguments */ Assert(module); Assert(module->hw); /* Get a pointer to the hardware module instance */ SercomUsart *const usart_hw = &(module->hw->USART); /* Index for generic clock */ uint32_t sercom_index = _sercom_get_sercom_inst_index(module->hw); uint32_t gclk_index; #if (SAML21) || (SAMR30) || (SAMR34) || (SAMR35) || (SAMC21) if (sercom_index == 5) { gclk_index = SERCOM5_GCLK_ID_CORE; } else { gclk_index = sercom_index + SERCOM0_GCLK_ID_CORE; } #else gclk_index = sercom_index + SERCOM0_GCLK_ID_CORE; #endif /* Cache new register values to minimize the number of register writes */ uint32_t ctrla = 0; uint32_t ctrlb = 0; #ifdef FEATURE_USART_ISO7816 uint32_t ctrlc = 0; #endif uint16_t baud = 0; uint32_t transfer_mode; enum sercom_asynchronous_operation_mode mode = SERCOM_ASYNC_OPERATION_MODE_ARITHMETIC; enum sercom_asynchronous_sample_num sample_num = SERCOM_ASYNC_SAMPLE_NUM_16; #ifdef FEATURE_USART_OVER_SAMPLE switch (config->sample_rate) { case USART_SAMPLE_RATE_16X_ARITHMETIC: mode = SERCOM_ASYNC_OPERATION_MODE_ARITHMETIC; sample_num = SERCOM_ASYNC_SAMPLE_NUM_16; break; case USART_SAMPLE_RATE_8X_ARITHMETIC: mode = SERCOM_ASYNC_OPERATION_MODE_ARITHMETIC; sample_num = SERCOM_ASYNC_SAMPLE_NUM_8; break; case USART_SAMPLE_RATE_3X_ARITHMETIC: mode = SERCOM_ASYNC_OPERATION_MODE_ARITHMETIC; sample_num = SERCOM_ASYNC_SAMPLE_NUM_3; break; case USART_SAMPLE_RATE_16X_FRACTIONAL: mode = SERCOM_ASYNC_OPERATION_MODE_FRACTIONAL; sample_num = SERCOM_ASYNC_SAMPLE_NUM_16; break; case USART_SAMPLE_RATE_8X_FRACTIONAL: mode = SERCOM_ASYNC_OPERATION_MODE_FRACTIONAL; sample_num = SERCOM_ASYNC_SAMPLE_NUM_8; break; } #endif /* Set data order, internal muxing, and clock polarity */ ctrla = (uint32_t)config->data_order | (uint32_t)config->mux_setting | #ifdef FEATURE_USART_OVER_SAMPLE config->sample_adjustment | config->sample_rate | #endif #ifdef FEATURE_USART_IMMEDIATE_BUFFER_OVERFLOW_NOTIFICATION (config->immediate_buffer_overflow_notification << SERCOM_USART_CTRLA_IBON_Pos) | #endif (config->clock_polarity_inverted << SERCOM_USART_CTRLA_CPOL_Pos); enum status_code status_code = STATUS_OK; transfer_mode = (uint32_t)config->transfer_mode; #ifdef FEATURE_USART_ISO7816 if(config->iso7816_config.enabled) { transfer_mode = config->iso7816_config.protocol_t; } #endif /* Get baud value from mode and clock */ #ifdef FEATURE_USART_ISO7816 if(config->iso7816_config.enabled) { baud = config->baudrate; } else { #endif switch (transfer_mode) { case USART_TRANSFER_SYNCHRONOUSLY: if (!config->use_external_clock) { status_code = _sercom_get_sync_baud_val(config->baudrate, system_gclk_chan_get_hz(gclk_index), &baud); } break; case USART_TRANSFER_ASYNCHRONOUSLY: if (config->use_external_clock) { status_code = _sercom_get_async_baud_val(config->baudrate, config->ext_clock_freq, &baud, mode, sample_num); } else { status_code = _sercom_get_async_baud_val(config->baudrate, system_gclk_chan_get_hz(gclk_index), &baud, mode, sample_num); } break; } /* Check if calculating the baudrate failed */ if (status_code != STATUS_OK) { /* Abort */ return status_code; } #ifdef FEATURE_USART_ISO7816 } #endif #ifdef FEATURE_USART_IRDA if(config->encoding_format_enable) { usart_hw->RXPL.reg = config->receive_pulse_length; } #endif /*Set baud val */ usart_hw->BAUD.reg = baud; /* Set sample mode */ ctrla |= transfer_mode; if (config->use_external_clock == false) { ctrla |= SERCOM_USART_CTRLA_MODE(0x1); } else { ctrla |= SERCOM_USART_CTRLA_MODE(0x0); } /* Set stopbits and enable transceivers */ ctrlb = #ifdef FEATURE_USART_IRDA (config->encoding_format_enable << SERCOM_USART_CTRLB_ENC_Pos) | #endif #ifdef FEATURE_USART_START_FRAME_DECTION (config->start_frame_detection_enable << SERCOM_USART_CTRLB_SFDE_Pos) | #endif #ifdef FEATURE_USART_COLLISION_DECTION (config->collision_detection_enable << SERCOM_USART_CTRLB_COLDEN_Pos) | #endif (config->receiver_enable << SERCOM_USART_CTRLB_RXEN_Pos) | (config->transmitter_enable << SERCOM_USART_CTRLB_TXEN_Pos); #ifdef FEATURE_USART_ISO7816 if(config->iso7816_config.enabled) { ctrla |= SERCOM_USART_CTRLA_FORM(0x07); if (config->iso7816_config.enable_inverse) { ctrla |= SERCOM_USART_CTRLA_TXINV | SERCOM_USART_CTRLA_RXINV; } ctrlb |= USART_CHARACTER_SIZE_8BIT; switch(config->iso7816_config.protocol_t) { case ISO7816_PROTOCOL_T_0: ctrlb |= (uint32_t)config->stopbits; ctrlc |= SERCOM_USART_CTRLC_GTIME(config->iso7816_config.guard_time) | \ (config->iso7816_config.inhibit_nack) | \ (config->iso7816_config.successive_recv_nack) | \ SERCOM_USART_CTRLC_MAXITER(config->iso7816_config.max_iterations); break; case ISO7816_PROTOCOL_T_1: ctrlb |= USART_STOPBITS_1; break; } } else { #endif ctrlb |= (uint32_t)config->stopbits; ctrlb |= (uint32_t)config->character_size; /* Check parity mode bits */ if (config->parity != USART_PARITY_NONE) { ctrla |= SERCOM_USART_CTRLA_FORM(1); ctrlb |= config->parity; } else { #ifdef FEATURE_USART_LIN_SLAVE if(config->lin_slave_enable) { ctrla |= SERCOM_USART_CTRLA_FORM(0x4); } else { ctrla |= SERCOM_USART_CTRLA_FORM(0); } #else ctrla |= SERCOM_USART_CTRLA_FORM(0); #endif } #ifdef FEATURE_USART_ISO7816 } #endif #ifdef FEATURE_USART_LIN_MASTER usart_hw->CTRLC.reg = ((usart_hw->CTRLC.reg) & SERCOM_USART_CTRLC_GTIME_Msk) | config->lin_header_delay | config->lin_break_length; if (config->lin_node != LIN_INVALID_MODE) { ctrla &= ~(SERCOM_USART_CTRLA_FORM(0xf)); ctrla |= config->lin_node; } #endif /* Set whether module should run in standby. */ if (config->run_in_standby || system_is_debugger_present()) { ctrla |= SERCOM_USART_CTRLA_RUNSTDBY; } /* Wait until synchronization is complete */ _usart_wait_for_sync(module); /* Write configuration to CTRLB */ usart_hw->CTRLB.reg = ctrlb; /* Wait until synchronization is complete */ _usart_wait_for_sync(module); /* Write configuration to CTRLA */ usart_hw->CTRLA.reg = ctrla; #ifdef FEATURE_USART_RS485 if ((usart_hw->CTRLA.reg & SERCOM_USART_CTRLA_FORM_Msk) != \ SERCOM_USART_CTRLA_FORM(0x07)) { usart_hw->CTRLC.reg &= ~(SERCOM_USART_CTRLC_GTIME(0x7)); usart_hw->CTRLC.reg |= SERCOM_USART_CTRLC_GTIME(config->rs485_guard_time); } #endif #ifdef FEATURE_USART_ISO7816 if(config->iso7816_config.enabled) { _usart_wait_for_sync(module); usart_hw->CTRLC.reg = ctrlc; } #endif return STATUS_OK; } /** * \brief Initializes the device * * Initializes the USART device based on the setting specified in the * configuration struct. * * \param[out] module Pointer to USART device * \param[in] hw Pointer to USART hardware instance * \param[in] config Pointer to configuration struct * * \return Status of the initialization. * * \retval STATUS_OK The initialization was successful * \retval STATUS_BUSY The USART module is busy * resetting * \retval STATUS_ERR_DENIED The USART has not been disabled in * advance of initialization * \retval STATUS_ERR_INVALID_ARG The configuration struct contains * invalid configuration * \retval STATUS_ERR_ALREADY_INITIALIZED The SERCOM instance has already been * initialized with different clock * configuration * \retval STATUS_ERR_BAUD_UNAVAILABLE The BAUD rate given by the * configuration * struct cannot be reached with * the current clock configuration */ enum status_code usart_init( struct usart_module *const module, Sercom *const hw, const struct usart_config *const config) { /* Sanity check arguments */ Assert(module); Assert(hw); Assert(config); enum status_code status_code = STATUS_OK; /* Assign module pointer to software instance struct */ module->hw = hw; /* Get a pointer to the hardware module instance */ SercomUsart *const usart_hw = &(module->hw->USART); uint32_t sercom_index = _sercom_get_sercom_inst_index(module->hw); uint32_t pm_index, gclk_index; #if (SAML22) || (SAMC20) pm_index = sercom_index + MCLK_APBCMASK_SERCOM0_Pos; gclk_index = sercom_index + SERCOM0_GCLK_ID_CORE; #elif (SAML21) || (SAMR30) || (SAMR34) || (SAMR35) if (sercom_index == 5) { pm_index = MCLK_APBDMASK_SERCOM5_Pos; gclk_index = SERCOM5_GCLK_ID_CORE; } else { pm_index = sercom_index + MCLK_APBCMASK_SERCOM0_Pos; gclk_index = sercom_index + SERCOM0_GCLK_ID_CORE; } #elif (SAMC21) pm_index = sercom_index + MCLK_APBCMASK_SERCOM0_Pos; if (sercom_index == 5){ gclk_index = SERCOM5_GCLK_ID_CORE; } else { 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 if (usart_hw->CTRLA.reg & SERCOM_USART_CTRLA_SWRST) { /* The module is busy resetting itself */ return STATUS_BUSY; } if (usart_hw->CTRLA.reg & SERCOM_USART_CTRLA_ENABLE) { /* Check the module is enabled */ return STATUS_ERR_DENIED; } /* Turn on module in PM */ #if (SAML21) || (SAMR30) || (SAMR34) || (SAMR35) if (sercom_index == 5) { system_apb_clock_set_mask(SYSTEM_CLOCK_APB_APBD, 1 << pm_index); } 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); /* Set character size */ module->character_size = config->character_size; /* Set transmitter and receiver status */ module->receiver_enabled = config->receiver_enable; module->transmitter_enabled = config->transmitter_enable; #ifdef FEATURE_USART_LIN_SLAVE module->lin_slave_enabled = config->lin_slave_enable; #endif #ifdef FEATURE_USART_START_FRAME_DECTION module->start_frame_detection_enabled = config->start_frame_detection_enable; #endif #ifdef FEATURE_USART_ISO7816 module->iso7816_mode_enabled = config->iso7816_config.enabled; #endif /* Set configuration according to the config struct */ status_code = _usart_set_config(module, config); if(status_code != STATUS_OK) { return status_code; } struct system_pinmux_config pin_conf; system_pinmux_get_config_defaults(&pin_conf); pin_conf.direction = SYSTEM_PINMUX_PIN_DIR_INPUT; 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); } } #if USART_CALLBACK_MODE == true /* Initialize parameters */ for (uint32_t i = 0; i < USART_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->callback_reg_mask = 0x00; module->callback_enable_mask = 0x00; module->rx_status = STATUS_OK; module->tx_status = STATUS_OK; /* Set interrupt handler and register USART software module struct in * look-up table */ uint8_t instance_index = _sercom_get_sercom_inst_index(module->hw); _sercom_set_handler(instance_index, _usart_interrupt_handler); _sercom_instances[instance_index] = module; #endif return status_code; } /** * \brief Transmit a character via the USART * * This blocking function will transmit a single character via the * USART. * * \param[in] module Pointer to the software instance struct * \param[in] tx_data Data to transfer * * \return Status of the operation. * \retval STATUS_OK If the operation was completed * \retval STATUS_BUSY If the operation was not completed, due to the USART * module being busy * \retval STATUS_ERR_DENIED If the transmitter is not enabled */ enum status_code usart_write_wait( struct usart_module *const module, const uint16_t tx_data) { /* Sanity check arguments */ Assert(module); Assert(module->hw); /* Get a pointer to the hardware module instance */ SercomUsart *const usart_hw = &(module->hw->USART); /* Check that the transmitter is enabled */ if (!(module->transmitter_enabled)) { return STATUS_ERR_DENIED; } #if USART_CALLBACK_MODE == true /* Check if the USART is busy doing asynchronous operation. */ if (module->remaining_tx_buffer_length > 0) { return STATUS_BUSY; } #else /* Check if USART is ready for new data */ if (!(usart_hw->INTFLAG.reg & SERCOM_USART_INTFLAG_DRE)) { /* Return error code */ return STATUS_BUSY; } #endif /* Write data to USART module */ usart_hw->DATA.reg = tx_data; while (!(usart_hw->INTFLAG.reg & SERCOM_USART_INTFLAG_TXC)) { /* Wait until data is sent */ } return STATUS_OK; } /** * \brief Receive a character via the USART * * This blocking function will receive a character via the USART. * * \param[in] module Pointer to the software instance struct * \param[out] rx_data Pointer to received data * * \return Status of the operation. * \retval STATUS_OK If the operation was completed * \retval STATUS_BUSY If the operation was not completed, * due to the USART module being busy * \retval STATUS_ERR_BAD_FORMAT If the operation was not completed, * due to configuration mismatch between USART * and the sender * \retval STATUS_ERR_BAD_OVERFLOW If the operation was not completed, * due to the baudrate being too low or the * system frequency being too high * \retval STATUS_ERR_BAD_DATA If the operation was not completed, due to * data being corrupted * \retval STATUS_ERR_DENIED If the receiver is not enabled */ enum status_code usart_read_wait( struct usart_module *const module, uint16_t *const rx_data) { /* Sanity check arguments */ Assert(module); Assert(module->hw); /* Error variable */ uint8_t error_code; /* Get a pointer to the hardware module instance */ SercomUsart *const usart_hw = &(module->hw->USART); /* Check that the receiver is enabled */ if (!(module->receiver_enabled)) { return STATUS_ERR_DENIED; } #if USART_CALLBACK_MODE == true /* Check if the USART is busy doing asynchronous operation. */ if (module->remaining_rx_buffer_length > 0) { return STATUS_BUSY; } #endif /* Check if USART has new data */ if (!(usart_hw->INTFLAG.reg & SERCOM_USART_INTFLAG_RXC)) { /* Return error code */ return STATUS_BUSY; } /* Read out the status code and mask away all but the 3 LSBs*/ error_code = (uint8_t)(usart_hw->STATUS.reg & SERCOM_USART_STATUS_MASK); /* Check if an error has occurred during the receiving */ if (error_code) { /* Check which error occurred */ if (error_code & SERCOM_USART_STATUS_FERR) { /* Clear flag by writing a 1 to it and * return with an error code */ usart_hw->STATUS.reg = SERCOM_USART_STATUS_FERR; return STATUS_ERR_BAD_FORMAT; } else if (error_code & SERCOM_USART_STATUS_BUFOVF) { /* Clear flag by writing a 1 to it and * return with an error code */ usart_hw->STATUS.reg = SERCOM_USART_STATUS_BUFOVF; return STATUS_ERR_OVERFLOW; } else if (error_code & SERCOM_USART_STATUS_PERR) { /* Clear flag by writing a 1 to it and * return with an error code */ usart_hw->STATUS.reg = SERCOM_USART_STATUS_PERR; return STATUS_ERR_BAD_DATA; } #ifdef FEATURE_USART_LIN_SLAVE else if (error_code & SERCOM_USART_STATUS_ISF) { /* Clear flag by writing 1 to it and * return with an error code */ usart_hw->STATUS.reg = SERCOM_USART_STATUS_ISF; return STATUS_ERR_PROTOCOL; } #endif #ifdef FEATURE_USART_COLLISION_DECTION else if (error_code & SERCOM_USART_STATUS_COLL) { /* Clear flag by writing 1 to it * return with an error code */ usart_hw->STATUS.reg = SERCOM_USART_STATUS_COLL; return STATUS_ERR_PACKET_COLLISION; } #endif } /* Read data from USART module */ *rx_data = usart_hw->DATA.reg; return STATUS_OK; } /** * \brief Transmit a buffer of characters via the USART * * This blocking function will transmit a block of \c length characters * via the USART. * * \note Using this function in combination with the interrupt (\c _job) functions is * not recommended as it has no functionality to check if there is an * ongoing interrupt driven operation running or not. * * \param[in] module Pointer to USART software instance struct * \param[in] tx_data Pointer to data to transmit * \param[in] length Number of characters to transmit * * \note If using 9-bit data, the array that *tx_data point to should be defined * as uint16_t array and should be casted to uint8_t* pointer. Because it * is an address pointer, the highest byte is not discarded. For example: * \code #define TX_LEN 3 uint16_t tx_buf[TX_LEN] = {0x0111, 0x0022, 0x0133}; usart_write_buffer_wait(&module, (uint8_t*)tx_buf, TX_LEN); \endcode * * \return Status of the operation. * \retval STATUS_OK If operation was completed * \retval STATUS_ERR_INVALID_ARG If operation was not completed, due to invalid * arguments * \retval STATUS_ERR_TIMEOUT If operation was not completed, due to USART * module timing out * \retval STATUS_ERR_DENIED If the transmitter is not enabled */ enum status_code usart_write_buffer_wait( struct usart_module *const module, const uint8_t *tx_data, uint16_t length) { /* Sanity check arguments */ Assert(module); Assert(module->hw); /* Check if the buffer length is valid */ if (length == 0) { return STATUS_ERR_INVALID_ARG; } /* Check that the transmitter is enabled */ if (!(module->transmitter_enabled)) { return STATUS_ERR_DENIED; } /* Get a pointer to the hardware module instance */ SercomUsart *const usart_hw = &(module->hw->USART); uint16_t tx_pos = 0; /* Blocks while buffer is being transferred */ while (length--) { /* Wait for the USART to be ready for new data and abort * operation if it doesn't get ready within the timeout*/ for (uint32_t i = 0; i <= USART_TIMEOUT; i++) { if (usart_hw->INTFLAG.reg & SERCOM_USART_INTFLAG_DRE) { break; } else if (i == USART_TIMEOUT) { return STATUS_ERR_TIMEOUT; } } /* Data to send is at least 8 bits long */ uint16_t data_to_send = tx_data[tx_pos++]; /* Check if the character size exceeds 8 bit */ if (module->character_size == USART_CHARACTER_SIZE_9BIT) { data_to_send |= (tx_data[tx_pos++] << 8); } /* Send the data through the USART module */ usart_write_wait(module, data_to_send); } /* Wait until Transmit is complete or timeout */ for (uint32_t i = 0; i <= USART_TIMEOUT; i++) { if (usart_hw->INTFLAG.reg & SERCOM_USART_INTFLAG_TXC) { break; } else if (i == USART_TIMEOUT) { return STATUS_ERR_TIMEOUT; } } return STATUS_OK; } /** * \brief Receive a buffer of \c length characters via the USART * * This blocking function will receive a block of \c length characters * via the USART. * * \note Using this function in combination with the interrupt (\c *_job) * functions is not recommended as it has no functionality to check if * there is an ongoing interrupt driven operation running or not. * * \param[in] module Pointer to USART software instance struct * \param[out] rx_data Pointer to receive buffer * \param[in] length Number of characters to receive * * \note If using 9-bit data, the array that *rx_data point to should be defined * as uint16_t array and should be casted to uint8_t* pointer. Because it * is an address pointer, the highest byte is not discarded. For example: * \code #define RX_LEN 3 uint16_t rx_buf[RX_LEN] = {0x0,}; usart_read_buffer_wait(&module, (uint8_t*)rx_buf, RX_LEN); \endcode * * \return Status of the operation. * \retval STATUS_OK If operation was completed * \retval STATUS_ERR_INVALID_ARG If operation was not completed, due to an * invalid argument being supplied * \retval STATUS_ERR_TIMEOUT If operation was not completed, due * to USART module timing out * \retval STATUS_ERR_BAD_FORMAT If the operation was not completed, * due to a configuration mismatch * between USART and the sender * \retval STATUS_ERR_BAD_OVERFLOW If the operation was not completed, * due to the baudrate being too low or the * system frequency being too high * \retval STATUS_ERR_BAD_DATA If the operation was not completed, due * to data being corrupted * \retval STATUS_ERR_DENIED If the receiver is not enabled */ enum status_code usart_read_buffer_wait( struct usart_module *const module, uint8_t *rx_data, uint16_t length) { /* Sanity check arguments */ Assert(module); Assert(module->hw); /* Check if the buffer length is valid */ if (length == 0) { return STATUS_ERR_INVALID_ARG; } /* Check that the receiver is enabled */ if (!(module->receiver_enabled)) { return STATUS_ERR_DENIED; } /* Get a pointer to the hardware module instance */ SercomUsart *const usart_hw = &(module->hw->USART); uint16_t rx_pos = 0; /* Blocks while buffer is being received */ while (length--) { /* Wait for the USART to have new data and abort operation if it * doesn't get ready within the timeout*/ for (uint32_t i = 0; i <= USART_TIMEOUT; i++) { if (usart_hw->INTFLAG.reg & SERCOM_USART_INTFLAG_RXC) { break; } else if (i == USART_TIMEOUT) { return STATUS_ERR_TIMEOUT; } } enum status_code retval; uint16_t received_data = 0; retval = usart_read_wait(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 == USART_CHARACTER_SIZE_9BIT) { rx_data[rx_pos++] = (received_data >> 8); } } return STATUS_OK; }