Сканирование 1-Wire и сохранение в EEPROM

sergek
sergek аватар
Offline
Зарегистрирован: 05.04.2020

Коллеги,

на ATMega 168pa реализован модуль, который работает с датчиками ds18b20 - измеряет температуру, и отдает их по протоколу Modbus RTU. Прошивка сделана так, что позволяет подключать новые датчики на шине 1-wire и сохранение их адресов в EEPROM. Для обнаружения датчиков используется функция scan, приведенная в тексте, она вызывается по команде, передаваемой по modbus.

Проблема в том, что при сканировании, если на шине находится более 2-х датчиков (например, 4), в EEPROM записываются не все адреса, а только 2 и еще небольшой огрызок. При этом при сканировании обнаруживаются все датчики (4 штуки). Поведение функции устойчивое, для записи всех 4-х датчиков приходится выполнять два последовательных цикла сканирования.

Как думаете, почему не удается записать сразу 4 адреса?

Функция сканирования во вложении приведена с минимальными упрощениями:

#include <OneWire.h>
#include <EEPROM.h>

#define SENSOR_PIN_1          9
#define ADDR_E_SEN_qty_1      5
#define ADDR_E_sensAddress 	  10

// максимальное количество датчиков на одной линии
const uint8_t MAX_SENSORS_QTY = 5;                      
typedef uint8_t DeviceAddress[8]; 
DeviceAddress sensAddress[MAX_SENSORS_QTY]; 

OneWire oneWire(SENSOR_PIN_1);

void setup() {
    // инициализация
}
void loop() {
    // измерение температур
    // обработка запросов modbus
    // сканирование датчиков на шине 1-wire
}

// функция сканирования датчиков QneWire
void scan() {

    uint8_t newQty = 0;                          
    DeviceAddress newSensors[MAX_SENSORS_QTY];   
    bool exists[MAX_SENSORS_QTY] = {false};      

    // сканирование шины и отбор новых датчиков
    while (newQty < MAX_SENSORS_QTY && oneWire.search(newSensors[newQty])) {
      // проверить контрольную сумму
      if (OneWire::crc8(newSensors[newQty], 7) == newSensors[newQty][7]) {
    
          // проверка наличия найденного датчика в существующем массиве
          bool found = false;             
          for(uint8_t j=0; j<MAX_SENSORS_QTY; j++) {  
              if(compareAddr(newSensors[newQty], sensAddress[j])) {
                  exists[j] = true;                                   
                  found = true;                              
              }
          }
          if(!found)
              newQty++;
      }
    }
    
    // новые датчики рассовываем по списку рабочих датчиков в свободные ячейки
    for (uint8_t i=0; i<newQty; i++) {
        for (uint8_t j=0; j<MAX_SENSORS_QTY; j++) {
            if(!exists[j]) {
                copyAddr(newSensors[i], sensAddress[j]);                                    
                exists[j] = true;
                break;
            }
        }
    }

    // обнуляем адреса несуществующих датчиков и подсчитываем общее кол-во
    uint8_t qty = 0;  
    for (uint8_t i=0; i<MAX_SENSORS_QTY; i++) {
        if(!exists[i]) {
            setZeroAddr(sensAddress[i]);  
        } else {
            qty++;
        }
    }

    // записываем в EEPROM
    EEPROM.update(ADDR_E_SEN_qty_1, qty);            
    EEPROM.put(ADDR_E_sensAddress, sensAddress);
}
//---------------------------------------------------------------------------

// сравнить адреса
bool compareAddr(DeviceAddress sens1, DeviceAddress sens2) {
    for(uint8_t i = 0; i < 8; i++) {
        if(sens1[i] != sens2[i])
            return false;
    }
    return true;
}
//---------------------------------------------------------------------------

// скопировать адрес
void copyAddr(DeviceAddress from, DeviceAddress to) {
    for(uint8_t i = 0; i < 8; i++) {
        to[i] = from[i];
    }
}
//---------------------------------------------------------------------------

// обнулить адрес
void setZeroAddr(DeviceAddress sens) {
    for(uint8_t i = 0; i < 8; i++) {
        sens[i] = 0;
    }
}
//---------------------------------------------------------------------------

 

mykaida
mykaida аватар
Offline
Зарегистрирован: 12.07.2018

sergek пишет:

Проблема в том, что при сканировании, если на шине находится более 2-х датчиков (например, 4), в EEPROM записываются не все адреса, а только 2 и еще небольшой огрызок. При этом при сканировании обнаруживаются все датчики (4 штуки). Поведение функции устойчивое, для записи всех 4-х датчиков приходится выполнять два последовательных цикла сканирования.

А что если выдать обнаруженные адреса на терминал? Они все выдаются или 2 и огрызок?

