4  * \brief SAM D20 SERCOM USART Asynchronous Driver
\r 
   6  * Copyright (C) 2012-2013 Atmel Corporation. All rights reserved.
\r 
  12  * Redistribution and use in source and binary forms, with or without
\r 
  13  * modification, are permitted provided that the following conditions are met:
\r 
  15  * 1. Redistributions of source code must retain the above copyright notice,
\r 
  16  *    this list of conditions and the following disclaimer.
\r 
  18  * 2. Redistributions in binary form must reproduce the above copyright notice,
\r 
  19  *    this list of conditions and the following disclaimer in the documentation
\r 
  20  *    and/or other materials provided with the distribution.
\r 
  22  * 3. The name of Atmel may not be used to endorse or promote products derived
\r 
  23  *    from this software without specific prior written permission.
\r 
  25  * 4. This software may only be redistributed and used in connection with an
\r 
  26  *    Atmel microcontroller product.
\r 
  28  * THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR IMPLIED
\r 
  29  * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
\r 
  30  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE
\r 
  31  * EXPRESSLY AND SPECIFICALLY DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR
\r 
  32  * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
\r 
  33  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
\r 
  34  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
\r 
  35  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
\r 
  36  * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
\r 
  37  * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
\r 
  38  * POSSIBILITY OF SUCH DAMAGE.
\r 
  44 #include "usart_interrupt.h"
\r 
  48  * Asynchronous write of a buffer with a given length
\r 
  50  * \param[in]  module   Pointer to USART software instance struct
\r 
  51  * \param[in]  tx_data  Pointer to data to be transmitted
\r 
  52  * \param[in]  length   Length of data buffer
