Skip to content

Commit

Permalink
Add time-outs to AvrI2c class
Browse files Browse the repository at this point in the history
  • Loading branch information
greiman committed Apr 29, 2021
1 parent 65741d0 commit dfe68ff
Show file tree
Hide file tree
Showing 5 changed files with 125 additions and 36 deletions.
2 changes: 2 additions & 0 deletions README.md
Expand Up @@ -10,6 +10,8 @@ Arduino style boards that have the SPI or Wire library.

Please read the html documentation in the SSD1306Ascii/doc folder.

Click on SSD1306Ascii.html in the doc folder.

Several options can be set by editing SSD1306Ascii/src/SSD1306Ascii.h.

The SSD1306Ascii library only requires a few bytes of RAM.
Expand Down
5 changes: 2 additions & 3 deletions examples/TickerTextDemo/TickerTextDemo.ino
Expand Up @@ -33,10 +33,7 @@ const char* text[] = {
"abcdefghijklmnopqrstuvwxyz ",
"ABCDEFGHIJKLMNOPQRSTUVWXYZ "
};

//------------------------------------------------------------------------------
String readAdc(uint8_t first, uint8_t last);

void setup() {
Wire.begin();
Wire.setClock(400000L);
Expand All @@ -53,8 +50,10 @@ void setup() {
// Try this for full screen width with set1X.
// oled.tickerInit(&state, Adafruit5x7, 2);
}

uint32_t tickTime = 0;
int n = 0;

void loop() {
if (tickTime <= millis()) {
tickTime = millis() + 30;
Expand Down
2 changes: 1 addition & 1 deletion library.properties
@@ -1,5 +1,5 @@
name=SSD1306Ascii
version=1.3.0
version=1.3.1
author=Bill Greiman <fat16lib@sbcglobal.net>
maintainer=Bill Greiman <fat16lib@sbcglobal.net>
sentence=Text display on small momochrome OLED modules.
Expand Down
9 changes: 4 additions & 5 deletions src/SSD1306Ascii.h
Expand Up @@ -94,7 +94,7 @@ inline void oledReset(uint8_t rst) {
//------------------------------------------------------------------------------
/**
* @struct TickerState
* @brief ticker status
* @brief ticker state
*/
struct TickerState {
const char* queue[TICKER_QUEUE_DIM]; ///< Queue of text pointers.
Expand Down Expand Up @@ -410,12 +410,12 @@ class SSD1306Ascii : public Print {
*/
size_t strWidth(const char* str) const;
/**
* @brief Initialize TickerState struct and clear ticker field.
* @brief Initialize TickerState struct.
*
* @param[in,out] state Ticker state.
* @param[in] font to be displayed.
* @param[in] font Font to be displayed.
* @param[in] row Row for ticker.
* @param[in] mag2X set magFactor to two if true.
* @param[in] mag2X Set magFactor to two if true.
* @param[in] bgnCol First column of ticker. Default is zero.
* @param[in] endCol Last column of ticker. Default is last column of display.
*/
Expand Down Expand Up @@ -443,7 +443,6 @@ class SSD1306Ascii : public Print {
* @brief Advance ticker by one pixel.
*
* @param[in,out] state Ticker state.
*
* @return Number of entries in text pointer queue.
*/
int8_t tickerTick(TickerState* state);
Expand Down
143 changes: 116 additions & 27 deletions src/utility/AvrI2c.h
Expand Up @@ -24,6 +24,15 @@
#ifndef AvrI2c_h
#define AvrI2c_h
#include <Arduino.h>
#if !defined(__AVR__) || !defined(TWSR)
#error AvrI2c not supported for this chip.
#else // !defined(__AVR__) || !defined(TWSR)

/** Maximum SCL frecquency */
const uint32_t MAX_SCL_CLOCK = F_CPU/(16+2*10);

/** Minimum SCL frecquency */
const uint32_t MIN_SCL_CLOCK = F_CPU/(16+2*255);

/** Bit to or with address for read start and read restart */
uint8_t const I2C_READ = 1;
Expand All @@ -42,11 +51,43 @@ uint8_t const TWSR_REP_START = 0x10;
/** slave address plus write bit transmitted, ACK received */
uint8_t const TWSR_MTX_ADR_ACK = 0x18;

/** slave address plus write bit transmitted, NACK received */
uint8_t const TWSR_MTX_ADR_NACK = 0x20;

/** data transmitted, ACK received */
uint8_t const TWSR_MTX_DATA_ACK = 0x28;

/** data transmitted, NACK received */
uint8_t const TWSR_MTX_DATA_NACK = 0x30;

/** arbitration lost */
uint8_t const TWSR_ARB_LOST = 0x38;

/** slave address plus read bit transmitted, ACK received */
uint8_t const TWSR_MRX_ADR_ACK = 0x40;

/** slave address plus read bit transmitted, NACK received */
uint8_t const TWSR_MRX_ADR_NACK = 0x48;

/** data byte has been received and ACK tramsmitted */
uint8_t const TWSR_MRX_DATA_ACK = 0x50;

/** data byte has been received and NACK tramsmitted */
uint8_t const TWSR_MRX_DATA_NACK = 0x58;
//------------------------------------------------------------------------------
#define AVR_I2C_DBG 0
#if AVR_I2C_DBG
static void AvrI2cFail(uint16_t line) {
Serial.print(F("DBG_FAIL: AvrI2c.h:"));
Serial.println(line);
}
#define DBG_PRINT_HEX(msg, val)\
{Serial.print(F(msg));Serial.println(val, HEX);}
#define AVR_I2C_FAIL_MACRO AvrI2cFail(__LINE__)
#else // AVR_I2C_DBG
#define AVR_I2C_FAIL_MACRO
#define DBG_PRINT_HEX(msg, val)
#endif // AVR_I2C_DBG
//------------------------------------------------------------------------------
/**
* \class AvrI2c
Expand All @@ -60,22 +101,39 @@ class AvrI2c {
* @brief Initialize prescalar and SLC clock rate.
* @param[in] fastMode Fast 400 kHz mode if true else standard 100 kHz mode.
*/
void begin(bool fastMode = true) {
bool begin(bool fastMode = true) {
// Zero prescaler.
TWSR = 0;
// Enable module and acks.
TWCR = (1 << TWEN) | (1 << TWEA);
// Set bit rate.
setClock(fastMode && F_CPU > 15000000 ? 400000 : 100000);
return setClock(fastMode && F_CPU > 15000000 ? 400000 : 100000);
}
/**
* @brief Disable TWI module.
*/
void end() {
TWCR &= ~((1 << TWEN) | (1 << TWEA));
}
/**
* @brief Read a byte and send Ack if more reads follow else
Nak to terminate read.
*
* @param[out] data byte received.
* @param[in] last Set true to terminate the read else false.
* @return The byte read from the I2C bus.
* @return true for success.
*/
uint8_t read(bool last) {
execCmd((1 << TWINT) | (1 << TWEN) | (last ? 0 : (1 << TWEA)));
return TWDR;
bool read(uint8_t* data, bool last) {
if (!execCmd((1 << TWINT) | (1 << TWEN) | (last ? 0 : (1 << TWEA)))) {
AVR_I2C_FAIL_MACRO;
return false;
}
*data = TWDR;
if (status() != (last ? TWSR_MRX_DATA_NACK : TWSR_MRX_DATA_ACK)) {
AVR_I2C_FAIL_MACRO;
return false;
}
return true;
}
/**
* @brief Issue a repeated start condition.
Expand All @@ -91,42 +149,60 @@ class AvrI2c {
/**
* @brief Set the I2C bit rate.
*
* @param[in] frequency Desired frequency in Hz.
* @param[in] clock Desired frequency in Hz.
* Valid range for a 16 MHz board is about 40 kHz to 444,000 kHz.
*/
void setClock(uint32_t frequency) {
TWBR = ((F_CPU / frequency) - 16) / 2;
bool setClock(uint32_t clock) {
if (clock < MIN_SCL_CLOCK || clock > MAX_SCL_CLOCK) {
AVR_I2C_FAIL_MACRO;
return false;
}
TWBR = ((F_CPU / clock) - 16) / 2;
return true;
}
/**
* @brief Issue a start condition.
*
* @param[in] addressRW I2C address with read/write bit.
* @param[in] addRW I2C address with read/write bit.
*
* @return The value true for success or false for failure.
*/
bool start(uint8_t addressRW) {
bool start(uint8_t addRW) {
// send START condition
execCmd((1 << TWINT) | (1 << TWSTA) | (1 << TWEN));
if (!execCmd((1 << TWINT) | (1 << TWSTA) | (1 << TWEN))) {
AVR_I2C_FAIL_MACRO;
return false;
}
if (status() != TWSR_START && status() != TWSR_REP_START) {
AVR_I2C_FAIL_MACRO;
return false;
}
// send device address and direction
TWDR = addressRW;
execCmd((1 << TWINT) | (1 << TWEN));
if (addressRW & I2C_READ) {
return status() == TWSR_MRX_ADR_ACK;
} else {
return status() == TWSR_MTX_ADR_ACK;
TWDR = addRW;
if (!execCmd((1 << TWINT) | (1 << TWEN))) {
AVR_I2C_FAIL_MACRO;
return false;
}
if (status() != (addRW & I2C_READ ? TWSR_MRX_ADR_ACK : TWSR_MTX_ADR_ACK)) {
AVR_I2C_FAIL_MACRO;
return false;
}
return true;
}
/** @return status from last TWI command - useful for library debug */
uint8_t status(void) {return status_;}
/** Issue a stop condition. */
void stop(void) {
bool stop(void) {
TWCR = (1 << TWINT) | (1 << TWEN) | (1 << TWSTO);

// wait until stop condition is executed and bus released
while (TWCR & (1 << TWSTO)) {}
for (uint8_t tout = 100; TWCR & (1 << TWSTO); tout--) {
if (tout == 0) {
AVR_I2C_FAIL_MACRO;
return false;
}
}
return true;
}
/**
* @brief Write a byte.
Expand All @@ -137,20 +213,33 @@ class AvrI2c {
*/
bool write(uint8_t data) {
TWDR = data;
execCmd((1 << TWINT) | (1 << TWEN));
return status() == TWSR_MTX_DATA_ACK;
if (!execCmd((1 << TWINT) | (1 << TWEN))) {
AVR_I2C_FAIL_MACRO;
return false;
}
if (status() != TWSR_MTX_DATA_ACK) {
AVR_I2C_FAIL_MACRO;
return false;
}
return true;
}

private:
uint8_t status_;

void execCmd(uint8_t cmdReg) {
bool execCmd(uint8_t cmdReg) {
// send command
TWCR = cmdReg;
// wait for command to complete
while (!(TWCR & (1 << TWINT))) {}
// status bits.
status_ = TWSR & 0xF8;
}
for (uint16_t n = 0; n != 0XFFFF; n++) {
if (TWCR & (1 << TWINT)) {
status_ = TWSR & 0xF8;
return true;
}
}
AVR_I2C_FAIL_MACRO;
return false;
}
};
#endif // !defined(__AVR__) || !defined(TWSR)
#endif // AvrI2c_h

0 comments on commit dfe68ff

Please sign in to comment.