Skip to content

Add new methods to Wire library #131

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 3 commits into from
Closed

Add new methods to Wire library #131

wants to merge 3 commits into from

Conversation

ghost
Copy link

@ghost ghost commented Oct 11, 2017

This solves issue #124
The library can now use an external buffer to write and read data in master mode.
The internal buffer size limitation (BUFFER_LENGTH) is no more a problem (#120) because we don't use in this case the internal buffers.

Note: when you use requestFrom() with an external buffer, do not use available() and read() functions to read the data. requestFrom() will return a not null value if data are available and the data are stored in the external buffer.

I took advantage of the fix to upgrade the library with the Arduino libraries rules.

@fpistm fpistm assigned fpistm and ghost and unassigned fpistm Oct 11, 2017
@fpistm fpistm self-requested a review October 11, 2017 12:38
if(size > I2C_TXRX_BUFFER_SIZE) {
size = I2C_TXRX_BUFFER_SIZE;
}

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldn't we rather return en error?
Otherwise application would get an incomplete frame and will not get an error ...

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I quickly patched a possible memory leak so I kept the function "void".
I can return 0 in case of error if you rather.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What I mean is that you need to properly manage errors.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done!

write(data[i]);
}
txBufferLength = quantity;
pWriteData = (uint8_t*)pdata;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

before the patch, write() was called and transmission happens.
Now only 2 variables are updated - how does transmission actually start ?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think you confused master and slave mode. I only change the master mode.
The transmission only happens when endtransmission() is called (before and now).

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

well what I see here is that you remove the lines:

  • for(size_t i = 0; i < quantity; ++i){
  •  write(data[i]);
    
  • }
    and replace them by txBufferLength and pWriteData assignment ... it looks like changing the dynamic

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

well what I see here is that you remove the lines:
for(size_t i = 0; i < quantity; ++i){
write(data[i]);
}
and replace them by txBufferLength and pWriteData assignment ... it looks like changing the dynamic

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Of course I changed the dynamic because I pass directly the data buffer pWriteData to the endTransmission() function (and so to the HAL) and I don't use anymore the internal buffer (low size) which is filled in the write() function.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok - I got confused by write() function as I thought it would actually transmit data on I2C lines. Thanks for the clarification.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In fact, @LMESTM is right. the dynamic is not the same and so the txbuffer construction is broken.
I've seen the issue with test on i2C eeprom. See code below:

        Wire.beginTransmission(ctrlByte);
        if (_nAddrBytes == 2) Wire.write( (byte) (addr >> 8) );   //high addr byte
        Wire.write( (byte) addr );                                //low addr byte
        Wire.write(values, nWrite);
        txStatus = Wire.endTransmission();

The 2 first calls of Wire.write() put the bytes in the internal txBuffer but when Wire.write(values, nWrite); is called, it initialize pWriteData.
Finally when Wire.endTransmission(); is called:

    // Select the buffer where are stored the data to send
    if(pWriteData != NULL) {
      data = pWriteData;
    } else {
      data = txBuffer;
    }

the txBuffer datas are ignored....

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There are 2 methods completely different now. You can't mix them as you see.

But maybe I found a solution to mix them and keep all older sketches compatibles.
I look at this.

@ghost ghost mentioned this pull request Oct 12, 2017
@ghost ghost mentioned this pull request Oct 25, 2017
@ghost
Copy link
Author

ghost commented Nov 7, 2017

Wire library updated to keep the write() method available as required by @fpistm .
I had to create a new method to allow to pass a pointer of buffer. I couldn't modify the write() prototype because it is a virtual method.
Finally I think it is better to add a method and keep the library as close as possible of the Arduino specifications.
@fpistm, @LMESTM is it OK for you?

@ghost ghost requested a review from LMESTM November 7, 2017 08:38
Copy link
Member

@LMESTM LMESTM left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

not sure about the proposal - I have posted comments and I would like to have @fpistm feedback as well.
EDIT: the good point is to have a separate API, this means that it will not break existing libraries and we may live with this proposal until we possibly find a better one ...

called only once after _beginTransmission()_. _endTransmission()_ must be called
immediately after. No other _write()_ method have to be called between
_beginTransmission()_ and _endTransmission()_ when _writeBuffer()_ is used.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I understand the idea and this would work ok - but I am not sure that this requirement (call only once, call endTRansmission immediately after ...) is acceptable - Isn't there an alternative that is more compliant with arduino defaut API ? Coudln't we simply increase internal buffer size ? Or maybe manage malloc & free of extra buffers in case size becomes > 255 then in endTransmission you would send the default buffers + extra ones ? ... other feedbacks are welcome of course

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We have discussed with @fprwi6labs about that. Changing the static buffer to a dynamic one will consume memory space. If several I2C instances are used and extra buffer size is required this will reach quickly the maximum space.
Proposal was to kept the current write function and add a new one with the usage restriction.

* **requestFrom(uint8_t address, uint16_t quantity, uint8_t \*pdata)**: this
function allows to read data and store them directly in pdata buffer.
Do not call _available()_ and _read()_ after this function because data are
directly stored in pdata buffer.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same here, if new methods are not easy to understand, I am not sure whether we should offer them

