SPI or I2C to UART Bridge

Dependents:   SC16IS750_Test mbed_SC16IS750 Xadow_SC16IS750_Test Xadow_MPU9150AHRS

SC16IS750.cpp

Committer:
wim
Date:
2014-02-20
Revision:
3:9783b6bde958
Parent:
2:76cb93b511f2
Child:
4:12446ee9f9c8

File content as of revision 3:9783b6bde958:

/* SC16IS750 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
 *
 * 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"

#define ENABLE_BULK_TRANSFERS             1
#define BULK_BLOCK_LEN                   16

/** Abstract class SC16IS750 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
  */
SC16IS750::SC16IS750() { 
//SC16IS750::SC16IS750() : Serial(NC, NC) {   //Fout, mag geen NC zijn
//SC16IS750::SC16IS750() : 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 SC16IS750::baud(int baudrate) {
  unsigned long divisor = SC16IS750_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 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  

};

/** Generate a break condition on the serial line
  *  @return none 
  */
void SC16IS750::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 SC16IS750::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.
  *  SC16IS750 supports only Flow, Pins can not be selected.
  *  This method sets 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) - 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; 
  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. 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
  * Should be in protection section. Public for testing purposes
  * If initialisation fails this method does not return.    
  *   @param none
  *   @return none 
  */
void SC16IS750::_init() {

  // Initialise SC16IS750

  // 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 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_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 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();
  }
#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.
  * 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() {
  
//  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() {

  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() {
 
//  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() {

  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 SC16IS750::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 SC16IS750::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 SC16IS750::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 SC16IS750::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
  *   @param  bits Bitpattern for I/O (1=output, 0=input)
  *   @return none
  */
void SC16IS750::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
  *   @param  bits Bitpattern for I/O (1= set output bit, 0 = clear output bit)
  *   @return none
  */
void SC16IS750::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
  *   @param  none
  *   @return bits Bitpattern for I/O (1= bit set, 0 = bit cleared)
  */
unsigned char SC16IS750::ioGetState() {
  return this->readRegister(IOSTATE) ;
}


/** Software Reset SC16IS750 device.
  * This method is specific to the SPI-I2C UART and not found on the 16750
  *   @param  none
  *   @return none
  */
void SC16IS750::swReset() {
  this->writeRegister(IOCTRL, IOC_SW_RST);     
}


//
// End Abstract Class Implementation
//



/** Class SC16IS750_SPI for a converter between SPI and a Serial port
  *
  * @code
  *
  * @endcode
  *
  */
SC16IS750_SPI::SC16IS750_SPI (SPI *spi, PinName cs) : _spi(spi), _cs(cs)  {
  _cs = 1;  // deselect
  
  _spi->format(8, 0);          
  _spi->frequency(1000000);
//  _spi->frequency(100000);  //test

  // Dont call _init() until SPI port has been configured.
  // That is why _init() is not called in parent Constructor 
  _init();

};

/** 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 SC16IS750_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 SC16IS750_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 SC16IS750_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;
}

//
// End SPI Implementation
//


/** Class SC16IS750_I2C for a converter between I2C and a Serial port
  *
  * @code
  *
  * @endcode
  *
  */
SC16IS750_I2C::SC16IS750_I2C(I2C *i2c, uint8_t deviceAddress) : _i2c(i2c), _slaveAddress(deviceAddress & 0xFE) {

  _i2c->frequency(400000);

  // Dont call _init() until I2C port has been configured.
  // That is why _init() is not called in parent Constructor 
  _init();
}


/** Write value to internal register.
  *   @param registerAddress   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 registerAddress   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] );
}


/** 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 SC16IS750_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
}


//
// End I2C Implementation
//