struct array и PROGMEM - как записать структуру массива и сам строковый массив в Flash-память

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

sadman41 пишет:

Конечно, сообщество меня изругает...

да ладно - хочется быть добрым - будь!

Только сам потом не пожалей.

traveler
Offline
Зарегистрирован: 27.09.2018

sadman41 пишет:

    strncpy_P(incomingNumber, (char*) users[i].phoneNum, sizeof(listUsr_t::phoneNum));

char val[] = ""  - не правильно скопировал....

Объясните, пожалуйста, ещё раз, зачем в указателе на строку стоит (char*), зачем????

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

Да че-то по привычке тип привел. Без него тоже работает.

traveler
Offline
Зарегистрирован: 27.09.2018

sadman41 пишет:

Не мог пройти мимо, даже нану новую вытащил из упаковки. Конечно, сообщество меня изругает...

#include <avr/pgmspace.h>

  struct listUsr_t
  {
  const char phoneNum[13];
  const char phonePass[5];
  const char phonePDU[13];
  };

  const listUsr_t PROGMEM users[] =
  {
  { "70000000000", "00000", "000000000000"},
  { "70000000001", "00001", "000000000000"},
  { "70000000002", "00002", "000000000000"},
  };

void setup() {
  char incomingNumber[20];
  Serial.begin(115200);
  Serial.println("Begin!");

  for (int i = 0; i < 3; i++) {
    strncpy_P(incomingNumber, (char*) users[i].phoneNum, sizeof(listUsr_t::phoneNum));
    Serial.println(incomingNumber);
  }
}

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

И еще я не совсем уверен, что sizeof можно напускать на такого жука... Ну, если что - поправят. 

Почему-то в данном примере помещение строковых констант прямо в массив эквивалентно помещению в массив ссылок на строковые константы (как на arduino.cc), но попытка перетащить данный пример в реальный скетч почему-то дает разные результаты по компиляции. Надо будет этот вопрос изучить. Может компилятор умничает опять.

char val[] = ""  - это чего, зарезервировали нуль байт памяти, а потом туда строку копируете? Вы там надеюсь, игрушку-пердушку собираете, а не управление газовым котлом...

СПАСИБО! Если сможете, объясните про (char*) перед указателем на строку в массиве...

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

b707 пишет:

да ладно - хочется быть добрым - будь!

Только сам потом не пожалей.

Выглядит угрожающе ))

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

sadman41 пишет:

Не мог пройти мимо, даже нану новую вытащил из упаковки. Конечно, сообщество меня изругает...

#include <avr/pgmspace.h>

 for (int i = 0; i < 3; i++) {
    strncpy_P(incomingNumber, (char*) users[i].phoneNum, sizeof(listUsr_t::phoneNum));
    Serial.println(incomingNumber);
  }
}

void loop() {
 
}

интересно, зачем второй параметр strncpy_P() приводить к типу char* - это ж специальная функция для строк во флеше, она и без этого адрес строки должна понимать... точно там нужно приведение?

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

Нет, можно без приведения, но я же тоже знатный копипастер )) Бахнул с готового кода, не отчистил. Внутренний рецензент тоже не зацепился - хуже-то не будет, говорит.

А теперь уже все, процитировано, не исправишь. Простите, люди добрые.

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

b707 пишет:

интересно, зачем второй параметр strncpy_P() приводить к типу char* - это ж специальная функция для строк во флеше, она и без этого адрес строки должна понимать... точно там нужно приведение?

Кстати, https://www.nongnu.org/avr-libc/user-manual/pgmspace.html

void foo(void)
{
    char buffer[10];
    
    for (unsigned char i = 0; i < 5; i++)
    {
        strcpy_P(buffer, (PGM_P)pgm_read_word(&(string_table[i])));
        
        // Display buffer on LCD.
    }
    return;
}

Наверное из такого источника я, ранее, в свой код и притащил приведение типа.

https://www.arduino.cc/reference/en/language/variables/utilities/progmem/

for (int i = 0; i < 6; i++)
  {
    strcpy_P(buffer, (char*)pgm_read_word(&(string_table[i]))); // Necessary casts and dereferencing, just copy.
    Serial.println(buffer);
    delay( 500 );
  }

 

sadman41
Offline
Зарегистрирован: 19.10.2016
Решил таки разобраться с тем, почему в реальном скетче я начал проигрывать в ресурсе. Заодно освежить знания по progmem и проиллюстрировать разные способы.
 
Забегая вперед скажу, что ТС избрал (осмысленно или нет - не знаю) как самый экономный по ресурсу для его задачи вариант, так и более красивый.
 
№1 (классический)
 
Ключевая особенность: в PGM структуре хранятся ссылки на PGM строки, т.е. в RAM ничего не подгружается
 
Плюсы: строки одного поля структуры могут быть разной длины, и это не дает оверхеда, каков мог быть в №3. Так же можно одну строку (указатель на нее) использовать в разных полях структуры и повторять в одних и тех же совершенно безнаказанно (без какого-либо пенальти).
Минусы: много писанины.
 
Sketch uses 1,138 bytes (3%) of program storage space. Maximum is 32,256 bytes.
Global variables use 21 bytes (1%) of dynamic memory, leaving 2,027 bytes for local variables. Maximum is 2,048 bytes.
 
#pragma GCC optimize ("O0")
struct listUsr_t
{
  const char* phoneNum;
  const char* phonePass;
  const char* phonePDU;
};

const char number_70000000000[] PROGMEM = "70000000000";
const char number_70000000001[] PROGMEM = "70000000002";
const char number_70000000002[] PROGMEM = "70000000003";

const char pass_70000000000[] PROGMEM = "00000";
const char pass_70000000001[] PROGMEM = "00001";
const char pass_70000000002[] PROGMEM = "00002";

const char pdu_70000000000[] PROGMEM = "000000000000";
const char pdu_70000000001[] PROGMEM = "000000000000";
const char pdu_70000000002[] PROGMEM = "000000000000";

const listUsr_t PROGMEM users[] =
{
  { number_70000000000, pass_70000000000, pdu_70000000000},
  { number_70000000001, pass_70000000001, pdu_70000000001},
  { number_70000000002, pass_70000000002, pdu_70000000002},
};

void setup() {
  char incomingNumber[] = "70000000002";
  pinMode(13, OUTPUT);
  digitalWrite(13, LOW);

  for (int i = 0; i < 3; i++) {
    // if (0x00 == strncmp_P(incomingNumber, (const char*) pgm_read_word(&(users[i].phoneNum)), 12))
    if (0x00 == strncmp_P(incomingNumber, (const char*) users[i].phoneNum, 12))
    {
      digitalWrite(13, HIGH);
    }
  }
}
void loop() {}
 
№2 (без фиксированной размерности поля структуры)
 
Ключевая особенность: в PGM структуре хранятся ссылки на строки, которые хранятся в RAM, ибо "Unfortunately, with GCC attributes, they affect only the declaration that they are attached to. So in this case, we successfully put the string_table variable, the array itself, in the Program Space. This DOES NOT put the actual strings themselves into Program Space. "
 
Плюсы: "красота" кода.
Минусы: бессмысленный оверхед по RAM, ожидаемый эффект практически не достигается
 
Sketch uses 1,088 bytes (3%) of program storage space. Maximum is 32,256 bytes.
Global variables use 65 bytes (3%) of dynamic memory, leaving 1,983 bytes for local variables. Maximum is 2,048 bytes.
 
#pragma GCC optimize ("O0")
struct listUsr_t
{
  const char* phoneNum;
  const char* phonePass;
  const char* phonePDU;
};

const listUsr_t PROGMEM users[] =
{
  { "70000000000", "00000", "000000000000"},
  { "70000000001", "00001", "000000000000"},
  { "70000000002", "00002", "000000000000"},
};

void setup() {
  char incomingNumber[] = "70000000001";
  pinMode(13, OUTPUT);
  digitalWrite(13, LOW);

  for (int i = 0; i < 3; i++) {
    //if (0x00 == strncmp(incomingNumber, (const char*) pgm_read_word(&(users[i].phoneNum)), 12)) 
    if (0x00 == strncmp_P(incomingNumber, (const char*) users[i].phoneNum, 12)) 
    {
      digitalWrite(13, HIGH);
    }
  }
}
void loop() {}

№3 (изначальный)

Ключевая особенность: в PGM структуре строки хранятся сразу как массивы символов (последовательности байт), а не через указатели на таковые массивы, поэтому нет необходимости в предварительном объявлении строк, как PGM-ресурса. Доступ к первой ячейке какой-либо строки осуществляется по простому смещению от начального адреса массива структур.
 
Плюсы: красота кода.
Минусы: строки с разной длиной принесут оверхед по progmem space, так как компилятор будет резервировать блоки объявленной размерности в любом случае.
 
Sketch uses 1,114 bytes (3%) of program storage space. Maximum is 32,256 bytes.
Global variables use 21 bytes (1%) of dynamic memory, leaving 2,027 bytes for local variables. Maximum is 2,048 bytes.
 
#pragma GCC optimize ("O0")
struct listUsr_t
{
  const char phoneNum[12];
  const char phonePass[6];
  const char phonePDU[13];
};