* @param quantity: number of bytes to write
* @retval number of bytes read. If > 0 then data are present in pdata buffer.
*/
size_t TwoWire::writeBuffer(uint8_t *pdata, size_t quantity)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add a warning about the pdata to ensure user do not freed or changed this buffer before endTransmission()

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done!

@ghost ghost requested a review from fpistm November 7, 2017 10:58
Copy link
Member

@fpistm fpistm left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok for me.
Wait NFC test and @LMESTM feedback

@LMESTM
Copy link
Member

LMESTM commented Nov 7, 2017

@fpistm @fprwi6labs as said above, ok with me as it will not break existing libraries and we may live with this proposal until we possibly find a better one - but I would not encourage a wide use of this API as long as it is not robust for begin/end and other APIs.

@ghost
Copy link
Author

ghost commented Nov 8, 2017

Updated to use dynamic memory allocation. No new method added. Compatibility kept.
In master, the internal buffer size is now limited to the heap size.

@ghost ghost requested review from LMESTM and fpistm November 8, 2017 14:21
Copy link
Member

@fpistm fpistm left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

API compatibility is preserved.
tested with i2C eeprom and its ok.

@@ -97,6 +99,10 @@ void TwoWire::begin(int address)

void TwoWire::end(void)
{
free(txBuffer);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Check if pointer is null before free it

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

free() accepts null pointer

If ptr is a null pointer, the function does nothing.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ok, i've got a doubt ;)

@@ -97,6 +99,10 @@ void TwoWire::begin(int address)

void TwoWire::end(void)
{
free(txBuffer);
txBuffer = nullptr;
free(rxBuffer);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ditto

@@ -109,6 +115,13 @@ uint8_t TwoWire::requestFrom(uint8_t address, uint8_t quantity, uint32_t iaddres
{
UNUSED(sendStop);
if (master == true) {
rxBuffer = allocateBuffer(rxBuffer, quantity);
// error if not memory block available to allocate the buffer
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

'if no'

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done!

// don't bother if buffer is full
if(txBufferLength >= BUFFER_LENGTH){
txBuffer = allocateBuffer(txBuffer, txBufferLength + 1);
// error if not memory block available to allocate the buffer
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

'if no'

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done!

@fpistm
Copy link
Member

fpistm commented Nov 13, 2017

Hi @fprwi6labs,
could you have a look at this commit to enhance buffer usage:
fpistm@c4b9f24

@ghost
Copy link
Author

ghost commented Nov 14, 2017

Hi @fpistm

could you have a look at this commit to enhance buffer usage:
fpistm/Arduino_Core_STM32@c4b9f24

It is OK for me.

@fpistm
Copy link
Member

fpistm commented Nov 14, 2017

Ok thanks. Waiting @LMESTM feedback

Copy link
Member

@LMESTM LMESTM left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Few comments to be addressed, but overall I like the idea of not chaging the standard API.
I think the PR should be renamed now because "Add new methods to wire lib" does not make sense anymore. Same for commits titles and commit messages, they need to be updated accordingly.

}

/**
* @brief Reset the buffer. Reduce is size if greater than BUFFER_LENGTH.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

typo: is size / its size
comment says that it reduce the size only if greater than BUFFER_LENGHT but code seems to do it unconditionnally

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Change in the enhancement

buffer = (uint8_t *)realloc(buffer, BUFFER_LENGTH * sizeof(uint8_t));
if(buffer != nullptr) {
memset(buffer, 0, BUFFER_LENGTH);
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

in case of nullptr, we need to raise an error (no memory ...).
I 'm not sure this is checked after every call to resetBuffer ...

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Checked in the R/W function

@@ -66,9 +66,11 @@ void TwoWire::begin(uint8_t address)
{
rxBufferIndex = 0;
rxBufferLength = 0;
rxBuffer = resetBuffer(rxBuffer);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if null raise en error ?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Error is raised later if rx/txBuffer is NULL


txBufferIndex = 0;
txBufferLength = 0;
txBuffer = resetBuffer(txBuffer);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ditto

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Error is raised later if rx/txBuffer is NULL

@@ -216,9 +224,15 @@ uint8_t TwoWire::endTransmission(uint8_t sendStop)
break;
}

//Reduce buffer size to free memory in case of large memory use
if(txBufferLength > BUFFER_LENGTH) {
txBuffer = resetBuffer(txBuffer);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

raise an errror if null ?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Will be removed in my PR.

@@ -241,8 +255,9 @@ size_t TwoWire::write(uint8_t data)
{
if(transmitting){
// in master transmitter mode
// don't bother if buffer is full
if(txBufferLength >= BUFFER_LENGTH){
txBuffer = allocateBuffer(txBuffer, txBufferLength + 1);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

maybe not efficient to allocate +1 byte each time - maybe you could go by steps (allocateBuffer(txBuffer, txBufferLength + BUFFER_LENGTH) each time it is needed) because reallocation can be costly in performances?
In that way BUFFER_LENGHT would be used as the "unit" of buffer allocation.

Copy link
Member

@fpistm fpistm Nov 15, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Mainly call to set first bytes of the data (ex: address).
See fpistm@c4b9f24
In this commit the min size is BUFFER_LENGTH.

@fpistm
Copy link
Member

fpistm commented Nov 15, 2017

This PR has been replaced by #152

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants