Записать struct в ЕЕПРОМ

kostyamat
Offline
Зарегистрирован: 16.11.2017

Ребята, прошу помощи, весь мозг сломал. Есть такой код

#include <EEPROM.h>
#include "EEPROMAnything.h"

struct apartment_t {
  byte number;
  uint32_t tlfn[5];
  uint32_t card[5];
  bool inService;
} aprt;




void setup() {
  Serial.begin(57600);
  Serial.println(sizeof(aprt));

  delay(2000);

    for (int i = 0; i < 1024; i++) {
      EEPROM.update(i, 255);

    }


   for (byte i = 0; i < 20; i++) {     // Тут я набиваю поля какими-то данными
      aprt.number = i + 1;
      for (byte x = 0; x < 5; x++) {
        aprt.tlfn[x] = 642000000 + x * i;    // генерирую некое число, просто для того, чтобы что-то записать
        aprt.card[x] = 100000000 + x * i;  // то же самое, что и выше, просто генерация числа
      }
      EEPROM_writeAnything(aprtAddress(i), aprt);
      Serial.println(aprtAddress(i));
    }
   



  for (byte i = 0; i < 20; i++) {
    EEPROM_readAnything(aprtAddress(i), aprt);
    Serial.print((String)aprt.number + ";");
    for (byte x = 0; x < 5; x++) {
      Serial.print((String)aprt.tlfn[x] + ";");
      Serial.print((String)aprt.card[x] + ";");
    }
    Serial.println(aprt.inService == true ? "yes" : "no");
    Serial.println(aprtAddress(i));
  }


}

void loop() {
  // put your main code here, to run repeatedly:

}




uint16_t aprtAddress(uint16_t num) {
  return num * sizeof(aprt);
}



содержимое EEPROMAnything.h

#include <EEPROM.h>
#include <Arduino.h>  // for type definitions

template <class T> int EEPROM_writeAnything(uint16_t ee, const T& value)
{
    const byte* p = (const byte*)(const void*)&value;
    unsigned int i;
    for (i = 0; i < sizeof(value); i++)
          EEPROM.write(ee++, *p++);
    return i;
}

template <class T> int EEPROM_readAnything(uint16_t ee, T& value)
{
    byte* p = (byte*)(void*)&value;
    unsigned int i;
    for (i = 0; i < sizeof(value); i++)
          *p++ = EEPROM.read(ee++);
    return i;
}

Проблема заключается в том, что при записи, все выводится правильно. А вот если очистку ЕЕПРОМ и запись закоментировать, то  первые 10 записей читает кашу, а потом остальные нормально, числа в полях совпадают.

Второй день вылечить не могу. В чем моя ошибка?

unsigned int всюду заменил на  uint16_t, потому как готовлюсь схожий код использовать с 24C512 и там таких записей дофига.

 

Кстати, если по ходу подскажите как организовать поиск по полю aprt.number и по массиву полей aprt.card[x] , я был бы очень признателен.



ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

На глаз ничего особенного не заметил, кроме использования EEPROM с нулевого адреса. Раньше с этим была проблема, потом её производитель исправил, но мы не знаем исправили ли её китайские производители клонов, потому предпочитаю адреса меньше 16 не использовать.

Запустил Вашу программу и не разобрался в выоде :(

Переделал программу, чтобы вывод был более читабельным, заодно поставил не с нулевого адреса и выбросил EEPROMAnything - нафига это надо, когда тоже самое есть в штатной библиотеке, только лучше сделано?

В итоге получилась вот такая программа

#include <EEPROM.h>

const uint16_t	StartAddress = 16;

struct apartment_t  : public Printable {
  byte number;
  uint32_t tlfn[5];
  uint32_t card[5];
  bool inService;

	size_t printTo(Print& p) const {
		size_t res = p.print(".number=") + p.println(number);
		for (byte x = 0; x < 5; x++) {
      	res += p.print(".tlfn[") + p.print(x) + p.print("]=") + p.print(tlfn[x]) + p.print("; ");
      	res += p.print(".card[") + p.print(x) + p.print("]=") + p.println(card[x]);
		}
		res += p.print(".inService=") + p.println(inService ? "yes" : "no");
		return res + p.print("---------------------");
	}
} aprt;

uint16_t aprtAddress(const uint8_t num) {
  return StartAddress + num * sizeof(aprt);
}

void setup() {
	  Serial.begin(57600);
	  Serial.println(sizeof(aprt));
	  delay(2000);
	  for (int i = 0; i < 1024; i++) EEPROM.update(i, 255);


	for (byte i = 0; i < 20; i++) {     // Тут я набиваю поля какими-то данными
      aprt.number = i + 1;
      for (byte x = 0; x < 5; x++) {
        aprt.tlfn[x] = 642000000 + x * i;    // генерирую некое число, просто для того, чтобы что-то записать
        aprt.card[x] = 100000000 + x * i;  // то же самое, что и выше, просто генерация числа
      }
      EEPROM.put(aprtAddress(i), aprt);
      Serial.print("@ address = "); Serial.println(aprtAddress(i));
      Serial.println(aprt);
    }
	Serial.println("======================================");
	
	for (byte i = 0; i < 20; i++) {
		EEPROM.get(aprtAddress(i), aprt);
      Serial.print("@ address = "); Serial.println(aprtAddress(i));
      Serial.println(aprt);
	}
}

void loop(void) {}

Выдаёт во это (до "=======" - то, что записываем, а после - то, что читаем).

44
@ address = 16
.number=1
.tlfn[0]=642000000; .card[0]=100000000
.tlfn[1]=642000000; .card[1]=100000000
.tlfn[2]=642000000; .card[2]=100000000
.tlfn[3]=642000000; .card[3]=100000000
.tlfn[4]=642000000; .card[4]=100000000
.inService=no
---------------------
@ address = 60
.number=2
.tlfn[0]=642000000; .card[0]=100000000
.tlfn[1]=642000001; .card[1]=100000001
.tlfn[2]=642000002; .card[2]=100000002
.tlfn[3]=642000003; .card[3]=100000003
.tlfn[4]=642000004; .card[4]=100000004
.inService=no
---------------------
@ address = 104
.number=3
.tlfn[0]=642000000; .card[0]=100000000
.tlfn[1]=642000002; .card[1]=100000002
.tlfn[2]=642000004; .card[2]=100000004
.tlfn[3]=642000006; .card[3]=100000006
.tlfn[4]=642000008; .card[4]=100000008
.inService=no
---------------------
@ address = 148
.number=4
.tlfn[0]=642000000; .card[0]=100000000
.tlfn[1]=642000003; .card[1]=100000003
.tlfn[2]=642000006; .card[2]=100000006
.tlfn[3]=642000009; .card[3]=100000009
.tlfn[4]=642000012; .card[4]=100000012
.inService=no
---------------------
@ address = 192
.number=5
.tlfn[0]=642000000; .card[0]=100000000
.tlfn[1]=642000004; .card[1]=100000004
.tlfn[2]=642000008; .card[2]=100000008
.tlfn[3]=642000012; .card[3]=100000012
.tlfn[4]=642000016; .card[4]=100000016
.inService=no
---------------------
@ address = 236
.number=6
.tlfn[0]=642000000; .card[0]=100000000
.tlfn[1]=642000005; .card[1]=100000005
.tlfn[2]=642000010; .card[2]=100000010
.tlfn[3]=642000015; .card[3]=100000015
.tlfn[4]=642000020; .card[4]=100000020
.inService=no
---------------------
@ address = 280
.number=7
.tlfn[0]=642000000; .card[0]=100000000
.tlfn[1]=642000006; .card[1]=100000006
.tlfn[2]=642000012; .card[2]=100000012
.tlfn[3]=642000018; .card[3]=100000018
.tlfn[4]=642000024; .card[4]=100000024
.inService=no
---------------------
@ address = 324
.number=8
.tlfn[0]=642000000; .card[0]=100000000
.tlfn[1]=642000007; .card[1]=100000007
.tlfn[2]=642000014; .card[2]=100000014
.tlfn[3]=642000021; .card[3]=100000021
.tlfn[4]=642000028; .card[4]=100000028
.inService=no
---------------------
@ address = 368
.number=9
.tlfn[0]=642000000; .card[0]=100000000
.tlfn[1]=642000008; .card[1]=100000008
.tlfn[2]=642000016; .card[2]=100000016
.tlfn[3]=642000024; .card[3]=100000024
.tlfn[4]=642000032; .card[4]=100000032
.inService=no
---------------------
@ address = 412
.number=10
.tlfn[0]=642000000; .card[0]=100000000
.tlfn[1]=642000009; .card[1]=100000009
.tlfn[2]=642000018; .card[2]=100000018
.tlfn[3]=642000027; .card[3]=100000027
.tlfn[4]=642000036; .card[4]=100000036
.inService=no
---------------------
@ address = 456
.number=11
.tlfn[0]=642000000; .card[0]=100000000
.tlfn[1]=642000010; .card[1]=100000010
.tlfn[2]=642000020; .card[2]=100000020
.tlfn[3]=642000030; .card[3]=100000030
.tlfn[4]=642000040; .card[4]=100000040
.inService=no
---------------------
@ address = 500
.number=12
.tlfn[0]=642000000; .card[0]=100000000
.tlfn[1]=642000011; .card[1]=100000011
.tlfn[2]=642000022; .card[2]=100000022
.tlfn[3]=642000033; .card[3]=100000033
.tlfn[4]=642000044; .card[4]=100000044
.inService=no
---------------------
@ address = 544
.number=13
.tlfn[0]=642000000; .card[0]=100000000
.tlfn[1]=642000012; .card[1]=100000012
.tlfn[2]=642000024; .card[2]=100000024
.tlfn[3]=642000036; .card[3]=100000036
.tlfn[4]=642000048; .card[4]=100000048
.inService=no
---------------------
@ address = 588
.number=14
.tlfn[0]=642000000; .card[0]=100000000
.tlfn[1]=642000013; .card[1]=100000013
.tlfn[2]=642000026; .card[2]=100000026
.tlfn[3]=642000039; .card[3]=100000039
.tlfn[4]=642000052; .card[4]=100000052
.inService=no
---------------------
@ address = 632
.number=15
.tlfn[0]=642000000; .card[0]=100000000
.tlfn[1]=642000014; .card[1]=100000014
.tlfn[2]=642000028; .card[2]=100000028
.tlfn[3]=642000042; .card[3]=100000042
.tlfn[4]=642000056; .card[4]=100000056
.inService=no
---------------------
@ address = 676
.number=16
.tlfn[0]=642000000; .card[0]=100000000
.tlfn[1]=642000015; .card[1]=100000015
.tlfn[2]=642000030; .card[2]=100000030
.tlfn[3]=642000045; .card[3]=100000045
.tlfn[4]=642000060; .card[4]=100000060
.inService=no
---------------------
@ address = 720
.number=17
.tlfn[0]=642000000; .card[0]=100000000
.tlfn[1]=642000016; .card[1]=100000016
.tlfn[2]=642000032; .card[2]=100000032
.tlfn[3]=642000048; .card[3]=100000048
.tlfn[4]=642000064; .card[4]=100000064
.inService=no
---------------------
@ address = 764
.number=18
.tlfn[0]=642000000; .card[0]=100000000
.tlfn[1]=642000017; .card[1]=100000017
.tlfn[2]=642000034; .card[2]=100000034
.tlfn[3]=642000051; .card[3]=100000051
.tlfn[4]=642000068; .card[4]=100000068
.inService=no
---------------------
@ address = 808
.number=19
.tlfn[0]=642000000; .card[0]=100000000
.tlfn[1]=642000018; .card[1]=100000018
.tlfn[2]=642000036; .card[2]=100000036
.tlfn[3]=642000054; .card[3]=100000054
.tlfn[4]=642000072; .card[4]=100000072
.inService=no
---------------------
@ address = 852
.number=20
.tlfn[0]=642000000; .card[0]=100000000
.tlfn[1]=642000019; .card[1]=100000019
.tlfn[2]=642000038; .card[2]=100000038
.tlfn[3]=642000057; .card[3]=100000057
.tlfn[4]=642000076; .card[4]=100000076
.inService=no
---------------------
======================================
@ address = 16
.number=1
.tlfn[0]=642000000; .card[0]=100000000
.tlfn[1]=642000000; .card[1]=100000000
.tlfn[2]=642000000; .card[2]=100000000
.tlfn[3]=642000000; .card[3]=100000000
.tlfn[4]=642000000; .card[4]=100000000
.inService=no
---------------------
@ address = 60
.number=2
.tlfn[0]=642000000; .card[0]=100000000
.tlfn[1]=642000001; .card[1]=100000001
.tlfn[2]=642000002; .card[2]=100000002
.tlfn[3]=642000003; .card[3]=100000003
.tlfn[4]=642000004; .card[4]=100000004
.inService=no
---------------------
@ address = 104
.number=3
.tlfn[0]=642000000; .card[0]=100000000
.tlfn[1]=642000002; .card[1]=100000002
.tlfn[2]=642000004; .card[2]=100000004
.tlfn[3]=642000006; .card[3]=100000006
.tlfn[4]=642000008; .card[4]=100000008
.inService=no
---------------------
@ address = 148
.number=4
.tlfn[0]=642000000; .card[0]=100000000
.tlfn[1]=642000003; .card[1]=100000003
.tlfn[2]=642000006; .card[2]=100000006
.tlfn[3]=642000009; .card[3]=100000009
.tlfn[4]=642000012; .card[4]=100000012
.inService=no
---------------------
@ address = 192
.number=5
.tlfn[0]=642000000; .card[0]=100000000
.tlfn[1]=642000004; .card[1]=100000004
.tlfn[2]=642000008; .card[2]=100000008
.tlfn[3]=642000012; .card[3]=100000012
.tlfn[4]=642000016; .card[4]=100000016
.inService=no
---------------------
@ address = 236
.number=6
.tlfn[0]=642000000; .card[0]=100000000
.tlfn[1]=642000005; .card[1]=100000005
.tlfn[2]=642000010; .card[2]=100000010
.tlfn[3]=642000015; .card[3]=100000015
.tlfn[4]=642000020; .card[4]=100000020
.inService=no
---------------------
@ address = 280
.number=7
.tlfn[0]=642000000; .card[0]=100000000
.tlfn[1]=642000006; .card[1]=100000006
.tlfn[2]=642000012; .card[2]=100000012
.tlfn[3]=642000018; .card[3]=100000018
.tlfn[4]=642000024; .card[4]=100000024
.inService=no
---------------------
@ address = 324
.number=8
.tlfn[0]=642000000; .card[0]=100000000
.tlfn[1]=642000007; .card[1]=100000007
.tlfn[2]=642000014; .card[2]=100000014
.tlfn[3]=642000021; .card[3]=100000021
.tlfn[4]=642000028; .card[4]=100000028
.inService=no
---------------------
@ address = 368
.number=9
.tlfn[0]=642000000; .card[0]=100000000
.tlfn[1]=642000008; .card[1]=100000008
.tlfn[2]=642000016; .card[2]=100000016
.tlfn[3]=642000024; .card[3]=100000024
.tlfn[4]=642000032; .card[4]=100000032
.inService=no
---------------------
@ address = 412
.number=10
.tlfn[0]=642000000; .card[0]=100000000
.tlfn[1]=642000009; .card[1]=100000009
.tlfn[2]=642000018; .card[2]=100000018
.tlfn[3]=642000027; .card[3]=100000027
.tlfn[4]=642000036; .card[4]=100000036
.inService=no
---------------------
@ address = 456
.number=11
.tlfn[0]=642000000; .card[0]=100000000
.tlfn[1]=642000010; .card[1]=100000010
.tlfn[2]=642000020; .card[2]=100000020
.tlfn[3]=642000030; .card[3]=100000030
.tlfn[4]=642000040; .card[4]=100000040
.inService=no
---------------------
@ address = 500
.number=12
.tlfn[0]=642000000; .card[0]=100000000
.tlfn[1]=642000011; .card[1]=100000011
.tlfn[2]=642000022; .card[2]=100000022
.tlfn[3]=642000033; .card[3]=100000033
.tlfn[4]=642000044; .card[4]=100000044
.inService=no
---------------------
@ address = 544
.number=13
.tlfn[0]=642000000; .card[0]=100000000
.tlfn[1]=642000012; .card[1]=100000012
.tlfn[2]=642000024; .card[2]=100000024
.tlfn[3]=642000036; .card[3]=100000036
.tlfn[4]=642000048; .card[4]=100000048
.inService=no
---------------------
@ address = 588
.number=14
.tlfn[0]=642000000; .card[0]=100000000
.tlfn[1]=642000013; .card[1]=100000013
.tlfn[2]=642000026; .card[2]=100000026
.tlfn[3]=642000039; .card[3]=100000039
.tlfn[4]=642000052; .card[4]=100000052
.inService=no
---------------------
@ address = 632
.number=15
.tlfn[0]=642000000; .card[0]=100000000
.tlfn[1]=642000014; .card[1]=100000014
.tlfn[2]=642000028; .card[2]=100000028
.tlfn[3]=642000042; .card[3]=100000042
.tlfn[4]=642000056; .card[4]=100000056
.inService=no
---------------------
@ address = 676
.number=16
.tlfn[0]=642000000; .card[0]=100000000
.tlfn[1]=642000015; .card[1]=100000015
.tlfn[2]=642000030; .card[2]=100000030
.tlfn[3]=642000045; .card[3]=100000045
.tlfn[4]=642000060; .card[4]=100000060
.inService=no
---------------------
@ address = 720
.number=17
.tlfn[0]=642000000; .card[0]=100000000
.tlfn[1]=642000016; .card[1]=100000016
.tlfn[2]=642000032; .card[2]=100000032
.tlfn[3]=642000048; .card[3]=100000048
.tlfn[4]=642000064; .card[4]=100000064
.inService=no
---------------------
@ address = 764
.number=18
.tlfn[0]=642000000; .card[0]=100000000
.tlfn[1]=642000017; .card[1]=100000017
.tlfn[2]=642000034; .card[2]=100000034
.tlfn[3]=642000051; .card[3]=100000051
.tlfn[4]=642000068; .card[4]=100000068
.inService=no
---------------------
@ address = 808
.number=19
.tlfn[0]=642000000; .card[0]=100000000
.tlfn[1]=642000018; .card[1]=100000018
.tlfn[2]=642000036; .card[2]=100000036
.tlfn[3]=642000054; .card[3]=100000054
.tlfn[4]=642000072; .card[4]=100000072
.inService=no
---------------------
@ address = 852
.number=20
.tlfn[0]=642000000; .card[0]=100000000
.tlfn[1]=642000019; .card[1]=100000019
.tlfn[2]=642000038; .card[2]=100000038
.tlfn[3]=642000057; .card[3]=100000057
.tlfn[4]=642000076; .card[4]=100000076
.inService=no
---------------------

Проблемы не вижу. Если я чего-то не понял и она есть, поясните пожалуйста в чём она (укажите номера строк и суть проблемы).

И да, кстати, мозг не ломайте. Как поётся в классической песне: "есть только две вещи, способные сломать мозг нормальному парню" и ардуины среди этих вещеё нет :))))

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

ЕвгенийП пишет:

На глаз ничего особенного не заметил, кроме использования EEPROM с нулевого адреса. Раньше с этим была проблема, потом её производитель исправил, но мы не знаем исправили ли её китайские производители клонов, потому предпочитаю адреса меньше 16 не использовать.

Первый раз слышу об этом, а что за проблема то была? Всегда использовал с 0го адреса флеши сторублёвых клонов с алиэкспресса, никогда проблем небыло и нет.

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

vlad072 пишет:

Первый раз слышу об этом, а что за проблема то была? 

Вот с сайта производителя. Они описывают проблему и говорят, что в "Newer parts" они её решили.

kostyamat
Offline
Зарегистрирован: 16.11.2017

ЕвгенийП пишет:

На глаз ничего особенного не заметил, кроме использования EEPROM с нулевого адреса. Раньше с этим была проблема, потом её производитель исправил, но мы не знаем исправили ли её китайские производители клонов, потому предпочитаю адреса меньше 16 не использовать.

Запустил Вашу программу и не разобрался в выоде :(

Переделал программу, чтобы вывод был более читабельным, заодно поставил не с нулевого адреса и выбросил EEPROMAnything - нафига это надо, когда тоже самое есть в штатной библиотеке, только лучше сделано?

В итоге получилась вот такая программа

Печать в примере не самоцель, а только лишь для отладки. Спасибо конечно, почерпнул некоторые интересности для себя.

Похоже, что проблема не в коде. Мне попалась плата NANO с битым ЕЕПРОМ. (абсолютно новая, твою ж мать!)

Работает и мой код, и ваш. Странно то, что первые 60 байт пишутся нормально, потом покоцанные области, потом снова нормально. Первый раз такое встретил. ДВА ДНЯ ЖИЗНИ В ТОПКУ!!! Обидно шо ппц.

Может подскажите функциию, или библиотеку, которая могла бы с приемлемой скоростью искать совпадения входной переменной с полями aprt.card[x] и\или aprt.number?

Дело в том, что аналогичный код будет работать с 24С512 и искать придется между несколькими сотнями записей, соответственно тисячей полей aprt.card[x]

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

Вводных мало. Записи-то хоть отсортированные?

kostyamat
Offline
Зарегистрирован: 16.11.2017

В смысле "отсортированные"?

Начальный адрес первой - известен, потом пишутся подряд, как в моем примере, общее количество таких записей фиксированно, то-есть тоже известно, длинна записи тоже известна.

Общая логика - RFID ридер читает карту, полученный 4-х байтный номер нужно сопоставить с записанными aprt.card[x] и открыть общую дверь. То есть нужно пробежаться по всем записям структуры, и по всем полям aprt.card[x] в них, и проверить совпадет ли пришедший номер с каким нибудь из записанных.

второй поиск - по введенному числу позиционироваться на запись с совпавши с числом aprt.number и вычитать в массив из него поля aprt.tlfn[x].

Нужен грамотный пример - как максимально эффективно, по скорости работы, организовать поиск в такой импровизированной БД. Не хотелось бы держать гостя под дверьми более секунды, пока ардуино будет лопатить несколько тисяч таких записей, соответственоо несколько тысяч записанных карт. Учитывая тот факт, что ОЗУ у нас очень мало, ничего вразумительного в голову особо не приходит. 

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

kostyamat пишет:

В смысле "отсортированные"?

Нужен грамотный пример - как максимально эффективно, по скорости работы, организовать поиск  

Чтобы максимально бстро искать данные нужно хранить не как попало, а отсортированными. ОТсортированные, это в смысле сначала идёт самый маленький, потом побольше, потом ещё поболье, а самый большой - последний.

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

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

Интересно, Atmel предполагал, что их МК будут в качестве серверов БД использовать?

kostyamat
Offline
Зарегистрирован: 16.11.2017

sadman41 пишет:

Интересно, Atmel предполагал, что их МК будут в качестве серверов БД использовать?

:D Смешно, да...

Короче, по быстрому нарисовал такое

const uint16_t countAprts = 20;
const byte countCard = 5;


uint16_t searchCard(uint32_t _card) {
uint16_t i = 0;
  while (i < countAprts) {
    EEPROM.get(aprtAddress(i), aprt);
    for (byte x = 0; x < countCards; x++) {
      if (aprt.card[x] == _card) {
        return aprt.number;
      }
    }
    i++;
  }

  return 0;
}

делает поиск в двадцати записах ~3мс, (это во внутренней ЕЕПРОМ) при количестве записей 20. Экстраполируя, предположу, что на 600 записей потратило бы около 90 мс. Вроде устаривает. (надеюсь на внешней ЕЕПРОМ скорость резко не упадет, в любом случае - реакция системы в +-500 мс кажется приемлемой).

Кстати, никто не знает, что работает быстрее while или for?

ПС. Битой ЕЕПРОМ оказалась у всех, последних купленных мной, НАНО. Покупал у локального продавца, не из Китая. В понедельник буду предъявлять. Но сам факт не радует ниразу.

 

UPDATE: Мда, на внешней ЕЕПРОМ 24С512, на частоте i2c в 400кГц (больше - микруха ЕЕПРОМ виснет) скорость поиска между 256 записями почти ровно 2 секунды. Не приемлемо. Буду думать.

andriano
andriano аватар
Offline
Зарегистрирован: 20.06.2015

ЕвгенийП пишет:

Чтобы максимально бстро искать данные нужно хранить не как попало, а отсортированными. ОТсортированные, это в смысле сначала идёт самый маленький, потом побольше, потом ещё поболье, а самый большой - последний.

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

Если массив статичный, то - да.

А если изменяемый, следует оценивать баланс:

Отсортированный: время вставки O(N), время поиска O(log(N)).

Неотсортированный: время вставки O(1), ремя поиска O(N).

Так что, возможно, целесобразно применять вместо массива, скажем, бинарное дерево, тогда и вставка и поиск O(log(N)).

Хотя, для 20 элементов ни одно, ни другое, ни тем более третье - не актуально.

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

kostyamat пишет:

UPDATE: Мда, на внешней ЕЕПРОМ 24С512, на частоте i2c в 400кГц (больше - микруха ЕЕПРОМ виснет) скорость поиска между 256 записями почти ровно 2 секунды. Не приемлемо. Буду думать.

Можно SPI-вариант попробовать:

https://www.st.com/en/memories/standard-spi-eeprom.html

ИлИ FRAM. Она читает/пишет на скорости интерфейса, как утверждает datasheet.

https://www.cypress.com/documentation/datasheets/fm25v20-2-mbit-256-k-8-serial-spi-f-ram

 

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

andriano пишет:

Хотя, для 20 элементов 

У ТС мелькали слова про сотни и тысячи.

kostyamat
Offline
Зарегистрирован: 16.11.2017

Короче,  как оказалось, библиотека, которую я использовал для работы с AT24C512C, а именно AT24C1024.h (указана во многих плейэроунд на оф.сайте и во многих проектах, что странно) не умеет писать\читать данные в страничном режиме (современные ЕЕПРОМ серии 24Сххх поддерживают такой режим, размер внутренней старницы 64, для С256, до 128 для С512-1024).

В результате родил такое:

/* создать закладку EEPROM24Cxxx.h, подключить #include "EEPROM24Cxxx.h"
Подтягивающие, к питанию, резисторы на А4-А5 лучше ставить 2кОм, а не 4к7, как в даташитах указано.
*/

#include <Arduino.h>  // for type definitions
#include <Wire.h>

#define deviceAddress 0x50                             // Адресс микросхемы по умолчанию, А0-А2 подключены к GND (или висят в воздухе, что не желательно). 
#define pageSize 128                                   // смотрите даташит на свою микросхему 24Схххх, размер страницы может быть как 64, так и 128




/* 
 Физический буффер i2c шины в Ардуино равен 32 байта, и увеличить мне не удалось, даже исправляя библиотеку Wire, поэтому, 
 воспользоваться прелестями чтения, 64 или 128 байт за раз, не получится. К тому же, увеличение буффера до 128 байт было бы 
 слишком расточительно для ОЗУ ардуино. Поэтому, читать будем кусками по 32 байта.
 */

template <class T> void EEPROM_get(uint16_t eeaddress, T& value) { 
  unsigned int num_bytes = sizeof(value);                           
  byte* p = (byte*)(void*)&value;                                 
  byte countChank = num_bytes / 32;
  byte restChank = num_bytes % 32;
  uint16_t addressChank = 0;

  for (byte i = 0; i < countChank; i++) {
    addressChank = eeaddress + 32 * i;
    Wire.beginTransmission(deviceAddress);
    Wire.write((int)(addressChank >> 8)); 
    Wire.write((int)(addressChank & 0xFF)); 
    Wire.endTransmission();
    Wire.requestFrom(deviceAddress, (num_bytes <= 32 ? num_bytes : 32));
    while (Wire.available()) *p++ = Wire.read();
  }
  if (restChank > 0) {
    Wire.beginTransmission(deviceAddress);
    Wire.write((int)((addressChank + 32) >> 8)); 
    Wire.write((int)((addressChank + 32) & 0xFF)); 
    Wire.endTransmission();
    Wire.requestFrom(deviceAddress, restChank);
    while (Wire.available()) *p++ = Wire.read();
  }

}



template <class T> void  EEPROM_put(uint16_t eeaddress, const T& value) {
  const byte* p = (const byte*)(const void*)&value;

  byte i = 0, counter = 0;
  uint16_t address;
  byte page_space;
  byte page = 0;
  byte num_writes;
  uint16_t data_len = 0;
  byte first_write_size;
  byte last_write_size;
  byte write_size;

  // Calculate length of data
  data_len = sizeof(value);

  // Calculate space available in first page
  page_space = int(((eeaddress / pageSize) + 1) * pageSize) - eeaddress;

  // Calculate first write size
  if (page_space > 16) {
    first_write_size = page_space - ((page_space / 16) * 16);
    if (first_write_size == 0) {
      first_write_size = 16;
    }
  }
  else {
    first_write_size = page_space;
  }

  // calculate size of last write
  if (data_len > first_write_size) {
    last_write_size = (data_len - first_write_size) % 16;
  }

  // Calculate how many writes we need
  if (data_len > first_write_size) {
    num_writes = ((data_len - first_write_size) / 16) + 2;
  }
  else {
    num_writes = 1;
  }

  i = 0;
  address = eeaddress;
  for (page = 0; page < num_writes; page++)   {
    if (page == 0) {
      write_size = first_write_size;
    }
    else if (page == (num_writes - 1)) {
      write_size = last_write_size;
    }
    else {
      write_size = 16;
    }

    Wire.beginTransmission(deviceAddress);
    Wire.write((int)((address) >> 8)); 
    Wire.write((int)((address) & 0xFF)); 
    counter = 0;
    do {
      Wire.write((byte) *p++);
      i++;
      counter++;
    }
    while ((counter < write_size));
    Wire.endTransmission();
    address += write_size;                      // увеличиваем адрес для следующего буфера
    delay(5);                                   // задержка нужна для того, чтобы ЕЕПРОМ успела зарядить ячейки памяти (смотрите даташит своего устройства)
  }
}

а это проверочный скетч

#include "EEPROM24Cxxx.h"
#include <EEPROM.h>
#include <Wire.h>


const byte countCards = 20;
const byte countTlfns = 10;
const uint16_t countAprts = 500;

uint32_t cardToSearch = 100009462;
unsigned long uTime;

struct apartment_t {
  int16_t number;
  uint32_t tlfn[countTlfns];
  uint32_t card[countCards];
  bool inService;
} aprt;




void setup() {
  Wire.begin();
  Serial.begin(115200);
  Wire.setClock(400000L);
  //Serial.println(sizeof(aprt));
  //Serial.println(sizeof(conf));
  delay(500);
  

  byte result = 0;
  Serial.print(F("Writing data to 24C512..."));
  uTime = millis();
 
  ///*
  result = 0;
  for (uint16_t i = 0; i < countAprts; i++) {
    aprt.number = i + 1;
    for (byte x = 0; x < countTlfns; x++) {
      aprt.tlfn[x] = 642000000 + x * i;
    }
    for (byte y = 0; y < countCards; y++) {
      aprt.card[y] = 100000000 + y * i;
    }

    EEPROM_put(aprtAddress(i), aprt);
    result = i % 10;
    if (result == 0)Serial.print(F("#"));
    result = i % 250;
    if (result == 0)Serial.println();
  }
  Serial.println();

  Serial.println((String)F("Writed in mS ") + (String)(millis() - uTime));
  while (!Serial.available());         // в активном окне монитора порта просто нажать Энтер для продолжения выполнения скетча

  //*/


  for (uint16_t i = 0; i < countAprts; i++) {
    EEPROM_get(aprtAddress(i), aprt);
    Serial.print((String)aprt.number + ";");
    for (byte x = 0; x < countTlfns; x++) {
      Serial.print("+" + (String)conf.preTel + (String)aprt.tlfn[x] + ";");
    }
    for (byte y = 0; y < countCards; y++) {
      Serial.print((String)aprt.card[y] + ";");
    }

    Serial.println(aprt.inService == true ? "yes" : "no");

  }

  uTime = millis();
  Serial.println((String)F("Apartment: ") + (String)searchCard(cardToSearch));
  Serial.println((String)F("Found in mS ") + (String)(millis() - uTime));
  Serial.println(sizeof(aprt));
}





void loop() {
  // put your main code here, to run repeatedly:

}




uint16_t aprtAddress(uint16_t num) {
  return 100 + (num * (sizeof(aprt)));
}



uint16_t searchCard(uint32_t _card) {
 // Wire.setClock(800000L);                      //  Это работает, переключает "на лету" частоту, НО! LCD1602 с I2C на частотах выше 400кГц виснет в усмерть, вешая шину напрочь.
  for (uint16_t i = 0; i < countAprts; i++) {
    EEPROM_get(aprtAddress(i), aprt);
    for (byte x = 0; x < countCards; x++) {
      if (aprt.card[x] == _card) {
        return aprt.number;
      }
    }
  }
 // Wire.setClock(400000L);                       //
  return 0;
}

В результате, скорость записи 500 структур, размером 123 байта, занимает около 24 секунд. А поиск отрабатівает за 2.2 секунды. Многовато таки. Можно ускорить в два раза установив частоту шины 800кГц или 1МГц, но дохнет LCD1602 c i2c модулем, увешивая шину намертво. Нужно в проекте экран менять. Но в общем, результат меня вроде устраивает. Если поднять частоту (нужно ставить резисторы ниже 2кОм - пилу искажает, у меня сейчас 3кОм стоят, и екран поменять) то реакция системы находится в приемлемых 1-1.2 секунды.

Вывод

Цитата:

Apartment: 499
Found in mS 2240

 

Может кто-то из грамотных глянет код и немного причешет (особенно запись, я этот код где-то на github выдернул и подправил, но похоже его можно причесать, если не ускорить, то хотя бы оптимизировать память), может еще есть идеи по увеличению скорости чтения, запись вроде в норме, и больше не разгониш. Или хотя бы оценку даст?

 

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

LCD высадить на SoftWire?

kostyamat
Offline
Зарегистрирован: 16.11.2017

Идея хорошая, вот только пугает тот факт, что LCD_1602_RUS.h сильно завязана на Wire.h, много лопатить ( я уже молчу о том, что чел не удосужился украинские и беларуские буквы привинтить, что ИМХО не комильфо конечно, еще и это делать), а ковырять либы мне скила не хватает, пока надеюсь. Да и с этими экспериментами работа над проектом встала как вкопанная, пока я шишки набиваю и скилы качаю. Помог бы кто с экраном.

UPDATE: Украинские Її, Єє Іі и апостроф в либу добавил. Смотрю ног у меня только две и осталось, ги... как раз для софтверного Wire. Остановился на этой либе http://wiki.seeedstudio.com/Arduino_Software_I2C_user_guide/. Завтра пилить буду. Не знаю получится ли.

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

kostyamat пишет:
Да и с этими экспериментами работа над проектом встала как вкопанная, пока я шишки набиваю и скилы качаю. Помог бы кто с экраном.

Ну так это для пользы дела шишки и потеря времени. У кого мало времени - просто покупает готовое.

kostyamat пишет:
Завтра пилить буду. Не знаю получится ли.

У меня висит LCD на SoftWire. Ничего в этой ситуации драматичного кроме скорости нет )) Да и её хватает, если 30 FPS на дисплей не гнать.

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

ты еще учти, что у Wire внутренний буфер на чтение/запись  32 байта всего, включая адрес ячейки, начиная с которой ты хочешь писать. 

kostyamat
Offline
Зарегистрирован: 16.11.2017

Так учел ведь, в комментариях к коду даже прописал. Поэтому читаю фреймами по 32 байта в цикле, а потом остаток дочитываю, если он есть, а пишу фреймами по 16 байт. Точнее не пишу, а набиваю ЕЕПРОМку байтами до размера ее страницы памяти, а потом разом всю страницу и пишу.