/* * Copyright (c) 2016 Intel Corporation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include #include #include #include #include #include #include "sensor_dht.h" /** * @brief Measure duration of signal send by sensor * * @param drv_data Pointer to the driver data structure * @param singnal_val Value of signal being measured * * @return duration in usec of signal being measured, * -1 if duration exceeds DHT_SIGNAL_MAX_WAIT_DURATION */ static int8_t dht_measure_signal_duration(struct dht_data *drv_data, uint32_t signal_val) { uint32_t val; uint32_t elapsed_cycles; uint32_t max_wait_cycles = (uint32_t)( (uint64_t)DHT_SIGNAL_MAX_WAIT_DURATION * (uint64_t)sys_clock_hw_cycles_per_sec / (uint64_t)USEC_PER_SEC ); uint32_t start_cycles = sys_cycle_get_32(); do { gpio_pin_read(drv_data->gpio, CONFIG_DHT_GPIO_PIN_NUM, &val); elapsed_cycles = sys_cycle_get_32() - start_cycles; if (elapsed_cycles >= max_wait_cycles) { return -1; } } while (val == signal_val); return (uint64_t)elapsed_cycles * (uint64_t)USEC_PER_SEC / (uint64_t)sys_clock_hw_cycles_per_sec; } static int dht_sample_fetch(struct device *dev, enum sensor_channel chan) { struct dht_data *drv_data = dev->driver_data; int ret = 0; int8_t signal_duration[DHT_DATA_BITS_NUM]; int8_t max_duration, min_duration, avg_duration; uint8_t buf[5]; unsigned int i, j; __ASSERT(chan == SENSOR_CHAN_ALL); /* send start signal */ gpio_pin_write(drv_data->gpio, CONFIG_DHT_GPIO_PIN_NUM, 0); sys_thread_busy_wait(DHT_START_SIGNAL_DURATION); gpio_pin_write(drv_data->gpio, CONFIG_DHT_GPIO_PIN_NUM, 1); /* switch to DIR_IN to read sensor signals */ gpio_pin_configure(drv_data->gpio, CONFIG_DHT_GPIO_PIN_NUM, GPIO_DIR_IN); /* wait for sensor response */ if (dht_measure_signal_duration(drv_data, 1) == -1) { ret = -EIO; goto cleanup; } /* read sensor response */ if (dht_measure_signal_duration(drv_data, 0) == -1) { ret = -EIO; goto cleanup; } /* wait for sensor data */ if (dht_measure_signal_duration(drv_data, 1) == -1) { ret = -EIO; goto cleanup; } /* read sensor data */ for (i = 0; i < DHT_DATA_BITS_NUM; i++) { /* LOW signal to indicate a new bit */ if (dht_measure_signal_duration(drv_data, 0) == -1) { ret = -EIO; goto cleanup; } /* HIGH signal duration indicates bit value */ signal_duration[i] = dht_measure_signal_duration(drv_data, 1); if (signal_duration[i] == -1) { ret = -EIO; goto cleanup; } } /* * the datasheet says 20-40us HIGH signal duration for a 0 bit and * 80us for a 1 bit; however, since dht_measure_signal_duration is * not very precise, compute the threshold for deciding between a * 0 bit and a 1 bit as the average between the minimum and maximum * if the durations stored in signal_duration */ min_duration = signal_duration[0]; max_duration = signal_duration[0]; for (i = 1; i < DHT_DATA_BITS_NUM; i++) { if (min_duration > signal_duration[i]) { min_duration = signal_duration[i]; } if (max_duration < signal_duration[i]) { max_duration = signal_duration[i]; } } avg_duration = ((int16_t)min_duration + (int16_t)max_duration) / 2; /* store bits in buf */ j = 0; memset(buf, 0, sizeof(buf)); for (i = 0; i < DHT_DATA_BITS_NUM; i++) { if (signal_duration[i] >= avg_duration) { buf[j] = (buf[j] << 1) | 1; } else { buf[j] = buf[j] << 1; } if (i % 8 == 7) { j++; } } /* verify checksum */ if (((buf[0] + buf[1] + buf[2] + buf[3]) & 0xFF) != buf[4]) { SYS_LOG_DBG("Invalid checksum in fetched sample"); ret = -EIO; } else { memcpy(drv_data->sample, buf, 4); } cleanup: /* switch to DIR_OUT and leave pin to HIGH until next fetch */ gpio_pin_configure(drv_data->gpio, CONFIG_DHT_GPIO_PIN_NUM, GPIO_DIR_OUT); gpio_pin_write(drv_data->gpio, CONFIG_DHT_GPIO_PIN_NUM, 1); return ret; } static int dht_channel_get(struct device *dev, enum sensor_channel chan, struct sensor_value *val) { struct dht_data *drv_data = dev->driver_data; __ASSERT(chan == SENSOR_CHAN_TEMP || chan == SENSOR_CHAN_HUMIDITY); val->type = SENSOR_VALUE_TYPE_INT_PLUS_MICRO; /* see data calculation example from datasheet */ #if defined(CONFIG_DHT_CHIP_DHT11) /* use only integral data byte */ if (chan == SENSOR_CHAN_HUMIDITY) { val->val1 = drv_data->sample[0] * 1000; val->val2 = 0; } else { /* chan == SENSOR_CHAN_TEMP */ val->val1 = drv_data->sample[2]; val->val2 = 0; } #elif defined(CONFIG_DHT_CHIP_DHT22) /* * use both integral and decimal data bytes; resulted 16bit data has * a resolution of 0.1 units */ int16_t raw_val, sign; if (chan == SENSOR_CHAN_HUMIDITY) { raw_val = (drv_data->sample[0] << 8) | drv_data->sample[1]; val->val1 = raw_val * 100; val->val2 = 0; } else { /* chan == SENSOR_CHAN_TEMP */ raw_val = (drv_data->sample[2] << 8) | drv_data->sample[3]; sign = raw_val & 0x8000; raw_val = raw_val & ~0x8000; val->val1 = raw_val / 10; val->val2 = (raw_val % 10) * 100000; /* handle negative value */ if (sign) { val->val1 = -val->val1; val->val2 = -val->val2; } } #endif return 0; } static struct sensor_driver_api dht_api = { .sample_fetch = &dht_sample_fetch, .channel_get = &dht_channel_get, }; static int dht_init(struct device *dev) { struct dht_data *drv_data = dev->driver_data; drv_data->gpio = device_get_binding(CONFIG_DHT_GPIO_DEV_NAME); if (drv_data->gpio == NULL) { SYS_LOG_ERR("Failed to get GPIO device."); return -EINVAL; } gpio_pin_configure(drv_data->gpio, CONFIG_DHT_GPIO_PIN_NUM, GPIO_DIR_OUT); gpio_pin_write(drv_data->gpio, CONFIG_DHT_GPIO_PIN_NUM, 1); dev->driver_api = &dht_api; return 0; } struct dht_data dht_data; DEVICE_INIT(dht_dev, CONFIG_DHT_NAME, &dht_init, &dht_data, NULL, SECONDARY, CONFIG_DHT_INIT_PRIORITY);