SPI or I2C to UART Bridge
Dependents: SC16IS750_Test mbed_SC16IS750 Xadow_SC16IS750_Test Xadow_MPU9150AHRS
SC16IS750.cpp
- Committer:
- wim
- Date:
- 2014-01-22
- Revision:
- 0:d64854a60f95
- Child:
- 1:0440152c5387
File content as of revision 0:d64854a60f95:
/* 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