Драйвер термометра
Термометр этот:
https://www.chipdip.ru/product/troyka-temperature-humidity-sensor-dht11
Из этого набора:
https://www.chipdip.ru/product0/9000318627
На странице набора есть ссылка на библиотеку (DHT11), которую я буду разбирать и на описание (datasheet) датчика, по которому писалась библиотека.
Описание объекта (TroykaDHT11.h) совсем короткое:
class DHT11
{
public:
// Создание объекта, присвоение внутренней переменной _pin номера пина
explicit DHT11(uint8_t pin);
// Чтение информации датчика:
uint8_t read();
// Температура и влажность из внутренних переменных, заполненных функцией read():
uint8_t getTemperatureC() const { return _temperatureC; }
uint8_t getHumidity() const { return _humidity; }
private:
uint8_t _pin; // Номер подключенного к датчику пина
uint8_t _temperatureC; // Температура
uint8_t _humidity; // Влажность
}
Нету в тексте ничего, кроме функции read(). Начну, все-таки с неё, а не с описания датчика. Я её чуть изменила. Почерк у всех разный. По-моему, эта экономия на скобках делает текст плохочитаемым. Не люблю))). Начало функции:
uint8_t DHT11::read() {
uint8_t bits[5]; // буфер данных датчика на 5 байт, т.е на 8*5=40 бит
uint8_t cnt = 7; // счетчик по номеру бита (от 0 до 7, т.е 8 бит)
uint8_t idx = 0; // Номер считываемого байта (от 0 до 4, т.е 5 штук)
// Длина цикла задержки (за время выполнений цикла должны прийти данные,
// иначе – ошибка:
unsigned int loopCnt = 10000;
unsigned long t; // Переменная для времени (в микросекундах)
uint8_t sum; // Переменная для значения суммы, проверяющей корректность полученных данных
// очистка буфера (видимо в нём может быть грязь, т.е не нули):
for (int i = 0; i < 5; i++)
{
bits[i] = 0;
}
Дальше – начало общения с датчиком:
// согласование с датчиком
pinMode(_pin, OUTPUT);
digitalWrite(_pin, LOW);
delay(18); // миллисекунды
digitalWrite(_pin, HIGH);
delayMicroseconds(40); // микросекунды
pinMode(_pin, INPUT);
// проверка отвечает ли датчик
// Возврат ошибки (DHT_ERROR_TIMEOUT), если на пине осталось LOW
// в течение всего цикла 10000 шагов (слишком долго):
loopCnt = 10000;
while (digitalRead(_pin) == LOW)
{
if (loopCnt == 0)
{
return DHT_ERROR_TIMEOUT;
}
else
{
loopCnt--;
}
}
// Аналогичная проверка на значение на слишком долгое значение HIGH:
loopCnt = 10000;
while (digitalRead(_pin) == HIGH)
{
if (loopCnt == 0)
{
return DHT_ERROR_TIMEOUT;
}
else
{
loopCnt--;
}
}
Пока не хочу читать описание. Наверное, так и надо. Дальше, если не случилось выхода (return из функции) по ошибке, чтение 40 бит данных:
// Считываем 40 бит
for (int i = 0; i < 40; i++) { // i – номер бита
// Проверки на слишком долгие задержки как выше:
loopCnt = 10000;
while (digitalRead(_pin) == LOW)
if (loopCnt-- == 0) return DHT_ERROR_TIMEOUT;
t = micros();
loopCnt = 10000;
while (digitalRead(_pin) == HIGH)
if (loopCnt-- == 0) return DHT_ERROR_TIMEOUT;
// Чтение информации если не было слишком больших задержек:
if ((micros() - t) > 40) // Если время HIGH>40 микросекунд, получена 1
{
// Начальное двоичное значение всех данных 0000 0000, потому, при получении 0
// ничего менять не надо. А если получено 1, то надо заменить на 1 значение
// бита с номером cnt. Это и делается путем выполнения операции ИЛИ (|)
// уже собранных данных bits[idx] и числа maskCnt, в котором 1 уже
// задвинута на нужное место:
mask1 = 1; // В двоичном виде 0000 0001
maskCnt = mask1<< cnt; // Сдвиг ”1” влево, При cnt=3, maskCnt =0000 0100
bits[idx] = bits[idx] | maskCnt; // Установка в 1 нужного бита
}
// следующий байт – изменение значений счётчиков:
if (cnt == 0)
{
cnt = 7; // Видимо, информация передается с последнего бита
idx++;
}
else
{
cnt--;
}
}
А теперь, все-таки, почитаю описание датчика:
Data consists of decimal and integral parts. A complete data transmission is 40bit, and the sensor sends higher data bit first.
… Старшие биты приедут первыми. Т.е 1 градус – 0000 0001 в той же последовательности и приедет, но если я буду считать биты с нулевого до седьмого, я получу bits[X][0]=0, bits[X][7] = 1 и общее 1000 0000, потому в тексте счётчик с 7 до 0
Data format: 8bit integral RH data + 8bit decimal RH data + 8bit integral T data + 8bit decimal T data + 8bit check sum. If the data transmission is right, the check-sum should be the last 8bit of "8bit integral RH data + 8bit decimal RH data + 8bit integral T data + 8bit decimal T data".
Последовательность байт:
1] Влажность (целое)
2] Влажность (десятичное)
3] Температура (целое)
4] Температура (десятичное)
5] Байт проверки, которые должен быть равен сумме всех предыдущих.
Читаю программный текст дальше, а там так:
// запись данных
_humidity = bits[0];
_temperatureC = bits[2];
// проверка контрольной суммы
sum = bits[0] + bits[2]; // !!!Должно быть sum = bits[0]+ bits[1] + bits[2]+ bits[3]
if (bits[4] != sum)
{
return DHT_ERROR_CHECKSUM;
}
else
{
return DHT_OK;
}
… т.е не учитываются значения после запятой. Т.е если есть какие-то десятые, то будет ошибка при чтении данных. Наверное, поэтому у меня этот датчик очень редко работал. В драйвере ошибка.
Остальное, вроде так, как и должно быть. Стартовый запрос данных от датчика и его ответ о готовности слать данные:
Общая схема передачи:
Т.е после начального диалога 40 бит информации, при которых 0 это HIGH 26-28 микросекунд, а 1 – 70 микросекунд.
Чёрное в схемах – изменение значений информационного канала (пина) микроконтроллером, серое – датчиком.
|