text: /*******************************************************************************
    SERCOM Universal Synchronous/Asynchrnous Receiver/Transmitter PLIB
  
    Company
      Microchip Technology Inc.
  
    File Name
      plib_sercom1_usart.c
  
    Summary
      USART peripheral library interface.
  
    Description
      This file defines the interface to the USART peripheral library. This
      library provides access to and control of the associated peripheral
      instance.
  
    Remarks:
      None.
  *******************************************************************************/
  
  /*******************************************************************************
  * Copyright (C) 2018 Microchip Technology Inc. and its subsidiaries.
  *
  * 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.
  *******************************************************************************/
  
  // *****************************************************************************
  // *****************************************************************************
  // Section: Included Files
  // *****************************************************************************
  // *****************************************************************************
  
  #include "interrupts.h"
  #include "plib_sercom1_usart.h"
  
  // *****************************************************************************
  // *****************************************************************************
  // Section: Global Data
  // *****************************************************************************
  // *****************************************************************************
  
  
  /* SERCOM1 USART baud value for 115200 Hz baud rate */
  #define SERCOM1_USART_INT_BAUD_VALUE            (63019UL)
  
  
  // *****************************************************************************
  // *****************************************************************************
  // Section: SERCOM1 USART Interface Routines
  // *****************************************************************************
  // *****************************************************************************
  
  void static SERCOM1_USART_ErrorClear( void )
  {
      uint8_t  u8dummyData = 0U;
      USART_ERROR errorStatus = (USART_ERROR) (SERCOM1_REGS->USART_INT.SERCOM_STATUS & (uint16_t)(SERCOM_USART_INT_STATUS_PERR_Msk | SERCOM_USART_INT_STATUS_FERR_Msk | SERCOM_USART_INT_STATUS_BUFOVF_Msk ));
  
      if(errorStatus != USART_ERROR_NONE)
      {
          /* Clear error flag */
          SERCOM1_REGS->USART_INT.SERCOM_INTFLAG = (uint8_t)SERCOM_USART_INT_INTFLAG_ERROR_Msk;
          /* Clear all errors */
          SERCOM1_REGS->USART_INT.SERCOM_STATUS = (uint16_t)(SERCOM_USART_INT_STATUS_PERR_Msk | SERCOM_USART_INT_STATUS_FERR_Msk | SERCOM_USART_INT_STATUS_BUFOVF_Msk);
  
          /* Flush existing error bytes from the RX FIFO */
          while((SERCOM1_REGS->USART_INT.SERCOM_INTFLAG & (uint8_t)SERCOM_USART_INT_INTFLAG_RXC_Msk) == (uint8_t)SERCOM_USART_INT_INTFLAG_RXC_Msk)
          {
              u8dummyData = (uint8_t)SERCOM1_REGS->USART_INT.SERCOM_DATA;
          }
      }
  
      /* Ignore the warning */
      (void)u8dummyData;
  }
  
  void SERCOM1_USART_Initialize( void )
  {
      /*
       * Configures USART Clock Mode
       * Configures TXPO and RXPO
       * Configures Data Order
       * Configures Standby Mode
       * Configures Sampling rate
       * Configures IBON
       */
      SERCOM1_REGS->USART_INT.SERCOM_CTRLA = SERCOM_USART_INT_CTRLA_MODE_USART_INT_CLK | SERCOM_USART_INT_CTRLA_RXPO(0x1UL) | SERCOM_USART_INT_CTRLA_TXPO(0x0UL) | SERCOM_USART_INT_CTRLA_DORD_Msk | SERCOM_USART_INT_CTRLA_IBON_Msk | SERCOM_USART_INT_CTRLA_FORM(0x0UL) | SERCOM_USART_INT_CTRLA_SAMPR(0UL) ;
  
      /* Configure Baud Rate */
      SERCOM1_REGS->USART_INT.SERCOM_BAUD = (uint16_t)SERCOM_USART_INT_BAUD_BAUD(SERCOM1_USART_INT_BAUD_VALUE);
  
      /*
       * Configures RXEN
       * Configures TXEN
       * Configures CHSIZE
       * Configures Parity
       * Configures Stop bits
       */
      SERCOM1_REGS->USART_INT.SERCOM_CTRLB = SERCOM_USART_INT_CTRLB_CHSIZE_8_BIT | SERCOM_USART_INT_CTRLB_SBMODE_1_BIT | SERCOM_USART_INT_CTRLB_RXEN_Msk | SERCOM_USART_INT_CTRLB_TXEN_Msk;
  
      /* Wait for sync */
      while((SERCOM1_REGS->USART_INT.SERCOM_SYNCBUSY) != 0U)
      {
          /* Do nothing */
      }
  
  
      /* Enable the UART after the configurations */
      SERCOM1_REGS->USART_INT.SERCOM_CTRLA |= SERCOM_USART_INT_CTRLA_ENABLE_Msk;
  
      /* Wait for sync */
      while((SERCOM1_REGS->USART_INT.SERCOM_SYNCBUSY) != 0U)
      {
          /* Do nothing */
      }
  }
  
  uint32_t SERCOM1_USART_FrequencyGet( void )
  {
      return 48000000UL;
  }
  
  bool SERCOM1_USART_SerialSetup( USART_SERIAL_SETUP * serialSetup, uint32_t clkFrequency )
  {
      bool setupStatus       = false;
      uint32_t baudValue     = 0U;
      uint32_t sampleRate    = 0U;
  
      if((serialSetup != NULL) && (serialSetup->baudRate != 0U))
      {
          if(clkFrequency == 0U)
          {
              clkFrequency = SERCOM1_USART_FrequencyGet();
          }
  
          if(clkFrequency >= (16U * serialSetup->baudRate))
          {
              baudValue = 65536U - (uint32_t)(((uint64_t)65536U * 16U * serialSetup->baudRate) / clkFrequency);
              sampleRate = 0U;
          }
          else if(clkFrequency >= (8U * serialSetup->baudRate))
          {
              baudValue = 65536U - (uint32_t)(((uint64_t)65536U * 8U * serialSetup->baudRate) / clkFrequency);
              sampleRate = 2U;
          }
          else if(clkFrequency >= (3U * serialSetup->baudRate))
          {
              baudValue = 65536U - (uint32_t)(((uint64_t)65536U * 3U * serialSetup->baudRate) / clkFrequency);
              sampleRate = 4U;
          }
          else
          {
              /* Do nothing */
          }
  
          if(baudValue != 0U)
          {
              /* Disable the USART before configurations */
              SERCOM1_REGS->USART_INT.SERCOM_CTRLA &= ~SERCOM_USART_INT_CTRLA_ENABLE_Msk;
  
              /* Wait for sync */
              while((SERCOM1_REGS->USART_INT.SERCOM_SYNCBUSY) != 0U)
              {
                  /* Do nothing */
              }
  
              /* Configure Baud Rate */
              SERCOM1_REGS->USART_INT.SERCOM_BAUD = (uint16_t)SERCOM_USART_INT_BAUD_BAUD(baudValue);
  
              /* Configure Parity Options */
              if(serialSetup->parity == USART_PARITY_NONE)
              {
                  SERCOM1_REGS->USART_INT.SERCOM_CTRLA =  (SERCOM1_REGS->USART_INT.SERCOM_CTRLA & ~(SERCOM_USART_INT_CTRLA_SAMPR_Msk | SERCOM_USART_INT_CTRLA_FORM_Msk)) | SERCOM_USART_INT_CTRLA_FORM(0x0UL) | SERCOM_USART_INT_CTRLA_SAMPR((uint32_t)sampleRate); 
                  SERCOM1_REGS->USART_INT.SERCOM_CTRLB = (SERCOM1_REGS->USART_INT.SERCOM_CTRLB & ~(SERCOM_USART_INT_CTRLB_CHSIZE_Msk | SERCOM_USART_INT_CTRLB_SBMODE_Msk)) | ((uint32_t) serialSetup->dataWidth | (uint32_t) serialSetup->stopBits);
              }
              else
              {
                  SERCOM1_REGS->USART_INT.SERCOM_CTRLA =  (SERCOM1_REGS->USART_INT.SERCOM_CTRLA & ~(SERCOM_USART_INT_CTRLA_SAMPR_Msk | SERCOM_USART_INT_CTRLA_FORM_Msk)) | SERCOM_USART_INT_CTRLA_FORM(0x1UL) | SERCOM_USART_INT_CTRLA_SAMPR((uint32_t)sampleRate); 
                  SERCOM1_REGS->USART_INT.SERCOM_CTRLB = (SERCOM1_REGS->USART_INT.SERCOM_CTRLB & ~(SERCOM_USART_INT_CTRLB_CHSIZE_Msk | SERCOM_USART_INT_CTRLB_SBMODE_Msk | SERCOM_USART_INT_CTRLB_PMODE_Msk)) | (uint32_t) serialSetup->dataWidth | (uint32_t) serialSetup->stopBits | (uint32_t) serialSetup->parity ;
              }
  
              /* Wait for sync */
              while((SERCOM1_REGS->USART_INT.SERCOM_SYNCBUSY) != 0U)
              {
                  /* Do nothing */
              }
  
              /* Enable the USART after the configurations */
              SERCOM1_REGS->USART_INT.SERCOM_CTRLA |= SERCOM_USART_INT_CTRLA_ENABLE_Msk;
  
              /* Wait for sync */
              while((SERCOM1_REGS->USART_INT.SERCOM_SYNCBUSY) != 0U)
              {
                  /* Do nothing */
              }
  
              setupStatus = true;
          }
      }
  
      return setupStatus;
  }
  
  USART_ERROR SERCOM1_USART_ErrorGet( void )
  {
      USART_ERROR errorStatus = (USART_ERROR) (SERCOM1_REGS->USART_INT.SERCOM_STATUS & (uint16_t)(SERCOM_USART_INT_STATUS_PERR_Msk | SERCOM_USART_INT_STATUS_FERR_Msk | SERCOM_USART_INT_STATUS_BUFOVF_Msk ));
  
      if(errorStatus != USART_ERROR_NONE)
      {
          SERCOM1_USART_ErrorClear();
      }
  
      return errorStatus;
  }
  
  
  void SERCOM1_USART_TransmitterEnable( void )
  {
      SERCOM1_REGS->USART_INT.SERCOM_CTRLB |= SERCOM_USART_INT_CTRLB_TXEN_Msk;
  
      /* Wait for sync */
      while((SERCOM1_REGS->USART_INT.SERCOM_SYNCBUSY) != 0U)
      {
          /* Do nothing */
      }
  }
  
  void SERCOM1_USART_TransmitterDisable( void )
  {
      SERCOM1_REGS->USART_INT.SERCOM_CTRLB &= ~SERCOM_USART_INT_CTRLB_TXEN_Msk;
  
      /* Wait for sync */
      while((SERCOM1_REGS->USART_INT.SERCOM_SYNCBUSY) != 0U)
      {
          /* Do nothing */
      }
  }
  
  bool SERCOM1_USART_Write( void *buffer, const size_t size )
  {
      bool writeStatus      = false;
      uint8_t *pu8Data      = (uint8_t*)buffer;
      uint32_t u32Length    = size;
  
      if(pu8Data != NULL)
      {
  
          /* Blocks while buffer is being transferred */
          while(u32Length > 0U)
          {
              u32Length -= 1U;
  
              /* Check if USART is ready for new data */
              while((SERCOM1_REGS->USART_INT.SERCOM_INTFLAG & (uint8_t)SERCOM_USART_INT_INTFLAG_DRE_Msk) == 0U)
              {
                  /* Do nothing */
              }
  
              /* Write data to USART module */
              if (((SERCOM1_REGS->USART_INT.SERCOM_CTRLB & SERCOM_USART_INT_CTRLB_CHSIZE_Msk) >> SERCOM_USART_INT_CTRLB_CHSIZE_Pos) != 0x01U)
              {
                  /* 8-bit mode */
                  SERCOM1_REGS->USART_INT.SERCOM_DATA = *pu8Data++;
              }
              else
              {
                  /* 9-bit mode */
                  SERCOM1_REGS->USART_INT.SERCOM_DATA = *(uint16_t*)pu8Data;
                  pu8Data += 2;
              }
          }
  
          writeStatus = true;
      }
  
      return writeStatus;
  }
  
  
  bool SERCOM1_USART_TransmitterIsReady( void )
  {
      bool transmitterStatus = false;
  
      if ((SERCOM1_REGS->USART_INT.SERCOM_INTFLAG & SERCOM_USART_INT_INTFLAG_DRE_Msk) == SERCOM_USART_INT_INTFLAG_DRE_Msk)
      {
          transmitterStatus = true;
      }
  
      return transmitterStatus;
  }
  
  bool SERCOM1_USART_TransmitComplete( void )
  {
      bool transmitComplete = false;
  
      if ((SERCOM1_REGS->USART_INT.SERCOM_INTFLAG & SERCOM_USART_INT_INTFLAG_TXC_Msk) == SERCOM_USART_INT_INTFLAG_TXC_Msk)
      {
          transmitComplete = true;
      }
  
      return transmitComplete;
  }
  
  void SERCOM1_USART_WriteByte( int data )
  {
      /* Check if USART is ready for new data */
      while((SERCOM1_REGS->USART_INT.SERCOM_INTFLAG & SERCOM_USART_INT_INTFLAG_DRE_Msk) == 0U)
      {
          /* Do nothing */
      }
  
      SERCOM1_REGS->USART_INT.SERCOM_DATA = (uint16_t)data;
  }
  
  void SERCOM1_USART_ReceiverEnable( void )
  {
      SERCOM1_REGS->USART_INT.SERCOM_CTRLB |= SERCOM_USART_INT_CTRLB_RXEN_Msk;
  
      /* Wait for sync */
      while((SERCOM1_REGS->USART_INT.SERCOM_SYNCBUSY) != 0U)
      {
          /* Do nothing */
      }
  }
  
  void SERCOM1_USART_ReceiverDisable( void )
  {
      SERCOM1_REGS->USART_INT.SERCOM_CTRLB &= ~SERCOM_USART_INT_CTRLB_RXEN_Msk;
  
      /* Wait for sync */
      while((SERCOM1_REGS->USART_INT.SERCOM_SYNCBUSY) != 0U)
      {
          /* Do nothing */
      }
  }
  
  bool SERCOM1_USART_Read( void *buffer, const size_t size )
  {
      bool readStatus        = false;
      uint8_t* pu8Data       = (uint8_t*)buffer;
      uint32_t processedSize = 0U;
      USART_ERROR errorStatus = USART_ERROR_NONE;
  
      if(pu8Data != NULL)
      {
  
          /* Clear error flags and flush out error data that may have been received when no active request was pending */
          SERCOM1_USART_ErrorClear();
  
          while(processedSize < size)
          {
              /* Check if USART has new data */
              while((SERCOM1_REGS->USART_INT.SERCOM_INTFLAG & SERCOM_USART_INT_INTFLAG_RXC_Msk) == 0U)
              {
                  /* Do nothing */
              }
  
              errorStatus = (USART_ERROR) (SERCOM1_REGS->USART_INT.SERCOM_STATUS & (uint16_t)(SERCOM_USART_INT_STATUS_PERR_Msk | SERCOM_USART_INT_STATUS_FERR_Msk | SERCOM_USART_INT_STATUS_BUFOVF_Msk));
  
              if(errorStatus != USART_ERROR_NONE)
              {
                  break;
              }
  
              if (((SERCOM1_REGS->USART_INT.SERCOM_CTRLB & SERCOM_USART_INT_CTRLB_CHSIZE_Msk) >> SERCOM_USART_INT_CTRLB_CHSIZE_Pos) != 0x01U)
              {
                  /* 8-bit mode */
                  *pu8Data++ = (uint8_t) (SERCOM1_REGS->USART_INT.SERCOM_DATA);
              }
              else
              {
                  /* 9-bit mode */
                  *(uint16_t*)pu8Data = (uint16_t) (SERCOM1_REGS->USART_INT.SERCOM_DATA);
                  pu8Data += 2;
              }
  
              processedSize += 1U;
          }
  
          if(size == processedSize)
          {
              readStatus = true;
          }
      }
  
      return readStatus;
  }
  
  bool SERCOM1_USART_ReceiverIsReady( void )
  {
      bool receiverStatus = false;
  
      if ((SERCOM1_REGS->USART_INT.SERCOM_INTFLAG & SERCOM_USART_INT_INTFLAG_RXC_Msk) == SERCOM_USART_INT_INTFLAG_RXC_Msk)
      {
          receiverStatus = true;
      }
  
      return receiverStatus;
  }
  
  int SERCOM1_USART_ReadByte( void )
  {
      return (int)SERCOM1_REGS->USART_INT.SERCOM_DATA;
  }
  
  