String и PROGMEM не работают

Mini469
Offline
Зарегистрирован: 20.03.2015

Коллеги

я не могу никак поженить String и PROGMEM

код упростил как мог для вопроса, меньше не получилось :-). Вставляешь в описание переменной PROGMEM и перестает работать, убираешь - работает. Ерунда какая-то. Кто может подскажет что?


//PROGMEM
const String str = "Qwerty";

void setup() {
  Serial.begin(9600);
  Serial.println(str);
}

void loop() {
}

 

 

KOCT9I
Offline
Зарегистрирован: 20.03.2015

While PROGMEM could be used on a single variable, it is really only worth the fuss if you have a larger block of data that needs to be stored, which is usually easiest in an array, (or another C data structure beyond our present discussion).

Using PROGMEM is also a two-step procedure. After getting the data into Flash memory, it requires special methods (functions), also defined in the pgmspace.h library, to read the data from program memory back into SRAM, so we can do something useful with it.

KOCT9I
Offline
Зарегистрирован: 20.03.2015

Попробуй написать //PROGMEMIS

kisoft
kisoft аватар
Offline
Зарегистрирован: 13.11.2012

Какой особый смысл хранить константный объект во флеше?

Mini469
Offline
Зарегистрирован: 20.03.2015

Большое меню - константный объект

UNO кряхтит, жалуется

Mini469
Offline
Зарегистрирован: 20.03.2015

KOCT9I пишет:

Попробуй написать //PROGMEMIS

А что это?

Я его не нашел в <avr/pgmspace.h>

И компилятор ругается

kisoft
kisoft аватар
Offline
Зарегистрирован: 13.11.2012

Mini469 пишет:

Большое меню - константный объект

UNO кряхтит, жалуется


Можно хотя бы маленький кусочек для примера? Не верю, что нужен именно String, именно объект. String можно заменить на const char или плоский массив const char, если там могут быть '\0'

Mini469
Offline
Зарегистрирован: 20.03.2015

kisoft пишет:
Mini469 пишет:

Большое меню - константный объект

UNO кряхтит, жалуется

Можно хотя бы маленький кусочек для примера? Не верю, что нужен именно String, именно объект. String можно заменить на const char или плоский массив const char, если там могут быть '\0'

да можно и без String - просто с ними удобнее, можно и без PROGMEMа - мега съест.

Просто за державу обидно, да и на нано хотелось уместиться.

просто на первый взгляд должно работать?

kisoft
kisoft аватар
Offline
Зарегистрирован: 13.11.2012

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

Пример жалко? Ладно, не настаиваю, Ваше право.

 

Mini469
Offline
Зарегистрирован: 20.03.2015

kisoft пишет:

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

Пример жалко? Ладно, не настаиваю, Ваше право.

 

Совсем не жалко, но пример чего? Пример того что не работает я приладил

а то что я пишу - пока кучка скетчей разных подсистем - показывать то нечего, просто на один из них ругнулась, что памяти маловато. Там конечно полно отладочных Print-ов, но это и не вся задумка - там управление мощностью, 3-х точечный термостат, управление потоком жидкости и большое меню, вот и решил сплавить во флэш. Ан нет, не лезет :-) 

я правильно понимаю проблему: PROGMEM и String не уживутся?

Но вот почему это тогда вот это с PROGMEM не работает, а без - работает?:

какое-то ругательное это слово PROGMEM...... в  eeprom уберу тогда хоть что-то, если поплохеет

//PROGMEM        
const  char*  str [2] [2]  = { {"QWERTY", "qwerty"},
                               {"ASDFGH", "asdfgh"}
 };

void setup() {
  Serial.begin(9600);
  Serial.println(str[0][0]);
}

void loop() {
}
kisoft
kisoft аватар
Offline
Зарегистрирован: 13.11.2012

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

 

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

Можете смеяться, но у меня оба примера работают, и с PROGMEM и без.

Во втором только надо добавить const  в правильном месте:











