SPI or I2C to UART Bridge
Dependents: SC16IS750_Test mbed_SC16IS750 Xadow_SC16IS750_Test Xadow_MPU9150AHRS
Diff: SC16IS750.cpp
- Revision:
- 1:0440152c5387
- Parent:
- 0:d64854a60f95
- Child:
- 2:76cb93b511f2
diff -r d64854a60f95 -r 0440152c5387 SC16IS750.cpp --- a/SC16IS750.cpp Wed Jan 22 16:39:37 2014 +0000 +++ b/SC16IS750.cpp Sun Feb 09 14:58:06 2014 +0000 @@ -1,6 +1,5 @@ /* 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 + * v0.1 WH, Nov 2013, Ported to mbed, Sparkfun Libs used as example. Added I2C and SPI I/F and 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, @@ -22,7 +21,7 @@ /** 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 @@ -31,7 +30,8 @@ */ //SC16IS750::SC16IS750() : Serial(NC, NC) { //Fout ??? SC16IS750::SC16IS750() { - init(); // initialise UART registers +// Dont call _init() here since the SPI or I2C port have not yet been configured... + //_init(); // initialise UART registers } @@ -46,10 +46,11 @@ _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(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 + } @@ -101,54 +102,164 @@ }; +/** Generate a break condition on the serial line + */ +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 + */ +void SC16IS750::set_break(bool enable) { -/** - * Initialise the UART. + 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. * - * If initialisation fails this method does not return. + * @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) */ -void SC16IS750::init() { +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 state + //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 +} + + +/** 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 and save in _config + // LCR, DLL/DLH 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 + // LCR, EFR + set_flow_control(); // Set default dataformat and save in _config + // LCR format(); + // Set default break condition and save in _config + // LCR + //set_break(); + // 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 + // 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 if(!connected()){ #if(0) // Lock up if we fail to initialise UART bridge. - while(1) { - }; + while(1) {}; #else printf("Failed to initialise UART bridge\r\n"); +#endif + } + else { + printf("Initialised UART bridge!\r\n"); + } + +} + +/** + * Flush the UART FIFOs while maintaining current FIFO mode. + * @param none + * @return none + */ +void SC16IS750::flush() { + + // reset TXFIFO, reset RXFIFO, non FIFO mode + this->writeRegister(FCR, FCR_TXFIFO_RST | FCR_RXFIFO_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 @@ -169,18 +280,10 @@ * @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. +/** Determine how many characters are available to read. * @return int Characters available to read */ int SC16IS750::readableCount() { @@ -201,12 +304,12 @@ return (this->writableCount() > 0); // Check datasheet for faster version } -/** Determine if how many characters available to write. - * @return int Characters available to write +/** Determine how much space available for writing characters. + * @return int character space available to write */ int SC16IS750::writableCount() { /* - * Get the number of chars (characters) available for reading. + * Get the available space for writing characters. * * This is data that's already stored in the transmit * buffer (which holds 64 chars). @@ -217,15 +320,13 @@ } - -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()'. - */ +/** + * 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; @@ -234,16 +335,19 @@ return this->readRegister(RHR); } - -void SC16IS750::putc(char value) { - /* - * Write char to UART. - */ - +/** + * 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; } @@ -254,6 +358,7 @@ 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.) }; } @@ -279,89 +384,108 @@ } #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(); - } -} - - +/** 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) ; +} -// Begin SPI Implementation +/** 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 register_address The address of the Register (enum RegisterName) + * @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) { - /* - * Write <data> char to the SC16IS750 register <registerAddress> - */ _cs = 0; // select; _spi->write(registerAddress); _spi->write(data); _cs = 1; // deselect; - - + +#if(0) //Test only DigitalOut myled2(LED_GREEN); myled2 = 0; //LED On wait(0.2); myled2 = 1; //LED Off wait(0.6); +#endif } /** Read value from internal register. - * @param register_address The address of the Register (enum RegisterName) + * @param registerAddress 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; + const char SPI_DUMMY_CHAR = 0xFF; char result; _cs = 0; // select; _spi->write(SPI_READ_MODE_FLAG | registerAddress); - result = _spi->write(SPI_DUMMY_char); + result = _spi->write(SPI_DUMMY_CHAR); _cs = 1; // deselect; return result; @@ -369,23 +493,28 @@ // // End SPI Implementation +// -// Begin I2C 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) { - _i2c->frequency(400000); + _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 register_address The address of the Register (enum RegisterName) + * @param registerAddress The address of the Register (enum RegisterName) * @param data The 8bit value to write * @return none */ @@ -400,7 +529,7 @@ /** Read value from internal register. - * @param register_address The address of the Register (enum RegisterName) + * @param registerAddress The address of the Register (enum RegisterName) * @return char The 8bit value read from the register */ char SC16IS750_I2C::readRegister(RegisterName registerAddress) { @@ -420,4 +549,5 @@ // -// End I2C Implementation \ No newline at end of file +// End I2C Implementation +// \ No newline at end of file