SPI or I2C to UART Bridge

Dependents:   SC16IS750_Test mbed_SC16IS750 Xadow_SC16IS750_Test Xadow_MPU9150AHRS

Revision:
0:d64854a60f95
Child:
1:0440152c5387
diff -r 000000000000 -r d64854a60f95 SC16IS750.cpp
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/SC16IS750.cpp	Wed Jan 22 16:39:37 2014 +0000
@@ -0,0 +1,423 @@
+/* SC16IS750 interface 
+ *   /////////////////////v1.0 Tedd OKANO, 18 Jul 2012, I2C I/F only, MIT License
+ *   v1.1 WH, Nov 2013, Added SPI I/F and more methods, MIT License
+ *
+ * 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    "SC16IS750.h"
+
+
+/** Abstract class SC16IS750 for converter between either SPI or I2C and a Serial port
+  *
+  * Supports both SPI and I2C interfaces through derived classes
+  *
+  * @code
+  *
+  * @endcode
+  */
+//SC16IS750::SC16IS750() : Serial(NC, NC) {   //Fout ???
+SC16IS750::SC16IS750() {  
+  init();  // initialise UART registers
+}
+
+
+/** Set baudrate of the serial port.    
+  *  @param  baud integer baudrate (4800, 9600 etc)
+  *  @return none
+  */
+void SC16IS750::baud(int baudrate) {
+  unsigned long divisor = BAUD_RATE_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_DIV_ENA);              // 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) 
+  */
+void SC16IS750::format(int bits, Serial::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 Serial::None:    lcr_tmp |= LCR_NONE;
+                          break;
+    case Serial::Odd:     lcr_tmp |= LCR_ODD;
+                          break;
+    case Serial::Even:    lcr_tmp |= LCR_EVEN;
+                          break;
+    case Serial::Forced1: lcr_tmp |= LCR_FORCED1;
+                          break;
+    case Serial::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  
+
+};
+
+
+/**
+  * Initialise the UART.
+  *
+  * If initialisation fails this method does not return.
+  */
+void SC16IS750::init() {
+
+  // Initialise SC16IS750
+
+  // Set default baudrate and save in _config
+  baud();
+
+  // Set dataflow and save in _config
+  // We need to enable flow control or we overflow buffers and
+  // lose data when used with the WiFly. Note that flow control 
+  // needs to be enabled on the WiFly for this to work but it's
+  // possible to do that with flow control enabled here but not there.
+  // TODO: Make this able to be configured externally?
+  _config.flowctrl = EFR_ENABLE_CTS | EFR_ENABLE_RTS | EFR_ENABLE_ENHANCED_FUNCTIONS,
+
+  this->writeRegister(LCR, 0xBF); // access EFR register
+  this->writeRegister(EFR, _config.flowctrl); // enable enhanced registers
+  
+  // Set default dataformat and save in _config
+  format();  
+
+  // Set default fifoformat and save in _config
+  this->writeRegister(FCR, 0x06); // reset TXFIFO, reset RXFIFO, non FIFO mode
+  this->writeRegister(FCR, 0x01); // enable FIFO mode   
+
+  // 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
+
+}
+
+
+/**
+  * Check that UART is connected and operational.
+  *  @param  none
+  *  @return bool true when connected, false otherwise
+  */
+bool SC16IS750::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.
+  *   @return 1 if there is a character available to read, 0 otherwise
+  */
+int SC16IS750::readable() {
+  /**
+   * Get the number of chars (characters) available for reading.
+   *
+   * This is data that's already arrived and stored in the receive
+   * buffer (which holds 64 chars).
+   * This alternative just checks if there's data but doesn't
+   * return how many characters are in the buffer:
+   */
+  return (this->readRegister(LSR) & 0x01);
+}
+
+/** Determine if how many characters available to read.
+  *   @return int Characters available to read
+  */
+int SC16IS750::readableCount() {
+  /*
+   * Get the number of chars (characters) available for reading.
+   *
+   * This is data that's already arrived and stored in the receive
+   * buffer (which holds 64 chars).
+   */
+
+  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 SC16IS750::writable() {
+  return (this->writableCount() > 0);  // Check datasheet for faster version
+}
+
+/** Determine if how many characters available to write.
+  *   @return int Characters available to write
+  */
+int SC16IS750::writableCount() {
+  /*
+   * Get the number of chars (characters) available for reading.
+   *
+   * This is data that's already stored in the transmit
+   * buffer (which holds 64 chars).
+   */
+
+  return (this->readRegister(TXLVL));
+//  return (readRegister(64 - TXLVL));  //Check datasheet
+}
+
+
+
+char SC16IS750::getc() {
+  /*
+   * Read char from UART.
+   *
+   * Returns char read or or -1 if no data available.
+   *
+   * Acts in the same manner as 'Serial.read()'.
+   */
+
+  if (!readable()) {
+    return -1;
+  }
+
+  return this->readRegister(RHR);
+}
+
+
+void SC16IS750::putc(char value) {
+  /*
+   * Write char to UART.
+   */
+
+  while (this->readRegister(TXLVL) == 0) {
+    // Wait for space in TX buffer
+  };
+  this->writeRegister(THR, value);
+}
+
+
+void SC16IS750::write(const char *str) {
+  /*
+   * Write string to UART.
+   */
+  write((const uint8_t *) str, strlen(str));  
+  while (this->readRegister(TXLVL) < 64) {
+    // Wait for empty TX buffer (slow)
+    // (But apparently still not slow enough to ensure delivery.)
+  };
+}
+
+#if ENABLE_BULK_TRANSFERS
+void SC16IS750::write(const uint8_t *buffer, size_t size) {
+  /*
+  
+    Write buffer to UART.
+ 
+   */
+  //select();
+  //transfer(THR); // TODO: Change this when we modify register addresses? (Even though it's 0x00.) 
+
+  while(size > 16) {
+    //transfer_bulk(buffer, 16); //ringbuffer?
+    size -= 16;
+    buffer += 16;
+  }
+  //transfer_bulk(buffer, size);
+
+  //deselect();
+}
+#endif
+
+void SC16IS750::flush() {
+  /*
+   * Flush characters from SC16IS750 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();
+  }
+}
+
+
+void SC16IS750::ioSetDirection(unsigned char bits) {
+  this->writeRegister(IODIR, bits);
+}
+
+
+void SC16IS750::ioSetState(unsigned char bits) {
+  this->writeRegister(IOSTATE, bits);
+}
+
+
+
+// Begin SPI Implementation
+//
+
+/** Class SC16IS750_SPI for a converter between SPI and a Serial port
+  *
+  */
+SC16IS750_SPI::SC16IS750_SPI (SPI *spi, PinName cs) : _spi(spi), _cs(cs)  {
+  _cs = 1;  // deselect
+  
+  _spi->format(8, 0);          
+  _spi->frequency(1000000);
+ 
+};
+
+/** Write value to internal register.
+  * Pure virtual, must be declared in derived class.   
+  *   @param register_address  The address of the Register (enum RegisterName)
+  *   @param data              The 8bit value to write
+  *   @return none 
+  */
+void SC16IS750_SPI::writeRegister(RegisterName registerAddress, char data) {
+  /*
+   * Write <data> char to the SC16IS750 register <registerAddress>
+   */
+
+  _cs = 0; //  select;
+  _spi->write(registerAddress);
+  _spi->write(data);
+  _cs = 1; //  deselect;
+  
+  
+//Test only  
+  DigitalOut myled2(LED_GREEN);  
+  myled2 = 0; //LED On
+  wait(0.2);
+  myled2 = 1; //LED Off
+  wait(0.6);  
+}
+
+
+/** Read value from internal register.
+  *   @param register_address  The address of the Register (enum RegisterName)
+  *   @return char             The 8bit value read from the register
+  */
+char SC16IS750_SPI::readRegister(RegisterName registerAddress) {
+  /*
+   * Read char from SC16IS750 register at <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;  
+}
+
+//
+// End SPI Implementation
+
+
+// Begin I2C Implementation
+//
+
+/** Class SC16IS750_I2C for a converter between I2C and a Serial port
+  *
+  */
+SC16IS750_I2C::SC16IS750_I2C(I2C *i2c, uint8_t deviceAddress) : _i2c(i2c), _slaveAddress(deviceAddress) {
+
+    _i2c->frequency(400000);
+
+}
+
+
+/** Write value to internal register.
+  *   @param register_address  The address of the Register (enum RegisterName)
+  *   @param data              The 8bit value to write
+  *   @return none 
+  */
+void SC16IS750_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 register_address  The address of the Register (enum RegisterName)
+  *   @return char             The 8bit value read from the register
+  */
+char SC16IS750_I2C::readRegister(RegisterName registerAddress) {
+  /*
+   * Read char from SC16IS750 register at <registerAddress>.
+   */
+   char w[1];
+   char r[1];
+    
+   w[0] = registerAddress;
+    
+   _i2c->write( _slaveAddress, w, 1 );
+   _i2c->read( _slaveAddress, r, 1 );
+
+   return ( r[0] );
+}
+
+
+//
+// End I2C Implementation
\ No newline at end of file