Продолжаем работать с модулем RMT и в прошлом занятии нам удалось добиться полноправного поиска устройства, причём мы также узнали, сколько у нас на шине присутствует устройств, работающих с протоколом 1-Wire, если таких устройств будет более одного.
Задача данного урока — обращение к устройству с уже известным ROM-кодом, так как если коды известны, то нет смысла из каждый раз перебирать по сложному алгоритму.
Схема урока пока будет такая же, как и в прошлом уроке — с одним датчиком, остальные мы также подключим чуть позже
И проект также был выполнен из проекта прошлого урока с именем RMT_ONEWIRE_SEARH_ROM и получил новое имя RMT_ONEWIRE_SEARH_KNOWN_ROM.
Откроем наш проект в Espressiv IDE, откроем файл owb.h и ниже функции _init добавим функцию, которая будет читать ROM-код устройства
1 2 3 4 5 6 7 |
//--------------------------------------------------------------------- owb_status owb_read_rom(const OneWireBus * bus, OneWireBus_ROMCode *rom_code) { owb_status status = OWB_STATUS_NOT_SET; return status; } //--------------------------------------------------------------------- |
Объявим на данную функцию прототип в соответствующем заголовочном файле owb.h, а также в данном файле объявим макрос для кода команды чтения ROM-кода и макрос с длиной ROM-кода
1 2 3 4 5 |
#define OWB_ROM_SEARCH 0xF0 ///< Perform Search ROM cycle to identify devices on the bus #define OWB_ROM_READ 0x33 ///< Read device ROM (single device on bus only) //--------------------------------------------------------------------- #define OWB_ROM_CODE_STRING_LENGTH (17) ///< Typical length of OneWire bus ROM ID as ASCII hex string, including null terminator //--------------------------------------------------------------------- |
Перейдём в функцию app_main файла main.c и в том случае, если у нас на шине только одно устройство, вызовем нашу функцию
1 2 3 4 5 6 |
printf("Found %d device%s\n", num_devices, num_devices == 1 ? "" : "s"); if (num_devices == 1) { OneWireBus_ROMCode rom_code; owb_status status = owb_read_rom(owb, &rom_code); } |
Затем отобразим наш ROM-код в терминале
1 2 3 4 5 6 7 8 9 10 11 |
owb_status status = owb_read_rom(owb, &rom_code); if (status == OWB_STATUS_OK) { char rom_code_s[OWB_ROM_CODE_STRING_LENGTH]; owb_string_from_rom_code(rom_code, rom_code_s, sizeof(rom_code_s)); printf("Single device %s present\n", rom_code_s); } else { printf("An error occurred reading ROM code: %d", status); } |
Только все мы прекрасно понимаем, что ничего у нас пока не прочитается и не отобразится, так как функция чтения — это пока только функция-заготовка.
Поэтому возвращаемся в функцию owb_read_rom файла owb.c и забьём нулями память, отведённую под переменную типа структуры свойств ROM-кода
1 2 3 |
owb_status status = OWB_STATUS_NOT_SET; memset(rom_code, 0, sizeof(OneWireBus_ROMCode)); |
Затем узнаем, не пустой ли адрес свойств шина и ROM-кода, а также инициализировано ли устройство. Если нет, то уходим из функции с соответствующим статусом, а если всё нормально, то создаём переменную булевого типа и вызываем функцию перезагрузки шины
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
memset(rom_code, 0, sizeof(OneWireBus_ROMCode)); if (!bus || !rom_code) { status = OWB_STATUS_PARAMETER_NULL; } else if (!_is_init(bus)) { status = OWB_STATUS_NOT_INITIALIZED; } else { bool is_present; bus->driver->reset(bus, &is_present); } |
Если устройство присутствует, то попытаемся отправить в шину команду чтения ROM-кода
1 2 3 4 5 6 |
bus->driver->reset(bus, &is_present); if (is_present) { uint8_t value = OWB_ROM_READ; bus->driver->write_bits(bus, value, 8); } |
Для удобства считывания всего ROM-кода мы ниже функции _read_bits добавим ещё одну функцию, которая будет читать сразу несколько байт из шины
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 |
//--------------------------------------------------------------------- owb_status owb_read_bytes(const OneWireBus * bus, uint8_t * buffer, unsigned int 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 { for (int i = 0; i < len; ++i) { uint8_t out; bus->driver->read_bits(bus, &out, 8); buffer[i] = out; } ESP_LOGD(TAG, "owb_read_bytes, len %d:", len); ESP_LOG_BUFFER_HEX_LEVEL(TAG, buffer, len, ESP_LOG_DEBUG); status = OWB_STATUS_OK; } return status; } //--------------------------------------------------------------------- |
В данной функции мы аналогично проверили наличие адресов и факт инициализации шины, а если всё нормально, то в цикле считываем все байты.
Вернёмся в функцию owb_read_rom и попытаемся прочитать наш ROM-код
1 2 |
bus->driver->write_bits(bus, value, 8); owb_read_bytes(bus, rom_code->bytes, sizeof(OneWireBus_ROMCode)); |
Ниже функции _calc_crc добавим функцию, которая будет считать контрольную суммы на целый блок байтов
1 2 3 4 5 6 7 8 9 10 11 |
static uint8_t _calc_crc_block(uint8_t crc, const uint8_t * buffer, size_t len) { do { crc = _calc_crc(crc, *buffer++); ESP_LOGD(TAG, "buffer 0x%02x, crc 0x%02x, len %d", (uint8_t)*(buffer - 1), (int)crc, (int)len); } while (--len > 0); return crc; } //--------------------------------------------------------------------- |
Ниже функции owb_crc8_byte добавим функцию, которая будет возвращать контрольную сумму на массив определенной длины
1 2 3 4 5 6 |
//--------------------------------------------------------------------- uint8_t owb_crc8_bytes(uint8_t crc, const uint8_t * data, size_t len) { return _calc_crc_block(crc, data, len); } //--------------------------------------------------------------------- |
Вернёмся в функцию owb_read_rom и в случае, если в свойствах шины установлен параметр проверки контрольной суммы, прочитаем контрольную сумму
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
owb_read_bytes(bus, rom_code->bytes, sizeof(OneWireBus_ROMCode)); if (bus->use_crc) { if (owb_crc8_bytes(0, rom_code->bytes, sizeof(OneWireBus_ROMCode)) != 0) { ESP_LOGE(TAG, "CRC failed"); memset(rom_code->bytes, 0, sizeof(OneWireBus_ROMCode)); status = OWB_STATUS_CRC_FAILED; } else { status = OWB_STATUS_OK; } } else { status = OWB_STATUS_OK; } |
Запишем ROM-код в символьный массив и в случае режима отладки выведем его в терминал
1 2 3 4 5 6 7 8 9 10 |
status = OWB_STATUS_OK; } } else { status = OWB_STATUS_OK; } char rom_code_s[OWB_ROM_CODE_STRING_LENGTH]; owb_string_from_rom_code(*rom_code, rom_code_s, sizeof(rom_code_s)); ESP_LOGD(TAG, "rom_code %s", rom_code_s); |
Выйдем из условия присутствия устройства на шине и в противном случае выведем сообщение об ошибке в терминал
1 2 3 4 5 6 7 |
ESP_LOGD(TAG, "rom_code %s", rom_code_s); } else { status = OWB_STATUS_DEVICE_NOT_RESPONDING; ESP_LOGE(TAG, "device not responding"); } |
Попробуем собрать код, прошить контроллер и посмотрим результат работы в терминале
Всё считалось и устройство определилось.
Посмотрим также исполнение нашей команды в логическом анализе
Здесь также всё отлично распознаётся.
Добавим ещё один датчик в схему
Если мы перезагрузим устройство, то наш новый код уже не сработает, так как он рассчитан только на одно устройство
Зато теперь мы знаем код второго устройства.
В случае наличия более одного устройства к определённому устройству нам теперь придётся обращаться по его коду.
В функции app_main файла main.c мы добавим блок с противным случаем (устройство не одно), в котором в комментарии напишем ROM-код пока первого устройства для удобства его записи в переменную типа структуры свойств ROM-кода, которую мы объявим ниже
1 2 3 4 5 6 7 8 9 10 11 12 13 |
printf("An error occurred reading ROM code: %d", status); } } else { // Search for a known ROM code (LSB first): // 0xb60416847630ff28 OneWireBus_ROMCode known_device1 = { .fields.family = { 0x28 }, .fields.serial_number = { 0xff, 0x30, 0x76, 0x84, 0x16, 0x04 }, .fields.crc = { 0xb6 }, }; } |
Занесём ROM-код в символьный массив, создадим переменную присутствия устройства булевого типа и пока присвоим ей значение false
1 2 3 4 5 |
.fields.crc = { 0xb6 }, }; char rom_code_s1[OWB_ROM_CODE_STRING_LENGTH]; owb_string_from_rom_code(known_device1, rom_code_s1, sizeof(rom_code_s1)); bool is_present = false; |
В файле owb.c ниже функции owb_read_rom добавим функцию, которая будет проверять ROM-код
1 2 3 4 5 6 7 |
//--------------------------------------------------------------------- owb_status owb_verify_rom(const OneWireBus * bus, OneWireBus_ROMCode rom_code, bool * is_present) { owb_status status = OWB_STATUS_NOT_SET; return status; } //--------------------------------------------------------------------- |
В теле данной функции опять же в случае наличия действующих адресов, а также факта инициализации шины объявим и инициализируем переменную типа структуры состояния поиска устройства
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
owb_status status = OWB_STATUS_NOT_SET; bool result = false; if (!bus || !is_present) { status = OWB_STATUS_PARAMETER_NULL; } else if (!_is_init(bus)) { status = OWB_STATUS_NOT_INITIALIZED; } else { OneWireBus_SearchState state = { .rom_code = rom_code, .last_discrepancy = 64, .last_device_flag = false, }; } |
Вызовем функцию поиска устройства на шине
1 2 3 4 |
.last_device_flag = false, }; bool is_found = false; _search(bus, &state, &is_found); |
Если устройство нашлось, то сравним считанный ROM-код с образцовым, если байты при сравнении совпадут, то выйдем из функции с результатом true
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
_search(bus, &state, &is_found); if (is_found) { result = true; for (int i = 0; i < sizeof(state.rom_code.bytes) && result; ++i) { result = rom_code.bytes[i] == state.rom_code.bytes[i]; ESP_LOGD(TAG, "%02x %02x", rom_code.bytes[i], state.rom_code.bytes[i]); } ESP_LOGD(TAG, "state.last_discrepancy %d, state.last_device_flag %d, is_found %d", state.last_discrepancy, state.last_device_flag, is_found); ESP_LOGD(TAG, "rom code %sfound", result ? "" : "not "); *is_present = result; status = OWB_STATUS_OK; } |
Объявим на нашу функцию прототип в заголовочном файле и в функции app_main файла main.c в случае наличия устройства выведем в терминал соответствующее сообщение, причём если коды совпали, то выведем сообщение, что данное устройство присутствует, а если нет — то не присутствует. Также в противном случае выведем сообщение о том, что устройства на шине вообще не обнаружены
1 2 3 4 5 6 7 8 9 10 |
bool is_present = false; owb_status search_status = owb_verify_rom(owb, known_device1, &is_present); if (search_status == OWB_STATUS_OK) { printf("Device %s is %s\n", rom_code_s1, is_present ? "present" : "not present"); } else { printf("An error occurred searching for known device: %d", search_status); } |
То же самое проделаем и для кода второго устройства
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
printf("An error occurred searching for known device: %d", search_status); } // Search for a known ROM code (LSB first): // 0x625d53c70664ff28 OneWireBus_ROMCode known_device2 = { .fields.family = { 0x28 }, .fields.serial_number = { 0xff, 0x64, 0x06, 0xc7, 0x53, 0x5d }, .fields.crc = { 0x62 }, }; char rom_code_s2[OWB_ROM_CODE_STRING_LENGTH]; owb_string_from_rom_code(known_device2, rom_code_s2, sizeof(rom_code_s2)); is_present = false; search_status = owb_verify_rom(owb, known_device2, &is_present); if (search_status == OWB_STATUS_OK) { printf("Device %s is %s\n", rom_code_s2, is_present ? "present" : "not present"); } else { printf("An error occurred searching for known device: %d", search_status); } |
Соберём код, прошьём контроллер и посмотрим результат в терминале
Отлично!
Добавим ещё один датчик в схему
Соберём код, прошьём контроллер и узнаем ROM-код оставшегося датчика
Напишем подобный код также и для него
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
printf("An error occurred searching for known device: %d", search_status); } // Search for a known ROM code (LSB first): // 0xad6629c70664ff28 OneWireBus_ROMCode known_device3 = { .fields.family = { 0x28 }, .fields.serial_number = { 0xff, 0x64, 0x06, 0xc7, 0x29, 0x66 }, .fields.crc = { 0xad }, }; char rom_code_s3[OWB_ROM_CODE_STRING_LENGTH]; is_present = false; owb_string_from_rom_code(known_device3, rom_code_s3, sizeof(rom_code_s3)); search_status = owb_verify_rom(owb, known_device3, &is_present); if (search_status == OWB_STATUS_OK) { printf("Device %s is %s\n", rom_code_s3, is_present ? "present" : "not present"); } else { printf("An error occurred searching for known device: %d", search_status); } |
Соберём, код, прошьём контроллер и снова посмотрим результат
Все три устройства определились.
Проверим также обмен в программе логического анализа
Здесь также всё хорошо.
Итак, на данном уроке мы научились искать на шине 1-Wire с использованием модуля RMT устройство с определённым кодом, также научились пользоваться командой чтения ROM-кода.
Всем спасибо за внимание!
Предыдущий урок Программирование МК ESP32 Следующий урок
Недорогие отладочные платы ESP32 можно купить здесь:
На AliExpress Недорогие отладочные платы ESP32
На Яндекс.Маркет Недорогие отладочные платы ESP32
Недорогие отладочные платы ESP32-S3 можно купить здесь:
Недорогие отладочные платы ESP32-S3
Датчик температуры в экране с проводом можно приобрести здесь:
На AliExpress DS18B20 в экране с проводом
На Яндекс.Маркет DS18B20 в экране с проводом
Логический анализатор 16 каналов можно приобрести (AliExpress) здесь
Смотреть ВИДЕОУРОК в YouTube (нажмите на картинку)
Смотреть ВИДЕОУРОК в Дзен (нажмите на картинку)
Здравствуйте. Смотрю Ваш канал практически с открытия. Микроконтроллерами увлекаюсь в качестве хобби. В основном avr. По мере выхода ваших уроков по STM, приобрел контроллеров, и с Вашей помощью воплощал свои идеи в не сложных проектах. В данный момент оказался без Ide. ST, keil не доступны. Буду учить с Вашей помощью Е SP. Подскажите варианты ПО для STM в 2022
Спасибо за такой тёплый комментарий! Простите, что оперативно отвечать не могу. С ПО для STM всё зависит от места проживания в данный момент. На территории России не всё доступно. Только я считаю, дело не в ПО, а в умении писать код. Поэтому не важно какая именно среда программирования. Удачи Вам в Ваших начинаниях!