2
\$\begingroup\$

I am using a B-L072Z-LRWAN1 board (which has a STM32L072 MCU) and I want to get data from a DHT11 sensor. To achieve this, I need a microseconds delay which I am not able to obtain.

The libraries provided by ST have a millisecond delay function but not a microsecond function.

I need some help. Thank you in advance!

\$\endgroup\$
  • \$\begingroup\$ Ups! The old title was saved from a previous post. My apologies. \$\endgroup\$ – Pablo Díaz Amores Oct 29 '18 at 11:20
  • \$\begingroup\$ This site gives an example of using an STM32 with the DHT11. It includes code, but I can't look inside to see how it works. The code is provided in a compressed file that my phone can't unpack. \$\endgroup\$ – JRE Oct 29 '18 at 11:30
  • \$\begingroup\$ You need to be more specific to have an answerable question. How many microseconds? With what minimum after error? And what maximum? \$\endgroup\$ – Chris Stratton Oct 29 '18 at 12:29
  • \$\begingroup\$ I need 40us and 80us delays to be able to read data from sensor. \$\endgroup\$ – Pablo Díaz Amores Nov 1 '18 at 14:57
1
\$\begingroup\$

You don't need microseconds delay, but a timer counter with microseconds accuracy.

Looking at the datasheet linked by @JRE, this is an asynchronous protocol. The host transmits a long low pulse (at least 18 milliseconds), and that's the only time when the MCU should delay anything. But you already have a milliseconds delay function (and anyway it'd be better done in the milliseconds interrupt that's probably already running).

Then the device answers with a start cycle and 40 data cycles, where the length of the high phase encodes the bit value. You have to measure the elapsed time between the rising and the falling edge.

If the milliseconds interrupt is running, then there is already something counting the cycles. Usually it's the SysTick timer. Wait for the rising edge on the pin, get a timestamp from SysTick->VAL, wait for the falling edge, read the counter again, and subtract the previous timestamp. Watch out for overflow, add SysTick->LOAD+1 to the result if it's negative. Divide the result by the clock frequency (in MHz) to get a microsecond value.

However, you might prefer to have the MCU do something useful, or let it sleep to conserve power while the data comes in.

A timer and DMA can do most of the work on their own

Read the TIM2/TIM3 functional description / Input capture mode section in the Reference Manual.

As the low phase of the signal should be always 50 us long, you can as well measure the time between two consecutive falling edges. The example in the manual is for rising edges, but it can of course be modified to capture falling edges by changing the CC1P and CC1NP bits in Step 3.

  • Pick a timer channel and the corresponding DMA channel. Use the table titled Summary of the DMA requests for each channel.
  • Set up the DMA channel to transfer 41 (for 1 start pulse + 40 data pulses) halfwords (16 bits) from the CCR register of the timer channel to a suitably sized array in memory.
  • You can enable the DMA transfer complete interrupt in order to run a handler when, well, the data transfer is complete.
  • Set up the timer with a prescaler that is your system clock frequency in MHz (write one less in PSC, and don't forget to set UG in EGR) to conveniently get a timestamp in milliseconds.
  • Proceed with the procedure described in the Input capture mode section. There is even a simple code example in the appendix. Adapt it for capturing falling edges, and in the last step, enable DMA instead of the channel interrupt in DIER.
  • As a safety net, you can enable the timer update interrupt. It will fire after 65535 microseconds, letting the program know that the transfer has timed out. A lower timeout value can be set in ARR.
\$\endgroup\$

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.