-
Notifications
You must be signed in to change notification settings - Fork 6.5k
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
drivers: sensor: qdec: fix QDEC overflow handling #75754
base: main
Are you sure you want to change the base?
drivers: sensor: qdec: fix QDEC overflow handling #75754
Conversation
Hello @mstasiaknordic, and thank you very much for your first pull request to the Zephyr project! |
a7c889e
to
73bf954
Compare
73bf954
to
9863bbe
Compare
CC: @tswaehn |
9863bbe
to
ab3b377
Compare
dear @mstasiaknordic as far as I understand, every qdec has a fixed number of digits, can be register size of ex. 16bit or 32bit. at some point when continue to rotate into same direction, there will be an overflow based on the internal register size. after overflow the counter should automatically continue to count ex. upwards. this may lead a 16bit qdec driver to return 32.767 (0x7FFF) and next value could be -32768 (0x8000). that is from my perspective expected behavior. overflows in number need to be handled by the user of the driver. so the qdec driver only returns the diff qdec counter value. => the user will always have the correct counter value when applying the diff from qdec to a self managed huger counter ex. int128_t. here is no actual overflow possible. in contrast => this is the actual critical part. if the qdec travels more than the available register width, then the user may lose precision, this is the actual overlow that need to be detected. so in conclusion: I think the use case of overflow is miss interpreted in the qdec driver - or my understanding may be different. |
Thank you for your response @tswaehn
Problem is that QDEC's internal register does not accept samples that would cause an overflow, so the sign-changing accumulation does not occur, the register remains unchanged while user expects a change of value. See : https://docs.nordicsemi.com/bundle/ps_nrf5340/page/chapters/qdec/qdec.html#ariaid-title5 My suggested change is that the user would be informed of such overflow happening with an errorcode signalizing that the data fetched by the user using |
dear @mstasiaknordic from the document it looks like the ACC counter is not turning around when continue to count ex. upwards. instead it is stuck at max or min. the workaround could be to reset the ACC counter on every readout, this prevents the counter to be stuck at max or min. this reset should be as quick as possible to not lose any counts that fly in while resetting. additionally there could be a uint16_t or uint32_t software counter that is updated on every reading ( this would, from my perspective then behave like a "normal" free running qdec and no external api changes required. additionally one could also report an overflow, if within two consecutive readings the ACC is stuck, because that would imply that a precision failure exists. which means the time of two readings was too long, or the qdec rotated too fast. what do you think? |
It is already implemented.
There is one -
Agreed, such overflow is now reported by an errorcode returned by |
dear @mstasiaknordic I need to disagree with you. I cannot find the suggested reset of ACC. additionally the overflow is generated on number overflow and not depending on two consecutive readings. therefore the last reading need to be stored somewhere, to compare with current reading, which I cannot find either. additionally from my perspective the channel_get() the question is, what values to return. a qdec usually returns increments. in your case you return degrees. Actually I have the feeling your implementation is a protractor instead of qdec. (protractor can be a specialized qdec, but not vice versa). usually a qdec can have like 4096 increments per rotation or even much more. this is really application dependant. in many applications you count multiple rotations of motors, forwards and backwards. |
Yes, though it was not designed by me and, I believe, is not the subject of this PR. |
could you please help me to find the code ? what is the expected output of sensor value after 2.5x full rotations of the qdec? |
said task here : https://github.com/zephyrproject-rtos/hal_nordic/blob/bda16da6f92380579b8cf681a5faf61e1fe55220/nrfx/hal/nrf_qdec.h#L96
With full you mean 360 degrees or reaching max value of the register? |
thank you for point this out. makes sense to me now. also agree to your changes made. |
I have looked at Zephyr QDEC sample, and it expects value returned from sensor to be degrees, thus I assume that driver should be responsible for the convertion from increments to degrees. |
wouldnt that loose precision? |
Yes, if we consider only |
just checked, some qdec drivers return position (increments) and others return degrees. although your mentioned sample indicates it should be degrees.
so, you are following the trend my personal opinion is to have this configurable to get raw units or real-world-units within the sensor configuration, ... as you mentioned, ... not part of this PR |
ab3b377
to
d0d1cfc
Compare
@@ -48,7 +50,7 @@ static void accumulate(struct qdec_nrfx_data *data, int32_t acc) | |||
((acc < 0) && (ACC_MIN - acc > data->acc)); | |||
|
|||
if (!overflow) { | |||
data->acc += acc; | |||
data->raw_acc += acc; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is wrong. The overflow is checked against data->acc
:
zephyr/drivers/sensor/nordic/qdec_nrfx/qdec_nrfx.c
Lines 49 to 50 in d0d1cfc
bool overflow = ((acc > 0) && (ACC_MAX - acc < data->acc)) || | |
((acc < 0) && (ACC_MIN - acc > data->acc)); |
data->raw_acc
is actually modified, so some overflows can be missed and some can be assumed incorrectly.
int32_t acc; | ||
int8_t overflow; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why not bool
?
if (data->overflow) { | ||
data->overflow = 0; | ||
data->raw_acc = 0; | ||
return -EOVERFLOW; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is not the proper way of handling the overflow. The accumulators should still be read in such case.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What value should be read in that case? One before applying the corrupted sample?
@@ -26,7 +26,9 @@ LOG_MODULE_REGISTER(qdec_nrfx, CONFIG_SENSOR_LOG_LEVEL); | |||
|
|||
|
|||
struct qdec_nrfx_data { | |||
int32_t raw_acc; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
raw_acc
is quite vague. IMO, it would be better to have acc
where you accumulate the readouts from the QDEC and e.g. fetched_acc
where the accumulated value would be stored after it was fetched.
data->acc = data->raw_acc; | ||
data->raw_acc = 0; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The accumulated value should be accessed with interrupts locked and the overflow should be reported here instead of before the accumulators reading.
tests/boards/nrf/qdec/src/main.c
Outdated
if (!overflow_possible) { | ||
zassert_true(rc == 0, "Failed to fetch sample (%d)", rc); | ||
} else { | ||
zassert_true(rc == -EOVERFLOW, "Failed to detect overflow"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is the overflow possible or expected? I think it is the latter and the variable name should be corrected, as well as the following comment:
zephyr/tests/boards/nrf/qdec/src/main.c
Lines 251 to 253 in d0d1cfc
/* may lead to overflows but currently driver does not detects that */ | |
qenc_emulate_verify_reading(1, 1000, false, true); | |
qenc_emulate_verify_reading(1, 1000, true, true); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
When running the test locally, it always led to overflow, so I agree
d0d1cfc
to
835fa4b
Compare
QDEC sensor driver fails to inform user of the overflow in the ACC register, which makes the most recently fetched data invalid. An error code return has been added to nrfx_qdec_sample_fetch(), that indicates that an overflow has occured, based on oveflow flag. Also, raw_acc field was added in the qdec_nrfx_data structure, to adjust QDEC to sensor API rules - two subsequent sensor_channel_get() calls should will yield the same values. Signed-off-by: Michał Stasiak <[email protected]>
Overflow errorcode is now correctly detected when expected. Subsequent sensor_channel_get() yield the same values, so the check can be no longer ignored. Added a sensor_sample_fetch() where missing for correct sensor_channel_get() calls. Signed-off-by: Michał Stasiak <[email protected]>
835fa4b
to
d4d834c
Compare
@anangl could you take a look? |
Fixed overflow handling in nrf QDEC sensor driver. It failed to inform user of an overflow in the ACC register, that caused the fetched sample to be invalid because of discarding data that would cause the overflow.
Now,
qdec_nrfx_sample_fetch()
returns error code if an overflow occured, based on overflow field in qdec data structure.Also, driver is now more adjusted to sensor driver API. It is guaranteed that two subsequent calls of
sensor_channel_get()
function for the same channels will yield the same value, ifsensor_sample_fetch()
has not been called in the meantime.