RETRO ROBOT E
Dependents: RETRO_ROBOT_SC16IS750E
Fork of SC16IS750 by
Diff: SC16IS750.cpp
- Revision:
- 0:d64854a60f95
- Child:
- 1:0440152c5387
--- /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