1
\$\begingroup\$

I'm writing some evaluation code with a lot of rx/tx operations, using the same two buffers and I started to think about how this could easily go very wrong.

I can argue that it is nice to clear/reinitialize the buffers with 0's or any other val for that matter for safety purpose, and it's not like my MCU can't spare the extra X ms operations.

But at the same time I could also argue that flushing them is just bloat operations as the program behaves as it should.

What is the best practice / industry standard when it comes to handling buffers continuously overwritten? Is flushing the buffers after each operation, like wearing a life jacket on land ?

void IIS328DQ_Read_All(void)
{
  txbuf[0] = IIS328DQ_REG_OUT_X_H;
  txbuf[1] = IIS328DQ_REG_OUT_X_L;
  txbuf[2] = IIS328DQ_REG_OUT_Y_H;
  txbuf[3] = IIS328DQ_REG_OUT_Y_L;
  txbuf[4] = IIS328DQ_REG_OUT_Z_H;
  txbuf[5] = IIS328DQ_REG_OUT_Z_L;

  // Request X axis high and low bytes
  ret = HAL_I2C_Master_Transmit(&hi2c1, IIS328DQSADWR, &txbuf[0], 2, 
  HAL_MAX_DELAY);
  ret = HAL_I2C_Master_Receive(&hi2c1, IIS328DQSADRD, &rxbuf[0], 2, 
  HAL_MAX_DELAY);
  // Request Y axis high and low bytes
  ret = HAL_I2C_Master_Transmit(&hi2c1, IIS328DQSADWR, &txbuf[2], 2, 
  HAL_MAX_DELAY);
  ret = HAL_I2C_Master_Receive(&hi2c1, IIS328DQSADRD, &rxbuf[2], 2, 
  HAL_MAX_DELAY);
  // Request Z axis high and low bytes
  ret = HAL_I2C_Master_Transmit(&hi2c1, IIS328DQSADWR, &txbuf[4], 2, 
  HAL_MAX_DELAY);
  ret = HAL_I2C_Master_Receive(&hi2c1, IIS328DQSADRD, &rxbuf[4], 2, 
  HAL_MAX_DELAY);


 // Combine high and low bytes 0b00001111+00001111 (Endianness) to 16 bit int
 x_raw = ((int16_t)rxbuf[0]<<8) + rxbuf[1];
 // Shift the 16 bits to a 12-bit representation
 x_raw = x_raw >> 4;
 y_raw = ((int16_t)rxbuf[2]<<8) + rxbuf[3];
 y_raw = y_raw >> 4;
 z_raw = ((int16_t)rxbuf[4]<<8) + rxbuf[5];
 z_raw = z_raw >> 4;

 // FS bits set to 00 - min 0.9 | typ 0.98 | max 1.1
 // convert raw data using sensitivity (0.98mg/digit ~ normal mode)
 axisArr[0] = x_raw * Sensitivity;
 axisArr[1] = y_raw * Sensitivity;
 axisArr[2] = z_raw * Sensitivity;
 clearBuf();
 }

clearBuf() simply fills the rx/tx buffers with 0's up to sizeof()

\$\endgroup\$
  • \$\begingroup\$ I must admit I see no advantage of zeroing here, whatsoever. Could you explain what safety gain this has in the context of such sample buffers? \$\endgroup\$ – Marcus Müller Jan 2 at 13:02
  • \$\begingroup\$ I was thinking the safety perspective would be to start over with a completely 0'ed buffer so no extra bytes are written if you mess up in SW say you need to write 4 bytes but you accidentally write 5, the 5th would just be 0 in our case. I don't really know, one could just say.. well then don't screw up :) \$\endgroup\$ – Sorenp Jan 2 at 13:03
  • \$\begingroup\$ I still don't understand why overwriting with zeros is safer than overwriting your last samples? That seems to make no sense to me. Or why reading zeros is safer than reading previous samples? \$\endgroup\$ – Marcus Müller Jan 2 at 13:07
  • \$\begingroup\$ "so no extra bytes are written if you mess up in software": not true, you're writing a defined number of zeros, aren't you? If you wrote samples after the end of your buffer, that'd be totally unaffected by your zeroing. Also, zeroing those would have no helpful effect, would it? \$\endgroup\$ – Marcus Müller Jan 2 at 13:09
  • 1
    \$\begingroup\$ Where the operation of the programme (and therefore the buffers) is fixed, as in this case, I see no reason or advantage to clearing the buffers at the end. It is in cases where the operation is not deterministic (unknown amount of data, for instance) that something needs to be done. (I often use a circular buffer and maintain a count). \$\endgroup\$ – Peter Smith Jan 2 at 13:13
2
\$\begingroup\$

In this specific example, yes I would continue to clear the buffers and call your clearBuf() function.

Why? Mainly because you aren't even handling that return status from your I2C tx and rx functions.

What happens if your device suddenly becomes unreachable via I2C and all your Receive calls are failing? I don't know what the HAL_I2C_Master_Receive does to the buffer when timeout duration is reached without looking in depth at their specific implementation but there's a decent chance they just leave the buffer untouched.

The result could be, if not caught via checking return status or other special handling, that you continue to process data as if it is not changing at all.

Real process data will typically be something non-zero so if you're reading a data stream that goes something like [1.56, 1.54, 1.58, 0, 0, 0, 0, 0, 0] a user may be alerted to your device being bad even if you didn't account for handling the specific error condition encountered (such as the one present in your code).

Evaluating this single failure mode of loss of I2C communication for two possible data stream scenarios either:

  1. Your data stream is typically zero and simply doesn't respond to changes due to the failure
  2. Your data stream is typically non-zero and has suddenly zero'd out due to the failure

Clearing your buffers makes the error condition visible in the second scenario even if it doesn't improve things for the first one.

So--you could (and should) fix your error handling to alleviate this particular issue, but what else are you missing? Sensors go bad, I2C operations fail, power can fluctuate, are you making sure you don't lie to your users?

| improve this answer | |
\$\endgroup\$
  • 1
    \$\begingroup\$ "you aren't even handling that return status from your I2C tx and rx functions." I removed that part as it made the piece of code way to big for posting here. I do have have asserts and some custom err codes. But thanks for the input, its very valid. \$\endgroup\$ – Sorenp Jan 3 at 6:22

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.