const listUsr_t PROGMEM users[] =
{
  { "70000000000", "00000", "000000000000"},
  { "70000000001", "00001", "000000000000"},
  { "70000000002", "00002", "000000000000"},
};

void setup() {
  char incomingNumber[] = "70000000001";
  pinMode(13, OUTPUT);
  digitalWrite(13, LOW);

  for (int i = 0; i < 3; i++) {
     if (0x00 == strncmp_P(incomingNumber, (const char*) users[i].phoneNum, 12)) 
    {
      digitalWrite(13, HIGH);
    }
  }
}
void loop() {}

P.S. В фрагментах кода закомментированы работающие методы получения правильного указателя на строку для каждого случая, так как сначала я оставил во всех трех один и те же, чтобы исследовать изменения только в объемах хранимых данных. Естественно, что для двух из трех этот метод не работал. Исправляюсь.

 

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

sadman41, отличное исследование!

А фраза про "Unfortunately... " - это откуда цитата?
Когда читаешь - вроде очевидно, но самому в голову не пришло.

 

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

b707 пишет:

sadman41, отличное исследование!

А фраза про "Unfortunately... " - это откуда цитата?
Когда читаешь - вроде очевидно, но самому в голову не пришло.

Отседова: https://www.nongnu.org/avr-libc/user-manual/pgmspace.html

traveler
Offline
Зарегистрирован: 27.09.2018

Почитал, очень даже доступно и качественно написано, и, исследовано.

Спасибо, еще раз!!!

sanek_c2012
Offline
Зарегистрирован: 17.02.2019

Привет, не поможешь разобраться?

traveler
Offline
Зарегистрирован: 27.09.2018

sanek_c2012 пишет:

Привет, не поможешь разобраться?

разобраться в чём???

sanek_c2012
Offline
Зарегистрирован: 17.02.2019

Нужно вывести битмап на матрицу 100х40 не получается засунуть фреймы в массив, чтобы перебирать его попорядку

traveler
Offline
Зарегистрирован: 27.09.2018

Массив в какую память писать?

sanek_c2012
Offline
Зарегистрирован: 17.02.2019

Я его progmem во флешь пишу, у меня проблема с выводом на ws2812 не знаю толком язык и совсем запутался

sanek_c2012
Offline
Зарегистрирован: 17.02.2019

Как сюда картинки скидывать?

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

sanek_c2012 пишет:

Как сюда картинки скидывать?

если не знаешь язык - то и картинки скидывать незачем.

Я хочу сразу предупредить - приходите с конкретными вопросами по УЖЕ НАПИСАННОМУ ВАМИ коду. Вопросы "не получается, покажите как" - здесь не прокатывают.

 

И еще - примеров, как эффекты для светодиодов во флеш положить - в гугле сотни.

sanek_c2012
Offline
Зарегистрирован: 17.02.2019

Я написал, есть битмап массив, примерно 30 блоков, как их вывести на ws2812, я их вывожу но хотелось бы более изящное решение

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

sanek_c2012 пишет:

Я написал, есть битмап массив, примерно 30 блоков, как их вывести на ws2812, я их вывожу но хотелось бы более изящное решение

не вижу. где вы написали

А вообще - в разделе "Песочница" прочтите приклееную тему - многие вопросы отпадут

sunjob
sunjob аватар
Offline
Зарегистрирован: 18.07.2013

sadman41 пишет:

... 3 варианта реализации ...

В фрагментах кода закомментированы работающие методы получения правильного указателя на строку для каждого случая, так как сначала я оставил во всех трех один и те же, чтобы исследовать изменения только в объемах хранимых данных. Естественно, что для двух из трех этот метод не работал. Исправляюсь.

так можно было вообще все удалить, оставив только инициализацию массива в setup() и пустой loopn()  :o)

pure cpp-code / avr_gcc-5.4.0 (только описание данных, без печати, пустой main() ... )

# DEBUG - dwarf-2 (для всех)

# 1 - DBG
Program:     658 bytes (2.0% Full)
Data:         36 bytes (1.8% Full)

# 2 - DBG
Program:     630 bytes (1.9% Full)
Data:        102 bytes (5.0% Full)

# 3 - DBG
Program:     640 bytes (2.0% Full)
Data:         36 bytes (1.8% Full)

# оптимизация -O2 (у всех одинаковый размер)
Program:     410 bytes (1.3% Full)
Data:         36 bytes (1.8% Full)

в принципе, те-же результаты

у меня были сомнение на счет 3го метода, т.к. за счет "фрагментарности" д.б. занимать больше места... но оптимизация взяла свое :о) ...