SPI or I2C to UART Bridge

Dependents:   SC16IS750_Test mbed_SC16IS750 Xadow_SC16IS750_Test Xadow_MPU9150AHRS

Revision:
2:76cb93b511f2
Parent:
1:0440152c5387
Child:
3:9783b6bde958
diff -r 0440152c5387 -r 76cb93b511f2 SC16IS750.cpp
--- a/SC16IS750.cpp	Sun Feb 09 14:58:06 2014 +0000
+++ b/SC16IS750.cpp	Thu Feb 13 17:12:02 2014 +0000
@@ -1,5 +1,5 @@
 /* SC16IS750 interface 
- *   v0.1 WH, Nov 2013, Ported to mbed, Sparkfun Libs used as example. Added I2C and SPI I/F and more methods
+ *   v0.1 WH, Nov 2013, Sparkfun Libs used as example. Added I2C I/F and many more methods. 
  *
  * 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,
@@ -19,6 +19,7 @@
 #include    "mbed.h"
 #include    "SC16IS750.h"
 
+#define ENABLE_BULK_TRANSFERS          0x01
 
 /** Abstract class SC16IS750 for converter between either SPI or I2C and a Serial port
   * Constructor for this Abstract Class is protected  
@@ -40,7 +41,7 @@
   *  @return none
   */
 void SC16IS750::baud(int baudrate) {
-  unsigned long divisor = BAUD_RATE_DIVISOR(baudrate);
+  unsigned long divisor = SC16IS750_BAUDRATE_DIVISOR(baudrate);
   char lcr_tmp;
   
   _config.baudrate = baudrate;               // Save baudrate
@@ -58,6 +59,7 @@
   *   @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 SC16IS750::format(int bits, Serial::Parity parity, int stop_bits) {
   char lcr_tmp = 0x00;
@@ -103,6 +105,7 @@
 };
 
 /** Generate a break condition on the serial line
+  *  @return none 
   */
 void SC16IS750::send_break() {
   // Wait for 1.5 frames before clearing the break condition
@@ -120,6 +123,7 @@
     
 /** Set a break condition on the serial line
   *  @param enable  break condition
+  *  @return none   
   */
 void SC16IS750::set_break(bool enable) {
 
@@ -136,10 +140,12 @@
 /** Set the flow control type on the serial port
   *  Added for compatibility with Serial Class.
   *  SC16IS750 supports only Flow, Pins can not be selected.
+  *  This method sets only hardware flow control. SC16IS750 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)
-  *  @param flow2 the second flow control pin (CTS for 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 SC16IS750::set_flow_control(Flow type, PinName flow1, PinName flow2) {
   char lcr_tmp; 
@@ -161,15 +167,55 @@
 
   }
 
-  //Save flowcontrol state
-  //enable enhanced functions  
-  _config.flowctrl = efr_tmp | EFR_ENABLE_ENHANCED_FUNCTIONS,
+  //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 only hardware flow control levels. SC16IS750 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 SC16IS750::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 SC16IS750::set_modem_control() {
+
+  //Note MCR[7:4] and MCR[2] only accessible when EFR[4] is set
+  if (SC16IS750_PRESCALER == SC16IS750_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
@@ -184,29 +230,40 @@
 
   // Software reset, assuming there is no access to the HW Reset pin
   swReset();
-  
-  // Set default baudrate and save in _config
-  // LCR, DLL/DLH  
+
+  // Set default baudrate (depends on prescaler) and save in _config
+  // DLL/DLH  
   baud();
 
-  // Set dataflow and save in _config
-  // LCR, EFR
-  set_flow_control(); 
-  
   // 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 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();
-  
-  // Set default fifoformat and save in _config
-  // FCR
-  _config.fifoenable = true;  
-  _config.fifoformat = FCR_RX_IRQ_NONE | FCR_ENA_FIFO_64;
-  flush();
-
+ 
   // The UART bridge should now be successfully initialised.
 
   // Test if UART bridge is present and initialised
@@ -224,12 +281,48 @@
 
 }
 
+
+/** 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 SC16IS750::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 SC16IS750::flush() {
+  // FCR is Write Only, use saved _config
 
   // reset TXFIFO, reset RXFIFO, non FIFO mode  
   this->writeRegister(FCR, FCR_TXFIFO_RST | FCR_RXFIFO_RST);
@@ -277,22 +370,30 @@
 
 
 /** 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 SC16IS750::readable() {
-  return (this->readRegister(LSR) & 0x01);
+  
+//  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 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));
 }
@@ -301,22 +402,25 @@
   *   @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
+ 
+//  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 SC16IS750::writableCount() {
-  /*
-   * Get the available space for writing characters.
-   *
-   * 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
+  return (this->readRegister(TXLVL));  // TX Level
 }
 
 
@@ -341,6 +445,7 @@
   *   @return value written  
   */ 
 int SC16IS750::putc(int value) {
+
   while (this->readRegister(TXLVL) == 0) {
     // Wait for space in TX buffer
     wait_us(10);
@@ -350,39 +455,62 @@
   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 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)
-    wait_us(10);    
-    // (But apparently still not slow enough to ensure delivery.)
-  };
-}
 
 #if ENABLE_BULK_TRANSFERS
-void SC16IS750::write(const uint8_t *buffer, size_t size) {
-  /*
+
+  #define BULK_BLOCK_LEN  16
+  int len, idx;
   
-    Write buffer to UART.
- 
-   */
-  //select();
-  //transfer(THR); // TODO: Change this when we modify register addresses? (Even though it's 0x00.) 
+  len = strlen(str);  
 
-  while(size > 16) {
-    //transfer_bulk(buffer, 16); //ringbuffer?
-    size -= 16;
-    buffer += 16;
+  // 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
+    // Note: can be optimized by writing registeraddress once and then repeatedsly write the bytes.
+    for (idx=0; idx<BULK_BLOCK_LEN; idx++) {
+      this->writeRegister(THR, str[idx]);
+    };
+        
+    len -= BULK_BLOCK_LEN;
+    str += BULK_BLOCK_LEN;
   }
-  //transfer_bulk(buffer, size);
+  
+  // Write remaining 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]);
+  }  
+
 
-  //deselect();
+#else
+  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  
 }
-#endif
+
+
 
 /** Set direction of I/O port pins.
   * This method is specific to the SPI-I2C UART and not found on the 16750