Продолжаем работать с модулем RMT и в прошлом занятии мы произвели инициализацию шины 1-Wire для работы с датчиком измерения температуры DS18B20.
На данном уроке мы попробуем использовать наш датчик по назначению, то есть для измерения температуры.
Лишний раз повторю, что с использованием других контроллеров мы всё это неоднократно проделывали, причём с STM32 мы в уроке 92 мы использовали один датчик DS18B20 на шине, а в уроке 94 у нас их было несколько. Поэтому, думаю, что с данной задачей мы легко справимся и с использование контроллера ESP32.
Схема урока пока будет такая же, как и в прошлом уроке — с одним датчиком, остальные мы также подключим чуть позже
Проект был сделан на основе проекта прошлого урока с именем RMT_DS18B20_INIT и получил новое имя RMT_DS18B20.
Откроем наш проект в Espressif IDE и в функции app_main файла main.c объявим массив для подсчёта количества ошибок, если таковые случатся, для каждого устройства, а также переменную для подсчёта количества измерений
1 2 3 4 |
owb_use_parasitic_power(owb, parasitic_power); int errors_count[MAX_DEVICES] = {0}; int sample_count = 0; |
Измерять мы будем один раз в секунду, поэтому объявим макрос с количеством милисекунд между измерениями
1 2 |
#define DS18B20_RESOLUTION (DS18B20_RESOLUTION_12_BIT) #define SAMPLE_PERIOD (1000) // milliseconds |
Затем организуем бесконечный цикл для измерений с использованием продвинутой задержки, которую мы изучали, когда работали над изучением ОС FreeRTOS с контроллером STM32
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
int sample_count = 0; if (num_devices > 0) { TickType_t last_wake_time = xTaskGetTickCount(); while (1) { vTaskDelayUntil(&last_wake_time, SAMPLE_PERIOD / portTICK_PERIOD_MS); } } else { printf("\nNo DS18B20 devices detected!\n"); } |
В файле DS18B20.c объявим макрос команды запуска конвертирования (аналого-цифрового преобразования) температуры датчиком
1 2 3 |
static const char *TAG = "ds18b20.c"; //--------------------------------------------------------------------- #define DS18B20_FUNCTION_TEMP_CONVERT 0x44 ///< Initiate a single temperature conversion |
Выше функции ds18b20_check_for_parasite_power добавим функцию, которая будет запускать конвертирование температуры
1 2 3 4 5 |
//--------------------------------------------------------------------- void ds18b20_convert_all(const OneWireBus * bus) { } //--------------------------------------------------------------------- |
На данную функцию объявим прототип в заголовочном файле и вызовем её в функции app_main файла main.c
1 2 3 4 |
TickType_t last_wake_time = xTaskGetTickCount(); while (1) { ds18b20_convert_all(owb); |
Перейдём в файл owb.c и выше функции owb_rmt_initialize добавим функцию, которая настроит жёстко ножку порта на притягивание к питанию
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
//--------------------------------------------------------------------- owb_status owb_set_strong_pullup(const OneWireBus * bus, bool enable) { owb_status status = OWB_STATUS_NOT_SET; if (!bus) { status = OWB_STATUS_PARAMETER_NULL; } else if (!_is_init(bus)) { status = OWB_STATUS_NOT_INITIALIZED; } else { if (bus->use_parasitic_power && bus->strong_pullup_gpio != GPIO_NUM_NC) { gpio_set_level(bus->strong_pullup_gpio, enable ? 1 : 0); ESP_LOGD(TAG, "strong pullup GPIO %d", enable); } // else ignore status = OWB_STATUS_OK; } return status; } //--------------------------------------------------------------------- |
Объявим прототип на данную функцию в заголовочном файле, вернёмся в файл ds18b20.c в функцию ds18b20_convert_all и в теле данной функции отправим в шину команду на конвертацию температуры и, используя функцию, которую мы только что добавили, притянем к питанию ножку
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
void ds18b20_convert_all(const OneWireBus * bus) { if (bus) { bool is_present = false; owb_reset(bus, &is_present); owb_write_byte(bus, OWB_ROM_SKIP); owb_write_byte(bus, DS18B20_FUNCTION_TEMP_CONVERT); owb_set_strong_pullup(bus, true); } else { ESP_LOGE(TAG, "bus is NULL"); } |
Далее нам надо как-то дождаться окончания конверсии температуры.
Добавим ниже функцию для этого
1 2 3 4 5 6 7 |
//--------------------------------------------------------------------- float ds18b20_wait_for_conversion(const DS18B20_Info * ds18b20_info) { float elapsed_time = 0.0f; return elapsed_time; } //--------------------------------------------------------------------- |
Конечно же, это пока только заготовка функции.
Объявим для данной функции прототип в заголовочном файле и вызовем её в файле main.c в функции app_main
1 2 |
ds18b20_convert_all(owb); ds18b20_wait_for_conversion(devices[0]); |
Вернёмся в файл ds18b20.c и объявим макрос времени, которое мы будем ждать до считывания температуры из скратчпада, в милисекундах. Данное время мы будем ожидать, если до этого обнаружились устройства на шине с паразитным питанием
1 2 3 4 |
#define DS18B20_FUNCTION_POWER_SUPPLY_READ 0xB4 ///< Determine if a device is using parasitic power //--------------------------------------------------------------------- static const int T_CONV = 750; // maximum conversion time at 12-bit resolution in milliseconds //--------------------------------------------------------------------- |
Выше функции _read_scratchpad добавим функцию для ожидания данного времени
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
//--------------------------------------------------------------------- static float _wait_for_duration(DS18B20_RESOLUTION resolution) { int64_t start_time = esp_timer_get_time(); if (_check_resolution(resolution)) { int divisor = 1 << (DS18B20_RESOLUTION_12_BIT - resolution); ESP_LOGD(TAG, "divisor %d", divisor); float max_conversion_time = (float)T_CONV / (float)divisor; int ticks = ceil(max_conversion_time / portTICK_PERIOD_MS); ESP_LOGD(TAG, "wait for conversion: %.3f ms, %d ticks", max_conversion_time, ticks); // wait at least this maximum conversion time vTaskDelay(ticks); } int64_t end_time = esp_timer_get_time(); return (float)(end_time - start_time) / 1000000.0f; } //--------------------------------------------------------------------- |
В функции ds18b20_wait_for_conversion в случае наличия на шине устройств с паразитным питанием вызовем данную функцию и заранее заготовим блок else для случая, когда таких устройств на шине не обнаружено
1 2 3 4 5 6 7 8 9 10 11 12 |
float elapsed_time = 0.0f; if (_is_init_ds(ds18b20_info)) { if (ds18b20_info->bus->use_parasitic_power) { elapsed_time = _wait_for_duration(ds18b20_info->resolution); } else { } } |
Выше функции _read_scratchpad добавим функцию, которая будет ждать результата конвертирования температуры от датчика, если устройств с паразитным питанием не обнаружено
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 |
//--------------------------------------------------------------------- static float _wait_for_device_signal(const DS18B20_Info * ds18b20_info) { float elapsed_time = 0.0f; if (_check_resolution(ds18b20_info->resolution)) { int divisor = 1 << (DS18B20_RESOLUTION_12_BIT - ds18b20_info->resolution); // allow for 10% overtime float max_conversion_time = (float)T_CONV / (float)divisor * 1.1; int max_conversion_ticks = ceil(max_conversion_time / portTICK_PERIOD_MS); ESP_LOGD(TAG, "wait for conversion: max %.0f ms, %d ticks", max_conversion_time, max_conversion_ticks); // wait for conversion to complete - all devices will pull bus low once complete TickType_t start_ticks = xTaskGetTickCount(); TickType_t duration_ticks = 0; uint8_t status = 0; do { vTaskDelay(1); owb_read_bit(ds18b20_info->bus, &status); duration_ticks = xTaskGetTickCount() - start_ticks; } while (status == 0 && duration_ticks < max_conversion_ticks); elapsed_time = duration_ticks * portTICK_PERIOD_MS; if (duration_ticks >= max_conversion_ticks) { ESP_LOGW(TAG, "conversion timed out"); } else { ESP_LOGD(TAG, "conversion took at most %.0f ms", elapsed_time); } } return elapsed_time; } //--------------------------------------------------------------------- |
Данная функция, хоть и выглядит громоздко, сложности никакой не представляет. Здесь мы в цикле считаем импульсы от датчика.
В функции ds18b20_wait_for_conversion в блоке противного случая вызовем нашу функцию
1 2 3 4 5 |
elapsed_time = _wait_for_duration(ds18b20_info->resolution); } else { elapsed_time = _wait_for_device_signal(ds18b20_info); |
Далее нам нужна будет функция считывания значения температуры. Для начала выше функции _read_scratchpad добавим функцию, которая будет конвертировать температуру из целого числа в удобочитаемое с плавающей точкой в градусах
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
//--------------------------------------------------------------------- static float _decode_temp(uint8_t lsb, uint8_t msb, DS18B20_RESOLUTION resolution) { float result = 0.0f; if (_check_resolution(resolution)) { // masks to remove undefined bits from result static const uint8_t lsb_mask[4] = { ~0x07, ~0x03, ~0x01, ~0x00 }; uint8_t lsb_masked = lsb_mask[resolution - DS18B20_RESOLUTION_9_BIT] & lsb; int16_t raw = (msb << 8) | lsb_masked; result = raw / 16.0f; } else { ESP_LOGE(TAG, "Unsupported resolution %d", resolution); } return result; } //--------------------------------------------------------------------- |
Выше функции ds18b20_check_for_parasite_power добавим функцию считывания температуры с датчика и преобразования результата в значение в градусах
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 |
//--------------------------------------------------------------------- DS18B20_ERROR ds18b20_read_temp(const DS18B20_Info * ds18b20_info, float * value) { DS18B20_ERROR err = DS18B20_ERROR_UNKNOWN; if (_is_init_ds(ds18b20_info)) { uint8_t temp_LSB = 0x00; uint8_t temp_MSB = 0x80; Scratchpad scratchpad = {0}; if ((err = _read_scratchpad(ds18b20_info, &scratchpad, 2)) == DS18B20_OK) { temp_LSB = scratchpad.temperature[0]; temp_MSB = scratchpad.temperature[1]; } // https://github.com/cpetrich/counterfeit_DS18B20#solution-to-the-85-c-problem if (scratchpad.reserved[1] == 0x0c && temp_MSB == 0x05 && temp_LSB == 0x50) { ESP_LOGE(TAG, "Read power-on value (85.0)"); err = DS18B20_ERROR_DEVICE; } float temp = _decode_temp(temp_LSB, temp_MSB, ds18b20_info->resolution); ESP_LOGD(TAG, "temp_LSB 0x%02x, temp_MSB 0x%02x, temp %f", temp_LSB, temp_MSB, temp); if (value) { *value = temp; } } return err; } //--------------------------------------------------------------------- |
Здесь происходит чтение скратчпада с последующим сбором в одну переменную младшего и старшего байта значения температуры. Затем при помощи функции преобразования в удобочитаемый вид значение превращается в значение в градусах и записывается по соответствующему адресу во входном параметре.
Объявим на данную функцию прототип в заголовочном файле и затем в функции app_main файла main.c мы вызовем данную функцию для каждого устройства
1 2 3 4 5 6 7 8 9 |
ds18b20_wait_for_conversion(devices[0]); float readings[MAX_DEVICES] = { 0 }; DS18B20_ERROR errors[MAX_DEVICES] = { 0 }; for (int i = 0; i < num_devices; ++i) { errors[i] = ds18b20_read_temp(devices[i], &readings[i]); } printf("\nTemperature readings (degrees C): sample %d\n", ++sample_count); |
Посчитаем ошибки, если таковые будут иметь место
1 2 3 4 5 6 7 8 9 |
printf("\nTemperature readings (degrees C): sample %d\n", ++sample_count); for (int i = 0; i < num_devices; ++i) { if (errors[i] != DS18B20_OK) { ++errors_count[i]; } printf(" %d: %.1f %d errors\n", i, readings[i], errors_count[i]); } |
У нас также может произойти ситуация, когда мы вообще не обнаружим устройств на шине. Соответственно, в данном случае мы не попадём в бесконечный цикл и провалимся вниз.
Давайте проработаем данный момент.
Перейдём в файл ds18b20.c и выше функции ds18b20_init добавим функцию освобождения памяти, запрошенного под переменную типа структуры со свойствами датчика
1 2 3 4 5 6 7 8 9 10 11 |
/--------------------------------------------------------------------- void ds18b20_free(DS18B20_Info ** ds18b20_info) { if (ds18b20_info != NULL && (*ds18b20_info != NULL)) { ESP_LOGD(TAG, "free %p", *ds18b20_info); free(*ds18b20_info); *ds18b20_info = NULL; } } //--------------------------------------------------------------------- |
Объявим на данную функцию прототип в заголовочном файле и вызовем её в функции app_main файла main.c для каждого датчика
1 2 3 4 5 6 |
printf("\nNo DS18B20 devices detected!\n"); } for (int i = 0; i < num_devices; ++i) { ds18b20_free(&devices[i]); } |
Вообще-то данный код добавлен только из эстетических соображений. По идее, у нас цикл будет нулевым, так как устройств на шине 0.
Проделаем ещё кое какие действия.
Для этого в файле owb.c в самом низу добавим функцию деинициализации шины
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
//--------------------------------------------------------------------- owb_status owb_uninitialize(OneWireBus * bus) { owb_status status = OWB_STATUS_NOT_SET; if (!_is_init(bus)) { status = OWB_STATUS_NOT_INITIALIZED; } else { bus->driver->uninitialize(bus); status = OWB_STATUS_OK; } return status; } //--------------------------------------------------------------------- |
Объявим на данную функцию прототип, вызовем её в функции app_main файла main.c и через секунду перезагрузим контроллер
1 2 3 4 5 6 7 |
ds18b20_free(&devices[i]); } owb_uninitialize(owb); printf("Restarting now.\n"); fflush(stdout); vTaskDelay(1000 / portTICK_PERIOD_MS); esp_restart(); |
Соберём код, прошьём контроллер и посмотрим результат в терминале
Отлично. Попробуем немного нагреть датчик рукой
Значение температуры возрастёт
Подключим остальные два датчика
Перезагрузим контроллер и посмотрим результат в терминале
Попробуем также нагреть рукой датчики
Посмотрим изменения показаний температуры
Показания увеличились.
Попробуем извлечь все датчики из схемы
Перезагрузим контроллер и посмотрим результат в терминале
Мы будем получать ошибки и перезагружаться, то есть нижний код также у нас работает.
Итак, на данном уроке нам удалось считать показания температуры с одного и нескольких датчиков DS18B20 с использование модуля RMT контроллера ESP32.
Всем спасибо за внимание!
Предыдущий урок Программирование МК ESP32 Следующий урок
Недорогие отладочные платы ESP32 можно купить здесь Недорогие отладочные платы ESP32
Недорогие отладочные платы ESP32/ESP32-C3/ESP32-S3 можно купить здесь Недорогие отладочные платы ESP32
Датчик температуры DS18B20 в экране с проводом можно приобрести здесь DS18B20
Логический анализатор 16 каналов можно приобрести здесь
Смотреть ВИДЕОУРОК в YouTube (нажмите на картинку)
Смотреть ВИДЕОУРОК в Дзен (нажмите на картинку)
Добавить комментарий