t
Fork of mbed-dev by
Diff: targets/hal/TARGET_Atmel/TARGET_SAM_CortexM0P/drivers/sercom/usart/usart.c
- Revision:
- 15:a81a8d6c1dfe
diff -r d797fbdad187 -r a81a8d6c1dfe targets/hal/TARGET_Atmel/TARGET_SAM_CortexM0P/drivers/sercom/usart/usart.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/targets/hal/TARGET_Atmel/TARGET_SAM_CortexM0P/drivers/sercom/usart/usart.c Wed Nov 04 16:30:11 2015 +0000 @@ -0,0 +1,752 @@ +/** + * \file + * + * \brief SAM SERCOM USART Driver + * + * Copyright (C) 2012-2015 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 "usart.h" +#include <pinmux.h> +#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 = sercom_index + SERCOM0_GCLK_ID_CORE; + + /* Cache new register values to minimize the number of register writes */ + uint32_t ctrla = 0; + uint32_t ctrlb = 0; + uint16_t baud = 0; + + 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; + + /* Get baud value from mode and clock */ + switch (config->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_IRDA + if(config->encoding_format_enable) { + usart_hw->RXPL.reg = config->receive_pulse_length; + } +#endif + + /* Wait until synchronization is complete */ + _usart_wait_for_sync(module); + + /*Set baud val */ + usart_hw->BAUD.reg = baud; + + /* Set sample mode */ + ctrla |= config->transfer_mode; + + if (config->use_external_clock == false) { + ctrla |= SERCOM_USART_CTRLA_MODE(0x1); + } else { + ctrla |= SERCOM_USART_CTRLA_MODE(0x0); + } + + /* Set stopbits, character size and enable transceivers */ + ctrlb = (uint32_t)config->stopbits | (uint32_t)config->character_size | +#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); + + /* 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_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 + usart_hw->CTRLC.reg &= ~(SERCOM_USART_CTRLC_GTIME(0x7)); + usart_hw->CTRLC.reg |= SERCOM_USART_CTRLC_GTIME(config->rs485_guard_time); +#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 have 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 (SAML21) || (SAMC20) || (SAMC21) +#if (SAML21) + 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; + } +#else + pm_index = sercom_index + MCLK_APBCMASK_SERCOM0_Pos; + gclk_index = sercom_index + SERCOM0_GCLK_ID_CORE; +#endif +#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) + 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 + /* 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 + + /* Wait until synchronization is complete */ + _usart_wait_for_sync(module); + + /* 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; + } + + /* Wait until synchronization is complete */ + _usart_wait_for_sync(module); + + /* 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); + + /* Wait until synchronization is complete */ + _usart_wait_for_sync(module); + + 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; +}