nox771 / i2c_t3

Enhanced I2C library for Teensy 3.x devices
156 stars 44 forks source link

Not an issue, but a question: Avoid memory issues due to virtual double buffering? #18

Closed Theremingenieur closed 6 years ago

Theremingenieur commented 6 years ago

I'm actually dealing with a Teensy LC and a 128x64 i2c OLED. I managed to increase the speed and to get a nice ~30fps frame rate thanks to this great library using DMA. I understand that the DMA channel has to be dynamically allocated but that eats up a lot of the precious and small dynamic memory of the weak-chested Teensy LC. Thus, I'm trying to save memory in other places: I had to increase the I2C TX buffer of the library to 1027 bytes which allows to transmit a full screen in one transmission. But... I have to use another 1024 byte array to render graphics for the screen before I write it to the TX buffer. That is (in my eyes perhaps unnecessary) double buffering and I wonder if there isn't a way to hand over the frame buffer pointer directly instead of copying it to the TX buffer. That would probably allow to "economize" some CPU cycles without all the write/copy operations and I'd not have to allocate such a huge TX buffer... Any ideas?

nox771 commented 6 years ago

Certainly it sounds like there is an unnecessary buffer copy. i2c_t3 has a Tx buffer because generally that is where the message is constructed. But in your case you are doing that elsewhere.

One simple option might be to use the I2C Tx buffer as your render area. This requires some direct editing of the i2cStruct, but it is a public struct so it should work.

Basically the first byte of the Tx buffer will hold the slave address, then after that it is open up to the limit of the buffer size. So a way to do this might be this:

Wire.beginTransmission(slaveAddr);  // this sets txBuffer[0] to slave addr, and inits I2C
uint8_t* ptrRenderBuf = &Wire.i2c->txBuffer[1];  // call this your buf[0]
//
// render as needed to ptrRenderBuf[]
//
Wire.i2c->txBufferLength = intRenderSize+1;  // +1 for slave addr, can use const if fixed size
Wire.endTransmission();

I haven't tried this, and I might have munged the syntax, but in theory it should be possible.

Edit: One thing to mention, if you are working concurrently with DMA, be aware that I2C data moves slow in comparison to core cycles, so this only works if your render doesn't outpace DMA. As far as a more general setup using dual external buffers with DMA (toggling between them), that would have to be a future TBD. It would be a while before I could get to something like that.

Theremingenieur commented 6 years ago

I'm not using DMA for rendering, thus, that shouldn't be a problem, it is exclusive for i2c. Your answer is very helpful, thank you. I didn't see that the i2c struct was public (no idea why I thought it was private) and I ignored the internals of the transmit buffer. So, I see much more clear now! Thanks again!