SPI or I2C to UART Bridge
Dependents: SC16IS750_Test mbed_SC16IS750 Xadow_SC16IS750_Test Xadow_MPU9150AHRS
Revision 0:d64854a60f95, committed 2014-01-22
- Comitter:
- wim
- Date:
- Wed Jan 22 16:39:37 2014 +0000
- Child:
- 1:0440152c5387
- Commit message:
- First Test Version
Changed in this revision
SC16IS750.cpp | Show annotated file Show diff for this revision Revisions of this file |
SC16IS750.h | Show annotated file Show diff for this revision Revisions of this file |
--- /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
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/SC16IS750.h Wed Jan 22 16:39:37 2014 +0000 @@ -0,0 +1,333 @@ +/* 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. + */ +#ifndef _SC16IS750_H +#define _SC16IS750_H + +//Default I2C Slaveaddress +#define DEFAULT_SC16IS750_ADDR 0x9A + +//Default baudrate +#define DEFAULT_BAUD_RATE 9600 + +#define ENABLE_BULK_TRANSFERS 0x01 + +//#include "Configuration.h" + +#define XTAL_FREQUENCY 14745600UL // On-board crystal (New mid-2010 Version) + +// See datasheet section 7.8 for configuring the +// "Programmable baud rate generator" +#define PRESCALER 1 // Default prescaler after reset +#define BAUD_RATE_DIVISOR(baud) ((XTAL_FREQUENCY/PRESCALER)/(baud*16UL)) + +// See section 8.4 of the datasheet for definitions +// of bits in the Line Control Register (LCR) +#define LCR_BITS5 0x00 +#define LCR_BITS6 0x01 +#define LCR_BITS7 0x02 +#define LCR_BITS8 0x03 + +#define LCR_BITS1 0x00 +#define LCR_BITS2 0x04 + +#define LCR_NONE 0x00 +#define LCR_ODD 0x08 +#define LCR_EVEN 0x18 +#define LCR_FORCED1 0x28 +#define LCR_FORCED0 0x38 + +#define LCR_BRK_ENA 0x40 +#define LCR_BRK_DIS 0x00 + +#define LCR_DIV_ENA 0x80 +#define LCR_DIV_DIS 0x00 + + +// See section 8.10 of the datasheet for definitions +// of bits in the Enhanced Features Register (EFR) +#define EFR_ENABLE_CTS (1 << 7) +#define EFR_ENABLE_RTS (1 << 6) +#define EFR_ENABLE_ENHANCED_FUNCTIONS (1 << 4) + +// See Chapter 11 of datasheet +#define SPI_READ_MODE_FLAG 0x80 + + +/** Abstract class SC16IS750 for a converter between either SPI or I2C and a Serial port + * + * Supports both SPI and I2C interfaces through derived classes + * + * @code + * + * @endcode + */ +//class SC16IS750 : public Serial { //Fout, geen Serial constr met Serial(NC, NC) toegestaan... +class SC16IS750 { + +public: + +// SC16IS750 Register definitions (shifted to align) + enum RegisterName { + RHR = 0x00 << 3, + THR = 0x00 << 3, + IER = 0x01 << 3, + FCR = 0x02 << 3, + IIR = 0x02 << 3, + LCR = 0x03 << 3, + MCR = 0x04 << 3, + LSR = 0x05 << 3, + MSR = 0x06 << 3, + SPR = 0x07 << 3, + TCR = 0x06 << 3, + TLR = 0x07 << 3, + TXLVL = 0x08 << 3, + RXLVL = 0x09 << 3, + IODIR = 0x0A << 3, + IOSTATE = 0x0B << 3, + IOINTMSK = 0x0C << 3, + reserved = 0x0D << 3, + IOCTRL = 0x0E << 3, + EFCR = 0x0F << 3, + DLL = 0x00 << 3, + DLH = 0x01 << 3, + EFR = 0x02 << 3, + XON1 = 0x04 << 3, + XON2 = 0x05 << 3, + XOFF1 = 0x06 << 3, + XOFF2 = 0x07 << 3, + } ; + + +// SC16IS750 configuration register values +struct SC16IS750_cfg { + char baudrate; + char dataformat; + char flowctrl; + char fifoformat; +}; + +/** Constructor + * + */ + SC16IS750(); + +/** Determine if there is a character available to read. + * @return 1 if there is a character available to read, 0 otherwise + */ + int readable(); + +/** Determine if how many characters available to read. + * @return int Characters available to read + */ + int readableCount(); + +/** 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 writable(); + +/** Determine if how much space is available to write characters. + * @return int Characterspace available for writing + */ + int writableCount(); + + char getc(); + void putc(char value); + + void write(const char *str); + +/** Set baudrate of the serial port. + * @param baud integer baudrate (4800, 9600 etc) + * @return none + */ + void baud(int baudrate = DEFAULT_BAUD_RATE); + +/** 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 format(int bits=8, Serial::Parity parity=Serial::None, int stop_bits=1); + +/** + * Check that UART is connected and operational. + * @param none + * @return bool true when connected, false otherwise + */ + bool connected(); + + +#if ENABLE_BULK_TRANSFERS + void write(const uint8_t *buffer, size_t size); +#else + // using Print::write; +#endif + void flush(); + +//required for Stream + int peek() {return 0;}; + + // These are specific to the SPI UART + void ioSetDirection(unsigned char bits); + void ioSetState(unsigned char bits); + +/** 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 + */ + virtual void writeRegister (RegisterName register_address, char data ) =0; + +/** Read value from internal register. + * Pure virtual, must be declared in derived class. + * @param register_address The address of the Register (enum RegisterName) + * @return char The 8bit value read from the register + */ + virtual char readRegister (RegisterName register_address ) =0; + +protected: +//protected is accessible to derived classes, but not to external users + +SC16IS750_cfg _config; + +private: +//private is not accessible to derived classes, nor external users + void init(); +}; + + + +/** Class SC16IS750_SPI for a converter between SPI and a Serial port + * + * @code + * #include "mbed.h" + * #include "SC16IS750.h" + * + * SPI spi(PTD2, PTD3, PTD1); //MOSI, MISO, SCK + * SC16IS750_SPI serial_spi(&spi, PTD0); + * + * Serial pc(USBTX,USBRX); + * + * int main() { + * pc.printf("\nHello World!\n"); + * + * while(1) { + * serial_spi.ioSetState(0x00); + * wait(0.5); + * pc.putc('*'); + * } + * } + * + * @endcode + */ +class SC16IS750_SPI : public SC16IS750 { +public: + +/** Create a SC16IS750_SPI object using a specified SPI bus and CS + * + * @param SPI &spi the SPI port to connect to + * @param cs the Pin of the CS + */ + SC16IS750_SPI(SPI *spi, PinName cs); + +/** Write value to internal register. + * @param register_address The address of the Register (enum RegisterName) + * @param data The 8bit value to write + * @return none + */ + virtual void writeRegister(SC16IS750::RegisterName registerAddress, char data); + +/** Read value from internal register. + * @param register_address The address of the Register (enum RegisterName) + * @return char The 8bit value read from the register + */ + virtual char readRegister(SC16IS750::RegisterName registerAddress); + +protected: +//protected is accessible to derived classes, but not to external users + + +private: + SPI *_spi; //SPI bus reference + DigitalOut _cs; //CS of SPI device + +}; + + + +/** Class SC16IS750_I2C for a converter between I2C and a Serial port + * + * @code + * #include "mbed.h" + * #include "SC16IS750.h" + * + * I2C i2c(PTE0, PTE1); //SDA, SCL + * SC16IS750_I2C serial_i2c(&i2c, DEFAULT_SC16IS750_ADDR); + * + * Serial pc(USBTX,USBRX); + * + * int main() { + * pc.printf("\nHello World!\n"); + * + * while(1) { + * serial_i2c.ioSetState(0x00); + * wait(0.5); + * pc.putc('*'); + * } + * } + * + * @endcode + */ +class SC16IS750_I2C : public SC16IS750 { +public: + +/** Create a SC16IS750_I2C object using a specified I2C bus and slaveaddress + * + * @param I2C &i2c the I2C port to connect to + * @param char deviceAddress the address of the SC16IS750 + */ + SC16IS750_I2C(I2C *i2c, uint8_t deviceAddress = DEFAULT_SC16IS750_ADDR); + +/** Write value to internal register. + * @param register_address The address of the Register (enum RegisterName) + * @param data The 8bit value to write + * @return none + */ + virtual void writeRegister(SC16IS750::RegisterName register_address, char data ); + +/** Read value from internal register. + * @param register_address The address of the Register (enum RegisterName) + * @return char The 8bit value read from the register + */ + virtual char readRegister(SC16IS750::RegisterName register_address ); + +protected: +//protected is accessible to derived classes, but not to external users + + +private: + I2C *_i2c; //I2C bus reference + uint8_t _slaveAddress; //I2C Slave address of device + +}; + + +#endif // _SC16IS750_H