\r 
  55 void _usart_write_buffer(
\r 
  56                 struct usart_module *const module,
\r 
  60         /* Sanity check arguments */
\r 
  64         /* Get a pointer to the hardware module instance */
\r 
  65         SercomUsart *const usart_hw = &(module->hw->USART);
\r 
  67         /* Write parameters to the device instance */
\r 
  68         module->remaining_tx_buffer_length = length;
\r 
  69         module->tx_buffer_ptr              = tx_data;
\r 
  70         module->tx_status                  = STATUS_BUSY;
\r 
  72         /* Enable the Data Register Empty Interrupt */
\r 
  73         usart_hw->INTENSET.reg = SERCOM_USART_INTFLAG_DRE;
\r 
  78  * Asynchronous read of a buffer with a given length
\r 
  80  * \param[in]  module   Pointer to USART software instance struct
\r 
  81  * \param[in]  rx_data  Pointer to data to be received
\r 
  82  * \param[in]  length   Length of data buffer
\r 
  85 void _usart_read_buffer(
\r 
  86                 struct usart_module *const module,
\r 
  90         /* Sanity check arguments */
\r 
  94         /* Get a pointer to the hardware module instance */
\r 
  95         SercomUsart *const usart_hw = &(module->hw->USART);
\r 
  97         /* Set length for the buffer and the pointer, and let
\r 
  98          * the interrupt handler do the rest */
\r 
  99         module->remaining_rx_buffer_length = length;
\r 
 100         module->rx_buffer_ptr              = rx_data;
\r 
 101         module->rx_status                  = STATUS_BUSY;
\r 
 103         /* Enable the RX Complete Interrupt */
\r 
 104         usart_hw->INTENSET.reg = SERCOM_USART_INTFLAG_RXC;
\r 
 108  * \brief Registers a callback
\r 
 110  * Registers a callback function which is implemented by the user.
\r 
 112  * \note The callback must be enabled by \ref usart_enable_callback,
\r 
 113  *       in order for the interrupt handler to call it when the conditions for
\r 
 114  *       the callback type are met.
\r 
 116  * \param[in]  module         Pointer to USART software instance struct
\r 
 117  * \param[in]  callback_func  Pointer to callback function
\r 
 118  * \param[in]  callback_type  Callback type given by an enum
\r 
 121 void usart_register_callback(
\r 
 122                 struct usart_module *const module,
\r 
 123                 usart_callback_t callback_func,
\r 
 124                 enum usart_callback callback_type)
\r 
 126         /* Sanity check arguments */
\r 
 128         Assert(callback_func);
\r 
 130         /* Register callback function */
\r 
 131         module->callback[callback_type] = callback_func;
\r 
 133         /* Set the bit corresponding to the callback_type */
\r 
 134         module->callback_reg_mask |= (1 << callback_type);
\r 
 138  * \brief Unregisters a callback
\r 
 140  * Unregisters a callback function which is implemented by the user.
\r 
 142  * \param[in,out]  module         Pointer to USART software instance struct
\r 
 143  * \param[in]      callback_type  Callback type given by an enum
\r 
 146 void usart_unregister_callback(
\r 
 147                 struct usart_module *const module,
\r 
 148                 enum usart_callback callback_type)
\r 
 150         /* Sanity check arguments */
\r 
 153         /* Unregister callback function */
\r 
 154         module->callback[callback_type] = NULL;
\r 
 156         /* Clear the bit corresponding to the callback_type */
\r 
 157         module->callback_reg_mask &= ~(1 << callback_type);
\r 
 161  * \brief Asynchronous write a single char
\r 
 163  * Sets up the driver to write the data given. If registered and enabled,
\r 
 164  * a callback function will be called when the transmit is completed.
\r 
 166  * \param[in]  module   Pointer to USART software instance struct
\r 
 167  * \param[in]  tx_data  Data to transfer
\r 
 169  * \returns Status of the operation
\r 
 170  * \retval STATUS_OK         If operation was completed
\r 
 171  * \retval STATUS_BUSY       If operation was not completed, due to the
\r 
 172  *                           USART module being busy
\r 
 173  * \retval STATUS_ERR_DENIED If the transmitter is not enabled
\r 
 175 enum status_code usart_write_job(
\r 
 176                 struct usart_module *const module,
\r 
 177                 const uint16_t tx_data)
\r 
 179         /* Sanity check arguments */
\r 
 181         Assert(module->hw);
\r 
 182         /* Check if the USART transmitter is busy */
\r 
 183         if (module->remaining_tx_buffer_length > 0) {
\r 
 184                 return STATUS_BUSY;
\r 
 187         /* Check that the transmitter is enabled */
\r 
 188         if (!(module->transmitter_enabled)) {
\r 
 189                 return STATUS_ERR_DENIED;
\r 
 192         /* Call internal write buffer function with length 1 */
\r 
 193         _usart_write_buffer(module, (uint8_t *)&tx_data, 1);
\r 
 199  * \brief Asynchronous read a single char
\r 
 201  * Sets up the driver to read data from the USART module to the data
\r 
 202  * pointer given. If registered and enabled, a callback will be called
\r 
 203  * when the receiving is completed.
\r 
 205  * \param[in]   module   Pointer to USART software instance struct
\r 
 206  * \param[out]  rx_data  Pointer to where received data should be put
\r 
 208  * \returns Status of the operation
\r 
 209  * \retval  STATUS_OK    If operation was completed
\r 
 210  * \retval  STATUS_BUSY  If operation was not completed,
\r 
 212 enum status_code usart_read_job(
\r 
 213                 struct usart_module *const module,
\r 
 214                 uint16_t *const rx_data)
\r 
 216         /* Sanity check arguments */
\r 
 219         /* Check if the USART receiver is busy */
\r 
 220         if (module->remaining_rx_buffer_length > 0) {
\r 
 221                 return STATUS_BUSY;
\r 
 224         /* Call internal read buffer function with length 1 */
\r 
 225         _usart_read_buffer(module, (uint8_t *)rx_data, 1);
\r 
 231  * \brief Asynchronous buffer write
\r 
 233  * Sets up the driver to write a given buffer over the USART. If registered and
\r 
 234  * enabled, a callback function will be called.
\r 
 236  * \param[in]  module   Pointer to USART software instance struct
\r 
 237  * \param[in]  tx_data  Pointer do data buffer to transmit
\r 
 238  * \param[in]  length   Length of the data to transmit
\r 
 240  * \returns Status of the operation
\r 
 241  * \retval STATUS_OK              If operation was completed successfully.
\r 
 242  * \retval STATUS_BUSY            If operation was not completed, due to the
\r 
 243  *                                USART module being busy
\r 
 244  * \retval STATUS_ERR_INVALID_ARG If operation was not completed, due to invalid
\r 
 246  * \retval STATUS_ERR_DENIED      If the transmitter is not enabled
\r 
 248 enum status_code usart_write_buffer_job(
\r 
 249                 struct usart_module *const module,
\r 
 253         /* Sanity check arguments */
\r 
 257                 return STATUS_ERR_INVALID_ARG;
\r 
 260         /* Check if the USART transmitter is busy */
\r 
 261         if (module->remaining_tx_buffer_length > 0) {
\r 
 262                 return STATUS_BUSY;
\r 
 265         /* Check that the receiver is enabled */
\r 
 266         if (!(module->transmitter_enabled)) {
\r 
 267                 return STATUS_ERR_DENIED;
\r 
 270         /* Issue internal asynchronous write */
\r 
 271         _usart_write_buffer(module, tx_data, length);
\r 
 277  * \brief Asynchronous buffer read
\r 
 279  * Sets up the driver to read from the USART to a given buffer. If registered
\r 
 280  * and enabled, a callback function will be called.
\r 
 282  * \param[in]  module   Pointer to USART software instance struct
\r 
 283  * \param[out] rx_data  Pointer to data buffer to receive
\r 
 284  * \param[in]  length   Data buffer length
\r 
 286  * \returns Status of the operation
\r 
 287  * \retval STATUS_OK              If operation was completed
\r 
 288  * \retval STATUS_BUSY            If operation was not completed, due to the
\r 
 289  *                                USART module being busy
\r 
 290  * \retval STATUS_ERR_INVALID_ARG If operation was not completed, due to invalid
\r 
 292  * \retval STATUS_ERR_DENIED      If the transmitter is not enabled
\r 
 294 enum status_code usart_read_buffer_job(
\r 
 295                 struct usart_module *const module,
\r 
 299         /* Sanity check arguments */
\r 
 304                 return STATUS_ERR_INVALID_ARG;
\r 
 307         /* Check that the receiver is enabled */
\r 
 308         if (!(module->receiver_enabled)) {
\r 
 309                 return STATUS_ERR_DENIED;
\r 
 312         /* Check if the USART receiver is busy */
\r 
 313         if (module->remaining_rx_buffer_length > 0) {
\r 
 314                 return STATUS_BUSY;
\r 
 317         /* Issue internal asynchronous read */
\r 
 318         _usart_read_buffer(module, rx_data, length);
\r 
 324  * \brief Cancels ongoing read/write operation
\r 
 326  * Cancels the ongoing read/write operation modifying parameters in the
\r 
 327  * USART software struct.
\r 
 329  * \param[in]  module            Pointer to USART software instance struct
\r 
 330  * \param[in]  transceiver_type  Transfer type to cancel
\r 
 332 void usart_abort_job(
\r 
 333                 struct usart_module *const module,
\r 
 334                 enum usart_transceiver_type transceiver_type)
\r 
 336         /* Sanity check arguments */
\r 
 338         Assert(module->hw);
\r 
 340         /* Get a pointer to the hardware module instance */
\r 
 341         SercomUsart *const usart_hw = &(module->hw->USART);
\r 
 343         switch(transceiver_type) {
\r 
 344                 case USART_TRANSCEIVER_RX:
\r 
 345                         /* Clear the interrupt flag in order to prevent the receive
\r 
 346                          * complete callback to fire */
\r 
 347                         usart_hw->INTFLAG.reg |= SERCOM_USART_INTFLAG_RXC;
\r 
 349                         /* Clear the software reception buffer */
\r 
 350                         module->remaining_rx_buffer_length = 0;
\r 
 354                 case USART_TRANSCEIVER_TX:
\r 
 355                         /* Clear the interrupt flag in order to prevent the receive
\r 
 356                          * complete callback to fire */
\r 
 357                         usart_hw->INTFLAG.reg |= SERCOM_USART_INTFLAG_TXC;
\r 
 359                         /* Clear the software reception buffer */
\r 
 360                         module->remaining_tx_buffer_length = 0;
\r 
 367  * \brief Get status from the ongoing or last asynchronous transfer operation
\r 
 369  * Returns the error from a given ongoing or last asynchronous transfer operation.
\r 
 370  * Either from a read or write transfer.
\r 
 372  * \param[in]  module            Pointer to USART software instance struct
\r 
 373  * \param[in]  transceiver_type  Transfer type to check
\r 
 375  * \return Status of the given job.
\r 
 376  * \retval STATUS_OK               No error occurred during the last transfer
\r 
 377  * \retval STATUS_BUSY             A transfer is ongoing
\r 
 378  * \retval STATUS_ERR_BAD_DATA     The last operation was aborted due to a
\r 
 379  *                                 parity error. The transfer could be affected
\r 
 380  *                                 by external noise.
\r 
 381  * \retval STATUS_ERR_BAD_FORMAT   The last operation was aborted due to a
\r 
 383  * \retval STATUS_ERR_OVERFLOW     The last operation was aborted due to a
\r 
 385  * \retval STATUS_ERR_INVALID_ARG  An invalid transceiver enum given.
\r 
 387 enum status_code usart_get_job_status(
\r 
 388                 struct usart_module *const module,
\r 
 389                 enum usart_transceiver_type transceiver_type)
\r 
 391         /* Sanity check arguments */
\r 
 394         /* Variable for status code */
\r 
 395         enum status_code status_code;
\r 
 397         switch(transceiver_type) {
\r 
 398         case USART_TRANSCEIVER_RX:
\r 
 399                         status_code = module->rx_status;
\r 
 402         case USART_TRANSCEIVER_TX:
\r 
 403                         status_code = module->tx_status;
\r 
 407                         status_code = STATUS_ERR_INVALID_ARG;
\r 
 411         return status_code;
\r 
 416  * Handles interrupts as they occur, and it will run callback functions
\r 
 417  * which are registered and enabled.
\r 
 419  * \param[in]  instance  ID of the SERCOM instance calling the interrupt
\r 
 422 void _usart_interrupt_handler(
\r 
 425         /* Temporary variables */
\r 
 426         uint16_t interrupt_status;
\r 
 427         uint16_t callback_status;
\r 
 428         uint8_t error_code;
\r 
 431         /* Get device instance from the look-up table */
\r 
 432         struct usart_module *module
\r 
 433                 = (struct usart_module *)_sercom_instances[instance];
\r 
 435         /* Pointer to the hardware module instance */
\r 
 436         SercomUsart *const usart_hw
\r 
 437                 = &(module->hw->USART);
\r 
 439         /* Wait for the synchronization to complete */
\r 
 440         _usart_wait_for_sync(module);
\r 
 442         /* Read and mask interrupt flag register */
\r 
 443         interrupt_status = usart_hw->INTFLAG.reg;
\r 
 444         callback_status = module->callback_reg_mask
\r 
 445                         &module->callback_enable_mask;
\r 
 447         /* Check if a DATA READY interrupt has occurred,
\r 
 448          * and if there is more to transfer */
\r 
 449         if (interrupt_status & SERCOM_USART_INTFLAG_DRE) {
\r 
 450                 if (module->remaining_tx_buffer_length) {
\r 
 451                         /* Write value will be at least 8-bits long */
\r 
 452                         uint16_t data_to_send = *(module->tx_buffer_ptr);
\r 
 453                         /* Increment 8-bit pointer */
\r 
 454                         (module->tx_buffer_ptr)++;
\r 
 456                         if (module->character_size == USART_CHARACTER_SIZE_9BIT) {
\r 
 457                                 data_to_send = (*(module->tx_buffer_ptr) << 8);
\r 
 458                                 /* Increment 8-bit pointer */
\r 
 459                                 (module->tx_buffer_ptr)++;
\r 
 461                         /* Write the data to send */
\r 
 462                         usart_hw->DATA.reg = (data_to_send & SERCOM_USART_DATA_MASK);
\r 
 464                         if (--(module->remaining_tx_buffer_length) == 0) {
\r 
 465                                 /* Disable the Data Register Empty Interrupt */
\r 
 466                                 usart_hw->INTENCLR.reg = SERCOM_USART_INTFLAG_DRE;
\r 
 467                                 /* Enable Transmission Complete interrupt */
\r 
 468                                 usart_hw->INTENSET.reg = SERCOM_USART_INTFLAG_TXC;
\r 
 472                         usart_hw->INTENCLR.reg = SERCOM_USART_INTFLAG_DRE;
\r 
 475         /* Check if the Transmission Complete interrupt has occurred and
\r 
 476          * that the transmit buffer is empty */
\r 
 478         if (interrupt_status & SERCOM_USART_INTFLAG_TXC) {
\r 
 480                 /* Disable TX Complete Interrupt, and set STATUS_OK */
\r 
 481                 usart_hw->INTENCLR.reg = SERCOM_USART_INTFLAG_TXC;
\r 
 482                 module->tx_status = STATUS_OK;
\r 
 484                 /* Run callback if registered and enabled */
\r 
 485                 if (callback_status & (1 << USART_CALLBACK_BUFFER_TRANSMITTED)) {
\r 
 486                         (*(module->callback[USART_CALLBACK_BUFFER_TRANSMITTED]))(module);
\r 
 489         /* Check if the Receive Complete interrupt has occurred, and that
\r 
 490          * there's more data to receive */
\r 
 492         if (interrupt_status & SERCOM_USART_INTFLAG_RXC) {
\r 
 494                 if (module->remaining_rx_buffer_length) {
\r 
 495                         /* Read out the status code and mask away all but the 4 LSBs*/
\r 
 496                         error_code = (uint8_t)(usart_hw->STATUS.reg & SERCOM_USART_STATUS_MASK);
\r 
 498                         /* Check if an error has occurred during the receiving */
\r 
 500                                 /* Check which error occurred */
\r 
 501                                 if (error_code & SERCOM_USART_STATUS_FERR) {
\r 
 502                                         /* Store the error code and clear flag by writing 1 to it */
\r 
 503                                         module->rx_status = STATUS_ERR_BAD_FORMAT;
\r 
 504                                         usart_hw->STATUS.reg |= SERCOM_USART_STATUS_FERR;
\r 
 505                                 } else if (error_code & SERCOM_USART_STATUS_BUFOVF) {
\r 
 506                                         /* Store the error code and clear flag by writing 1 to it */
\r 
 507                                         module->rx_status = STATUS_ERR_OVERFLOW;
\r 
 508                                         usart_hw->STATUS.reg |= SERCOM_USART_STATUS_BUFOVF;
\r 
 509                                 } else if (error_code & SERCOM_USART_STATUS_PERR) {
\r 
 510                                         /* Store the error code and clear flag by writing 1 to it */
\r 
 511                                         module->rx_status = STATUS_ERR_BAD_DATA;
\r 
 512                                         usart_hw->STATUS.reg |= SERCOM_USART_STATUS_PERR;
\r 
 515                                 /* Run callback if registered and enabled */
\r 
 516                                 if (callback_status
\r 
 517                                                 & (1 << USART_CALLBACK_ERROR)) {
\r 
 518                                         (*(module->callback[USART_CALLBACK_ERROR]))(module);
\r 
 523                                 /* Read current packet from DATA register,
\r 
 524                                  * increment buffer pointer and decrement buffer length */
\r 
 525                                 uint16_t received_data = (usart_hw->DATA.reg & SERCOM_USART_DATA_MASK);
\r 
 527                                 /* Read value will be at least 8-bits long */
\r 
 528                                 *(module->rx_buffer_ptr) = received_data;
\r 
 529                                 /* Increment 8-bit pointer */
\r 
 530                                 module->rx_buffer_ptr += 1;
\r 
 532                                 if (module->character_size == USART_CHARACTER_SIZE_9BIT) {
\r 
 533                                         /* 9-bit data, write next received byte to the buffer */
\r 
 534                                         *(module->rx_buffer_ptr) = (received_data >> 8);
\r 
 535                                         /* Increment 8-bit pointer */
\r 
 536                                         module->rx_buffer_ptr += 1;
\r 
 539                                 /* Check if the last character have been received */
\r 
 540                                 if(--(module->remaining_rx_buffer_length) == 0) {
\r 
 541                                         /* Disable RX Complete Interrupt,
\r 
 542                                          * and set STATUS_OK */
\r 
 543                                         usart_hw->INTENCLR.reg = SERCOM_USART_INTFLAG_RXC;
\r 
 544                                         module->rx_status = STATUS_OK;
\r 
 546                                         /* Run callback if registered and enabled */
\r 
 547                                         if (callback_status
\r 
 548                                                         & (1 << USART_CALLBACK_BUFFER_RECEIVED)) {
\r 
 549                                                 (*(module->callback[USART_CALLBACK_BUFFER_RECEIVED]))(module);
\r 
 554                         /* This should not happen. Disable Receive Complete interrupt. */
\r 
 555                         usart_hw->INTENCLR.reg = SERCOM_USART_INTFLAG_RXC;
\r