Skip to content

drivers: add generic 1-Wire bus driver #14897

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

Closed
wants to merge 15 commits into from

Conversation

haukepetersen
Copy link
Contributor

Contribution description

This PR adds a generic driver for interfacing with devices that use the Dallas Integrated 1-Wire bus (e.g. the ds18b20 temperature sensors). It allows to read/write specific devices, as well as scanning the bus for connected devices and to discover their ROM codes (addresses).

I had this code laying around for some years now and finally found some time to clean it up...

A PR for a ds18b20 device driver based on this PR will be opened separately.

Testing procedure

Connect one or more 1-Wire devices (e.g. ds18b20 sensors) to a board of your choice, flash the included test application (tests/driver_onewire), and run the init and discover shell commands.

Issues/PRs references

none

@haukepetersen haukepetersen added Type: new feature The issue requests / The PR implemements a new feature for RIOT Area: drivers Area: Device drivers CI: ready for build If set, CI server will compile all applications for all available boards for the labeled PR labels Aug 31, 2020
@haukepetersen
Copy link
Contributor Author

pushed some minor doxygen and test app type fixes

Copy link
Contributor

@benpicco benpicco left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice addition, I have some comments.

0xC2, 0x9C, 0x7E, 0x20, 0xA3, 0xFD, 0x1F, 0x41,
0x00, 0x9D, 0x23, 0xBE, 0x46, 0xDB, 0x65, 0xF8,
0x8C, 0x11, 0xAF, 0x32, 0xCA, 0x57, 0xE9, 0x74
};
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I guess sys/checksum/crc8.c was too slow?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes. Or at least I'd like to use this optimized version better then using the slower, generic solution...

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

IMO the implementation would still be better placed in sys/checksum, so that other potential users could also use the implementation.

One could even leave the decision on using the fast or the small CRC8 implementation to the user like this:

static inline uint8_t crc8(const uint8_t *data, size_t len, uint8_t g_polynom, uint8_t seed)
{
    if (IS_USED(MODULE_CRC8_FAST) && __builtin_constant_p(g_polynom)) {
        switch (g_polynom) {
        case CRC8_POLY_DALLAS:
            return crc8_dallas(data, len, seed);
        }
    }
    return crc8_generic(data, len, g_polynom, seed);
}

Here the crc8 function would pick the fast implementation over the generic if the module crc8_fast is used and the value of g_polynom is known at compile time (which is in all but the most obscure scenarios the case), so that the static inline function is always replaced with either a call to crc8_generic() or to the optimized crc implementation for the given polynomial (such as crc8_dallas()).

@haukepetersen
Copy link
Contributor Author

addressed comments.

Copy link
Contributor

@benpicco benpicco left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I tried the test on avr-rss2 which has a DS18B20 connected to PD7.
Unfortunately the ATmega GPIO driver does not support GPIO_OD_PU, but according to the documentation there is a pull-up on the board.

So I tried GPIO_IN:

2020-08-31 15:29:53,752 #  init 3 7 2
2020-08-31 15:29:53,755 # Initializing 1-Wire bus at GPIO_PIN(3, 7)
2020-08-31 15:29:53,760 # 1-Wire bus successfully initialized
2020-08-31 15:32:50,303 # Discovery finished, found 10 devices:
2020-08-31 15:32:50,304 # Dev # - ROM code
2020-08-31 15:32:50,306 # [ 0]    0000000000000000
2020-08-31 15:32:50,309 # [ 1]    0000000000000080
2020-08-31 15:32:50,312 # [ 2]    0000000000000040
2020-08-31 15:32:50,312 # [ 3]    00000000000000C0
2020-08-31 15:32:50,315 # [ 4]    0000000000000020
2020-08-31 15:32:50,318 # [ 5]    00000000000000A0
2020-08-31 15:32:50,320 # [ 6]    0000000000000060
2020-08-31 15:32:50,321 # [ 7]    00000000000000E0
2020-08-31 15:32:50,324 # [ 8]    0000000000000010
2020-08-31 15:32:50,327 # [ 9]    0000000000000090

@haukepetersen
Copy link
Contributor Author

I tried the test on avr-rss2 which has a DS18B20 connected to PD7

Hm, just remembered (from drivers/include/ds18.h):

  • @note Due to timing issues present on some boards this drivers features two
  • ways of reading information from the sensor. The optimized uses accurate
  • delays to handle this, while the second way polls the line for changes. If
  • you know that your board can handle ~3us resolution with the xtimer module,
  • then the optimized way is recommended. To used the optimized way add the
  • ds18_optimized module. Also this driver test application has a whitelist of
  • the boards this driver has been tested on and known to work.

