SC16IS740 I2C to UART Bridge, based on SC16IS750 by Wim Huiskamp https://os.mbed.com/users/wim/code/SC16IS750/

Dependents:   SC16IS740_test

Revision:
0:db4396ad001f
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/SC16IS740.cpp	Wed Aug 12 05:16:11 2020 +0000
@@ -0,0 +1,1190 @@
+/* SC16IS740 I2C or SPI to UART bridge 
+ *   v0.1 WH, Nov 2013, Sparkfun WiFly Shield code library alpha 0 used as example, Added I2C I/F and many more methods.
+ *                      https://forum.sparkfun.com/viewtopic.php?f=13&t=21846
+ *   v0.2 WH, Feb 2014, Added Doxygen Documentation, Added Hardware Reset pin methods. 
+ *   v0.3 WH, Dec 2014, Added support for SC16IS752 dual UART.  
+ *   v0.4 WH, Dec 2014, Added Repeated Start for I2C readRegister(). Set I2C clock at 100kb/s. Fixed and added some comments.
+ *   v0.5 LVD, Aug 2020, Renames for use with SC16IS740 on the Mikroe UART I2C/SPI Click board and updated Mbed OS 6 calls.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of this software
+ * and associated documentation files (the "Software"), to deal in the Software without restriction,
+ * including without limitation the rights to use, copy, modify, merge, publish, distribute,
+ * sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all copies or
+ * substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING
+ * BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
+ * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#include    "mbed.h"
+#include    "SC16IS740.h"
+
+#define ENABLE_BULK_TRANSFERS             1
+#define BULK_BLOCK_LEN                   16
+
+/**
+    The SC16IS740 is functionally and electrically
+    identical to the SC16IS740, with the exception of the programmable I/O pins which are
+    only present on the SC16IS740.
+ */
+/** Abstract class SC16IS740 for converter between either SPI or I2C and a Serial port
+  * Constructor for this Abstract Class is protected  
+  * Supports both SPI and I2C interfaces through derived classes
+  *
+  * @code
+  *
+  * @endcode
+  */
+SC16IS740::SC16IS740() { 
+//SC16IS740::SC16IS740() : Serial(NC, NC) {   //Fout, mag geen NC zijn
+//SC16IS740::SC16IS740() : SerialBase(NC, NC) { //Fout, mag geen NC zijn
+// Dont call _init() here since the SPI or I2C port have not yet been configured...
+  //_init();  // initialise UART registers
+}
+
+
+/** Set baudrate of the serial port.    
+  *  @param  baud integer baudrate (4800, 9600 etc)
+  *  @return none
+  */
+void SC16IS740::baud(int baudrate) {
+  unsigned long divisor = SC16IS740_BAUDRATE_DIVISOR(baudrate);
+  char lcr_tmp;
+  
+  _config.baudrate = baudrate;               // Save baudrate
+
+  lcr_tmp = this->readRegister(LCR);                            // Read current LCR register
+  this->writeRegister(LCR, lcr_tmp | LCR_ENABLE_DIV);           // Enable Divisor registers
+  this->writeRegister(DLL, ( divisor       & 0xFF));            //   write divisor LSB
+  this->writeRegister(DLH, ((divisor >> 8) & 0xFF));            //   write divisor MSB
+  this->writeRegister(LCR, lcr_tmp);                            // Restore LCR register, activate regular RBR, THR and IER registers  
+  
+}
+
+
+/** Set the transmission format used by the serial port.   
+  *   @param bits      The number of bits in a word (5-8; default = 8)
+  *   @param parity    The parity used (Serial::None, Serial::Odd, Serial::Even, Serial::Forced1, Serial::Forced0; default = Serial::None)
+  *   @param stop_bits The number of stop bits (1 or 2; default = 1) 
+  *   @return none   
+  */
+void SC16IS740::format(int bits, BufferedSerial::Parity parity, int stop_bits) {
+  char lcr_tmp = 0x00;
+  
+  switch (bits) {
+    case 5:  lcr_tmp |= LCR_BITS5;
+             break;
+    case 6:  lcr_tmp |= LCR_BITS6;
+             break;
+    case 7:  lcr_tmp |= LCR_BITS7;
+             break;
+    case 8:  lcr_tmp |= LCR_BITS8;
+             break;
+    default: lcr_tmp |= LCR_BITS8;     
+  }
+
+  switch (parity) {
+    case BufferedSerial::None:    lcr_tmp |= LCR_NONE;
+                          break;
+    case BufferedSerial::Odd:     lcr_tmp |= LCR_ODD;
+                          break;
+    case BufferedSerial::Even:    lcr_tmp |= LCR_EVEN;
+                          break;
+    case BufferedSerial::Forced1: lcr_tmp |= LCR_FORCED1;
+                          break;
+    case BufferedSerial::Forced0: lcr_tmp |= LCR_FORCED0;
+                          break;                      
+    default:              lcr_tmp |= LCR_NONE;     
+  }
+
+  switch (stop_bits) {
+    case 1:  lcr_tmp |= LCR_BITS1;
+             break;
+    case 2:  lcr_tmp |= LCR_BITS2;
+             break;
+    default: lcr_tmp |= LCR_BITS1;     
+  }
+
+  _config.dataformat = lcr_tmp;      // Save dataformat   
+
+  this->writeRegister(LCR, lcr_tmp); // Set LCR register, activate regular RBR, THR and IER registers  
+
+};
+
+/** Generate a break condition on the serial line
+  *  @return none 
+  */
+void SC16IS740::send_break() {
+  // Wait for 1.5 frames before clearing the break condition
+  // This will have different effects on our platforms, but should
+  // ensure that we keep the break active for at least one frame.
+  // We consider a full frame (1 start bit + 8 data bits bits + 
+  // 1 parity bit + 2 stop bits = 12 bits) for computation.
+  // One bit time (in us) = 1000000/_baud
+  // Twelve bits: 12000000/baud delay
+  // 1.5 frames: 18000000/baud delay
+  set_break(true);
+  wait_us(18000000/_config.baudrate);
+  set_break(false);    
+};
+    
+/** Set a break condition on the serial line
+  *  @param enable  break condition
+  *  @return none   
+  */
+void SC16IS740::set_break(bool enable) {
+
+  if (enable) {
+    _config.dataformat |= LCR_BRK_ENA;      // Save dataformat         
+  }
+  else {
+    _config.dataformat &= ~LCR_BRK_ENA;     // Save dataformat           
+  }
+
+  this->writeRegister(LCR, _config.dataformat); // Set LCR register
+}
+
+/** Set the flow control type on the serial port
+  *  Added for compatibility with Serial Class.
+  *  SC16IS740 supports only Flow, Pins can not be selected.
+  *  This method sets hardware flow control. SC16IS740 supports XON/XOFF, but this is not implemented.  
+  *
+  *  @param type the flow control type (Disabled, RTS, CTS, RTSCTS)     
+  *  @param flow1 the first flow control pin (RTS for RTS or RTSCTS, CTS for CTS) - NOT USED
+  *  @param flow2 the second flow control pin (CTS for RTSCTS) - NOT USED
+  *  @return none   
+  */
+void SC16IS740::set_flow_control(Flow type, PinName flow1, PinName flow2) {
+  char lcr_tmp; 
+  char efr_tmp = 0x00;
+  
+  // We need to enable flow control to prevent overflow of buffers and
+  // lose data when used with fast devices like the WiFly.
+
+  switch (type) {
+     case Disabled : 
+                     break;
+     case RTS:       efr_tmp = EFR_ENABLE_RTS;
+                     break;     
+     case CTS:       efr_tmp = EFR_ENABLE_CTS;                     
+                     break;     
+     case RTSCTS:    efr_tmp = EFR_ENABLE_RTS | EFR_ENABLE_CTS;
+                     break;
+     default:             ;    
+
+  }
+
+  //Save flowcontrol mode and enable enhanced functions  
+  _config.flowctrl = efr_tmp | EFR_ENABLE_ENHANCED_FUNCTIONS;
+
+  lcr_tmp = this->readRegister(LCR);                       // save LRC register
+  this->writeRegister(LCR, LCR_ENABLE_ENHANCED_FUNCTIONS); // write magic number 0xBF to enable access to EFR register
+  this->writeRegister(EFR, _config.flowctrl);              // set flow and enable enhanced functions
+  this->writeRegister(LCR, lcr_tmp);                       // restore LCR register
+}  
+
+/** Set the RX FIFO flow control levels
+  *  This method sets hardware flow control levels. SC16IS740 supports XON/XOFF, but this is not implemented.
+  *  Should be called BEFORE Auto RTS is enabled.  
+  *
+  *  @param resume trigger level to resume transmission (0..15, meaning 0-60 with a granularity of 4)     
+  *  @param halt trigger level to resume transmission (0..15, meaning 0-60 with granularity of 4)       
+  *  @return none   
+  */
+void SC16IS740::set_flow_triggers(int resume, int halt) {
+
+  // sanity checks
+  halt = halt & 0x0F;
+  resume = resume & 0x0F;  
+  if (halt <= resume) {
+    halt   = TCR_HALT_DEFAULT;
+    resume = TCR_RESUME_DEFAULT;  
+  }
+
+  // Note: TCR accessible only when EFR[4]=1 and MCR[2]=1
+  this->writeRegister(TCR, (resume << 4) | halt);          // set TCR register
+}
+
+
+/** Set the Modem Control register
+  *  This method sets prescaler, enables TCR and TLR
+  *
+  *  @param none 
+  *  @return none 
+  */
+void SC16IS740::set_modem_control() {
+
+  //Note MCR[7:4] and MCR[2] only accessible when EFR[4] is set
+  if (SC16IS740_PRESCALER == SC16IS740_PRESCALER_1) { // Default prescaler after reset
+    this->writeRegister(MCR, MCR_PRESCALE_1 | MCR_ENABLE_TCR_TLR);
+  }  
+  else { 
+    this->writeRegister(MCR, MCR_PRESCALE_4 | MCR_ENABLE_TCR_TLR);
+  }
+}  
+
+ 
+
+/** Initialise internal registers
+  * Should be in protection section. Public for testing purposes
+  * If initialisation fails this method does not return.    
+  *   @param none
+  *   @return none 
+  */
+void SC16IS740::_init() {
+
+  // Initialise SC16IS740
+
+  // Hardware reset, assuming there is a HW Reset pin
+  this->hwReset();  
+
+  // Software reset, assuming there is no access to the HW Reset pin
+  swReset();
+  
+  // Set default baudrate (depends on prescaler) and save in _config
+  // DLL/DLH  
+  baud();
+
+  // Set default dataformat and save in _config
+  // LCR 
+  format();  
+
+  // Set dataflow mode and Enables enhanced functions
+  // Save in _config
+  // EFR
+  set_flow_control(); 
+  
+
+  // FIFO control, sets TX and RX IRQ trigger levels and enables FIFO and save in _config
+  // Note FCR[5:4] only accessible when EFR[4] is set (enhanced functions enable)
+  // FCR, TLR
+  set_fifo_control();
+  flush();
+
+  // Modem control, sets prescaler, enable TCR and TLR
+  // Note MCR[7:4] and MCR[2] only accessible when EFR[4] is set (enhanced functions enable)
+  set_modem_control();
+
+  // Set RTS trigger levels
+  // Note TCR only accessible when EFR[4] is set (enhanced functions enable) and MCR[2] is set
+  set_flow_triggers();
+
+
+  // Set default break condition and save in _config
+  // LCR   
+  //set_break();
+ 
+  // The UART bridge should now be successfully initialised.
+
+  // Test if UART bridge is present and initialised
+  if(!connected()){ 
+#if(0)  
+    // Lock up if we fail to initialise UART bridge.
+    while(1) {};
+#else    
+    printf("Failed to initialise UART bridge\r\n");    
+#endif    
+  }
+  else {
+    printf("Initialised UART bridge!\r\n");      
+  }
+
+}
+
+
+/** FIFO control, sets TX and RX trigger levels and enables FIFO and save in _config
+  *  Note FCR[5:4] (=TX_IRQ_LVL) only accessible when EFR[4] is set (enhanced functions enable)
+  *  Note TLR only accessible when EFR[4] is set (enhanced functions enable) and MCR[2] is set 
+  *   @param  none
+  *   @return none
+  */
+void SC16IS740::set_fifo_control() {
+
+  // Set default fifoformat 
+  // FCR
+  _config.fifoenable = true;  
+
+  // Note FCR[5:4] (=TX_IRQ_LVL) only accessible when EFR[4] is set (enhanced functions enable)
+//  _config.fifoformat = FCR_RX_IRQ_8 | FCR_TX_IRQ_56;
+  _config.fifoformat = FCR_RX_IRQ_8 | FCR_TX_IRQ_8;  //Default
+
+  if (_config.fifoenable)
+    // enable FIFO mode and set FIFO control values  
+    this->writeRegister(FCR, _config.fifoformat | FCR_ENABLE_FIFO);
+  else
+    // disable FIFO mode and set FIFO control values  
+    this->writeRegister(FCR, _config.fifoformat);
+
+  // Set Trigger level register TLR for RX and TX interrupt generation
+  // Note TLR only accessible when EFR[4] is set (enhanced functions enable) and MCR[2] is set
+  //   TRL Trigger levels for RX and TX are 0..15, meaning 0-60 with a granularity of 4 chars    
+  // When TLR for RX or TX are 'Zero' the corresponding values in FCR are used. The FCR settings
+  // have less resolution (only 4 levels) so TLR is considered an enhanced function.
+  this->writeRegister(TLR, 0x00);                                     // Use FCR Levels
+//  this->writeRegister(TLR, (TLR_RX_DEFAULT << 4) | TLR_TX_DEFAULT);   // Use Default enhanced levels
+
+}    
+
+
+/**
+  * Flush the UART FIFOs while maintaining current FIFO mode.
+  *   @param  none
+  *   @return none
+  */
+void SC16IS740::flush() {
+  // FCR is Write Only, use saved _config
+
+  // reset TXFIFO, reset RXFIFO, non FIFO mode  
+  this->writeRegister(FCR, FCR_TX_FIFO_RST | FCR_RX_FIFO_RST);
+  
+  if (_config.fifoenable)
+    // enable FIFO mode and set FIFO control values  
+    this->writeRegister(FCR, _config.fifoformat | FCR_ENABLE_FIFO);
+  else
+    // disable FIFO mode and set FIFO control values  
+    this->writeRegister(FCR, _config.fifoformat);
+  
+#if(0)
+//original
+ /*
+   * Flush characters from SC16IS740 receive buffer.
+   */
+
+  // Note: This may not be the most appropriate flush approach.
+  //       It might be better to just flush the UART's buffer
+  //       rather than the buffer of the connected device
+  //       which is essentially what this does.
+  while(readable() > 0) {
+    getc();
+  }
+#endif
+
+}
+
+
+/**
+  * Check that UART is connected and operational.
+  *  @param  none
+  *  @return bool true when connected, false otherwise
+  */
+bool SC16IS740::connected() {
+  // Perform read/write test to check if UART is working
+  const char TEST_CHARACTER = 'H';
+
+  this->writeRegister(SPR, TEST_CHARACTER);
+
+  return (this->readRegister(SPR) == TEST_CHARACTER);
+}
+
+
+/** Determine if there is a character available to read.
+  * This is data that's already arrived and stored in the receive
+  * buffer (which holds 64 chars).
+  *
+  *   @return 1 if there is a character available to read, 0 otherwise
+  */
+int SC16IS740::readable() {
+  
+//  if (this->readableCount() > 0) { // Check count
+  if (this->readRegister(LSR) & LSR_DR) { // Data in Receiver Bit, at least one character waiting
+    return 1;
+  }
+  else {
+    return 0; 
+  }
+}
+
+/** Determine how many characters are available to read.
+  * This is data that's already arrived and stored in the receive
+  * buffer (which holds 64 chars).
+  *
+  *   @return int Characters available to read
+  */
+int SC16IS740::readableCount() {
+
+  return (this->readRegister(RXLVL));
+}
+
+/** Determine if there is space available to write a character.    
+  *   @return 1 if there is a space for a character to write, 0 otherwise
+  */
+int SC16IS740::writable() {
+ 
+//  if ((this->writableCount() > 0) { // Check count
+  if (this->readRegister(LSR) & LSR_THRE) { // THR Empty, space for at least one character
+    return 1;
+  }
+  else {
+    return 0;  
+  }
+}
+
+/** Determine how much space available for writing characters.
+  * This considers data that's already stored in the transmit
+  * buffer (which holds 64 chars).
+  *
+  *   @return int character space available to write
+  */
+int SC16IS740::writableCount() {
+
+  return (this->readRegister(TXLVL));  // TX Level
+}
+
+
+/**
+  * Read char from UART Bridge.
+  * Acts in the same manner as 'Serial.read()'.  
+  *   @param none    
+  *   @return char read or -1 if no data available. 
+  */ 
+int SC16IS740::getc() {
+
+  if (!readable()) {
+    return -1;
+  }
+
+  return this->readRegister(RHR);
+}
+
+
+/**
+  * Write char to UART Bridge. Blocking when no free space in FIFO
+  *   @param value char to be written    
+  *   @return value written  
+  */ 
+int SC16IS740::putc(int value) {
+
+  while (this->readRegister(TXLVL) == 0) {
+    // Wait for space in TX buffer
+    wait_us(10);
+  };
+  this->writeRegister(THR, value);
+  
+  return value;
+}
+
+
+/**
+  * Write char string to UART Bridge. Blocking when no free space in FIFO
+  *   @param *str char string to be written    
+  *   @return none  
+  */
+void SC16IS740::writeString(const char *str) {
+
+#if ENABLE_BULK_TRANSFERS
+  int len, idx;
+  
+  len = strlen(str);  
+
+  // Write blocks of BULK_BLOCK_LEN  
+  while (len > BULK_BLOCK_LEN) {
+    while(this->readRegister(TXLVL) < BULK_BLOCK_LEN) {
+      // Wait for space in TX buffer
+      wait_us(10);
+    };  
+  
+    // Write a block of BULK_BLOCK_LEN bytes
+#if (0)    
+    // Note: can be optimized by writing registeraddress once and then repeatedly write the bytes.
+    for (idx=0; idx<BULK_BLOCK_LEN; idx++) {
+      this->writeRegister(THR, str[idx]);
+    };
+#else
+    // optimized
+    this->writeDataBlock(str, BULK_BLOCK_LEN);    
+#endif
+              
+    len -= BULK_BLOCK_LEN;
+    str += BULK_BLOCK_LEN;
+  }
+  
+  // Write remaining bytes 
+  // Note: can be optimized by writing registeraddress once and then repeatedly write the bytes.  
+  for (idx=0; idx<len; idx++) {
+    while (this->readRegister(TXLVL) == 0) {
+      // Wait for space in TX buffer
+      wait_us(10);
+    };
+    this->writeRegister(THR, str[idx]);
+  }  
+
+
+#else
+  // Single writes instead of bulktransfer
+  int len, idx;
+  
+  len = strlen(str);
+  for (idx=0; idx<len; idx++) {
+    while (this->readRegister(TXLVL) == 0) {
+      // Wait for space in TX buffer
+      wait_us(10);
+    };
+    this->writeRegister(THR, str[idx]);
+  }  
+#endif  
+}
+
+
+/**
+  * Write byte array to UART Bridge. Blocking when no free space in FIFO
+  *   @param *data byte array to be written    
+  *   @param len   number of bytes to write  
+  *   @return none  
+  */
+void SC16IS740::writeBytes(const char *data, int len) {
+
+#if ENABLE_BULK_TRANSFERS
+  int idx;
+  
+  // Write blocks of BULK_BLOCK_LEN  
+  while (len > BULK_BLOCK_LEN) {
+    while(this->readRegister(TXLVL) < BULK_BLOCK_LEN) {
+      // Wait for space in TX buffer
+      wait_us(10);
+    };  
+  
+    // Write a block of BULK_BLOCK_LEN bytes
+#if (0)    
+    // Note: can be optimized by writing registeraddress once and then repeatedly write the bytes.
+    for (idx=0; idx<BULK_BLOCK_LEN; idx++) {
+      this->writeRegister(THR, data[idx]);
+    };
+#else
+    // optimized
+    this->writeDataBlock(data, BULK_BLOCK_LEN);    
+#endif
+              
+    len  -= BULK_BLOCK_LEN;
+    data += BULK_BLOCK_LEN;
+  }
+  
+  // Write remaining bytes 
+  // Note: can be optimized by writing registeraddress once and then repeatedly write the bytes.  
+  for (idx=0; idx<len; idx++) {
+    while (this->readRegister(TXLVL) == 0) {
+      // Wait for space in TX buffer
+      wait_us(10);
+    };
+    this->writeRegister(THR, data[idx]);
+  }  
+
+
+#else
+  // Single writes instead of bulktransfer
+  int idx;
+  
+  for (idx=0; idx<len; idx++) {
+    while (this->readRegister(TXLVL) == 0) {
+      // Wait for space in TX buffer
+      wait_us(10);
+    };
+    this->writeRegister(THR, str[idx]);
+  }  
+#endif  
+}
+
+
+/** Set direction of I/O port pins.
+  * This method is specific to the SPI-I2C UART and not found on the 16750
+  * Note: The SC16IS752 does not have separate GPIOs for Channel_A and Channel_B.
+  *   @param  bits Bitpattern for I/O (1=output, 0=input)
+  *   @return none
+  */
+void SC16IS740::ioSetDirection(unsigned char bits) {
+  this->writeRegister(IODIR, bits);
+}
+
+/** Set bits of I/O port pins.
+  * This method is specific to the SPI-I2C UART and not found on the 16750
+  * Note: The SC16IS752 does not have separate GPIOs for Channel_A and Channel_B.  
+  *   @param  bits Bitpattern for I/O (1= set output bit, 0 = clear output bit)
+  *   @return none
+  */
+void SC16IS740::ioSetState(unsigned char bits) {
+  this->writeRegister(IOSTATE, bits);
+}
+
+/** Get bits of I/O port pins.
+  * This method is specific to the SPI-I2C UART and not found on the 16750
+  * Note: The SC16IS752 does not have separate GPIOs for Channel_A and Channel_B.  
+  *   @param  none
+  *   @return bits Bitpattern for I/O (1= bit set, 0 = bit cleared)
+  */
+unsigned char SC16IS740::ioGetState() {
+  return this->readRegister(IOSTATE) ;
+}
+
+
+/** Software Reset SC16IS740 device.
+  * This method is specific to the SPI-I2C UART and not found on the 16750
+  * Note: The SC16IS752 does not have separate Reset for Channel_A and Channel_B.  
+  *   @param  none
+  *   @return none
+  */
+void SC16IS740::swReset() {
+  this->writeRegister(IOCTRL, IOC_SW_RST);     
+}
+
+
+//
+// End Abstract Class Implementation
+//
+
+
+
+//
+// Begin SPI Class Implementation
+//
+
+
+/** Create an SC16IS740_SPI object using a specified SPI bus and CS
+  *
+  * @param SPI &spi the SPI port to connect to 
+  * @param cs  Pinname of the CS pin (active low)
+  * @param rst Pinname for Reset pin (active low) Optional, Default = NC 
+  */    
+SC16IS740_SPI::SC16IS740_SPI (SPI *spi, PinName cs, PinName rst) : _spi(spi), _cs(cs)  {
+  _cs = 1;  // deselect
+  
+  _spi->format(8, 0);          
+  _spi->frequency(1000000);
+
+  // The hardware Reset pin is optional. Test and make sure whether it exists or not to prevent illegal access.
+  if (rst != NC) {
+    _reset = new DigitalOut(rst);   //Construct new pin 
+    _reset->write(1);               //Deactivate    
+  }
+  else {
+    // No Hardware Reset pin       
+    _reset = NULL;                  //Construct dummy pin     
+  }  
+
+
+  // Dont call _init() until SPI port has been configured.
+  // That is why _init() is not called in parent Constructor 
+  _init();
+
+};
+
+
+/** Destruct SC16IS740_SPI bridge object
+  *
+  * @param  none
+  * @return none
+  */ 
+SC16IS740_SPI::~SC16IS740_SPI() {
+   if (_reset != NULL) {delete _reset;}  // Reset pin
+}
+
+/** Write value to internal register.
+  * Pure virtual, must be declared in derived class.   
+  *   @param registerAddress   The address of the Register (enum RegisterName)
+  *   @param data              The 8bit value to write
+  *   @return none 
+  */
+void SC16IS740_SPI::writeRegister(RegisterName registerAddress, char data) {
+
+  _cs = 0; //  select;
+  _spi->write(registerAddress);
+  _spi->write(data);
+  _cs = 1; //  deselect;
+
+}
+
+
+/** Read value from internal register.
+  *   @param registerAddress   The address of the Register (enum RegisterName)
+  *   @return char             The 8bit value read from the register
+  */
+char SC16IS740_SPI::readRegister(RegisterName registerAddress) {
+
+  // Used in SPI read operations to flush slave's shift register
+  const char SPI_DUMMY_CHAR = 0xFF; 
+
+  char result;
+
+  _cs = 0; //  select;
+  _spi->write(SPI_READ_MODE_FLAG | registerAddress);
+  result = _spi->write(SPI_DUMMY_CHAR);
+  _cs = 1; //  deselect;
+
+  return result;  
+}
+
+
+/** Write multiple datavalues to Transmitregister.
+  * More Efficient implementation than writing individual bytes
+  * Assume that previous check confirmed that the FIFO has sufficient free space to store the data  
+  * Pure virtual, must be declared in derived class.   
+  *   @param char* databytes   The pointer to the block of data
+  *   @param len               The number of bytes to write
+  *   @return none 
+  */
+void SC16IS740_SPI::writeDataBlock (const char *data, int len) {
+  int i;
+  
+  _cs = 0; //  select;
+  
+  // Select the Transmit Holding Register
+  // Assume that previous check confirmed that the FIFO has sufficient free space to store the data
+  _spi->write(THR);
+  
+  for (i=0; i<len; i++, data++)
+    _spi->write(*data);
+    
+  _cs = 1; //  deselect;
+}
+
+
+/** Hardware Reset SC16IS740 device.
+  * This method is only available when the Reset pin has been declared and is also connected
+  *   @param  none
+  *   @return none
+  */
+void SC16IS740_SPI::hwReset() {
+  
+  if (_reset != NULL){
+    _reset->write(0);  //activate
+    //wait_us(100*1000);   //test only            
+    // ThisThread::sleep_for(100ms); //test only  
+    _reset->write(1);  //deactivate
+  }  
+  else {
+    printf("Hardware Reset pin is not available...\n\r");     
+  }
+
+}
+
+//
+// End SPI Implementation
+//
+
+
+
+//
+// Begin I2C Implementation
+//
+
+/** Create a SC16IS740_I2C object for a bridge between I2C and a Serial port
+  *
+  * @param I2C &i2c the I2C port to connect to 
+  * @param char deviceAddress the I2C slave address of the SC16IS740
+  * @param rst Pinname for Reset pin (active low) Optional, Default = NC   
+  *  
+  */  
+SC16IS740_I2C::SC16IS740_I2C(I2C *i2c, uint8_t deviceAddress, PinName rst) : _i2c(i2c), _slaveAddress(deviceAddress & 0xFE) {
+
+//  _i2c->frequency(400000);
+  _i2c->frequency(100000);  
+ 
+  // The hardware Reset pin is optional. Test and make sure whether it exists or not to prevent illegal access.
+  if (rst != NC) {
+    _reset = new DigitalOut(rst);   //Construct new pin 
+    _reset->write(1);               //Deactivate    
+  }
+  else {
+    // No Hardware Reset pin       
+    _reset = NULL;                  //Construct dummy pin     
+  }  
+  
+  // Dont call _init() until I2C port has been configured.
+  // That is why _init() is not called in parent Constructor 
+  _init();
+}
+
+
+/** Destruct SC16IS740_I2C bridge object
+  *
+  * @param  none
+  * @return none
+  */ 
+SC16IS740_I2C::~SC16IS740_I2C() {
+   if (_reset != NULL) {delete _reset;}  // Reset pin
+}
+
+
+/** Write value to internal register.
+  *   @param registerAddress   The address of the Register (enum RegisterName)
+  *   @param data              The 8bit value to write
+  *   @return none 
+  */
+void SC16IS740_I2C::writeRegister(RegisterName registerAddress, char data) {
+  char w[2];
+
+  w[0] = registerAddress;
+  w[1] = data;
+
+  _i2c->write( _slaveAddress, w, 2 );
+}
+
+
+/** Read value from internal register.
+  *   @param registerAddress   The address of the Register (enum RegisterName)
+  *   @return char             The 8bit value read from the register
+  */
+char SC16IS740_I2C::readRegister(RegisterName registerAddress) {
+  /*
+   * Read char from SC16IS740 register at <registerAddress>.
+   */
+   char w[1];
+   char r[1];
+    
+   w[0] = registerAddress;
+    
+//   _i2c->write( _slaveAddress, w, 1 );
+   _i2c->write(_slaveAddress, w, 1, true);   //Repeated Start   
+   _i2c->read( _slaveAddress, r, 1 );
+
+   return ( r[0] );
+}
+
+
+/** Write multiple datavalues to Transmitregister.
+  * More Efficient implementation than writing individual bytes
+  * Assume that previous check confirmed that the FIFO has sufficient free space to store the data
+  * Pure virtual, must be declared in derived class.   
+  *   @param char* databytes   The pointer to the block of data
+  *   @param len               The number of bytes to write
+  *   @return none 
+  */
+void SC16IS740_I2C::writeDataBlock (const char *data, int len) {
+  
+#if(0)  
+  int i;
+  char w[BULK_BLOCK_LEN];
+  
+  // Select the Transmit Holding Register
+  // Assume that previous check confirmed that the FIFO has sufficient free space to store the data
+  w[0] = THR;
+
+  // copy the data..
+  for (i=0; i<len; i++)
+    w[i+1] = data[i];
+    
+  _i2c->write( _slaveAddress, w, len + 1);  
+#else
+  int i;
+  
+  _i2c->start();
+  _i2c->write(_slaveAddress); 
+
+  // Select the Transmit Holding Register
+  // Assume that previous check confirmed that the FIFO has sufficient free space to store the data
+  _i2c->write(THR);
+
+  // send the data..
+  for (i=0; i<len; i++)
+    _i2c->write(data[i]);
+    
+  _i2c->stop();  
+#endif
+}
+
+
+/** Hardware Reset SC16IS740 device.
+  * This method is only available when the Reset pin has been declared and is also connected
+  *   @param  none
+  *   @return none
+  */
+void SC16IS740_I2C::hwReset() {
+  
+  if (_reset != NULL){
+    _reset->write(0);  //activate
+//    wait_ms(100);
+    wait_us(1000*1000);   //test only            
+    _reset->write(1);  //deactivate
+  }  
+  else {
+    printf("Hardware Reset pin is not available...\n\r");     
+  }
+}
+
+
+//
+// End I2C Implementation
+//
+
+
+
+//
+// Begin SPI Class Implementation for 16SCIS752 dual UART
+//
+
+
+/** Create an SC16IS752_SPI object using a specified SPI bus and CS
+  * Note: The SC16IS752 does not have separate GPIOs for Channel_A and Channel_B. 
+  * Note: The SC16IS752 does not have separate Reset for Channel_A and Channel_B.     
+  *
+  * @param SPI &spi the SPI port to connect to 
+  * @param cs  Pinname of the CS pin (active low)
+  * @param rst Pinname for Reset pin (active low) Optional, Default = NC 
+  * @param channel UART ChannelName, Default = Channel_A      
+  */    
+SC16IS752_SPI::SC16IS752_SPI (SPI *spi, PinName cs, PinName rst, ChannelName channel) : _spi(spi), _cs(cs), _channel(channel)  {
+  _cs = 1;  // deselect
+  
+  _spi->format(8, 0);          
+  _spi->frequency(1000000);
+
+  // The hardware Reset pin is optional. Test and make sure whether it exists or not to prevent illegal access.
+  if (rst != NC) {
+    _reset = new DigitalOut(rst);   //Construct new pin 
+    _reset->write(1);               //Deactivate    
+  }
+  else {
+    // No Hardware Reset pin       
+    _reset = NULL;                  //Construct dummy pin     
+  }  
+
+
+  // Dont call _init() until SPI port has been configured.
+  // That is why _init() is not called in parent Constructor 
+  _init();
+
+};
+
+
+/** Destruct SC16IS740_SPI bridge object
+  *
+  * @param  none
+  * @return none
+  */ 
+SC16IS752_SPI::~SC16IS752_SPI() {
+   if (_reset != NULL) {delete _reset;}  // Reset pin
+}
+
+
+/** Write value to internal register.
+  * Pure virtual, must be declared in derived class.   
+  *   @param registerAddress   The address of the Register (enum RegisterName)
+  *   @param data              The 8bit value to write
+  *   @return none 
+  */
+void SC16IS752_SPI::writeRegister(RegisterName registerAddress, char data) {
+
+  _cs = 0; //  select;
+  _spi->write(registerAddress | _channel);
+  _spi->write(data);
+  _cs = 1; //  deselect;
+
+}
+
+
+/** Read value from internal register.
+  *   @param registerAddress   The address of the Register (enum RegisterName)
+  *   @return char             The 8bit value read from the register
+  */
+char SC16IS752_SPI::readRegister(RegisterName registerAddress) {
+
+  // Used in SPI read operations to flush slave's shift register
+  const char SPI_DUMMY_CHAR = 0xFF; 
+
+  char result;
+
+  _cs = 0; //  select;
+  _spi->write(SPI_READ_MODE_FLAG | registerAddress | _channel);
+  result = _spi->write(SPI_DUMMY_CHAR);
+  _cs = 1; //  deselect;
+
+  return result;  
+}
+
+
+/** Write multiple datavalues to Transmitregister.
+  * More Efficient implementation than writing individual bytes
+  * Assume that previous check confirmed that the FIFO has sufficient free space to store the data  
+  * Pure virtual, must be declared in derived class.   
+  *   @param char* databytes   The pointer to the block of data
+  *   @param len               The number of bytes to write
+  *   @return none 
+  */
+void SC16IS752_SPI::writeDataBlock (const char *data, int len) {
+  int i;
+  
+  _cs = 0; //  select;
+  
+  // Select the Transmit Holding Register
+  // Assume that previous check confirmed that the FIFO has sufficient free space to store the data
+  _spi->write(THR | _channel);
+  
+  for (i=0; i<len; i++, data++)
+    _spi->write(*data);
+    
+  _cs = 1; //  deselect;
+}
+
+
+/** Hardware Reset SC16IS752 device.
+  * This method is only available when the Reset pin has been declared and is also connected
+  *   @param  none
+  *   @return none
+  */
+void SC16IS752_SPI::hwReset() {
+  
+  if (_reset != NULL){
+    _reset->write(0);  //activate
+//    wait_ms(100);
+    wait_us(1000*1000);   //test only            
+    _reset->write(1);  //deactivate
+  }  
+  else {
+    printf("Hardware Reset pin is not available...\n\r");     
+  }
+
+}
+
+//
+// End SPI Implementation
+//
+
+
+
+//
+// Begin I2C Implementation for 16SCIS752 dual UART
+//
+
+/** Create a SC16IS752_I2C object for a bridge between I2C and a Serial port
+  * Note: The SC16IS752 does not have separate GPIOs for Channel_A and Channel_B. 
+  * Note: The SC16IS752 does not have separate Reset for Channel_A and Channel_B.     
+  *
+  * @param I2C &i2c the I2C port to connect to 
+  * @param char deviceAddress the I2C slave address of the SC16IS740
+  * @param rst Pinname for Reset pin (active low) Optional, Default = NC   
+  * @param channel UART Channel, Default = Channel_A    
+  */  
+SC16IS752_I2C::SC16IS752_I2C(I2C *i2c, uint8_t deviceAddress, PinName rst, ChannelName channel) : _i2c(i2c), _slaveAddress(deviceAddress & 0xFE), _channel(channel) {
+
+//  _i2c->frequency(400000);
+  _i2c->frequency(100000);
+   
+  // The hardware Reset pin is optional. Test and make sure whether it exists or not to prevent illegal access.
+  if (rst != NC) {
+    _reset = new DigitalOut(rst);   //Construct new pin 
+    _reset->write(1);               //Deactivate    
+  }
+  else {
+    // No Hardware Reset pin       
+    _reset = NULL;                  //Construct dummy pin     
+  }  
+  
+  // Dont call _init() until I2C port has been configured.
+  // That is why _init() is not called in parent Constructor 
+  _init();
+}
+
+
+/** Destruct SC16IS752_I2C bridge object
+  *
+  * @param  none
+  * @return none
+  */ 
+SC16IS752_I2C::~SC16IS752_I2C() {
+   if (_reset != NULL) {delete _reset;}  // Reset pin
+}
+
+
+/** Write value to internal register.
+  *   @param registerAddress   The address of the Register (enum RegisterName)
+  *   @param data              The 8bit value to write
+  *   @return none 
+  */
+void SC16IS752_I2C::writeRegister(RegisterName registerAddress, char data) {
+  char w[2];
+
+  w[0] = registerAddress | _channel;
+  w[1] = data;
+
+  _i2c->write( _slaveAddress, w, 2 );
+}
+
+
+/** Read value from internal register.
+  *   @param registerAddress   The address of the Register (enum RegisterName)
+  *   @return char             The 8bit value read from the register
+  */
+char SC16IS752_I2C::readRegister(RegisterName registerAddress) {
+  /*
+   * Read char from SC16IS752 register at <registerAddress>.
+   */
+   char w[1];
+   char r[1];
+    
+   w[0] = registerAddress | _channel;
+    
+//   _i2c->write( _slaveAddress, w, 1 );
+   _i2c->write(_slaveAddress, w, 1, true);   //Repeated Start
+   _i2c->read(_slaveAddress, r, 1 );
+
+   return ( r[0] );
+}
+
+
+/** Write multiple datavalues to Transmitregister.
+  * More Efficient implementation than writing individual bytes
+  * Assume that previous check confirmed that the FIFO has sufficient free space to store the data
+  * Pure virtual, must be declared in derived class.   
+  *   @param char* databytes   The pointer to the block of data
+  *   @param len               The number of bytes to write
+  *   @return none 
+  */
+void SC16IS752_I2C::writeDataBlock (const char *data, int len) {
+  
+#if(0)  
+  int i;
+  char w[BULK_BLOCK_LEN];
+  
+  // Select the Transmit Holding Register
+  // Assume that previous check confirmed that the FIFO has sufficient free space to store the data
+  w[0] = THR | _channel;
+
+  // copy the data..
+  for (i=0; i<len; i++)
+    w[i+1] = data[i];
+    
+  _i2c->write( _slaveAddress, w, len + 1);  
+#else
+  int i;
+  
+  _i2c->start();
+  _i2c->write(_slaveAddress); 
+
+  // Select the Transmit Holding Register
+  // Assume that previous check confirmed that the FIFO has sufficient free space to store the data
+  _i2c->write(THR | _channel);
+
+  // send the data..
+  for (i=0; i<len; i++)
+    _i2c->write(data[i]);
+    
+  _i2c->stop();  
+#endif
+}
+
+
+/** Hardware Reset SC16IS752 device.
+  * This method is only available when the Reset pin has been declared and is also connected
+  *   @param  none
+  *   @return none
+  */
+void SC16IS752_I2C::hwReset() {
+  
+  if (_reset != NULL){
+    _reset->write(0);  //activate
+//    wait_ms(100);
+    wait_us(1000*1000);   //test only            
+    _reset->write(1);  //deactivate
+  }  
+  else {
+    printf("Hardware Reset pin is not available...\n\r");     
+  }
+}
+
+
+//
+// End I2C Implementation
+//
\ No newline at end of file