-
Notifications
You must be signed in to change notification settings - Fork 2.1k
drivers/ds18: Add Maxim Integrated 1-Wire temperature sensor driver #10011
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
Conversation
@aabadie Do you still have access to the hardware to test this? |
Normally yes but I have to find it in my mess :) |
@leandrolanzieri, I found it but I still can't make the sensor to give a valid temperature (tested with different DS18_PARAM_PULL values PU/PD) but it always display the following (with debug enabled): 2018-09-25 11:08:08,323 - INFO # [DS18] Reset and read scratchpad
2018-09-25 11:08:08,325 - INFO # [DS18] Received byte: 0xff
2018-09-25 11:08:08,326 - INFO # [DS18] Received byte: 0xff
2018-09-25 11:08:08,326 - INFO # Temperature [ºC]: 163.77 I tested with an Arduino Zero and a Nucleo L073. I setup the pin correctly for both boards. Maybe my sensor is broken ? I only have this one so I cannot test with another one to check this. |
@aabadie thanks a lot for testing! |
Partial ACK: on the fundamentals. |
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.
Nice one, have been waiting for this driver a long time :-). Found some issues that I see, I think all of them should be quite easy to fix.
The reason I had some concrete code snippets at hand is, that I also implemented this driver some time (~2 years) ago, but never got to quite finish it. But maybe there is more useful stuff for you in my branch: https://github.com/haukepetersen/RIOT/tree/add_driver_ds18b20
But for now, I'd say lets go with the driver as is (only the small fixes left), and once its merged we could think about things like factoring out the 1wire driver etc...
drivers/ds18/Makefile
Outdated
@@ -0,0 +1,2 @@ | |||
MODULE = ds18 |
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.
no need for this line
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.
Removed
drivers/ds18/ds18.c
Outdated
|
||
static void ds18_release(ds18_t *dev) | ||
{ | ||
/* Init pin as input and enable pull-up */ |
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.
comment doesnt fit to code anymore, 'pull-up' is not always ture, depending on dev->in_mode...
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.
Fixed
drivers/ds18/ds18_saul.c
Outdated
ds18_t *d = (ds18_t *)dev; | ||
|
||
ds18_get_temperature(d, &temperature); | ||
res->val[0] = temperature; |
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.
how about:
ds18_get_temperature((ds18_t *)dev, &res->val[0]);
would reduce the code above into a single line.
But aside from that: it would be good to also check the return value of read_temperature
and return a -ECANCELED
in case of an error.
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.
You are right, much better.
|
||
/* Measure time low of device pin, timeout after slot time*/ | ||
start = xtimer_now_usec(); | ||
while (!gpio_read(dev->pin) && measurement < DS18_DELAY_SLOT) { |
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 seems to be wasting resources by by reading the pin in a loop. As the pulse widths are specified, it would be more efficient to sample the pin state once in the middle of the corresponding pulse. The code I wrote for this (a long time ago) did something like this:
gpio_clear(owi->pin);
xtimer_usleep(T_RW_PULSE);
gpio_init(owi->pin, owi->imode);
xtimer_usleep(T_R_SAMPLE);
in = gpio_read(owi->pin);
xtimer_usleep(T_R_RECOVER);
gpio_init(owi->pin, owi->omode);
gpio_set(owi->pin);
with
#define T_RW_PULSE (3)
#define T_W_HOLD (57)
#define T_W_RECOVER (10)
#define T_R_SAMPLE (7)
#define T_R_RECOVER (55)
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.
also this way, all this timeout stuff is not needed anymore...
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 am having trouble with this one. There seems to be a problem with the xtimer on the board I am using (which has the sensor). I also did some tests on some nucleo boards and raised an issue here #10073.
I am not getting the needed time accuracy so I did not change the reading function just yet, as I am not able to test it right now. I already ordered a sensor so I can test it with different boards.
Any ideas to approach this problem?
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.
True, more-or-less precise timings are quite mandatory for using my proposed code block...
So until we get the timer imprecision sorted out, how about we go with a dual approach? We could include both code blocks in the code, #if - #else
ing them based on a sub(pseudo)module, making my variant optional. This way people can use the more efficient code once they made sure their platform is handle the needed timings...
Of course we would need to document this in the module description :-)
} | ||
|
||
DEBUG("[DS18] Convert T\n"); | ||
ds18_write_byte(dev, DS18_CMD_SKIPROM); |
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.
maybe put a comment in that this command triggers a conversion on all devices connected to the bus...
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.
Added
drivers/ds18/ds18.c
Outdated
|
||
/* Fixed point value to uint16_t */ | ||
uint16_t degrees = (b2 << 4) + (b1 >> 4); | ||
uint16_t mdegrees = (b1 & 0xF) * 6.25; |
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.
Seeing a floating point number (6.25
) in the code makes my head hurt :-) In the worst case, this pulls in ~5K of ROM or soft floating point library into the code - I guess this is not wanted here, right?
My proposal for the conversion:
int32_t res = ((int32_t)(b2 << 8 | b1) * 625);
*temperature = (int16_t)(res / 10);
The trick is to use a larger type for intermediate conversion by basically shifting the position of the decimal point temporarily.
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.
Thanks, changed this.
drivers/ds18/include/ds18_internal.h
Outdated
@@ -0,0 +1,64 @@ | |||
/* |
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 would move this file to drivers/ds18/ds18_internal.h
. This way it is only accessible from this driver and pulled out of the global include path...
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.
Moved
drivers/include/ds18.h
Outdated
*/ | ||
typedef struct { | ||
gpio_t pin; /**< Pin the sensor is connected to */ | ||
gpio_mode_t in_mode; /**< Pin output mode */ |
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 about the output mode?! I think it makes much sense to specify it as well. Or maybe even only specify the output mode in the configuration, and deduct the input mode from it:
// in the _init() function:
dev->in_mode = (params->out_mode == GPIO_OD_PU) ? GPIO_IN_PU : GPIO_IN;
rationale behind this is, that CPUs that allow for GPIO_OD_PU
have pull-up resistors implemented. These are then also usable for input mode...
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.
Makes sense, changed the param struct to have out_mode
* @return 0 on success | ||
* @return -1 on error | ||
*/ | ||
int ds18_get_temperature(ds18_t *dev, int16_t *temperature); |
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 would prefer to split this function into 3 separate ones, something like:
int ds18_trigger(dev); // trigger a new conversion
int ds18_read(dev, temp); // read the results of the last conversion
int ds18_get_temp(dev, temp); // convenience function for trigger->wait->read
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.
Splitted the function
* @ingroup drivers_saul | ||
* @brief Driver interface for the DS18 temperature sensors | ||
* | ||
* This driver provides @ref drivers_saul capabilities. |
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.
Would you please also add some more documentation about the supported (or not supported) features and limitations of this driver?!
E.g.
- does not allow for addressing devices -> only supports a single device on the bus
- hardcodes the 1wire bus functionality into the driver
- does not allow for configuration of the sampling width
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.
Added
0ac1887
to
f8f3b1a
Compare
17bac9b
to
a612d6d
Compare
I added a submodule |
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.
few more findings, once fixed I am happy :-)
drivers/ds18/ds18.c
Outdated
{ | ||
int res; | ||
|
||
res = ds18_reset(dev); |
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.
please remove this here, it is done already in ds18_trigger()
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.
Done.
drivers/ds18/ds18.c
Outdated
|
||
#if defined(MODULE_DS18_OPTIMIZED) | ||
DEBUG("[DS18] Using optimized read function"); | ||
#endif |
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.
please remove this block, I think it is rather for internal debugging...
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.
Removed
drivers/ds18/ds18_internal.h
Outdated
#define DS18_DELAY_R_RECOVER (DS18_DELAY_SLOT - DS18_SAMPLE_TIME) | ||
/** @} */ | ||
|
||
|
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.
remove newline
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.
Done
/** | ||
* @brief convenience fuction for triggering a conversion and reading the | ||
* value | ||
* |
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.
here should also be a note or description that this function will block for an amount of time (don't remember the value...).
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 added a note on that.
sys/auto_init/auto_init.c
Outdated
@@ -437,6 +437,42 @@ void auto_init(void) | |||
extern void auto_init_veml6070(void); | |||
auto_init_veml6070(); | |||
#endif | |||
#ifdef MODULE_DS18 |
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 like something went wrong when you rebased, most of these entries are unrelated to this PR, right?!
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.
Oops, I screwed up yep :(
91840bd
to
f185003
Compare
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.
ACK from my side for concept, code, and documentation.
I did however NOT test the code nor did I check for coding style... So @danpetry would you mind?!
Yes I'll do that tomorrow. |
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 few small nits, just putting them down now in case I forget on monday :)
drivers/ds18/ds18.c
Outdated
|
||
int ds18_get_temperature(ds18_t *dev, int16_t *temperature) | ||
{ | ||
int res; |
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.
unused variable. Weren't you getting compile 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.
Removed
@@ -0,0 +1,64 @@ | |||
/* |
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 file should go in the /internal folder in the driver
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 haven't seen an internal
folder in the common drivers structure
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.
Sorry, /include
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.
Here #10011 (comment) @haukepetersen asked to move it out of the folder so it is not in the global path.
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.
ok
drivers/ds18/ds18.c
Outdated
start = xtimer_now_usec(); | ||
while (!gpio_read(dev->pin) && measurement < DS18_DELAY_SLOT) { | ||
measurement = xtimer_now_usec() - start; | ||
}; |
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.
please remove semicolon
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.
Done
After @danpetry reported failures in readings with I found that the delayed introduced by the call to EDIT: |
tests/driver_ds18: Add test application for DS18B20 sensor. tests/driver_ds18: Add whitelist of boards
4608e54
to
8b8790c
Compare
Tested on all three whitelisted boards |
Contribution description
This PR is based on #6512. It adds the driver for the 1-Wire temperature sensors DS18 and SAUL interface.
What this PR adds:
ds18_read_bit
function.Testing procedure
DS18_PARAM_PIN
andDS18_PARAM_PULL
accordingly to your board.BOARD=sensebox_samd21 make flash term -C tests/driver_ds18
.Issues/PRs references
Based on #6512.
EDIT
Some measurements were performed on different platforms. Due to the need of timing accuracy for the implementation of the 1-wire protocol a whitelist of boards has been added to the test application. These boards have been tested and are known to work with the current implementation of the driver.
Measurements and captures can be found here: time_measurements_2.zip
@danpetry Reported the following time measurements (he took 3 samples of '1' and 3 of '0'):
Sensebox
Samr21-xpro
Nucleo L152RE