Сканирование 1-Wire и сохранение в EEPROM
- Войдите на сайт для отправки комментариев
Коллеги,
на 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;
}
}
//---------------------------------------------------------------------------
Проблема в том, что при сканировании, если на шине находится более 2-х датчиков (например, 4), в EEPROM записываются не все адреса, а только 2 и еще небольшой огрызок. При этом при сканировании обнаруживаются все датчики (4 штуки). Поведение функции устойчивое, для записи всех 4-х датчиков приходится выполнять два последовательных цикла сканирования.
А что если выдать обнаруженные адреса на терминал? Они все выдаются или 2 и огрызок?
bool exists[MAX_SENSORS_QTY] = {false};Не вникал, но здесь инициализирован только первый элемент массива, из пяти. Так задумано?
bool exists[MAX_SENSORS_QTY] = {false};Не вникал, но здесь инициализирован только первый элемент массива, из пяти. Так задумано?
Нет. Так инициализируются ВСЕ элементы одним значением. Правда, '=' тут лишнее, надо
bool exists[MAX_SENSORS_QTY] {false};Да, наврал я, инициализируются, все. Первый - заданным значением, остальные - нулями. В данном случае, значение с этими нулями удачно совпадает.
инициализируются, все. Первый - заданным значением, остальные - нулями.
это если '=' стоит. если не стоит, мемсетом инициализатор размазывается по всему массиву. :)
Upd. Опять я неправ, разобрался, инициализируется, как ты и говоришь, только первое значение, остальные - конструктором по умолчанию для заданного типа.
А что если выдать обнаруженные адреса на терминал? Они все выдаются или 2 и огрызок?
Правильный вопрос )) Выдается 2 и огрызок. Правда, я читаю эти адреса из регистров модбас, но туда необъяснимым образом заносятся только те, которые записываются в EEPROM.
При этом количество найденных адресов (
qty) пишется правильное - 4. И это я объяснить не могу((это если '=' стоит. если не стоит, мемсетом инициализатор размазывается по всему массиву. :)
Upd. Опять я неправ, разобрался, инициализируется, как ты и говоришь, только первое значение, остальные - конструктором по умолчанию для заданного типа.
Чтобы все заинитились одинаково, нужно, по-моему, запятую ставить в курлы: array[4] = {false,}
Нет, всё равно инитица только первый, я проверил. :)
Я вообще всегда думал, инитятся только элементы которые указаны, а остальные остаются как есть, с мусором.
Оказывается, мусор остаётся только если массив (локальный!) совсем не инициализировать. Если инициализирован хотя бы один элемент, остальные инициализируются значением по умолчанию.
Ну, буду знать, если не забуду. :)
ЗЫ: А без "=" я вообще не знал, и раньше не пользовался. И компилятор мой, которым проверял, тоже такого не понимает - наверно старенький он, что ли..
Попробуйте в 30 строке поставить oneWire.reset_search();
Попробуйте в 30 строке поставить oneWire.reset_search();
Увы, не помогло. И reset() тоже.
Кстати, что касается вашего предыдущего вопроса (про вывод обнаруженных адресов): если не записывать обнаруженные адреса в EEPROM, а просто их передавать в регистрах modbus, то они туда попадают все четыре. Как запись в память связана с ОЗУ, ума не приложу ((
Но ведь 2 датчика обнаруживает и записывает правильно. Что за...
Не забываем reset_search()
Я уже отвечал - не влияет.
Вот что интересно. Если в программе пытаешься записать все 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 байта.
И еще добавлю - пробовал разные способы записи: eeprom_write_block, записывать по байтам - все едино.
а при чем тут регистры Модбас? - в коде в заглавном сообщении никакого Модбас нет. Или вы, как многие новички - выложили в форум один код, а запускаете другой?
Давайте работать именно с тем кодом. что в первом сообщении. Вставьте именно в этот код вывод найденных адресов в сериал - где-то примерно в 43 строке. А после 45 еще добавте диагностику типа "Найден новый адрес" или "Найденный адрес уже есть в массиве"
И выкладывайте результваты сюда
Кстати, сводного ОЗУ - 163 байта.
как вы это определили? - по диагностике при компиляции скетча?
тогда почти наверняка причина в нехватке памяти. Вывод при компиляции показывает расход памяти только на глобальные переменные, - и они уже занимают 84% ОЗУ. А у вас еще куча локальных. в том числе массивы
попробуйте для начала уменьшить MAX_SENSORS_QTY, например, до 4. Если после этого в ЕЕПРОМ запишется не 2.5 адреса. а скажем три - все будет ясно. дело в памяти
а при чем тут регистры Модбас? - в коде в заглавном сообщении никакого Модбас нет. Или вы, как многие новички - выложили в форум один код, а запускаете другой?
Давайте работать именно с тем кодом. что в первом сообщении. Вставьте именно в этот код вывод найденных адресов в сериал - где-то примерно в 43 строке. А после 45 еще добавьте диагностику типа "Найден новый адрес" или "Найденный адрес уже есть в массиве"
И выкладывайте результаты сюда
На плате, где установлена atmega, Serial используется под modbus, поэтому всю отладку я веду через запросы к регистрам. Железо уже разобрали и готовят увезти на объект. Как только соберем новый стенд, сделаю минимальный пример, выложу.
На плате, где установлена atmega, Serial используется под modbus, поэтому всю отладку я веду через запросы к регистрам. Железо уже разобрали и готовят увезти на объект. Как только соберем новый стенд, сделаю минимальный пример, выложу.
не майтесь фигней. Запустите код из заголовка на обычной Уно или нано.
железо увозят на обьект, а программа не работает? Интересно. вы всегда так делаете - сначала собираете готовое жедезо, а только потом начинаете писать и тестировать код? :)
как вы это определили? - по диагностике при компиляции скетча?
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; }тогда почти наверняка причина в нехватке памяти. Вывод при компиляции показывает расход памяти только на глобальные переменные, - и они уже занимают 84% ОЗУ. А у вас еще куча локальных. в том числе массивы
84% флеша, а его можно забивать под завязку. Использование динамической памяти при компиляции показывает 825 байт, что при 1 кб ОЗУ микросхемы примерно соответствует показаниям memoryFree().
попробуйте для начала уменьшить MAX_SENSORS_QTY, например, до 4. Если после этого в ЕЕПРОМ запишется не 2.5 адреса. а скажем три - все будет ясно. дело в памяти
Попробую, спасибо.
железо увозят на обьект, а программа не работает? Интересно. вы всегда так делаете - сначала собираете готовое железо, а только потом начинаете писать и тестировать код? :)
Нет ;) Я занимаюсь серверной частью, которая общается с этими платами по modbus, а модули разрабатывали другие. Как только выяснилась такая проблема, мне пришлось подключиться ((
не майтесь фигней. Запустите код из заголовка на обычной Уно или нано.
Вот по причине, которую я написал выше, я не все понял. Уно- это отладочная плата такая? %))