Datak
Offline
Зарегистрирован: 09.10.2014
    bool exists[MAX_SENSORS_QTY] = {false};

Не вникал, но здесь инициализирован только первый элемент массива, из пяти. Так задумано?

DetSimen
DetSimen аватар
Offline
Зарегистрирован: 25.01.2017

Datak пишет:

    bool exists[MAX_SENSORS_QTY] = {false};

Не вникал, но здесь инициализирован только первый элемент массива, из пяти. Так задумано?

Нет. Так инициализируются ВСЕ элементы одним значением. Правда, '=' тут лишнее, надо 

bool exists[MAX_SENSORS_QTY] {false};
Datak
Offline
Зарегистрирован: 09.10.2014

DetSimen пишет:
Так инициализируются ВСЕ элементы одним значением.

Да, наврал я, инициализируются, все. Первый - заданным значением, остальные - нулями. В данном случае, значение с этими нулями удачно совпадает.

DetSimen
DetSimen аватар
Offline
Зарегистрирован: 25.01.2017

Datak пишет:

DetSimen пишет:
Так инициализируются ВСЕ элементы одним значением.

инициализируются, все. Первый - заданным значением, остальные - нулями. 

это если '=' стоит. если не стоит, мемсетом инициализатор размазывается по всему массиву. :) 

Upd.  Опять я неправ, разобрался, инициализируется, как ты и говоришь, только первое значение, остальные - конструктором по умолчанию для заданного типа. 

sergek
sergek аватар
Offline
Зарегистрирован: 05.04.2020

mykaida пишет:

А что если выдать обнаруженные адреса на терминал? Они все выдаются или 2 и огрызок?

Правильный вопрос )) Выдается 2 и огрызок. Правда, я читаю эти адреса из регистров модбас, но туда необъяснимым образом заносятся только те, которые записываются в EEPROM.

При этом количество найденных адресов (qty) пишется правильное - 4. И это я объяснить не могу((

 

sadman41
Offline
Зарегистрирован: 19.10.2016

DetSimen пишет:

это если '=' стоит. если не стоит, мемсетом инициализатор размазывается по всему массиву. :) 

Upd.  Опять я неправ, разобрался, инициализируется, как ты и говоришь, только первое значение, остальные - конструктором по умолчанию для заданного типа. 

Чтобы все заинитились одинаково, нужно, по-моему, запятую ставить в курлы:  array[4] = {false,}

DetSimen
DetSimen аватар
Offline
Зарегистрирован: 25.01.2017

Нет, всё равно инитица только первый, я проверил. :) 

Datak
Offline
Зарегистрирован: 09.10.2014

Я вообще всегда думал, инитятся только элементы которые указаны, а остальные остаются как есть, с мусором.

Оказывается, мусор остаётся только если массив (локальный!) совсем не инициализировать. Если инициализирован хотя бы один элемент, остальные инициализируются значением по умолчанию.

Ну, буду знать, если не забуду. :) 
 

ЗЫ: А без "=" я вообще не знал, и раньше не пользовался. И компилятор мой, которым проверял, тоже такого не понимает - наверно старенький он, что ли..

mykaida
mykaida аватар
Offline
Зарегистрирован: 12.07.2018

Попробуйте в 30 строке поставить oneWire.reset_search();

sergek
sergek аватар
Offline
Зарегистрирован: 05.04.2020

mykaida пишет:

Попробуйте в 30 строке поставить oneWire.reset_search();

Увы, не помогло. И reset() тоже.