PROGMEM        
const char* const str [2] [2] = { {"QWERTY", "qwerty"},
                                  {"ASDFGH", "asdfgh"}
};

Почему работает - так сразу не пойму.

По-моему, такое может быть, если разработчики специально предусмотрели версию Serial.println понимающую указатель на строку в PROGMEM. Выяснять и уточнять не полезу - лениво мне. :)

А насчёт первого примера - вряд ли там будет заметная экономия от использования PROGMEM. Объект String, скорее всего, состоит только из указателя на строку. Ну ещё, может быть, её длины.

Но при этом сама строка размещается всё равно в обычной памяти, выделенной функцией malloc.

-----

Upd: Вот, попробуйте если интересно.

String Str( "1234567890" );

void setup( )
 {
  Serial.begin( 9600 );
  Serial.println( Str );
  Serial.print( sizeof( Str ) );
}

void loop( ) { }

Оказывается, размер объекта типа String всегда равен 6 байтам, независимо от длины самого текста.

(Кроме указателя и длины, там ещё какой-то array length. В чём разница не понял, но по смыслу, думаю, это размер зарезервированной памяти, который может быть больше длины строки).

Mini469
Offline
Зарегистрирован: 20.03.2015

Datak пишет:

Можете смеяться, но у меня оба примера работают, и с PROGMEM и без

-----

Спасибо,

а какая у Вас версия IDE?

Я поставил 1.5.8. Да еще на маке.  Может просто мой глюк с IDE? Я согласен с тем, что выгоды может не быть, но откровенный мусор при использовании PROGMEMа выводить то не должно, явно при его использовании указатели начинают показывать на деревню дедушке  

Ваш код попробую

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

У меня 1.5.7 на XP, правда сомневаюсь, что от этого зависит.

Не подскажу я в чём там дело, не знаю.

Logik
Offline
Зарегистрирован: 05.08.2014

И вот что входит (v.1.6 ковырял). Как ни странно, но String таки умеет работать с PROGMEM. Для этого служит класс





// When compiling programs with this class, the following gcc parameters
// dramatically increase performance and memory (RAM) efficiency, typically
// with little or no increase in code size.
//     -felide-constructors
//     -std=c++0x

class __FlashStringHelper;

Используемый в соответствующих методах и операторах, например метод





String & String::copy(const __FlashStringHelper *pstr, unsigned int length)
{
	if (!reserve(length)) {
		invalidate();
		return *this;
	}
	len = length;
	strcpy_P(buffer, (PGM_P)pstr);
	return *this;
}

Обратите внимание на функцию strcpy_P, таких прелеснейших много, лежат в ..\hardware\tools\avr\avr\include\avr\pgmspace.h и ждут когда мы их заюзаем. На всяк случай скопипащу сюды, может кому пригодятся, мне например.





extern PGM_VOID_P memchr_P(PGM_VOID_P, int __val, size_t __len) __ATTR_CONST__;
extern int memcmp_P(const void *, PGM_VOID_P, size_t) __ATTR_PURE__;
extern void *memcpy_P(void *, PGM_VOID_P, size_t);
extern void *memmem_P(const void *, size_t, PGM_VOID_P, size_t) __ATTR_PURE__;
extern PGM_VOID_P memrchr_P(PGM_VOID_P, int __val, size_t __len) __ATTR_CONST__;
extern char *strcat_P(char *, PGM_P);
extern PGM_P strchr_P(PGM_P, int __val) __ATTR_CONST__;
extern PGM_P strchrnul_P(PGM_P, int __val) __ATTR_CONST__;
extern int strcmp_P(const char *, PGM_P) __ATTR_PURE__;
extern char *strcpy_P(char *, PGM_P);
extern int strcasecmp_P(const char *, PGM_P) __ATTR_PURE__;
extern char *strcasestr_P(const char *, PGM_P) __ATTR_PURE__;
extern size_t strcspn_P(const char *__s, PGM_P __reject) __ATTR_PURE__;
extern size_t strlcat_P (char *, PGM_P, size_t );
extern size_t strlcpy_P (char *, PGM_P, size_t );
extern size_t strlen_P(PGM_P) __ATTR_CONST__; /* program memory can't change */
extern size_t strnlen_P(PGM_P, size_t) __ATTR_CONST__; /* program memory can't change */
extern int strncmp_P(const char *, PGM_P, size_t) __ATTR_PURE__;
extern int strncasecmp_P(const char *, PGM_P, size_t) __ATTR_PURE__;
extern char *strncat_P(char *, PGM_P, size_t);
extern char *strncpy_P(char *, PGM_P, size_t);
extern char *strpbrk_P(const char *__s, PGM_P __accept) __ATTR_PURE__;
extern PGM_P strrchr_P(PGM_P, int __val) __ATTR_CONST__;
extern char *strsep_P(char **__sp, PGM_P __delim);
extern size_t strspn_P(const char *__s, PGM_P __accept) __ATTR_PURE__;
extern char *strstr_P(const char *, PGM_P) __ATTR_PURE__;

 

