mbed library sources
Fork of mbed-src by
Diff: targets/hal/TARGET_Atmel/TARGET_SAM21/drivers/sercom/usart/usart.c
- Revision:
- 613:bc40b8d2aec4
- Parent:
- 612:fba1c7dc54c0
- Child:
- 614:9d86c2ae5de0
--- a/targets/hal/TARGET_Atmel/TARGET_SAM21/drivers/sercom/usart/usart.c Tue Aug 18 15:00:09 2015 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,679 +0,0 @@ -#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) { -#ifdef FEATURE_USART_LIN_SLAVE - if(config->lin_slave_enable) { - ctrla |= SERCOM_USART_CTRLA_FORM(0x5); - } else { - ctrla |= SERCOM_USART_CTRLA_FORM(1); - } -#else - ctrla |= SERCOM_USART_CTRLA_FORM(1); -#endif - 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 - } - - /* 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; - - 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); -#if (SAML21) - uint32_t pm_index = sercom_index + MCLK_APBCMASK_SERCOM0_Pos; -#else - uint32_t pm_index = sercom_index + PM_APBCMASK_SERCOM0_Pos; -#endif - uint32_t gclk_index = sercom_index + SERCOM0_GCLK_ID_CORE; - - 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 */ - system_apb_clock_set_mask(SYSTEM_CLOCK_APB_APBC, 1 << pm_index); - - /* 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; -}