-
-
Notifications
You must be signed in to change notification settings - Fork 8.2k
Add option in DHT driver to do the measure without blocking interrupts #6946
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
base: master
Are you sure you want to change the base?
Conversation
4fdf251
to
2021e34
Compare
This is done because ESP8266 has a PWM relying on interrupts. Doing the DHT measure with interruption blocking, sometimes makes glitches on the PWM.
To reproduce issue, assuming dht is on gpio 0 and led on gpio14 import dht You should see glitches on the leds change to d=dht.DHT22(Pin(0), False), no more glitches |
drivers/dht/dht.c
Outdated
@@ -37,7 +37,9 @@ | |||
#define mp_hal_pin_od_high_dht mp_hal_pin_od_high | |||
#endif | |||
|
|||
STATIC mp_obj_t dht_readinto(mp_obj_t pin_in, mp_obj_t buf_in) { | |||
STATIC mp_obj_t dht_readinto(mp_obj_t pin_in, mp_obj_t buf_in, mp_obj_t irq_lock) { | |||
mp_uint_t irq_state=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.
This does not follow the code style in the file. Put spaces around '='. Also there seems to be no need for the 0 initialization.
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.
unfortunately you have to :-( That is why I added it.
+1: ../../drivers/dht/dht.c: In function 'dht_readinto':
./xtirq.h:48:5: error: 'irq_state' may be used uninitialized in this function [-Werror=maybe-uninitialized]
asm volatile ("wsr %0, ps; rsync" : : "a" (ps));
^
../../drivers/dht/dht.c:41:15: note: 'irq_state' was declared here
mp_uint_t irq_state;
^
cc1: all warnings being treated as errors
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.
Well, either I'm missing something or compiler is wrong about that but well, in any way, we want to make it happy.
docs/esp8266/tutorial/dht.rst
Outdated
>>> while not measure_ok: | ||
>>> try: | ||
>>> d.measure() | ||
>>> measure_ok = 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.
A minor comment, I think the more common way of handling it would be to get rid of this variable and use brake like so:
while True:
try:
d.measure()
break
except [...]
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.
yes ! good point.
drivers/dht/dht.c
Outdated
@@ -54,7 +56,9 @@ STATIC mp_obj_t dht_readinto(mp_obj_t pin_in, mp_obj_t buf_in) { | |||
mp_hal_pin_od_low(pin); | |||
mp_hal_delay_ms(18); | |||
|
|||
mp_uint_t irq_state = mp_hal_quiet_timing_enter(); | |||
if (mp_obj_is_true(irq_lock)) { |
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.
mp_obj_is_true() is a little involved function so it would be worth calling it only once instead if tree times. You should consider creating a bool that will "cache" this computation once, something like:
bool irq_lock_true = mp_obj_is_true(irq_lock);
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.
well, I considered the call to mp_obj_is_true() as almost free. I can do that if you prefer.
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.
Check its implementation here. For most cases it is, indeed, but if someone passes some object there, it does require quite some checks.
Ultimately it's not my decision, I'm just a random guy doing a review, but I do think its worth saving those few CPU cycles.
|
||
def measure(self): | ||
buf = self.buf | ||
dht_readinto(self.pin, buf) | ||
dht_readinto(self.pin, buf, self.irq_block) |
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.
We could consider putting a retry loop here in case self.irq_block is false to free the users from doing that. I would avoid the infinite loop, though.. so maybe some fixed amount of retries should be introduced?
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.
I prefer to let the user decide to manage that as he prefers. In my case, I have other things to do so I can't lock main loop for too long, so I retry later. DHT is already really slow, not worth adding more delays.
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.
Seems valid argument, indeed. I was just afraid that anybody has to implement this loop while using this argument anyways.
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.
With a DHT sensor you have to implement a try:except: retry.. loop anyway because they are not very stable.
As a general comment, the same issue exist with onewire module (as DHT protocol is very similiar to onewire bus) so if you are already putting a warning to the documentation, maybe it would make sense to mention onewire case as well? |
Yeah, why not. But in the onewire, bits are read one by one and the protocol is waaaaaay faster (locking is shorter), so I'm not sure it can produce visible effects. |
True. But if this (locking for ~70us) is not a problem then maybe for DHD it would also be ok to do a lock/unlock in every iteration of the loop (which takes 80-120us) and only for a machine_time_pulse_us(). This way we still can get a wrong result if entering into the critical region is delayed much more than 50us (the time of a low pulse before each high pulse we are measuring) but we might get a little better results than without any locking while the overall latency would be still be much smaller. DHT will send 26-28us pulse for "0" and 70us for "1" but dht.c is considering a "1" everything that is >48us so we already have a safety margin of around 22us in case our machine_time_pulse_us() starts late (when the 50us low singal is already over). To sum up, my understanding is this:
What do you think ? |
Theoretically you are right. But I think this is over complicated for a very little benefit. My software is running for almost a year with this patch and 3 channel PWM in parallel, I observed very few checksum errors, and no weird measures that could have gone through checksum verification. |
This is an automated heads-up that we've just merged a Pull Request See #13763 A search suggests this PR might apply the STATIC macro to some C code. If it Although this is an automated message, feel free to @-reply to me directly if |
This is done because ESP8266 has a PWM relying on interrupts.
Doing the DHT measure with interruption blocking, sometimes makes
glitches on the PWM.
fixes #6945