Продолжаем работать с модулем RMT и в прошлом занятии мы смогли обращаться к устройствам с уже известными ROM-кодами и сверять коды данных устройств с уже известными.
Сегодня мы уже попробуем поработать с нашим устройством, уже заведомо зная о том, что перед нами именно датчик температуры DS18B20.
Повторюсь, что с данными датчиками мы неоднократно работали с использованием других контролеров, работали как с одним датчиком на шине, так и с несколькими, поэтому, благодаря данному факту, добиться нашей цели мы сможем намного быстрее и проще, лишний раз не изучая специфические команды, регистры и тайминги датчика температуры.
На данном занятии мы пока произведём только инициализацию датчика, но а вернее — инициализацию шины под работу с датчиком DS18B20.
Схема урока пока будет такая же, как и в прошлом уроке — с одним датчиком, остальные мы также подключим чуть позже
Проект урока также был сделан из проекта прошлого урока с именем RMT_ONEWIRE_SEARH_KNOWN_ROM и назвали мы его RMT_DS18B20_INIT.
Откроем наш проект в Espressif IDE и для красоты в файле Kconfig.projbuild изменим наименование пункта согласно нашей теме
menu "DS18B20 Configuration"
Также создадим два файла, в котором мы будем работать именно с нашим датчиком — ds18b20.h и ds18b20.c
1 2 3 4 5 6 7 8 9 10 11 12 |
#ifndef MAIN_DS18B20_H_ #define MAIN_DS18B20_H_ //--------------------------------------------------------------------- #include <unistd.h> #include <stdbool.h> #include <string.h> #include <math.h> #include "esp_log.h" #include "owb.h" //--------------------------------------------------------------------- //--------------------------------------------------------------------- #endif /* MAIN_DS18B20_H_ */ |
1 2 3 4 |
#include "ds18b20.h" //--------------------------------------------------------------------- static const char *TAG = "ds18b20.c"; //--------------------------------------------------------------------- |
В файле CMakeLists.txt подключим наш новый файл
set(COMPONENT_SRCS "main.c owb.c ds18b20.c")
Также нашу библиотеку подключим в файле main.h
1 2 |
#include "owb.h" #include "ds18b20.h" |
Также чтобы проект не ругался на false и true, подключим надлежащий заголовочный файл
1 2 |
#include <stdio.h> #include <stdbool.h> |
Попробуем собрать наш проект, и если всё нормально собралось, в файле ds18b20.h объявим перечисляемый тип для различных разрешений значений датчика, а также тип структуры для хранения нужной информации о датчике
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
#include "owb.h" //--------------------------------------------------------------------- typedef enum { DS18B20_RESOLUTION_INVALID = -1, ///< Invalid resolution DS18B20_RESOLUTION_9_BIT = 9, ///< 9-bit resolution, LSB bits 2,1,0 undefined DS18B20_RESOLUTION_10_BIT = 10, ///< 10-bit resolution, LSB bits 1,0 undefined DS18B20_RESOLUTION_11_BIT = 11, ///< 11-bit resolution, LSB bit 0 undefined DS18B20_RESOLUTION_12_BIT = 12, ///< 12-bit resolution (default) } DS18B20_RESOLUTION; //--------------------------------------------------------------------- typedef struct { bool init; ///< True if struct has been initialised, otherwise false bool solo; ///< True if device is intended to be the only one connected to the bus, otherwise false bool use_crc; ///< True if CRC checks are to be used when retrieving information from a device on the bus const OneWireBus * bus; ///< Pointer to 1-Wire bus information relevant to this device OneWireBus_ROMCode rom_code; ///< The ROM code used to address this device on the bus DS18B20_RESOLUTION resolution; ///< Temperature measurement resolution per reading } DS18B20_Info; //--------------------------------------------------------------------- |
В функции app_main файла main.c объявим указатель на массив типов структуры, который мы только что объявили
1 2 3 4 5 |
printf("An error occurred searching for known device: %d", search_status); } } // Create DS18B20 devices on the 1-Wire bus DS18B20_Info * devices[MAX_DEVICES] = {0}; |
Весь код выше мы пока оставим для пущей информативности, хотя можно было бы уже немалую часть удалить.
В файле ds18b20.c добавим функцию для запроса памяти под переменную типа нашей структуры, а также заполнения данной памяти нулями
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
static const char *TAG = "ds18b20.c"; //--------------------------------------------------------------------- DS18B20_Info * ds18b20_malloc(void) { DS18B20_Info * ds18b20_info = malloc(sizeof(*ds18b20_info)); if (ds18b20_info != NULL) { memset(ds18b20_info, 0, sizeof(*ds18b20_info)); ESP_LOGD(TAG, "malloc %p", ds18b20_info); } else { ESP_LOGE(TAG, "malloc failed"); } return ds18b20_info; } //--------------------------------------------------------------------- |
Объявим на данную функцию прототип в заголовочном файле и в функции app_main файла main.c организуем цикл, равный количеству найденных устройств, в котором объявим переменную на тип нашей структуры и с помощью только что добавленной функции запросим память и инициализируем её нулями
1 2 3 4 5 |
DS18B20_Info * devices[MAX_DEVICES] = {0}; for (int i = 0; i < num_devices; ++i) { DS18B20_Info * ds18b20_info = ds18b20_malloc(); // heap allocation } |
Присвоим адрес данного указателя адресу соответствующего элемента массива
1 2 |
DS18B20_Info * ds18b20_info = ds18b20_malloc(); // heap allocation devices[i] = ds18b20_info; |
В файле ds18b20.c добавим функцию первичной инициализации переменно типа структуры информации о датчике
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
static const char *TAG = "ds18b20.c"; //--------------------------------------------------------------------- static void _init_ds(DS18B20_Info * ds18b20_info, const OneWireBus * bus) { if (ds18b20_info != NULL) { ds18b20_info->bus = bus; memset(&ds18b20_info->rom_code, 0, sizeof(ds18b20_info->rom_code)); ds18b20_info->use_crc = false; ds18b20_info->resolution = DS18B20_RESOLUTION_INVALID; ds18b20_info->solo = false; // assume multiple devices unless told otherwise ds18b20_info->init = true; } else { ESP_LOGE(TAG, "ds18b20_info is NULL"); } } //--------------------------------------------------------------------- |
Разрешающую способность пока не устанавливаем.
Ниже функции ds18b20_malloc добавим функцию, которая будет узнавать текущую разрешающую способность датчика
1 2 3 4 5 6 7 |
//--------------------------------------------------------------------- DS18B20_RESOLUTION ds18b20_read_resolution(DS18B20_Info * ds18b20_info) { DS18B20_RESOLUTION resolution = DS18B20_RESOLUTION_INVALID; return resolution; } //--------------------------------------------------------------------- |
Объявим структуру, в которой будет храниться текущее состояние памяти датчика — показание температуры в целом числе и прочие нужные показатели
1 2 3 4 5 6 7 8 9 10 11 12 |
static const char *TAG = "ds18b20.c"; //--------------------------------------------------------------------- typedef struct { uint8_t temperature[2]; // [0] is LSB, [1] is MSB uint8_t trigger_high; uint8_t trigger_low; uint8_t configuration; uint8_t reserved[3]; uint8_t crc; } __attribute__((packed)) Scratchpad; //--------------------------------------------------------------------- |
Ниже функции _init_ds добавим функцию, которая будет проверять, инициализирован ли у нас на данный момент наш датчик
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
//--------------------------------------------------------------------- static bool _is_init_ds(const DS18B20_Info * ds18b20_info) { bool ok = false; if (ds18b20_info != NULL) { if (ds18b20_info->init) { // OK ok = true; } else { ESP_LOGE(TAG, "ds18b20_info is not initialised"); } } else { ESP_LOGE(TAG, "ds18b20_info is NULL"); } return ok; } //--------------------------------------------------------------------- |
В файле ds18b20.h объявим перечисляемый тип, хранящий различные варианты ошибок датчика
1 2 3 4 5 6 7 8 9 10 11 12 13 |
} DS18B20_Info; //--------------------------------------------------------------------- typedef enum { DS18B20_ERROR_UNKNOWN = -1, ///< An unknown error occurred, or the value was not set DS18B20_OK = 0, ///< Success DS18B20_ERROR_DEVICE, ///< A device error occurred DS18B20_ERROR_CRC, ///< A CRC error occurred DS18B20_ERROR_OWB, ///< A One Wire Bus error occurred DS18B20_ERROR_NULL, ///< A parameter or value is NULL } DS18B20_ERROR; //--------------------------------------------------------------------- |
Вернёмся в файл ds18b20.c и ниже функции _is_init_ds добавим функцию, которая будет читать память датчика
1 2 3 4 5 6 7 8 9 10 11 12 13 |
//--------------------------------------------------------------------- static DS18B20_ERROR _read_scratchpad(const DS18B20_Info * ds18b20_info, Scratchpad * scratchpad, size_t count) { if (!scratchpad) { return DS18B20_ERROR_NULL; } DS18B20_ERROR err = DS18B20_ERROR_UNKNOWN; return err; } //--------------------------------------------------------------------- |
В теле данной функции мы установим размер скратчпада, равный размеру структуры
1 2 3 4 5 6 |
DS18B20_ERROR err = DS18B20_ERROR_UNKNOWN; if (ds18b20_info->use_crc) { count = sizeof(Scratchpad); } |
Выше добавим функцию, которая будет определять минимальное значение из двух возможных
1 2 3 4 5 6 |
//--------------------------------------------------------------------- static size_t _min(size_t x, size_t y) { return x > y ? y : x; } //--------------------------------------------------------------------- |
Вернёмся в функцию _read_scratchpad и узнаем, что больше: значение в переменной или размер структуры
1 2 3 |
count = sizeof(Scratchpad); } count = _min(sizeof(Scratchpad), count); |
Если мы в режиме отладке, то выведем размер в терминал
1 2 3 |
count = _min(sizeof(Scratchpad), count); ESP_LOGD(TAG, "scratchpad read: CRC %d, count %d", ds18b20_info->use_crc, count); |
Выше добавим функцию, которая будет возвращать адрес нашего датчика, вернее структуры со свойствами датчика, если он (адрес) существует
1 2 3 4 5 6 7 |
//--------------------------------------------------------------------- static bool _address_device(const DS18B20_Info * ds18b20_info) { bool present = false; return present; } //--------------------------------------------------------------------- |
В файле owb.c исправим для начала ошибку в функции owb_verify_rom. Данная ошибка была допущена, когда я писал код во время видеоурока, в текстовой версии урока данной ошибки нет. Я забыл после строки
_search(bus, &state, &is_found);
добавить условие, был просто блок.
Исправим данный недочёт
1 2 3 |
_search(bus, &state, &is_found); if (is_found) |
Ниже данной функции добавим функцию перезагрузки устройства, которая будет использовать уже готовую функцию
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
//--------------------------------------------------------------------- owb_status owb_reset(const OneWireBus * bus, bool * a_device_present) { owb_status status = OWB_STATUS_NOT_SET; if (!bus || !a_device_present) { status = OWB_STATUS_PARAMETER_NULL; } else if(!_is_init(bus)) { status = OWB_STATUS_NOT_INITIALIZED; } else { status = bus->driver->reset(bus, a_device_present); } return status; } //--------------------------------------------------------------------- |
Объявим на данную функцию прототип в заголовочном файле, вернёмся в файл ds18b20.c и в функции _address_device сбросим наш датчик, если он инициализирован, если не инициализирован, выведем соответствующее сообщение в терминале
1 2 3 4 5 6 7 8 9 |
bool present = false; if (_is_init_ds(ds18b20_info)) { owb_reset(ds18b20_info->bus, &present); } else { ESP_LOGE(TAG, "ds18b20 device not responding"); } |
В файле owb.c ниже функции owb_read_bytes добавим функцию, которая будет отправлять байт в шину
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
//--------------------------------------------------------------------- owb_status owb_write_byte(const OneWireBus * bus, uint8_t data) { 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 { ESP_LOGD(TAG, "owb_write_byte: %02x", data); bus->driver->write_bits(bus, data, 8); status = OWB_STATUS_OK; } return status; } //--------------------------------------------------------------------- |
Ниже добавим функцию, которая будет отправлять в шину несколько байтов
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 |
//--------------------------------------------------------------------- owb_status owb_write_bytes(const OneWireBus * bus, const uint8_t * buffer, size_t len) { owb_status status = OWB_STATUS_NOT_SET; if (!bus || !buffer) { status = OWB_STATUS_PARAMETER_NULL; } else if (!_is_init(bus)) { status = OWB_STATUS_NOT_INITIALIZED; } else { ESP_LOGD(TAG, "owb_write_bytes, len %d:", len); ESP_LOG_BUFFER_HEX_LEVEL(TAG, buffer, len, ESP_LOG_DEBUG); for (int i = 0; i < len; i++) { bus->driver->write_bits(bus, buffer[i], 8); } status = OWB_STATUS_OK; } return status; } //--------------------------------------------------------------------- |
Ещё ниже добавим функцию, которая будет отправлять в шину ROM-код устройства для того, чтобы мы могли обратиться к нужному датчику
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
//--------------------------------------------------------------------- owb_status owb_write_rom_code(const OneWireBus * bus, OneWireBus_ROMCode rom_code) { 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 { owb_write_bytes(bus, (uint8_t*)&rom_code, sizeof(rom_code)); status = OWB_STATUS_OK; } return status; } //--------------------------------------------------------------------- |
Объявим на данные три функции прототипы в заголовочном файле owb.h и также в нём добавим макросы ещё двух команд — команды пропуска чтения ROM-кода и команды отправки кода в случае наличия нескольких устройств
1 2 3 |
#define OWB_ROM_READ 0x33 ///< Read device ROM (single device on bus only) #define OWB_ROM_MATCH 0x55 ///< Address a specific device on the bus by ROM #define OWB_ROM_SKIP 0xCC ///< Address all devices on the bus simultaneously |
Вернёмся в файл ds18b20.c и в функции _address_device, если у нас только одно устройство, отправим соответствующую команду в шину
1 2 3 4 5 6 7 8 |
owb_reset(ds18b20_info->bus, &present); if (present) { if (ds18b20_info->solo) { owb_write_byte(ds18b20_info->bus, OWB_ROM_SKIP); } } |
В противном случае мы отправляем другую команду и отправляем код устройства
1 2 3 4 5 6 7 |
owb_write_byte(ds18b20_info->bus, OWB_ROM_SKIP); } else { owb_write_byte(ds18b20_info->bus, OWB_ROM_MATCH); owb_write_rom_code(ds18b20_info->bus, ds18b20_info->rom_code); } |
Добавим макрос с командой чтения скратчпада
1 2 3 |
static const char *TAG = "ds18b20.c"; //--------------------------------------------------------------------- #define DS18B20_FUNCTION_SCRATCHPAD_READ 0xBE ///< Read 9 bytes of data (including CRC) from the device scratchpad |
В файле owb.h добавим прототип для функции генерации контрольной суммы
1 2 |
owb_status owb_use_crc(OneWireBus * bus, bool use_crc); uint8_t owb_crc8_bytes(uint8_t crc, const uint8_t * data, size_t len); |
Также добавим прототип для функции чтения нескольких байтов
1 2 |
char * owb_string_from_rom_code(OneWireBus_ROMCode rom_code, char * buffer, size_t len); owb_status owb_read_bytes(const OneWireBus * bus, uint8_t * buffer, unsigned int len); |
Вернёмся в файл ds18b20.c и в функции _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 38 39 40 41 42 43 44 45 46 47 48 |
ESP_LOGD(TAG, "scratchpad read: CRC %d, count %d", ds18b20_info->use_crc, count); if (_address_device(ds18b20_info)) { // read scratchpad if (owb_write_byte(ds18b20_info->bus, DS18B20_FUNCTION_SCRATCHPAD_READ) == OWB_STATUS_OK) { if (owb_read_bytes(ds18b20_info->bus, (uint8_t *)scratchpad, count) == OWB_STATUS_OK) { ESP_LOG_BUFFER_HEX_LEVEL(TAG, scratchpad, count, ESP_LOG_DEBUG); err = DS18B20_OK; if (!ds18b20_info->use_crc) { // Without CRC, or partial read: ESP_LOGD(TAG, "No CRC check"); bool is_present = false; owb_reset(ds18b20_info->bus, &is_present); // terminate early } else { // With CRC: if (owb_crc8_bytes(0, (uint8_t *)scratchpad, sizeof(*scratchpad)) != 0) { ESP_LOGE(TAG, "CRC failed"); err = DS18B20_ERROR_CRC; } else { ESP_LOGD(TAG, "CRC ok"); } } } else { ESP_LOGE(TAG, "owb_read_bytes failed"); err = DS18B20_ERROR_OWB; } } else { ESP_LOGE(TAG, "owb_write_byte failed"); err = DS18B20_ERROR_OWB; } } else { err = DS18B20_ERROR_DEVICE; } |
Выше функции _address_device добавим функцию, которая будет узнавать, находится ли разрядность возвращаемого значения температуры датчика в пределах от 9 до 12 бит
1 2 3 4 5 6 |
//--------------------------------------------------------------------- static bool _check_resolution(DS18B20_RESOLUTION resolution) { return (resolution >= DS18B20_RESOLUTION_9_BIT) && (resolution <= DS18B20_RESOLUTION_12_BIT); } //--------------------------------------------------------------------- |
В функции ds18b20_read_resolution прочитаем скратчпад и проверим, что наша разрядность находится в необходимых пределах
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
DS18B20_RESOLUTION resolution = DS18B20_RESOLUTION_INVALID; if (_is_init_ds(ds18b20_info)) { // read scratchpad up to and including configuration register Scratchpad scratchpad = {0}; _read_scratchpad(ds18b20_info, &scratchpad, offsetof(Scratchpad, configuration) - offsetof(Scratchpad, temperature) + 1); resolution = ((scratchpad.configuration >> 5) & 0x03) + DS18B20_RESOLUTION_9_BIT; if (!_check_resolution(resolution)) { ESP_LOGE(TAG, "invalid resolution read from device: 0x%02x", scratchpad.configuration); resolution = DS18B20_RESOLUTION_INVALID; } else { ESP_LOGD(TAG, "Resolution read as %d", resolution); } } |
Ниже добавим функцию инициализации датчика, если он только один на шине
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
void ds18b20_init_solo(DS18B20_Info * ds18b20_info, const OneWireBus * bus) { if (ds18b20_info != NULL) { _init_ds(ds18b20_info, bus); ds18b20_info->solo = true; // ROM code not required ds18b20_info->resolution = ds18b20_read_resolution(ds18b20_info); } else { ESP_LOGE(TAG, "ds18b20_info is NULL"); } } //--------------------------------------------------------------------- |
Объявим на данную функцию прототип в функции app_main файла main.c вызовем нашу функцию инициализации в случае присутствия на шине только одного датчика
1 2 3 4 5 6 |
devices[i] = ds18b20_info; if (num_devices == 1) { printf("Single device optimisations enabled\n"); ds18b20_init_solo(ds18b20_info, owb); // only one device on bus } |
Попробуем собрать код и прошить контроллер и посмотрим, что у нас в терминале
Мы видим, что у нас хотя бы ошибок нет.
Посмотрим теперь, что в логическом анализе
Там также всё хорошо.
Подключим ещё один датчик
Проверять код нет смысла, так как мы ещё не написали его для нескольких датчиков.
Поэтом идём в файл ds18b20.c и выше функции ds18b20_init_solo добавим ещё одну подобную функцию, которая будет инициализировать уже несколько устройств
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
//--------------------------------------------------------------------- void ds18b20_init(DS18B20_Info * ds18b20_info, const OneWireBus * bus, OneWireBus_ROMCode rom_code) { if (ds18b20_info != NULL) { _init_ds(ds18b20_info, bus); ds18b20_info->rom_code = rom_code; // read current resolution from device as it may not be power-on or factory default ds18b20_info->resolution = ds18b20_read_resolution(ds18b20_info); } else { ESP_LOGE(TAG, "ds18b20_info is NULL"); } } //--------------------------------------------------------------------- |
Данная функция по большому счёту отличается от нижней лишь тем, что мы здесь не выставляем параметр присутствия только одного устройства в переменной структуры.
Объявим на данную функцию прототип в заголовочном файле и в функции app_main файла main.c в случае присутствия нескольких устройств на шине вызовем нашу функцию
1 2 3 4 5 6 |
ds18b20_init_solo(ds18b20_info, owb); // only one device on bus } else { ds18b20_init(ds18b20_info, owb, device_rom_codes[i]); // associate with bus and device } |
Соберём код, прошьём контроллер и посмотрим результат в терминале
Код отработал правильно. Ошибок нет, просто у нас пока отсутствует третий датчик, а проверка выше у нас была трёх датчиков. Подключим третий
Перезагрузим контроллер и посмотрим результат сначала в терминале
А затем и в программе логического анализа
Здесь тоже всё правильно, команды теперь отправляются без пропуска ROM-кода.
В файле ds18b20.c выше функции ds18b20_read_resolution добавим функцию установки использования или неиспользования контрольной суммы
1 2 3 4 5 6 7 8 9 10 |
//--------------------------------------------------------------------- void ds18b20_use_crc(DS18B20_Info * ds18b20_info, bool use_crc) { if (_is_init_ds(ds18b20_info)) { ds18b20_info->use_crc = use_crc; ESP_LOGD(TAG, "use_crc %d", ds18b20_info->use_crc); } } //--------------------------------------------------------------------- |
Выше функции ds18b20_init добавим функцию, которая будет устанавливать разрядность величины температуры
1 2 3 4 5 6 7 |
//--------------------------------------------------------------------- bool ds18b20_set_resolution(DS18B20_Info * ds18b20_info, DS18B20_RESOLUTION resolution) { bool result = false; return result; } //--------------------------------------------------------------------- |
В данной функции мы аналогично прочитаем скратчпад
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
bool result = false; if (_is_init_ds(ds18b20_info)) { if (_check_resolution(ds18b20_info->resolution)) { // read scratchpad up to and including configuration register Scratchpad scratchpad = {0}; _read_scratchpad(ds18b20_info, &scratchpad, offsetof(Scratchpad, configuration) - offsetof(Scratchpad, temperature) + 1); } else { ESP_LOGE(TAG, "Unsupported resolution %d", resolution); } } |
Далее запишем в поле переменной пока начальное значение
1 2 3 4 5 6 |
_read_scratchpad(ds18b20_info, &scratchpad, offsetof(Scratchpad, configuration) - offsetof(Scratchpad, temperature) + 1); // modify configuration register to set resolution uint8_t value = (((resolution - 1) & 0x03) << 5) | 0x1f; scratchpad.configuration = value; ESP_LOGD(TAG, "configuration value 0x%02x", value); |
Только теперь для установки разрешающей способности датчика нам потребуется функция, которая будет скратчпад уже писать, то есть отправлять в шину значения, которые будут в него прописываться.
Выше функции ds18b20_malloc добавим пока заготовку такой функции
1 2 3 4 5 6 7 |
//--------------------------------------------------------------------- static bool _write_scratchpad(const DS18B20_Info * ds18b20_info, const Scratchpad * scratchpad, bool verify) { bool result = false; return result; } //--------------------------------------------------------------------- |
Объявим макрос команды записи скратчпада
1 2 3 |
static const char *TAG = "ds18b20.c"; //--------------------------------------------------------------------- #define DS18B20_FUNCTION_SCRATCHPAD_WRITE 0x4E ///< Write 3 bytes of data to the device scratchpad at positions 2, 3 and 4 |
В функции _write_scratchpad отравим данную команду в шину, и затем отправим 3 байта, которые находятся по указателю во входном параметре, вернее в поле trigger_high, которое соответствует тому месту в памяти датчика, где находятся необходимые значения для разрядности температуры. После этого мы считаем данное значение обратно из датчика и сравним его со значением, находящимся в поле, тем самым проверив, правильно ли у нас всё прописалось в память датчика. Если нет, то выведем ошибку в терминал
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 |
bool result = false; if (_is_init_ds(ds18b20_info)) { if (_address_device(ds18b20_info)) { ESP_LOGD(TAG, "scratchpad write 3 bytes:"); ESP_LOG_BUFFER_HEX_LEVEL(TAG, &scratchpad->trigger_high, 3, ESP_LOG_DEBUG); owb_write_byte(ds18b20_info->bus, DS18B20_FUNCTION_SCRATCHPAD_WRITE); owb_write_bytes(ds18b20_info->bus, (uint8_t *)&scratchpad->trigger_high, 3); result = true; if (verify) { Scratchpad read = {0}; if (_read_scratchpad(ds18b20_info, &read, offsetof(Scratchpad, configuration) + 1) == DS18B20_OK) { if (memcmp(&scratchpad->trigger_high, &read.trigger_high, 3) != 0) { ESP_LOGE(TAG, "scratchpad verify failed: " "wrote {0x%02x, 0x%02x, 0x%02x}, " "read {0x%02x, 0x%02x, 0x%02x}", scratchpad->trigger_high, scratchpad->trigger_low, scratchpad->configuration, read.trigger_high, read.trigger_low, read.configuration); result = false; } } else { ESP_LOGE(TAG, "read scratchpad failed"); result = false; } } } } |
То есть у нас не совсем полноценная функция записи скратчпада, которая умеет писать только 3 байта, которые требуются для установки значения разрядности. А полноценная нам и не нужна, так как кроме этого нам отправлять в датчик нечего.
Вернёмся в функцию ds18b20_set_resolution и отправим значение разрешающей способности в датчик, затем, если всё удачно отправилось, то запишем данное разрешение в соответствующее поле переменной структуры свойств датчика, а если нет, то прочитаем разрешающую способность из датчика и присвоим тогда его значение полю
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
ESP_LOGD(TAG, "configuration value 0x%02x", value); // write bytes 2, 3 and 4 of scratchpad result = _write_scratchpad(ds18b20_info, &scratchpad, /* verify */ true); if (result) { ds18b20_info->resolution = resolution; ESP_LOGD(TAG, "Resolution set to %d bits", (int)resolution); } else { // Resolution change failed - update the info resolution with the value read from configuration ds18b20_info->resolution = ds18b20_read_resolution(ds18b20_info); ESP_LOGW(TAG, "Resolution consistency lost - refreshed from device: %d", ds18b20_info->resolution); } |
Объявим прототипы на наши функции ds18b20_use_crc и ds18b20_set_resolution в заголовочный файл, а затем в файле main.c объявим макрос со значением желаемой разрешающей способности
1 2 |
#define MAX_DEVICES (8) #define DS18B20_RESOLUTION (DS18B20_RESOLUTION_12_BIT) |
В функции app_main вызовем наши функции
1 2 3 4 |
ds18b20_init(ds18b20_info, owb, device_rom_codes[i]); // associate with bus and device } ds18b20_use_crc(ds18b20_info, true); // enable CRC check on all reads ds18b20_set_resolution(ds18b20_info, DS18B20_RESOLUTION); |
Извлечём пока дополнительные два датчика, соберём код, прошьём контроллер и посмотрим результат в терминале
Ошибок не замечено.
Посмотрим результат в логическом анализе
Вроде бы тоже всё нормально. Судить точно сложно, так как специфические коды программой не распознаются.
Добавим остальные датчики обратно в схему и перезагрузим контроллер
Ошибок также не обнаружено.
Для полноправной инициализации давайте также проверим факт наличия на шине устройств с паразитным питанием.
Для этого в файле ds18b20.c выше функции ds18b20_init добавим функцию, которая поможет нам решить данную задачу
1 2 3 4 5 6 7 |
//--------------------------------------------------------------------- DS18B20_ERROR ds18b20_check_for_parasite_power(const OneWireBus * bus, bool * present) { DS18B20_ERROR err = DS18B20_ERROR_UNKNOWN; return err; } //--------------------------------------------------------------------- |
В теле данной функции произведём сброс устройства и отправим байт с командой пропуска чтения ROM-кода
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
DS18B20_ERROR err = DS18B20_ERROR_UNKNOWN; ESP_LOGD(TAG, "ds18b20_check_for_parasite_power"); if (bus) { bool reset_present; if ((err = owb_reset(bus, &reset_present)) == DS18B20_OK) { ESP_LOGD(TAG, "owb_reset OK"); if ((err = owb_write_byte(bus, OWB_ROM_SKIP)) == DS18B20_OK) { ESP_LOGD(TAG, "owb_write_byte(ROM_SKIP) OK"); } } } else { ESP_LOGE(TAG, "bus is NULL"); err = DS18B20_ERROR_NULL; } |
Объявим макрос для команды определения, использует ли устройство паразитное питание
1 2 |
#define DS18B20_FUNCTION_SCRATCHPAD_WRITE 0x4E ///< Write 3 bytes of data to the device scratchpad at positions 2, 3 and 4 #define DS18B20_FUNCTION_POWER_SUPPLY_READ 0xB4 ///< Determine if a device is using parasitic power |
Вот выдержка из технической документации с кодами команд
Вернемся в функцию и передадим в шину данную команду
1 2 3 4 5 6 |
ESP_LOGD(TAG, "owb_write_byte(ROM_SKIP) OK"); if ((err = owb_write_byte(bus, DS18B20_FUNCTION_POWER_SUPPLY_READ)) == DS18B20_OK) { // Parasitic-powered devices will pull the bus low during read time slot ESP_LOGD(TAG, "owb_write_byte(POWER_SUPPLY_READ) OK"); } |
Перейдём в файл owb.c и выше функции owb_read_bytes добавим функцию чтения одного бита. Она нужна нам для определения уровня
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
//--------------------------------------------------------------------- owb_status owb_read_bit(const OneWireBus * bus, uint8_t * out) { owb_status status = OWB_STATUS_NOT_SET; if (!bus || !out) { status = OWB_STATUS_PARAMETER_NULL; } else if (!_is_init(bus)) { status = OWB_STATUS_NOT_INITIALIZED; } else { bus->driver->read_bits(bus, out, 1); ESP_LOGD(TAG, "owb_read_bit: %02x", *out); status = OWB_STATUS_OK; } return status; } //--------------------------------------------------------------------- |
Объявим прототип для данной функции в заголовочном файле и в функции ds18b20_check_for_parasite_power файла ds18b20.c, вызовем функцию чтения бита и запишем его значение в переменную present по указателю. В принципе, тип bool, как и бит имеет только два значения
1 2 3 4 5 6 7 8 9 10 |
ESP_LOGD(TAG, "owb_write_byte(POWER_SUPPLY_READ) OK"); uint8_t value = 0; if ((err = owb_read_bit(bus, &value)) == DS18B20_OK) { ESP_LOGD(TAG, "owb_read_bit OK: 0x%02x", value); if (present) { *present = !(bool)(value & 0x01u); } } |
Объявим на данную функцию прототип и в функции app_main файла main.c с помощью данной функции узнаем, есть ли у нас устройства с паразитным питанием
1 2 3 4 5 6 7 8 9 10 |
ds18b20_set_resolution(ds18b20_info, DS18B20_RESOLUTION); } // Check for parasitic-powered devices bool parasitic_power = false; ds18b20_check_for_parasite_power(owb, ¶sitic_power); if (parasitic_power) { printf("Parasitic-powered devices detected"); } |
В режиме паразитной мощности устройства не могут ответить, когда преобразование завершено, поэтому ожидание преобразования температуры должно выполняться в течение заданного времени.
В файле owb.c выше функции owb_read_rom добавим функцию, которая соответствующему полю переменной структуры свойств шины присвоит значение использования или неиспользования режима наличия устройства на шине с паразитным питанием
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
//--------------------------------------------------------------------- owb_status owb_use_parasitic_power(OneWireBus * bus, bool use_parasitic_power) { 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 { bus->use_parasitic_power = use_parasitic_power; ESP_LOGD(TAG, "use_parasitic_power %d", bus->use_parasitic_power); status = OWB_STATUS_OK; } return status; } //--------------------------------------------------------------------- |
Объявим на данную функцию прототип в заголовочном файле и в функции app_main файла main.c вызовем данную функцию в том случае, если такое устройство на шине было обнаружено
1 2 3 4 |
printf("Parasitic-powered devices detected"); } owb_use_parasitic_power(owb, parasitic_power); |
Соберём код, прошьём контроллер и посмотрим результат в терминале
Результат тот же, что и ранее, значит никаких устройств с паразитным питанием на нашей шине нет.
В программе логического анализа мы видим, что команда наша в шину передана
После передачи команды шина осталась в высоком состоянии, значит данных устройств нет
Итак, на данном уроке мы написали инициализацию датчика DS18B20 с использованием модуля RMT контроллера. Осталось нам теперь научиться читать значения температуры со всех датчиков на шине.
Всем спасибо за внимание!
Предыдущий урок Программирование МК ESP32 Следующий урок
Недорогие отладочные платы ESP32 можно купить здесь Недорогие отладочные платы ESP32
Недорогие отладочные платы ESP32/ESP32-C3/ESP32-S3 можно купить здесь Недорогие отладочные платы ESP32
Датчик температуры DS18B20 в экране с проводом можно приобрести здесь DS18B20
Логический анализатор 16 каналов можно приобрести здесь
Смотреть ВИДЕОУРОК в RuTube (нажмите на картинку)
Смотреть ВИДЕОУРОК в YouTube (нажмите на картинку)
Смотреть ВИДЕОУРОК в Дзен (нажмите на картинку)
Добавить комментарий