Mini469
Offline
Зарегистрирован: 20.03.2015

откомпилированный в новом IDE 1.6.1 скетч (оба, с учетом замечания про второй const) работают

похоже это косяк IDE:

в ARDUINO 1.6.0rc1 упоминается:

[libraries]

* LiquidCrystal: added setRowOffsets function to support different LCD hardware configurations (Mark Sproul)
* LiquidCrystal: various improvements and optimizations (Matthijs Kooijman)
* Fixed PROGMEM error in Robot_Control/examples/explore/R06_Wheel_Calibration
* SD: Fixed SPI transaction mismatch (Paul Stoffregen)

и еще 

ARDUINO 1.6.0rc3 - 2015.02.03


[ide]
* Bunch of bugfix on MacOSX build (https://github.com/arduino/Arduino/pull/2590)
 
возможно это оно...
pilnikov
pilnikov аватар
Offline
Зарегистрирован: 28.08.2015

Доброго времени суток уважаемые форумчане. Помогите плиз нубу. Пытаюсь научить "великому и могучему" LCD  16х2. Столкнулся с проблемой копирования строчек типа wchar_t из PROGMEM. 

Библиотека русификации LiquidCrystal_1602_RUS выводит символы кириллицы (utf8) только когда они типа wchar_t .

вот так работает:

  lcd.print(L"Подождите!");
Кусок кода
PROGMEM const wchar_t month_01[] = L"Фев";

PROGMEM const wchar_t * const month_table[] =
{ month_00, month_01, month_02, month_03, month_04, month_05, month_06,
  month_07, month_08, month_09, month_10, month_11
};
когда данные были типа char работало так:
  char msg_buffer[30];
  strcpy_P(msg_buffer, (char*)pgm_read_word(&(month_table[number]))); // Необходимые преобразования типов и разыменования для копирования.
  lcd.print(msg_buffer);
  Serial.print(msg_buffer);
}

сейчас эта же конструкция работает как char, а вот аналога strcpy_P для wchar_t я не могу подобрать.

если сделать вот так :

  wchar_t msg_buffer[30];
  strcpy_P(msg_buffer, (wchar_t*)pgm_read_word(&(month_table[number]))); // Необходимые преобразования типов и разыменования для копирования.
  lcd.print(msg_buffer);

то ругается на несоответствие типов для strcpy_P (cannot convert 'wchar_t*' to 'char*' for argument '1' to 'char* strcpy_P(char*, const char*)' )

DIYMan
DIYMan аватар
Offline
Зарегистрирован: 23.11.2015

pilnikov пишет:

то ругается на несоответствие типов для strcpy_P (cannot convert 'wchar_t*' to 'char*' for argument '1' to 'char* strcpy_P(char*, const char*)' )

А что мешает вычитать строку побайтово, юзая pgm_read_byte? У вас есть массив word (коим и является wchar_t на данной платформе), который расположен во флеше попа к писе, т.е. последовательно. Получаете указатель на его начало, и вычитываете побайтово. Навскидку, как вариант.