Probably the above is the case for the 'avr-rss2' board?! That would explain the failed device scan. Seems like I have to do some magic as well for the onewire driver?! Damn, not getting this off my hands so easily as it seems...

@benpicco
Copy link
Contributor

benpicco commented Aug 31, 2020

I tried tests/driver_ds18 and it would only print

2020-08-31 16:32:23,017 # Temperature [ºC]: -0.07

which I can assure you is not correct either.
Maybe @herjulf knows more? I'm using rev 2.3a of the board which has the sensor connected.

AFAIU xtimer is not suitable for anything that requires exact timings - we'd have to use a dedicated periph timer for that.
Fortunately most MCUs have plenty of those.

@haukepetersen
Copy link
Contributor Author

Pretty sure this is because of bad timings on AVR-based hardware... Will add a polling mode as done in the already merged ds18 driver to see if that helps...

@haukepetersen
Copy link
Contributor Author

If you try to build with USEMODULE=onewire_poll now, does that help?

@benpicco
Copy link
Contributor

2020-08-31 16:52:21,823 #  init 3 7 2
2020-08-31 16:52:21,826 # Initializing 1-Wire bus at GPIO_PIN(3, 7)
2020-08-31 16:52:21,831 # 1-Wire bus successfully initialized
2020-08-31 16:52:25,344 #  discover
# stuck here forever

@haukepetersen
Copy link
Contributor Author

Ok, so much for the quick and dirty fix... Will need to get home (where I have the fitting HW at hand) and see what I broke. Will post an update then.

@herjulf
Copy link
Contributor

herjulf commented Aug 31, 2020

Hej,
Can only verify benpicco's testing. Even when using onewire_poll. Yes board uses external pull-up actually the 2.3a board has two one-wire buses.
init 3 7 2 (onboard DS18B20) and init 3 6 2 (external) with this test.

2020-08-31 16:52:21,823 #  init 3 7 2
2020-08-31 16:52:21,826 # Initializing 1-Wire bus at GPIO_PIN(3, 7)
2020-08-31 16:52:21,831 # 1-Wire bus successfully initialized
2020-08-31 16:52:25,344 #  discover
# stuck here forever

@haukepetersen
Copy link
Contributor Author

Japp, as said, I screwed up with the quick-port of the polling mode. Need to investigate with HW at hand :-)

@haukepetersen
Copy link
Contributor Author

but this seems not to be possible with this driver yet.

no, its not. Will add a comment in the doc addressing this.

@kaspar030
Copy link
Contributor

And I consider getting it to run on a high-level timer as personal challenge by now :-)

I'll try to help with that.

Can you post a baseline on how much overhead "xtimer_spin()" poses? and maybe also ztimer_spin() (which just landed some days ago). The latter takes at least 3us, but is fairly accurate after that, on nrf52840dk.

@haukepetersen
Copy link
Contributor Author

will do once I have the hardware at my hands

@herjulf
Copy link
Contributor

herjulf commented Sep 14, 2020

_pull(owi);
xtimer_usleep(1U);
_release(owi);
xtimer_usleep(1U);
_pull(owi);
xtimer_usleep(1U);
_release(owi);
xtimer_usleep(1U);

xtimer-sleep1u

sleeping 1us gets 50us

@kaspar030
Copy link
Contributor

sleeping 1us gets 50us

Can you try xtimer_spin((xtimer_ticks32_t){1}) and ztimer_spin(ZTIMER_USEC, 1)? The latter probably needs USEMODULE += ztimer_usec somewhere in the makefile.

@herjulf
Copy link
Contributor

herjulf commented Sep 14, 2020

Hmm
ztimer_spin ==> ztimer_sleep?

@herjulf
Copy link
Contributor

herjulf commented Sep 14, 2020

@kaspar030
xtimer-spin1

Better is seems. ztimer is confusing now...

@kaspar030
Copy link
Contributor

ztimer_spin ==> ztimer_sleep?

ztimer_spin() got merged fairly recently, maybe rebase to master?

@herjulf
Copy link
Contributor

herjulf commented Sep 15, 2020

@kaspar030. Yes got it. Will test later...

@herjulf
Copy link
Contributor

