diff --git a/extras/i2c/i2c.c b/extras/i2c/i2c.c index 7fbae65..59dfcaa 100644 --- a/extras/i2c/i2c.c +++ b/extras/i2c/i2c.c @@ -38,17 +38,19 @@ #define CLK_STRETCH (10) static bool started; +static bool flag; static uint8_t g_scl_pin; static uint8_t g_sda_pin; inline bool i2c_status(void) { - return started; + return flag; } void i2c_init(uint8_t scl_pin, uint8_t sda_pin) { started = false; + flag = false ; g_scl_pin = scl_pin; g_sda_pin = sda_pin; @@ -62,17 +64,32 @@ void i2c_init(uint8_t scl_pin, uint8_t sda_pin) // I2C bus idle state. gpio_write(g_scl_pin, 1); gpio_write(g_sda_pin, 1); + + // Prevent user, if frequency is high + if (sdk_system_get_cpu_freq() == SYS_CPU_80MHZ) + if (I2C_CUSTOM_DELAY_80MHZ == 1) + debug("Max frequency is 320Khz at 80MHz"); + } static inline void i2c_delay(void) { + uint32_t delay; if (sdk_system_get_cpu_freq() == SYS_CPU_160MHZ) { - for (volatile uint8_t __j = 0 ; __j < 5 ; __j++) {} ; + __asm volatile ( + "movi %0, %1" "\n" + "1: addi %0, %0, -1" "\n" + "bnez %0, 1b" "\n" + : "=a" (delay) : "i" (I2C_CUSTOM_DELAY_160MHZ)); } else { - for (volatile uint8_t __j = 0 ; __j < 0 ; __j++) {} ; + __asm volatile ( + "movi %0, %1" "\n" + "1: addi %0, %0, -1" "\n" + "bnez %0, 1b" "\n" + : "=a" (delay) : "i" (I2C_CUSTOM_DELAY_80MHZ)); } } @@ -129,7 +146,7 @@ void i2c_start(void) } // Output stop condition -void i2c_stop(void) +bool i2c_stop(void) { uint32_t clk_stretch = CLK_STRETCH; // Set SDA to 0 @@ -144,7 +161,12 @@ void i2c_stop(void) debug("arbitration lost in i2c_stop"); } i2c_delay(); + if (!started) { + debug("link was break!"); + return false ; //If bus was stop in other way, the current transmission Failed + } started = false; + return true; } // Write a bit to I2C bus @@ -208,6 +230,36 @@ uint8_t i2c_read(bool ack) return byte; } +static int i2c_bus_test(bool force) +{ + taskENTER_CRITICAL(); // To prevent task swaping after checking flag and before set it! + bool status = flag ; // get current status + if(force) + { + flag = true ; // force bus on + taskEXIT_CRITICAL(); + if(status) + i2c_stop(); //Bus was busy, stop it. + + } + else + { + if (status) + { + taskEXIT_CRITICAL(); + debug("busy"); + taskYIELD(); // If bus busy, change task to try finish last com. + return -EBUSY ; // If bus busy, dont read + } + else + { + flag = true ; // Set Bus busy + taskEXIT_CRITICAL(); + } + } + return 0 ; +} + bool i2c_slave_write(uint8_t slave_addr, uint8_t *data, uint8_t len) { bool success = false; @@ -217,8 +269,10 @@ bool i2c_slave_write(uint8_t slave_addr, uint8_t *data, uint8_t len) if (!i2c_write(slave_addr << 1)) break; while (len--) { - if (!i2c_write(*data++)) + if (!i2c_write(*data++)) { + debug("Device dont Ack"); break; + } } i2c_stop(); success = true; @@ -233,12 +287,14 @@ bool i2c_slave_read(uint8_t slave_addr, uint8_t data, uint8_t *buf, uint32_t len do { i2c_start(); if (!i2c_write(slave_addr << 1)) { + debug("Device dont Ack"); break; } i2c_write(data); i2c_stop(); i2c_start(); if (!i2c_write(slave_addr << 1 | 1)) { // Slave address + read + debug("Device dont Ack"); break; } while(len) { @@ -254,3 +310,62 @@ bool i2c_slave_read(uint8_t slave_addr, uint8_t data, uint8_t *buf, uint32_t len } return success; } + +int i2c_slave_write_16(uint8_t slave_addr, uint8_t *data, uint16_t *buf, uint8_t len, bool force) +{ + if(i2c_bus_test(force)) + return -EBUSY ; + i2c_start(); + if (!i2c_write(slave_addr << 1)) + goto error; + if(data != NULL) { + if (!i2c_write(*data)) + goto error; + } + while (len--) { + if (!i2c_write((uint8_t)(*buf >> 8))) + goto error; + if (!i2c_write((uint8_t)(*buf++))) + goto error; + } + i2c_stop(); + flag = false ; // Bus free + return 0; + + error: + debug("Device dont Ack"); + i2c_stop(); + flag = false ; // Bus free + return -EIO; +} + +int i2c_slave_read_16(uint8_t slave_addr, uint8_t *data, uint16_t *buf, uint32_t len, bool force) +{ + if(i2c_bus_test(force)) + return -EBUSY ; + if(data != NULL) { + i2c_start(); + if (!i2c_write(slave_addr << 1)) + goto error; + if (!i2c_write(*data)) + goto error; + i2c_stop(); + } + i2c_start(); + if (!i2c_write(slave_addr << 1 | 1)) // Slave address + read + goto error; + while(len) { + *buf = i2c_read(0) << 8 | i2c_read(len == 1) ; + buf++; + len--; + } + i2c_stop(); + flag = false ; // Bus free + return 0; + + error: + debug("Device dont Ack"); + i2c_stop(); + flag = false ; // Bus free + return -EIO; +} diff --git a/extras/i2c/i2c.h b/extras/i2c/i2c.h index 8654594..61235dd 100644 --- a/extras/i2c/i2c.h +++ b/extras/i2c/i2c.h @@ -27,16 +27,39 @@ #include #include +#include +#include +#include #ifdef __cplusplus extern "C" { #endif + +#define I2C_FREQUENCY_400K true // for test WIP + +/* + * Some bit can be transmit slower. + * Selected frequency fix the speed of a bit transmission + * I2C lib take the maximum frequency defined + */ + +#ifdef I2C_FREQUENCY_500K +#define I2C_CUSTOM_DELAY_160MHZ 6 +#define I2C_CUSTOM_DELAY_80MHZ 1 //Sry, maximum is 320kHz at 80MHz +#elif defined(I2C_FREQUENCY_400K) +#define I2C_CUSTOM_DELAY_160MHZ 10 +#define I2C_CUSTOM_DELAY_80MHZ 1 //Sry, maximum is 320kHz at 80MHz +#else +#define I2C_CUSTOM_DELAY_160MHZ 100 +#define I2C_CUSTOM_DELAY_80MHZ 20 +#endif + // I2C driver for ESP8266 written for use with esp-open-rtos // Based on https://en.wikipedia.org/wiki/I²C#Example_of_bit-banging_the_I.C2.B2C_Master_protocol // With calling overhead, we end up at ~320kbit/s -///////Level 0 API +//Level 0 API /** * Init bitbanging I2C driver on given pins @@ -66,8 +89,9 @@ void i2c_start(void); /** * Send stop condition + * @return false if link was broken */ -void i2c_stop(void); +bool i2c_stop(void); /** * get status from I2C bus. @@ -75,10 +99,10 @@ void i2c_stop(void); */ bool i2c_status(void); -///////Level 1 API (Don't need functions above) +//Level 1 API (Don't need functions above) /** - * Write 'len' bytes from 'buf' to slave. Return true if slave acked. + * Write 'len' bytes from 'buf' to slave. * @param slave_addr slave device address * @param buf Pointer to data buffer * @param len Number of byte to send @@ -87,7 +111,7 @@ bool i2c_status(void); bool i2c_slave_write(uint8_t slave_addr, uint8_t *buf, uint8_t len); /** - * Issue a read operation and send 'data', followed by reading 'len' bytes + * Issue a read operation and read 'data', followed by reading 'len' bytes * from slave into 'buf'. * @param slave_addr slave device address * @param data register address to send @@ -97,6 +121,29 @@ bool i2c_slave_write(uint8_t slave_addr, uint8_t *buf, uint8_t len); */ bool i2c_slave_read(uint8_t slave_addr, uint8_t data, uint8_t *buf, uint32_t len); +/** + * Write len word at register. + * @param slave_addr slave device address + * @param data Register address pointer to send (ignored if NULL) + * @param buf Pointer to 16 bits data buffer + * @param len Number of word to read + * @param force If true, current i2c link will be break to force this one (Use with precaution) + * @return Non-zero if error occured + */ +int i2c_slave_write_16(uint8_t slave_addr, uint8_t *data, uint16_t *buf, uint8_t len, bool force); + +/** + * Issue a read operation at 'data'(!= NULL) , followed by reading 'len' bytes + * from slave into 'buf'. + * @param slave_addr slave device address + * @param data Register address pointer to send (ignored if NULL) + * @param buf Pointer to 16 bits data buffer + * @param len Number of word to read + * @param force If true, current i2c link will be break to force this one (Use with precaution) + * @return Non-zero if error occured + */ +int i2c_slave_read_16(uint8_t slave_addr, uint8_t *data, uint16_t *buf, uint32_t len, bool force); + #ifdef __cplusplus }