Кстати, что касается вашего предыдущего вопроса (про вывод обнаруженных адресов): если не записывать обнаруженные адреса в EEPROM, а просто их передавать в регистрах modbus, то они туда попадают все четыре. Как запись в память связана с ОЗУ, ума не приложу ((

sergek
sergek аватар
Offline
Зарегистрирован: 05.04.2020

Но ведь 2 датчика обнаруживает и записывает правильно. Что за...

vlad072
Offline
Зарегистрирован: 01.08.2017

Не забываем reset_search()

sergek
sergek аватар
Offline
Зарегистрирован: 05.04.2020

Я уже отвечал - не влияет.

Вот что интересно. Если в программе пытаешься записать все 4 адреса:

    // записываем в EEPROM
    EEPROM.update(ADDR_E_SEN_qty_1, qty);           
    EEPROM.put(ADDR_E_sensAddress, sensAddress);

то в память пишутся 2,5 адреса. И при этом в sensAddress те же 2,5 адреса!

А если закомментировать EEPROM.put, или намеренно сделать ошибку, например, EEPROM.update - то в sensAddress все 4 адреса. В обоих случаях qty пишется правильно - 4.

Как EEPROM влияет на ОЗУ, не понимаю. Кстати, сводного ОЗУ - 163 байта.

 
sergek
sergek аватар
Offline
Зарегистрирован: 05.04.2020

И еще добавлю - пробовал разные способы записи: eeprom_write_block, записывать по байтам - все едино.

b707
Offline
Зарегистрирован: 26.05.2017

а при чем тут регистры Модбас? - в коде в заглавном сообщении никакого Модбас нет. Или вы, как многие новички - выложили в форум один код, а запускаете другой?

Давайте работать именно с тем кодом. что в первом сообщении. Вставьте именно в этот код вывод найденных адресов в сериал - где-то примерно в 43 строке. А после 45 еще добавте диагностику типа "Найден новый адрес" или "Найденный адрес уже есть в массиве"

И выкладывайте результваты сюда

b707
Offline
Зарегистрирован: 26.05.2017

sergek пишет:

Кстати, сводного ОЗУ - 163 байта.

 

как вы это определили? - по диагностике при компиляции скетча?

тогда почти наверняка причина в нехватке памяти. Вывод при компиляции показывает расход памяти только на глобальные переменные, - и они уже занимают 84% ОЗУ. А у вас еще куча локальных. в том числе массивы

попробуйте для начала уменьшить MAX_SENSORS_QTY, например, до 4. Если после этого в ЕЕПРОМ запишется не 2.5 адреса. а скажем три - все будет ясно. дело в памяти

sergek
sergek аватар
Offline
Зарегистрирован: 05.04.2020

b707 пишет:

а при чем тут регистры Модбас? - в коде в заглавном сообщении никакого Модбас нет. Или вы, как многие новички - выложили в форум один код, а запускаете другой?

Давайте работать именно с тем кодом. что в первом сообщении. Вставьте именно в этот код вывод найденных адресов в сериал - где-то примерно в 43 строке. А после 45 еще добавьте диагностику типа "Найден новый адрес" или "Найденный адрес уже есть в массиве"

И выкладывайте результаты сюда

На плате, где установлена atmega, Serial используется под modbus, поэтому всю отладку я веду через запросы к регистрам.  Железо уже разобрали и готовят увезти на объект. Как только соберем новый стенд, сделаю минимальный пример, выложу.

b707
Offline
Зарегистрирован: 26.05.2017

sergek пишет:

На плате, где установлена atmega, Serial используется под modbus, поэтому всю отладку я веду через запросы к регистрам.  Железо уже разобрали и готовят увезти на объект. Как только соберем новый стенд, сделаю минимальный пример, выложу.

не майтесь фигней. Запустите код из заголовка на обычной Уно или нано. 

железо увозят на обьект, а программа не работает? Интересно. вы всегда так делаете - сначала собираете готовое жедезо, а только потом начинаете писать и тестировать код? :)

sergek
sergek аватар
Offline
Зарегистрирован: 05.04.2020

b707 пишет:

как вы это определили? - по диагностике при компиляции скетча?

https://alexgyver.ru/lessons/code-optimisation/

// Функция, возвращающая количество свободного ОЗУ
extern int __bss_end;
extern void *__brkval;
int memoryFree() {
  int freeValue;
  if ((int)__brkval == 0)
    freeValue = ((int)&freeValue) - ((int)&__bss_end);
  else
    freeValue = ((int)&freeValue) - ((int)__brkval);
  if(freeValue < 0)
      freeValue = 0xFFFF;
  return freeValue;
}

b707 пишет:

тогда почти наверняка причина в нехватке памяти. Вывод при компиляции показывает расход памяти только на глобальные переменные, - и они уже занимают 84% ОЗУ. А у вас еще куча локальных. в том числе массивы

84% флеша, а его можно забивать под завязку. Использование динамической памяти при компиляции показывает 825 байт, что при 1 кб ОЗУ микросхемы примерно соответствует показаниям memoryFree().

b707 пишет:

попробуйте для начала уменьшить MAX_SENSORS_QTY, например, до 4. Если после этого в ЕЕПРОМ запишется не 2.5 адреса. а скажем три - все будет ясно. дело в памяти

Попробую, спасибо.

sergek
sergek аватар
Offline
Зарегистрирован: 05.04.2020

b707 пишет:

железо увозят на обьект, а программа не работает? Интересно. вы всегда так делаете - сначала собираете готовое железо, а только потом начинаете писать и тестировать код? :)

Нет ;) Я занимаюсь серверной частью, которая общается с этими платами по modbus, а модули разрабатывали другие. Как только выяснилась такая проблема, мне пришлось подключиться ((

b707 пишет:

не майтесь фигней. Запустите код из заголовка на обычной Уно или нано. 

Вот по причине, которую я написал выше, я не все понял. Уно- это отладочная плата такая? %))