herjulf commented Sep 15, 2020

   ztimer_spin(ZTIMER_USEC, 1);
   _release(owi);
   ztimer_spin(ZTIMER_USEC, 1);
   _pull(owi);
   ztimer_spin(ZTIMER_USEC, 1);
   _release(owi);
   ztimer_spin(ZTIMER_USEC, 1);
   _pull(owi);
   ztimer_spin(ZTIMER_USEC, 1);
   _release(owi);

ztimer_spin1us

Something missing? First two spins is about 70us. Then..

@kaspar030
Copy link
Contributor

Something missing? First two spins is about 70us. Then..

Thanks @herjulf! Something seems quite off with ztimer on the arduino, I'll need to debug that.

@herjulf
Copy link
Contributor

herjulf commented Sep 16, 2020

@kaspar030.
Yes onewire and small arduino are legacy and worked 10 years ago. onewire we still use for some external temp measurements. Otherwise BME280 (not 680) and SHT25.

Looking a ztimer... It should be possible to abstract even Mac Symbol Counter I hope?
This is a 32 bit timer/counter on the amegsl RF ones. It's atomic and has 3 HW threshholds via ISR. It is sourced via 16MHz xtal or the RTC 32kHz clock crystal. It has a fallback to RTC when you go to the lowest power modes and sync's when you switch power modes.
With this you can get very decent timing with few uA or less. IMO it can be worthwhile getting a RF atmega even if you don't use the radio. But it's
connected to the radio sort of drawback.

@kaspar030
Copy link
Contributor

kaspar030 commented Sep 16, 2020

Yes onewire and small arduino are legacy and worked 10 years ago.

is that using timers / bitbanging? or is it using a uart?

@herjulf
Copy link
Contributor

herjulf commented Sep 21, 2020

Yes onewire and small arduino are legacy and worked 10 years ago.

is that using timers / bitbanging? or is it using a uart?

Simplest. bitbanging and busywaiting. DS1820 usually.
If I understand the RIOT arch the smallest timer is limited by the ISR plus the context switch time. Do we have any data/timings for context switch on different arch?
Also I need to understand sched_context_switch_request and context push and pop takes about 100 instructions for avr's. So is this the major overhead? Or is making the 8bit addresses atomic the problem? Or?

BTW I forgot to say the MAC Symbol Counter (MSC) is 16uS counts. I can get timer interrupts in RIOT now but it's not connected to anything useful. Also a question...

Still traveling for some days...

@kfessel
Copy link
Contributor

kfessel commented Sep 23, 2020

onewire_t contains the line gpio_mode_t imode; /**< GPIO pin input mode, deducted from omode */
if it is deduced from omode there is no need to save it.
It is also very unlikely that this will change or first be known at runtime thus it should be configured (either into a define or a const) and compiled into, making the onewire_t a gpio pin.

@ArcticSnow ArcticSnow mentioned this pull request Oct 2, 2020
10 tasks
@stale
Copy link

stale bot commented Jun 3, 2021

This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. If you want me to ignore this issue, please mark it with the "State: don't stale" label. Thank you for your contributions.

@stale stale bot added the State: stale State: The issue / PR has no activity for >185 days label Jun 3, 2021
@stale stale bot closed this Jul 8, 2021
@benpicco
Copy link
Contributor

This implementation suggests it's also possible to (ab)use UART for this.

@herjulf
Copy link
Contributor

herjulf commented Sep 29, 2021

Thanks for pointing this possibility out. But the ultimate solution would be to keep the IRQ latency low. Remember seeing all the pushing and popping of registers for every IRQ. Well we do survive...

@Enoch247
Copy link
Contributor

Enoch247 commented May 4, 2022

I would like to take over this stale PR. Should I create a new PR, or is there a way to re-open it and assign it to me in such a way that I am edit the branch?

@maribu
Copy link
Member

maribu commented May 4, 2022

I can re-open it, but I have no way to assign it to you. So I guess the best is to open a new PR and post a link to that here.

@herjulf
Copy link
Contributor

herjulf commented May 5, 2022

Yes please share any new links...

@maribu
Copy link
Member

maribu commented May 5, 2022

See #18051

@Enoch247
Copy link
Contributor

If I understand correctly, the items that I need to addressed to get this PR merged are:

Have I missed anything? I will use the PR I opened (#18051) to do this work.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Area: drivers Area: Device drivers State: stale State: The issue / PR has no activity for >185 days Type: new feature The issue requests / The PR implemements a new feature for RIOT
Projects
None yet
Development

Successfully merging this pull request may close these issues.

8 participants