Giter VIP home page Giter VIP logo

brzo_i2c's People

Contributors

aefeinstein avatar flannelhead avatar pasko-zh avatar per1234 avatar squix78 avatar valkuc avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

brzo_i2c's Issues

MEMW not always present

Although Wiki warns that MEMW is required between GPIO register writes, it is occasionally missing. So, e.g., in brzo_i2c.c lines 332-333 the actual order of SDA LOW and SCL HIGH may depend on how the CPU manages the cache. Also in some cases MEMW is missing between a register write and a timing loop, e.g. line 309. If the CPU delays writing to memory, the SCL pulse would end up shorter by the amount of this delay, no?

Multiple I2C

I haven't find a way to contact the author of this program, so I fill a request:

I am using an ESP8266 (but it would be an Arduino I'd think it would be the same) to read temperature from BME280 (I2C Master), but I'd like to add I2C Slave capabilities on the same ESP to communicate with another device (Slave is a strong constraint). I read the source code of this library, and I don't see any reason why it could be feasible:

  • This is software I2C
  • I would like to act like an I2C server: when I receive a request from the master, I read temperature as an I2C master.

You'll probably ask yourself why my ESP & BME280 aren't slave with different addresses ?

Well there are several reasons that makes me want continue on this solution:

  • There are several BME280, which requires an I2C multiplexer.... Coupling Multiplexer for BME280 and no mutiplexer for ESP seems for me a bit... dirty (I can live with it though)
  • Some BME280 are not always working properly, I have no idea whether it comes from the mutliplexer, or the wires. I tried some things, but for me it looks strange, it depends on the order of the BME on the multiplexer, or something strange like that.
  • I'd like to have a much stronger approach of the entire installation: I'd like to have entirely independent component (component would consist of ESP + BME + other sensors), with different I2C address. Either the component works, either it's not, and that's it. I can test components individually, and consider them as a whole from the master perspective.

Why don't I use Wifi ? Because it does, but my wife doesn't want Wifi for children 😀

I would highly appreciate any suggestion, or if you think it is a valid use case like me, and think it is feasible quite easily, I can provide a PR (if you are interested of course).

Thank you in advance,

Cheers.

Arduino IDE compile error with new ESP8266 core 3.0.0

/arduino/libraries/Brzo_I2C/brzo_i2c.c: In function 'brzo_i2c_write':
/arduino/libraries/Brzo_I2C/brzo_i2c.c:72:2: error: cannot find a register in class 'RL_REGS' while reloading 'asm'
72 | asm volatile (
| ^~~
/arduino/libraries/Brzo_I2C/brzo_i2c.c:72:2: error: 'asm' operand has impossible constraints
exit status 1
Fehler beim Kompilieren für das Board Generic ESP8266 Module.

Can this be easy fixed? Thanks.
I'm using latest brzo version...

Brzo i2c should support longer Clock Stretch Timeouts

Currently, brzo i2c uses a uint16_t datatype for holding the number of iterations iteration_scl_clock_stretch for the timeout of SCL clock stretching. The maximum timeout is therefore around 5 msec (milli seconds).

There are i2c slaves, like for instance the HTU21, which stretch the clock up to 50 msec (milli seconds) or even more!

Thus, iteration_scl_clock_stretch should be of datatype uint32_t instead.

Novice obsession

你好,很抱歉打扰你,希望你在百忙之中帮我分析下困扰我已久的问题:
在使用您的库之后,存储和读取操作的返回结果都是0,读取是有结果的,但是存储好像并没有执行,只是偶尔会执行一次。我是用esp8266和24c16通讯

Hello, I'm sorry to disturb you. I hope you can help me analyze the problems that have been bothering me for a long time in your busy schedule:

After using your library, the return results of the storage and reading operations are both 0, and the reading has results, but the storage does not seem to execute, only occasionally. I use esp8266 to communicate with 24C16

brzo_i2c_write: allow subsequent calls

One thing that makes library hard to use for EEPROM write operations.
Write operation to EEPROM is made by two steps: first, send address; second, send data to write. Address and Data must sent as single one array. The tricky part here is that if I will use two separate calls to brzo_i2c_write I will have additional "Setup write" before address and data - and this is error. Address and Data must be sent one after another, so imagine that I have next function prototype to write data to EEPROM:
bool at24c_write(uint16_t addr, uint8_t* data, uint8_t len)
I can't write body like that because in this case I will have extra "Setup write" between address and data transmission:

uint8_t data_addr[2];
data_addr[0] = (uint8_t)(((unsigned)addr) >> 8);
data_addr[1] = (uint8_t)(((unsigned)addr) & 0xff);
brzo_i2c_write(data_addr, 2, true);
brzo_i2c_write(data, len, false);

To overrun this I need to malloc temporary array with size of data + 2, copy first two bytes of address into it, then copy data and after that send it in one brzo_i2c_write call. I guess this looks like redundant memory allocation:

uint8 *d = os_malloc(len + 2);
d[0] = (uint8)(((unsigned)addr) >> 8);
d[1] = (uint8)(((unsigned)addr) & 0xff);
os_memcpy(d+2, data, len);
brzo_i2c_write(d, len+2, false);
os_free(d);

Here is some ideas:

  • Move device address transmission to separate function (maybe in start_transaction?) or...
  • Introduce bool argument to brzo_i2c_write function to control send or not to send device address.

Add `Wire` like interface api

It would be great if this library would be supported as a drop in replacement for the Wire library. There could be a thin wrapper around this lib to make integration into existing code simpler.

GCC 10 compilation issue with Arduino

Hey,

ESP8266 Arduino Core project plans to use current version of GCC as default some time soon, but something had changed in compilation process since 4.8.2 and brzo can no longer build.

esp8266/Arduino#6294
https://github.com/earlephilhower/esp-quick-toolchain/releases

I commented on the GCC issue directly in hope it was something noticed during the migration of the Core sources, but it was suggested I should post here. Building using PIatformIO.

xtensa-lx106-elf-gcc -o ".pio/build/d1_mini/lib8be/Brzo I2C_ID335/brzo_i2c.c.o" -c -std=gnu99 -Wpointer-arith -Wno-implicit-function-declaration -Wl,-EL -fno-inline-functions -nostdlib -Os -mlongcalls -mtext-section-literals -falign-functions=4 -U__STRICT_ANSI__ -ffunction-sections -fdata-sections -fno-exceptions -Wall -DPLATFORMIO=40305 -DESP8266 -DARDUINO_ARCH_ESP8266 -DARDUINO_ESP8266_WEMOS_D1MINI -DF_CPU=80000000L -D__ets__ -DICACHE_FLASH -DARDUINO=10805 -DARDUINO_BOARD=\"PLATFORMIO_D1_MINI\" -DFLASHMODE_DIO -DLWIP_OPEN_SRC -DNONOSDK22x_190703=1 -DTCP_MSS=536 -DLWIP_FEATURES=1 -DLWIP_IPV6=0 -DVTABLES_IN_FLASH "-I/home/max/.platformio/lib/Brzo I2C_ID335/src" -I.pio/build/d1_mini/core -I/home/max/.platformio/packages/framework-arduinoespressif8266@src-31d658a59f41540201fc3726a1394910/tools/sdk/include -I/home/max/.platformio/packages/framework-arduinoespressif8266@src-31d658a59f41540201fc3726a1394910/tools/sdk/libc/xtensa-lx106-elf/include -I/home/max/.platformio/packages/framework-arduinoespressif8266@src-31d658a59f41540201fc3726a1394910/cores/esp8266 -I/home/max/.platformio/packages/framework-arduinoespressif8266@src-31d658a59f41540201fc3726a1394910/tools/sdk/lwip2/include -I/home/max/.platformio/packages/framework-arduinoespressif8266@src-31d658a59f41540201fc3726a1394910/variants/d1_mini "/home/max/.platformio/lib/Brzo I2C_ID335/src/brzo_i2c.c"
/home/max/.platformio/lib/Brzo I2C_ID335/src/brzo_i2c.c: In function 'brzo_i2c_write':
/home/max/.platformio/lib/Brzo I2C_ID335/src/brzo_i2c.c:72:2: error: cannot find a register in class 'RL_REGS' while reloading 'asm'
   72 |  asm volatile (
      |  ^~~
/home/max/.platformio/lib/Brzo I2C_ID335/src/brzo_i2c.c:72:2: error: 'asm' operand has impossible constraints
*** [.pio/build/d1_mini/lib8be/Brzo I2C_ID335/brzo_i2c.c.o] Error 1

I also noticed #9 (comment), but I have no idea what exactly is missing from flags / defines. ICACHE_RAM_ATTR seems to be there, optimization flags do not change anything.

SDA set before SCL while preparing STOP in brzo_i2c_read()

In brzo_i2c.c lines 706-710, while preparing for a STOP, SDA is brought LOW a few instructions before SCL is brought LOW (i.e., an unintended START condition). Two of my three I2C slaves didn't mind, but the third one (BPS120 pressure sensor) did. Took me 4 days to find the problem :)

Platformio installation instructions confusing

The README says "If you are developing with PlatformIO then simply use the library manager to install brzo_i2c". That brzo_i2c link takes you to a generic page that is not helpful. The actual name of the package, as would be used in a "lib_deps" field in platformio.ini, is "pasko-zh/Brzo I2C". So I think it would be clearer to say:

  • If you are developing with PlatformIO then add pasko-zh/Brzo I2C to lib_deps in platformio.ini

Enabling -Wall GCC option shows some warnings

I'm not familar with assembler, please check either those variables are used or can be safely removed:

'a_bit_index' is used uninitialized in this function [-Wuninitialized]	brzo_i2c.c	line 768
'a_bit_index' may be used uninitialized in this function [-Wmaybe-uninitialized]	brzo_i2c.c	line 73
'a_bit_index' may be used uninitialized in this function [-Wmaybe-uninitialized]	brzo_i2c.c	line 389
'a_in_value' is used uninitialized in this function [-Wuninitialized]	brzo_i2c.c	line 768
'a_in_value' may be used uninitialized in this function [-Wmaybe-uninitialized]	brzo_i2c.c	line 73
'a_in_value' may be used uninitialized in this function [-Wmaybe-uninitialized]	brzo_i2c.c	line 389
'a_set' is used uninitialized in this function [-Wuninitialized]	brzo_i2c.c	line 768
'a_set' is used uninitialized in this function [-Wuninitialized]	brzo_i2c.c	line 1082
'a_set' may be used uninitialized in this function [-Wmaybe-uninitialized]	brzo_i2c.c	line 73
'a_set' may be used uninitialized in this function [-Wmaybe-uninitialized]	brzo_i2c.c	line 389
'a_temp1' is used uninitialized in this function [-Wuninitialized]	brzo_i2c.c	line 768
'a_temp1' is used uninitialized in this function [-Wuninitialized]	brzo_i2c.c	line 1082
'a_temp1' may be used uninitialized in this function [-Wmaybe-uninitialized]	brzo_i2c.c	line 73
'a_temp1' may be used uninitialized in this function [-Wmaybe-uninitialized]	brzo_i2c.c	line 389

Clock stretch logic

It seems (correct me if I'm wrong) that BRZO uses the following clock stretch logic:

  1. Release SCL (i.e., set SCL HIGH).
  2. Wait time T equal to one half clock cycle.
  3. Read SCL.
    A. If SCL is still LOW, assume the slave is stretching clock. Wait for SCL to go HIGH, then let it stay HIGH for half-cycle time T, then bring it LOW to read/write the next bit.
    B. If SCL is HIGH, assume the slave is not stretching clock. Bring SCL LOW immediately to read/write the next bit.

The consequence is that if the slave stretches clock by an amount less than T, BRZO will not notice, will bring SCL LOW, and the SCL pulse could end up being very short. But I'm puzzled because BRZO seems to be deliberately coded in this way. Why? Am I missing something?

Question about "round" method

Hi - nice library! Making use of it in my non-RTOS esp SDK application the linker is not able to find the round() function in any of the system libraries I'm already including. Where are you expecting to pick this up from? I'm already having problems with available RAM so I'm keen not to include any extra library functions unnecessarily!

Add r/w functions to take a separate uint8_t register address

In #13 it came up about keeping the library clean by avoiding a whole host of wrapper functions that more complex libraries provide.

While I agree with keeping it clean and minimal; I disagree those extra commands clutter a usable and useful I2C library. Useful work with I2C devices requires flipping bits without altering the rest of the register, changing endianness on Word read/writes, and most of those other provided functions.

In fact, when you look at the implementation of those extra commands, they don't actually do any bus work. They keep using the same couple of read/write functions to do the actual Wire work. So you could take any of those libraries, replace a few functions with the brzo functions and get the full functionality of the library without adding any functions to brzo at all.

And that's what I recommend as a target for brzo. Being a useful bit banging on the bus part for Wire and libraries like Wire. The Wire library (and others) already support using different underlying bit banging methods, so making brzo compatible with them in that sense (to be used as the bit banging method underlying the library) is where I think brzo (and other platform/MCU specific libraries) belong.

So how does this relate to optionally providing register addresses separate from the data?
(And functions like writeByte that don't take a buffer address but instead just take a direct value..)

Unlike bit flipping and byte ordering, sending the register address is something that happens on the wire and is required in many, if not most, command circumstances.

The way most code deals with the register and device addresses keeps those items in separate variables, or even in #define statements which makes is extremely cumbersome to always put at address 0 of the data buffer.

Having #define statements with values from the product sheets, or hand crafted for the various modes of the device can save a lot of code space and pointer dereference requirements by hardcoding those immediate values straight into the function calls.

The compiler optimizer might even find ways to rearrange that code to make it even more efficient. When everything is always wrapped behind a pointer dereference, there is fewer opportunities for those optimizations. The Tensilica assembly can even use smaller commands in some cases when it knows it's dealing with immediate values.

And those arguments are secondary to the primary argument which is how programmers actually work with I2C devices. They have data buffers where the data to send to the device and read from the device get stored, then they have #define and const uint8_t and other mechanisms to store the device and register addresses that those data buffers belong too.

So it's just plain inconvenient to rearrange my data buffers to force a #define value into data_buffer[0] when I could have more easily placed it as its own value in the function call. To the brzo function it just means sending a byte from a separate location before sending the data at the pointer location. It's exactly the same idea as the device Id. It's not the "data" it's about the "where/what" to send/read the data to/from.

I've already done the work for this with the write function. I'll post my changes in the next comment and you can tell me if you think this is keeping with the spirit of a clean/lean i2c bit banging machine. :)

Question: Logic Levels and ...

Hello,

I have a question. ESP8266 is using 3.3v on the GPIO pins, right?
I am assuming most I2C devices are 5v.
Are you assuming a logic level converter is in use or is there no issue between the 3.3 and 5v.
OR am I just wrong and all I2C is 3.3v?

Unfortunately, I forgot the second question. grrr

I2C stucks after clock stretching time exceeds

I got an issue that make I2C bus unusable after receiving code 8 from brzo_i2c_end_transaction. The SCL/SDA line remains low and makes not possible to use bus until reinit.
How to reproduce:

  1. init i2c with clock stretching: brzo_i2c_setup(3000)
  2. start a write operation to i2c slave
  3. make slave to not answer in time (I'm doing that by putting my slave in debug pause)
  4. first received error code will be 8 (Clock Stretching by slave exceeded maximum clock stretching time)
  5. all subsequent calls to i2c write will return error code 1 (Bus not free, i.e. either SDA or SCL is low)

Make disabling interrupts optional

A huge thank you for this library. It's easily the best I2C driver one can have on ESP8266, in terms of speed and usability.

However, I recently had some problems using this library, sampling an IMU via I2C at 1 kHz and doing continuous WebSocket communication and PWM realized by the HW timer simultaneously. When the interrupts were disabled during I2C transactions, there were frequent stalls / resets, probably caused by some clashes due to missed interrupts. I then commented out the lines where the interrupts were disabled, and stalls became less frequent. (There are still some, but they aren't probably related to brzo_i2c.)

Therefore it would be great to have a configuration option in brzo_i2c.h where the user could opt out of disabling the interrupts. This should be a simple addition, and the default behaviour should be left unchanged.

I would be able to supply the patch as a pull request if desired.

limited number of bytes?

First: Thanks a lot for your great work! :)

Currently I'm trying to generate some sound with the ESP8266 and a MCP4725 (DAC with a I2C inteface).
Since I would like to write a lot of data in one rush, i discovered that you made the following variable definitions in the write (and read) function:
void ICACHE_RAM_ATTR brzo_i2c_write(uint8_t _data, _uint8_t no_of_bytes*, boolean repeated_start)

My Question:
Is there a good reason you limited the number of bytes (no_of_bytes) to 256?
Could you increase that to at least an uint32_t?

I tested that localy with some random values and it seems to work fine so far. But you may be a better judge.

Keep your good work up :)

Support speeds < 100KHz

Thanks for a very nice library!

I've noticed that it's sometimes necessary to use speeds < 100KHz to talk to a device, which the library currently doesn't support.

As an example, at 100KHz, it's almost impossible (very error-prone) to talk to an ATMega running at 8MHz or even an ATTiny, such as the reasonably popular BlinkM LEDs.

I can work around this by setting iteration_scl_halfcycle accordingly to achieve speeds like 50KHz or even slower, but the library currently doesn't support this out of the box.

Doing a pull request for this would be trivial, but I'm unsure about how to approach it without breaking the current API, e.g. right now, any speeds <100KHz would get treated as 100KHz. Maybe adding a new function like start_transaction_slow is the safest way without breaking backwards compatibility, which just supports speeds (roughly) between 10-100KHz?

I2C bus handle for multiple instances posibility

Hi
At first, thank very much for your job!

I'd propose little improvement for your library - it is add possibility to instantiate multiple instances of I2C bus object. It allows use more then one I2C bus with different IO lines simultaneously.

My proposition is to use pointer to structure wich descripts I2C bus instance. This structure (casted to void pointer for hide internal bus data from end user) used as handle for manipulate single instanse of I2C bus. Each instance of I2C bus should be binded to different couple GPIO lines.

Also I used semaphore as latch to lock I2C bus to avoid interleaving requests within one transaction to different devices.

Unfortunately I can't offer clean diff of changes with your code because I made a lot changes when adapted your code to use with esp8266_rtos_sdk and formated code style.

You could see changed library at my test project:
https://github.com/sacculus/aircontrol.v2.x.x/include/brzo_i2c.h
https://github.com/sacculus/aircontrol.v2.x.x/user/brzo_i2c.c

Treating the library like a virtual I2C hub device

The work on adding a "clock_stretch" and making different devices work at different speeds on the bus gave me lots of ideas; but namely it grounded something I'd been thinking about for a while now; that it should be possible to encode all the configuration required for an I2C Request in a single numeric identifier that can be "reused" and "passed around".

An application would feed in the details into a function and get back a single 32-bit or 64-bit identfier that represented that "request" as a "request_id". Then one mechanism to trigger a request is just to ask for it by request_id. These ids (with certain "fill in the blank" bits masked out) could be sent over a network or other communication lines to have other devices execute them at your request and send back the results. [This is like what slave I2C devices in some chips do, or what an I2C aggregator/hub is doing.]

So I started wondering about using an I2C bus library that acted like a "virtual i2c hub".

I'd register requests, set up precanned fetches/puts, and then interact with it much the same I would an actual device. I'd call a couple "update" functions at the start of "loop()" and a "commitChanges()" at the end; and the library would get the state/values from the preconfigured devices, I'd manipulate/read them inside loop(), and the library would push back the changes.

We could call commitChanges(), or update(), as often as required.

For example:

  • sda_pin (8 bits)+ scl_pin (8 bits) == 16-bit "bus id".
  • Device Address (8 bits) +Time Window (32 bits) == 40-bit "device id"
  • Register Address (8-bits) + Data Length (8-bits) + Flags(8-bits) == 24-bit "Item Id"

"Device Id" + "Item Id" == 64-bit "Request Id"

void setup() {
    I2CBus i2c_bus1(sda, scl);
    uint64_t req_id = i2c_get_request_id(
            device_address, bus_speed, clock_stretch
            , register_address, data_length, is_read, start_repeat, ... 
    );
    i2c_bus1.execRequest(req_id, &data[6]);
    i2c_bus1.setSlaveRequest(slave_id, req_id, (uint8_t*)&the_device_struct);  // Populate data into the_device_struct
}

void loop() {
    i2c_bus1.updateSlaves();

    if (the_device_struct.data_available) {
        Serial.println("Got Value: " + String(the_device_struct.important_value));
    }

    the_device_struct.config_setting = get_new_setting();

    i2c_bus1.commitChanges();
}

Migrate from Wire library

I am failing to get this library working with a BME280 Environment Sensor.
I moved from the Arduino Wire library to brzo_i2c since the AMS iAQ-Core air quality sensor did not work correctly with the builtin Wire lib.
I took the code from Adafruit and changed the parts handling I2C communication.

Original Code:

void Adafruit_BME280::write8(byte reg, byte value) {
    if (_cs == -1) {
        Wire.beginTransmission((uint8_t)_i2caddr);
        Wire.write((uint8_t)reg);
        Wire.write((uint8_t)value);
        Wire.endTransmission();
    } else {
        if (_sck == -1)
            SPI.beginTransaction(SPISettings(500000, MSBFIRST, SPI_MODE0));
        digitalWrite(_cs, LOW);
        spixfer(reg & ~0x80); // write, bit 7 low
        spixfer(value);
        digitalWrite(_cs, HIGH);
    if (_sck == -1)
        SPI.endTransaction(); // release the SPI bus
    }
}

uint8_t Adafruit_BME280::read8(byte reg) {
    uint8_t value;
    
    if (_cs == -1) {
        Wire.beginTransmission((uint8_t)_i2caddr);
        Wire.write((uint8_t)reg);
        Wire.endTransmission();
        Wire.requestFrom((uint8_t)_i2caddr, (byte)1);
        value = Wire.read();
    } else {
        if (_sck == -1)
            SPI.beginTransaction(SPISettings(500000, MSBFIRST, SPI_MODE0));
        digitalWrite(_cs, LOW);
        spixfer(reg | 0x80); // read, bit 7 high
        value = spixfer(0);
        digitalWrite(_cs, HIGH);
        if (_sck == -1)
            SPI.endTransaction(); // release the SPI bus
    }
    return value;
}

uint16_t Adafruit_BME280::read16(byte reg)
{
    uint16_t value;

    if (_cs == -1) {
        Wire.beginTransmission((uint8_t)_i2caddr);
        Wire.write((uint8_t)reg);
        Wire.endTransmission();
        Wire.requestFrom((uint8_t)_i2caddr, (byte)2);
        value = (Wire.read() << 8) | Wire.read();
    } else {
        if (_sck == -1)
            SPI.beginTransaction(SPISettings(500000, MSBFIRST, SPI_MODE0));
        digitalWrite(_cs, LOW);
        spixfer(reg | 0x80); // read, bit 7 high
        value = (spixfer(0) << 8) | spixfer(0);
        digitalWrite(_cs, HIGH);
        if (_sck == -1)
            SPI.endTransaction(); // release the SPI bus
    }

    return value;
}

uint32_t Adafruit_BME280::read24(byte reg)
{
    uint32_t value;

    if (_cs == -1) {
        Wire.beginTransmission((uint8_t)_i2caddr);
        Wire.write((uint8_t)reg);
        Wire.endTransmission();
        Wire.requestFrom((uint8_t)_i2caddr, (byte)3);

        value = Wire.read();
        value <<= 8;
        value |= Wire.read();
        value <<= 8;
        value |= Wire.read();
    } else {
        if (_sck == -1)
            SPI.beginTransaction(SPISettings(500000, MSBFIRST, SPI_MODE0));
        digitalWrite(_cs, LOW);
        spixfer(reg | 0x80); // read, bit 7 high

        value = spixfer(0);
        value <<= 8;
        value |= spixfer(0);
        value <<= 8;
        value |= spixfer(0);

        digitalWrite(_cs, HIGH);
        if (_sck == -1)
            SPI.endTransaction(); // release the SPI bus
    }

    return value;
}

My adapted version: (except leaving out the SPI stuff and Wire.begin() the rest of the code has not changed)

#define I2C_FREQ 100
brzo_i2c_setup(4,5,10000);
i2c_buffer = (uint8_t*)os_malloc(10);
//...
void BME280::write8(byte reg, byte value) {
    i2c_buffer[0] = reg;
    i2c_buffer[1] = value;
    brzo_i2c_start_transaction((uint8_t)_i2caddr, I2C_FREQ);
    brzo_i2c_write(i2c_buffer, 1, false);
    brzo_i2c_write(&i2c_buffer[1], 1, false);
    uint8_t error = brzo_i2c_end_transaction();
    Serial.print("write8  W: ");Serial.print(reg, HEX);Serial.print(", R: ");Serial.print(value, HEX);
    Serial.print(" (");Serial.print(value, BIN);Serial.print("), i2c: "); Serial.println(error);
}

uint8_t BME280::read8(byte reg) {
    i2c_buffer[0] = reg;
    brzo_i2c_start_transaction((uint8_t)_i2caddr, I2C_FREQ);
    brzo_i2c_write(i2c_buffer, 1, false);
    brzo_i2c_read(&i2c_buffer[1], 1, false);
    uint8_t error = brzo_i2c_end_transaction();
    Serial.print("read8  W: ");Serial.print(reg, HEX);Serial.print(", R: ");Serial.print(i2c_buffer[1], HEX);
    Serial.print(" (");Serial.print(i2c_buffer[1], BIN);Serial.print("), i2c: "); Serial.println(error);
    return i2c_buffer[1];
}

uint16_t BME280::read16(byte reg)
{
    uint16_t value;
    i2c_buffer[0] = reg;
    brzo_i2c_start_transaction((uint8_t)_i2caddr, I2C_FREQ);
    brzo_i2c_write(i2c_buffer, 1, false);
    brzo_i2c_read(&i2c_buffer[1], 2, false);
    uint8_t error = brzo_i2c_end_transaction();
    value = i2c_buffer[1]<<8 | i2c_buffer[2];
    Serial.print("read16 W: ");Serial.print(reg, HEX);Serial.print(", R: ");Serial.print(value, HEX);Serial.print(", i2c: "); Serial.println(error);
    return value;
}

uint32_t BME280::read24(byte reg)
{
    uint32_t value = 0;
    i2c_buffer[0]=reg;
    brzo_i2c_start_transaction((uint8_t)_i2caddr, I2C_FREQ);
    brzo_i2c_write(i2c_buffer, 1, false);
    brzo_i2c_read(&i2c_buffer[1], 3, false);
    uint8_t error = brzo_i2c_end_transaction();
    value = ((uint32_t)i2c_buffer[1])<<16 | ((uint16_t)i2c_buffer[2])<<8 | i2c_buffer[3];
    Serial.print("read24 W: ");Serial.print(reg, HEX);Serial.print(", R: ");Serial.print(value, HEX);Serial.print(", i2c: "); Serial.println(error);
    return value;
}

I get the following output when requesting the registers holding measured data:

write8  W: F4, R: 51 (1010001), i2c: 0   //(1) bme280.takeForcedMeasurement();
read8  W: F3, R: 0 (0), i2c: 0
read24 W: FA, R: 800000, i2c: 0          //bme280.readTemperature();
read24 W: FA, R: 800000, i2c: 0          //bme280.readPressure();
read24 W: F7, R: 800000, i2c: 0          
read24 W: FA, R: 800000, i2c: 0          //bme280.readHumidity();
read16 W: FD, R: 8000, i2c: 0
Temp: nan *C	Pressure: nan hPa	Humidity: nan % // Calculated

For me it seems like reading works, but the BME280 does not measure and hence does not fill its registers with correct values. According to the datasheet 800000 is the default value. Writing (1) should force measurement and after this the registers should be filled with data. I guess there is something wrong in the write8(...) method.

If I change back to wire (by copy-pasting the methods shown above), the BME280 works fine (so there is no hardware fault)...

Maybe an example in the Wiki comparing Arduinos Wire-lib and brzo_i2c would be helpful :)

A 24-bit "Time Window" to represent "Frequency" and "Clock Stretch"

I've been playing with making these "Request Ids" to represent precanned I2C Item fetches.

Two of the items in the description list for buses and devices are the busSpeed and the clockStretch.

Currently a 16-bit and 32-bit value. Together they require 48-bits; busSpeed is represented in kHz, (1KHz to 65.5Mhz) and the other is clock stretch represented in nanoseconds (0 nanoseconds to ~4.3 seconds). I wanted to make them smaller (and increase their value range) if I could.

So I came up with a different way to encode the same ideas. Instead of representing frequency as "the number of pulses in a second" I imagined describing frequency as "the time between pulse changes" (a much smaller number).

To make a long story short, I ended up hacking together the concept of a 24-bit "TimeWindow" structure using a badly hacked version of floating point numbers to "play with it".

A "Time Window" is a "pulse start" and a "stretch time".

As an example, 400kHz is a pulse time of 2.5 ms, e.g. 0.250 x 10^-5 seconds {-, 5, 250}
0.001 x 10^-9 seconds == 1 THz {-, 9, 1}
0.600 x 10^2 seconds == 0.0167 Hz (a clock pulse of ~1 minute) {+, 2, 600}

And that's how this structure encodes a pulse_frequency, a 10-bit "significant digits", a 4-bit exponent, and a 1-bit exponent sign. "Significant digits" are the three digits after the decimal point.
The value range then is 0.001 to 1.024; and the exponent is between -15 and +15; using 1 second as a base.

For the clock stretch, I split the byte 50/50; 4-bits for "Significance" and 4-bits for "Exponent". This gives a significance range of 0.1 to 1.6 * 10 +/- 15. Using 0.1 microseconds as a base.

Combined the two values take up 24-bits.

Here's the structure in code:

	/*
	 * This time_window32_t represents two 16-bit floats; 
	 * one for "time between pulses" (1 == 1 second); the other for "stretch before timeout" (1 == 0.1 microseconds)
	 * together they make a repeating clock frequency and a "clock stretch" to deal with device inconsistencies
	 */ 
	union time_window32_t {
		uint32_t bytes;
		struct {
			uint16_t pulse_exp_sign:1;   // 0 or 1
			uint16_t pulse_exp:4;        // 0 through 15
			uint16_t pulse_sig:10;       // 0 through 1023 (exp0 == 1s) (0.001x10^-15) (0.001 femtosecond) to [1.023 x 10^15] (32 million years)
			uint16_t stretch_exp_sign:1; // 0 or 1
			uint8_t stretch_exp:4;       // 0 through 15
			uint8_t stretch_sig:4;       // 1 through 16 (exp0 == 0.1us (or 100ns); range is [1 x 10^-23] (time for light to travel 1 femtometer) to [1.6 x 10^8 (~5 years))
			uint8_t reserved;           // reserved; the request length ultimately ends up in this spot in a txn_request_id
		} settings;
	};

This method is an extremely inefficient (aka really horrendous) way to represent floating point numbers. The main point right now was introducing the concept and gauging feedback. I like that both these values are encoded into a single 24-bit value.
I could probably learn from the techniques that a "half-precision" (16-bit) IEEE floating point value uses.

Another change would be "clock stretch" being a divisor of "pulse frequency"; so rather than having a prefixed value like it does now, it's always some fraction of a "pulse time"... This should use the available values better to get more reasonable granularity. For instance, 6 hours + 1 femtosecond (i.e. exactly 6 hours) seems like a very unlikely time window to me. There's likely a better way to use the clock stretch bits to either make pulse frequency better or express more reasonable values within a pulse range.

I still expect users to express "frequency" in terms of "kHz" and "clock_stretch" in terms of nanoseconds. I just see the library transforming these two separate values into a single 24-bit "time window" identifier.

I have another use case for this "time window" to provide data synchronization frequencies. For instance, "these values must be refreshed from the device at least every 5 minutes +/- 15 seconds" or "these values must be synchronized every 500 nanoseconds +/- 10 nanoseconds". Once set, the library ensures that those frequencies for data synchronization are maintained.

ACK polling

Hello,
I need to read and write to AT24C32 i2c eeprom. To ensure data was written by chip I need to use "acknowledge polling".
From datasheet:

ACKNOWLEDGE POLLING: Once the internally-timed write cycle has started and the
EEPROM inputs are disabled, acknowledge polling can be initiated. This involves sending
a start condition followed by the device address word. The read/write bit is
representative of the operation desired. Only if the internal write cycle has completed
will the EEPROM respond with a zero, allowing the read or write sequence to continue.

How can I do ACK polling with you library?

Not working with HTU21

I am failing to get your lib to work with the HTU21 Temperature/Humidity sensor. I am not quite shure if this is a problem with the lib or with my code. I always get a return code of 2 when finishing the transaction.

#include "brzo_i2c.h"

unsigned char buf[10];

void setup() {

  Serial.begin(115200);

  Serial.println("Beginning application");

  brzo_i2c_setup(4,5,20000);

  brzo_i2c_start_transaction(0x80, 100);
  buf[0] = 0xE7;
  brzo_i2c_write(&buf[0], 1, true);
  brzo_i2c_read(&buf[1], 1, false);
  unsigned char returnCode = brzo_i2c_end_transaction();

  Serial.println(returnCode, HEX);
  Serial.println(buf[1], HEX);
}

Cannot compile for Heltec Wifi Kit 32

Hello,

Today I get compile error. Can you please confirm if this lib works ok for Heltec Wifi Kit 32.
Meanwhile, I will test sample code.

Thank you!

..........................
WARNING: library Brzo_I2C claims to run on (esp8266) architecture(s) and may be incompatible with your current board which runs on (esp32) architecture(s).
..........................
libraries\Brzo_I2C\brzo_i2c.c.o:(.iram1.literal+0x2c): undefined reference to `system_get_cpu_freq'

libraries\Brzo_I2C\brzo_i2c.c.o:(.literal.brzo_i2c_setup+0x14): undefined reference to `system_get_cpu_freq'

libraries\Brzo_I2C\brzo_i2c.c.o: In function `brzo_i2c_start_transaction':

C:\Users\IlikeB00bs\Documents\Arduino\libraries\Brzo_I2C/brzo_i2c.c:989: undefined reference to `system_get_cpu_freq'

libraries\Brzo_I2C\brzo_i2c.c.o: In function `brzo_i2c_setup':

C:\Users\IlikeB00bs\Documents\Arduino\libraries\Brzo_I2C/brzo_i2c.c:1044: undefined reference to `system_get_cpu_freq'

collect2.exe: error: ld returned 1 exit status

exit status 1
Error compiling for board Heltec_WIFI_Kit_32.

after 1 slave fails others stop working

I'm using brzo_i2c in my hobby project.
I have ESP8266 as master and 3 slaves on i2c line (2 displays and 1 sensor).
Esp8266 communicates with all slaves every 500ms. All works fine when all 3 salves are connected.
But, if I disconnect 1 of the slaves, all 3 stop working.

So, in this case communication with absent slave fails, i.e. it just does not respond to its address, but all other slaves which are connected stop responding as well.

Any idea why could this happen?

I'm planning to have several instances of my device in different configurations, for example with one display only, or without sensor, so, I would like to have same software flashed to ESP8266 and detect if i2c slave device is present or not in runtime.

INA219

Hello pasko,
i have found your nice i²c library and wanted to try it out for my
(INA219). I tried to modify a simple library and came up with my forked version.

In short: It does not work, but i think i am not completely wrong. Could you have a look at it and give me a hint, how to make this work (at all).

There are basically only to functions that i have changed, and i think the problem is this one:

int16_t INA219::readRegister16(uint8_t reg)
{
    int16_t value;

    Wire.beginTransmission(inaAddress);
    #if ARDUINO >= 100
        Wire.write(reg);
    #else
        Wire.send(reg);
    #endif
    Wire.endTransmission();

    delay(1);

    Wire.beginTransmission(inaAddress);
    Wire.requestFrom(inaAddress, 2);
    while(!Wire.available()) {};
    #if ARDUINO >= 100
        uint8_t vha = Wire.read();
        uint8_t vla = Wire.read();
    #else
        uint8_t vha = Wire.receive();
        uint8_t vla = Wire.receive();
    #endif;
    Wire.endTransmission();

    value = vha << 8 | vla;

    return value;
}

changed to:

int16_t INA219_brzo::readRegister16(uint8_t reg)
{
    int16_t value;

    brzo_i2c_start_transaction(ADDR, SCL_frequency_KHz);
    brzo_i2c_write(&reg, 1, true); // 1 byte, repeat 
    brzo_i2c_end_transaction();

    brzo_i2c_start_transaction(ADDR, SCL_frequency_KHz);
    brzo_i2c_read(&buffer[0], 2, false);
    // uint8_t vha = brzo_i2c_end_transaction();
    // uint8_t vla = brzo_i2c_end_transaction();

    uint8_t vha = buffer[0];
    uint8_t vla = buffer[1];

    brzo_i2c_end_transaction();

    value = vha << 8 | vla;

    return value;
}

kind regards
Jürgen
P.S.: I really appreciate your work and your incredibly intimedating ASM code!

Disclaimer: I have hardly any experience with C coding and iam very new to all this MCU stuff.

write/read should allow to send/read from any position in the buffer

brzo_i2c_write/read(uint8_t *data, uint8_t no_of_bytes, boolean repeated_start)
expects a pointer to a buffer *data. It then always sends/reads the no_of_bytes starting from the beginning of the buffer, i.e. data[0] ... data[no_of_bytes - 1].

Instead, brzo_i2c_write/read should allow that any pointer to an elemen (of array of byte) is passed to it and that this then taken as the base address from which no_of_bytes is sent/read. E.g., brzo_i2c_write(&buffer[4], 5, true) would send 5 bytes starting from buffer[4]

Migrating ADS1015 from Wire

Hi,
I'm trying to re-implement TI ADS1015 ADC interface using brzo_i2c library, since Adafruit's implementation using Wire.h is not fast enough to read data at the rate ADS1015 cat produce.

On the same hardware (esp-12F, 4.7k pull-up resistors, ADS1015 test board),
the following code in Wire.h works:

#include <Arduino.h>
#include <Wire.h>

#define SCL_PIN 5
#define SDA_PIN 4
#define ADS1X15_ADDRESS   (0x48) 
#define I2C_SPEED     100

void setup() {
  Serial.begin(115200);
  Wire.begin(SDA_PIN,SCL_PIN);
  Wire.setClock(I2C_SPEED * 1000L);
}

void loop() {
	uint16_t result = 0;
	uint16_t cfg_value = 50627;
	uint8_t b0, b1;
	// Configure to start ADC
	Wire.beginTransmission(ADS1X15_ADDRESS);
	Wire.write(1); // Config register
	Wire.write((uint8_t)(cfg_value >> 8)); // 2 bytes of config
    Wire.write((uint8_t)(cfg_value & 0xFF));
	Wire.endTransmission();

	delay(2);  // Some sleep for ADC conversion to be done

	// Read data
	Wire.beginTransmission(ADS1X15_ADDRESS);
	Wire.write(0); // Data register
	Wire.endTransmission();
	// now, actually read
	Wire.requestFrom(ADS1X15_ADDRESS,2);
	b0 = Wire.read();
	b1 = Wire.read();
	result = ((b0 << 8) | b1);
	Serial.printf("Received: %u\n",result);
	// Sleep for a while
	delay(1000);

}

And the following code in brzo_i2c does not:

#include <Arduino.h>
#include <brzo_i2c.h>

#define SCL_PIN 5
#define SDA_PIN 4
#define ADS1X15_ADDRESS   (0x48) 
#define I2C_SPEED     100

void setup() {
	Serial.begin(115200);
	brzo_i2c_setup(SDA_PIN, SCL_PIN,20000);
}

void loop() {
	uint16_t cfg_value = 50627;
	uint8_t rc;
	uint8_t buffer[2];
	
	// Configure to start ADC
	brzo_i2c_start_transaction(ADS1X15_ADDRESS, I2C_SPEED);
	buffer[0] = 1;
	brzo_i2c_write(buffer,1, true);
	buffer[0] = (uint8_t)(cfg_value >> 8);
	buffer[1] = (uint8_t)(cfg_value & 0xFF);
	brzo_i2c_write(buffer,2,false);

	rc = brzo_i2c_end_transaction();
	if (rc > 0) {
		Serial.printf("Write failed with rc=%u\n",rc);
	}
	delay(2);  // Some sleep for ADC conversion to be done
	// Read data
	brzo_i2c_start_transaction(ADS1X15_ADDRESS, I2C_SPEED);
	buffer[0] = 0;
	brzo_i2c_write(buffer,1, true);
	brzo_i2c_read(buffer,2,false);
	rc=brzo_i2c_end_transaction();
	if (rc > 0) {
		Serial.printf("Read failed with rc=%u\n",rc);
	}
	result=((buffer[0]<<8)|buffer[1]);
	Serial.printf("Received: %u\n",result);
	delay(1000);
}

Wire-based code correctly returns different ADC values, depending on what the signal is.

brzo-i2c based code executes correctly (rc is always 0), but data coming from the device is always the same.

Hardware is OK, clock frequency of 100kHz is low enough (Wire-based example runs good at up to 600kHz).
Changing repeated_start from true to false has no effect.

Is there anything else that can be done to make this setup work?
Thanks.

Implementing I2C slave for MAX11615 not working as expected

I am implementing i2c communication with MAX11615 and i am not getting desired result. Below is a snapshot to give more clarity on the slave. Datasheet
image

My code looks like

uint8_t SDA_PIN = 4;
uint8_t SCL_PIN = 5;
uint8_t MAX11615_ADR = 0x33;
uint8_t buffer[8];
uint8_t error = 0;
float pH = 0.0;
float EC = 0.0;
uint8_t ICACHE_RAM_ATTR get_pH_EC(float *p, float *e) {
  uint32_t a_code = 0;
  uint32_t b_code = 0;
  uint32_t c_code = 0;
  uint32_t d_code = 0;
  uint8_t bcode = 0;
  brzo_i2c_start_transaction(MAX11615_ADR, 400);
  buffer[0] = 0xD2; 
  buffer[1] = 0x09;
  
  brzo_i2c_write(buffer, 2, false); //writes 2 bytes to slave address, repeated start is false
  brzo_i2c_read(buffer, 8, false); // read 8 bytes to slave address, repeated start is false
  bcode = brzo_i2c_end_transaction();
  delay(50);
  a_code = ((buffer[0] << 4) | buffer[1]);
  b_code = ((buffer[2] << 4) | buffer[3]);
  c_code = ((buffer[4] << 4) | buffer[5]);
  d_code = ((buffer[6] << 4) | buffer[7]);
  *p = a_code;
  *e = b_code;
  Serial.print(" | a = "); Serial.print(a_code);
  Serial.print(" | b = "); Serial.print(b_code);
  Serial.print(" | c = "); Serial.print(c_code);
  Serial.print(" | d = "); Serial.print(d_code); 
  return bcode;
}
void setup() {
  delay(1000);
  Serial.begin(115200);
  brzo_i2c_setup(SDA_PIN, SCL_PIN, 2000);
}
void loop() {
  Serial.println();
  Serial.println("Waiting 5 seconds...");
  delay(5000);
  error = get_pH_EC(&pH, &EC);
  if (error == 0) {
    //Print ph and ec
  }
  else {
    Serial.print("Brzo error : ");
    Serial.println(error);
  }
} 

What is funny is the values are same for a_code, b_code, c_code, d_code. The setup and configuration byte is proper and checked with manufacturer. Is it possible that the write is not happening correctly and hence giving value from only one channel ?
I am not very versed with writing assembly so not sure how to debug this.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.