очередной раз PROGMEM

5N62V
Offline
Зарегистрирован: 25.02.2016

Всем бобра!

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

Собсно код:

#include <avr\pgmspace.h>

const char str_0[] PROGMEM = "Для выхода - SELECT";
const char str_1[] PROGMEM = "ЗАДЕРЖКА ВКЛЮЧЕНИЯ";
const char str_2[] PROGMEM = "ДО СВИДАНИЯ!";
const char* const text[] PROGMEM = {str_0, str_1, str_2};


void setup() {
Serial.begin(115200);
while(!Serial);
  Serial.println("Мы тут");
}

void loop() {

for (byte i = 0; i<3; i++) softPrint(text[i]);

}

void softPrint( const char* const st){

  char textBuffer[30];
 // strcpy_P(textBuffer, (char*)pgm_read_word(st));
  strcpy_P(textBuffer, (char*)__LPM_word((uint16_t)(st)));
  Serial.println(textBuffer);
   
}

Толи я аргумент неправильно в функцию передаю, толи я его принимаю не правильно, но скорее всего я его обрабатываю не верно. Код компилится, и зависает.  В сериал печатается только М.

Может кто подскажет как правильно?  Здоровый глум и кидание ссылками приветствуются! 

5N62V
Offline
Зарегистрирован: 25.02.2016

Слегка модефицировал :

#include <avr\pgmspace.h>

const char str_0[] PROGMEM = "Для выхода - SELECT";
const char str_1[] PROGMEM = "ЗАДЕРЖКА ВКЛЮЧЕНИЯ";
const char str_2[] PROGMEM = "ДО СВИДАНИЯ!";
const char* const text[] PROGMEM = {str_0, str_1, str_2};


void setup() {
Serial.begin(115200);
while(!Serial);
  Serial.println("Мы тут");
}

void loop() {

for (byte i = 0; i<3; i++) {
  Serial.println((uint16_t)text[i]);
  delay(1000);
  softPrint(text[i]);

              }

}

void softPrint( const char* const st){

  char textBuffer[30];
 // strcpy_P(textBuffer, (char*)pgm_read_word(st));
  strcpy_P(textBuffer, (char*)__LPM_word((uint16_t)(st)));
  Serial.println(textBuffer);
   
}

В итоге вечный перегруз:

Мы тут
47106
%⸮d
1542
Мы тут
47106
%⸮d
1542
......
DIYMan
DIYMan аватар
Offline
Зарегистрирован: 23.11.2015

Читать до просветления: https://playground.arduino.cc/Main/PROGMEM

5N62V
Offline
Зарегистрирован: 25.02.2016

DIYMan пишет:

Читать до просветления: https://playground.arduino.cc/Main/PROGMEM

Да я читал это.  У меня все работало до тех пор, пока я не попытался блок копирования в буфер и печати вынести в отдельную функцию. И тут меня заклинило. 

pgm_read_word, ну или __LPM_word((uint16_t)(х)), что то же самое, требуют передать адрес. И по всей видимости 47106 - это и есть адрес, который передается первый раз. 

Безусловно я где-то косячу, но где - понять не могу.

 

 

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

Плохо читали, видимо. Вопрос: у вас параметр функции какой тип имеет? В нём есть ключевое слово PROGMEM, указывающее, что это адрес в PROGMEM? Вот как ответите на этот вопрос - сразу станет легче ;)

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

У Вас сами указатели (массив text) тоже сидит в PROGMEM, а Вы используете их в строке 6 как если бы они были в оперативной памяти.

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

5N62V - небольшой совет. Не кладите массив text[] в PROGMEM. В нем же только ссылки, места они занимают мало. Зато код станет значительно проще и примерно вдвое короче.

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

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

У Вас сами указатели (массив text) тоже сидит в PROGMEM, а Вы используете их в строке 6 как если бы они были в оперативной памяти.

Евгений, в строке 6 все нормально. Указатели на PROGMEM -такие же указатели, как любые другие. Их можно сохранить в массиве без вских "оберток". Главное - при доступе к содержимому по этим указателям сообщить компилятору, что это ПРОГМЕМ. Проблема - в процедуре SOftPrint

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

#include <avr\pgmspace.h>

const char str_0[] PROGMEM = "Для выхода - SELECT";
const char str_1[] PROGMEM = "ЗАДЕРЖКА ВКЛЮЧЕНИЯ";
const char str_2[] PROGMEM = "ДО СВИДАНИЯ!";
const char* const text[] = {str_0, str_1, str_2};


void setup() {
Serial.begin(115200);
while(!Serial);
  Serial.println("Мы тут");
}

void loop() {

for (byte i = 0; i<3; i++) {
  Serial.println( (const __FlashStringHelper *)  text[i]);
  delay(1000);
 }

такой вариант. И никакой буфер для печати не нужен.

5N62V
Offline
Зарегистрирован: 25.02.2016

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

У Вас сами указатели (массив text) тоже сидит в PROGMEM, а Вы используете их в строке 6 как если бы они были в оперативной памяти.

Мысль не понял. В точности передрано из первоисточника , на который DIY-Man ссылался.

5N62V
Offline
Зарегистрирован: 25.02.2016

b707 пишет:



такой вариант. И никакой буфер для печати не нужен.

Благодарю. Обязательно попробую. Но тут как бы все равно нужно понимание, как сообщить функции, что переданный адрес лежит в ПРОГМЕМ.  Мне без отдельной функции - никуда. Потому как есть меню, есть туева хуча функций под каждое меню, которые по идее должны выдергивать строки из ПРОГМЕМ и разкидывать их по дисплею.

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

5N62V пишет:
Мысль не понял. В точности передрано из первоисточника , на который DIY-Man ссылался.

Возможно, я неясно выразился, поскольку другие тоже не поняли.

Вот смотрите. Здесь Ваша программа с минимальными изменениями - она отлично работает, все строки печатаются правильно. Проверяйте. А вот Ваша uint16_t)text[i] - печатается странно.

Посмотрите на строку 24. Сравните с тем, что было у Вас. Думаю, всё поймёте.

#include <avr\pgmspace.h>

const char str_0[] PROGMEM = "For exit - SELECT";
const char str_1[] PROGMEM = "Delayed power on";
const char str_2[] PROGMEM = "Bye!";
const char * const text[] PROGMEM = {str_0, str_1, str_2};

void setup(void) {
	Serial.begin(115200);
	while (!Serial);
	Serial.println("Fun begins!");
}

void loop(void) {
	for (byte i = 0; i < 3; i++) {
		Serial.println((uint16_t)text[i]);
		delay(1000);
		softPrint(i);
	}
}

void softPrint(const int index) {
	char textBuffer[30];
	strcpy_P(textBuffer, pgm_read_word_near((int)(text + index)));
	Serial.println(textBuffer);
}

Если непонятки останутся - не стесняйтесь, спрашивайте.

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

5N62V пишет:

 Но тут как бы все равно нужно понимание, как сообщить функции, что переданный адрес лежит в ПРОГМЕМ. 

https://gist.github.com/sticilface/e54016485fcccd10950e93ddcd4461a3

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

5N62V пишет:
как бы все равно нужно понимание
Золотые слова!

Если после моего поста в голове не устаканилось как это делать на пальцах, и почему в строке 16 печатается чушь и как её поправить, то я могу пройтись каждой букве, не стесняйтесь.

5N62V
Offline
Зарегистрирован: 25.02.2016

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

5N62V пишет:
как бы все равно нужно понимание
Золотые слова!

Если после моего поста в голове не устаканилось как это делать на пальцах, и почему в строке 16 печатается чушь и как её поправить, то я могу пройтись каждой букве, не стесняйтесь.

Семен Семеныч! (с)  :))) Да, натупил я мощно. Как у Вас....  элегантно получилось! Все понятно! Спасибище!    Работает супер! :)))

Ну ок, у меня с элегантными решениями проблема из-за недостатка опыта. Я как понимаю, так и пишу, как чукча.  Но интереса ради (знаний для) , как правильно передать в ф-цию указатель на строку из ПРОГМЕМ,  на базе моего примера?

Я нашел, что нужно использовать PGM_P, но как его не ставлю, компилятор ругается...

5N62V
Offline
Зарегистрирован: 25.02.2016

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

 почему в строке 16 печатается чушь и как её поправить, то я могу пройтись каждой букве, не стесняйтесь.

Про чушь раскройте мысль, если не затруднит. Спасибо!

5N62V
Offline
Зарегистрирован: 25.02.2016

5N62V пишет:

 Но интереса ради (знаний для) , как правильно передать в ф-цию указатель на строку из ПРОГМЕМ,  на базе моего примера?

Я нашел, что нужно использовать PGM_P, но как его не ставлю, компилятор ругается...

Вопрос снимается. Ответ в коде: нафига передавать указатель, если как правило строки в ПРОГМЕМ глобальные. Проще , действительно, передавать индекс.  Разве что вопрос может быть актуален для строк, которые объявлены в какой-то функции как  static помещены в ПРОГМЕМ.

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

5N62V пишет:

[Вопрос снимается. Ответ в коде: нафига передавать указатель, если как правило строки в ПРОГМЕМ глобальные. Проще , действительно, передавать индекс. 

по-моему, вот это как раз чушь. При чем тут глобальность строк? В коде Евгения передается как раз указатель, а не индекс - посмотрите внимательнее.

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

по русски. вот это

pgm_read_word_near((int)(text + index))

отдает слово (адрес), которое прочитало из прогмема по адресу text+index 

5N62V
Offline
Зарегистрирован: 25.02.2016

b707 пишет:

по-моему, вот это как раз чушь. При чем тут глобальность строк? В коде Евгения передается как раз указатель, а не индекс - посмотрите внимательнее.

  массив указателей глобален, не так выразился.

Посмотрел. Как я понял - передается индекс, который прибавляется к указателю на массив указателей, и по сути индексирует его.  Равзе не так?

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

5N62V пишет:

 Как я понял - передается индекс, который прибавляется к указателю на массив указателей, и по сути индексирует его.  Равзе не так?

В любом массиве Си выражение (text +index) - это указатель на элемент text[index]. И не важно, что за массив - глобальный, локальный. статический... и где он лежит - в оперативке или на флеше.

5N62V
Offline
Зарегистрирован: 25.02.2016

b707 пишет:

В любом массиве Си выражение (text +index) - это указатель на элемент text[index]. И не важно, что за массив - глобальный, локальный. статический... и где он лежит - в оперативке или на флеше.

Не совсем понятно в чем я не прав.... Передали byte, приняли int, в результате операций получили указатель на ячейку, где лежит адрес нужной нам строки в ПРОГМЕМ. 

text +index - это у нас уже внутри функции, а передали-то мы только index!

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

5N62V пишет:

text +index - это у нас уже внутри функции, а передали-то мы только index!

тогда вы правы. я думал вы про строку 24

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

5N62V, понимаете, с указателями в прогмем, это люди просто перешли на более высокий уровень абстракции и запудрили мозги. Давайте снимем пудру и поймём насколько всё просто. Сначала поговорим, а потом я сделаю маленикий пример с передачей в функцию именно "указателя".

Итак, что нужно понимать чётко и не давать запудирь себе мозг: Адрес в прогмем (как и любой другой указатель) это число. Вот это держите в голове всё время.

Дальше. Вот в этой записи

const char str_0[] PROGMEM = "For exit - SELECT";
const char str_1[] PROGMEM = "Delayed power on";
const char str_2[] PROGMEM = "Bye!";
const char * const text[] PROGMEM = {str_0, str_1, str_2};

на самом деле написано, что str_0, str_1 и str_2 - это адреса (суть числа) соответствующих строк в прогмем. Заметьте эти адреса по любому глобальны, т.к. сам прогмем глобален.

А вот массив text содержит эти самые адреса. То есть

text[0] - это адрес в прогмем строки "For exit - SELECT" (обозначим его чере А)
a text+0 - это адрес в прогмем адреса А. Т.е. это адрес в прогмем по которому в прогмем хранится адрес строки "For exit - SELECT"

text[1] - это адрес в прогмем строки "Delayed power on" (обозначим его чере B)
a text+1 - это адрес в прогмем адреса B. Т.е. это адрес в прогмем по которому в прогмем хранится адрес строки ""Delayed power on"

Ещё раз - все адреса - это просто числа - номера ячеек прогмем.

Вернёмся к строке 16 в предыдущем примере. Вы там печатаете Serial.println((uint16_t)text[i]);, но ведь text[i] хранится в прогмем! А Вы печатаете нечто, что хранится в RAM по тому же адресу! 

Теперь, когда Вы понимаете, что  text+i - это адрес в прогмем адреса в прогмем i-ой строки, а text[i] - это хранящийся в прогмем адрес в прогмем i-ой троки, Вы можете передавать в Вашу функцию что хотите и как хотите.

1. индекс
2. адрес адреса строки
3. адрес строки

Пример с передачей индекса (почти как было)

#include <avr\pgmspace.h>

const char str_0[] PROGMEM = "For exit - SELECT";
const char str_1[] PROGMEM = "Delayed power on";
const char str_2[] PROGMEM = "Bye!";
const char * const text[] PROGMEM = {str_0, str_1, str_2};

void setup(void) {
	Serial.begin(115200);
	while (!Serial);
	Serial.println("Fun begins!");
	// Передаём индекс.
	// Функция должна сама взять из прогмем число по адресу "text + индекс"
	// И испольовать это число как адрес строки в прогмем
	softPrint(1);
}

void softPrint(const int index) {
	char textBuffer[30];
	// addrStroki - просто число, которое мы прочитали из прогмем
	// по адресу text + index, т.е. addrStroki - это text[index]
	const char * addrStroki = pgm_read_word_near((int)(text + index));
	strcpy_P(textBuffer, addrStroki);
	Serial.println(textBuffer);
}

void loop(void) {}

Пример с передачей адреса адреса строки

#include <avr\pgmspace.h>

const char str_0[] PROGMEM = "For exit - SELECT";
const char str_1[] PROGMEM = "Delayed power on";
const char str_2[] PROGMEM = "Bye!";
const char * const text[] PROGMEM = {str_0, str_1, str_2};

void setup(void) {
	Serial.begin(115200);
	while (!Serial);
	Serial.println("Fun begins!");
	// Передаём адрес в прогмем адреса в прогмем нужной строки
	// Функция должна сама взять из прогмем число по переданному адресу 
	// И испольовать это число как адрес строки в прогмем
	softPrint((int)(text + 1));
}

void softPrint(const int addrOfAddr) {
	char textBuffer[30];
	// addrStroki - просто число, которое мы прочитали из прогмем
	// по адресу addrOfAddr
	const char * addrStroki = pgm_read_word_near(addrOfAddr);
	strcpy_P(textBuffer, addrStroki);
	Serial.println(textBuffer);
}

void loop(void) {}

Пример с передачей адреса строки

#include <avr\pgmspace.h>

const char str_0[] PROGMEM = "For exit - SELECT";
const char str_1[] PROGMEM = "Delayed power on";
const char str_2[] PROGMEM = "Bye!";
const char * const text[] PROGMEM = {str_0, str_1, str_2};

void setup(void) {
	Serial.begin(115200);
	while (!Serial);
	Serial.println("Fun begins!");
	// Передаём готовый адрес строки в прогмем
	// Функция должна просто прочитать оттуда строку
	softPrint(pgm_read_word_near((int)(text + 1)));
}

void softPrint(const char * addrOfString) {
	char textBuffer[30];
	// addrOfString - уже готовый адрес строки - просто читаем
	strcpy_P(textBuffer, addrOfString);
	Serial.println(textBuffer);
}

void loop(void) {}

Как видите, передавать можно всё, что угодно, важно помнить где у Вас адрес чего, что где хранится, и не путаться.

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

Евгений, епром и прогмем - разные вещи.

И спасибо за "людей, запудривших мозги" :)

5N62V
Offline
Зарегистрирован: 25.02.2016

Толково Вы, Евгений , расписали! Спасибо Вам за труд! Может стоит в прикреплялку занести. Трудно переоценить полезность понимания работы с ПРОГМЕМ. 

ПС. Читаем еепром, понимаем флеш. 

5N62V
Offline
Зарегистрирован: 25.02.2016

b707 пишет:

И спасибо за "людей, запудривших мозги" :)

Да это, наверное, в адрес создателей <avr\pgmspace.h>  :))

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

b707 пишет:

Евгений, епром и прогмем - разные вещи.

Да, переклинило, сейчас поправлю.

А под "людьми" я не Вас имел в виду, если что. Но если это было обидно - извините, и в мыслях не держал кого-то обижать, извините.

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

5N62V пишет:

b707 пишет:

И спасибо за "людей, запудривших мозги" :)

Да это, наверное, в адрес создателей <avr\pgmspace.h>  :))

Точно!

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

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

А под "людьми" я не Вас имел в виду, если что. Но если это было обидно - извините, и в мыслях не держал кого-то обижать, извините.

та не, я не обиделся, там же смайл :)

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

Del.

BuonanotteMasha
BuonanotteMasha аватар
Offline
Зарегистрирован: 02.01.2018

Доброй ночи. Работает код следующим образом: в монитор порта отправляется цифра( в идеале думаю ограничить 0 - 5) и из  массива содержащего количество строк и сами строки, записанные в PROGMEM,  выводится в порт ссответствующая числу строка.

// TEST
#include <Wire.h>
#include <LiquidCrystal_I2C.h> //закомментировать если дисплей без I2C
LiquidCrystal_I2C lcd(0x27, 16, 2);  //вместо (0x3f) указываем адрес своего дисплея 
#include <avr/pgmspace.h>

char menu[6][10];

enum state_t : byte {REFLOW_STATE_IDLE,
  REFLOW_STATE_MENU_STEPS,
  REFLOW_STATE_MENU_BOTTOM_HEAT,
  REFLOW_STATE_MENU_BOTTOM_PWR,
  REFLOW_STATE_MENU_STEP_RAMP,
  REFLOW_STATE_MENU_RESET};


const char string_0[] PROGMEM = "IDLE ";
const char string_1[] PROGMEM = "STEPS";
const char string_2[] PROGMEM = "HEAT ";
const char string_3[] PROGMEM = "PWR  ";
const char string_4[] PROGMEM = "RAMP ";
const char string_5[] PROGMEM = "RESET";
  
 const char *const string_table[] PROGMEM  = { string_0, string_1, string_2, string_3, string_4, string_5 };
/*
const char* menu[] = {
  "Clean plate",
  "Working...",
  "Sensor settings",
  "Effect 1",
  "Effect 2",
  "Effect 3"
};
*/


void setup () {
  Serial.begin(9600); 
  lcd.begin(); // закомментировать если дисплей без I2C
}  // void setup()


void loop() {
  
    static state_t stateID = REFLOW_STATE_IDLE;  
    while (!Serial.available()); 
    byte s = Serial.read()-'0';
    stateID = (state_t)(s);   
    strcpy_P(menu[stateID], (char*)pgm_read_word(&(string_table[s])));
    Serial.println(menu[stateID]); 
}  // void loop()

Возможно ли упростить данный код в замене двумерного массива символов(буфера) на более простой одномерный?

char menu[6][10];

И вообще насколько грамотно написан код, взял чужой, сделал с PROGMEM. Дайте совет?

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

Вы тут массив используете, как временный буфер в 10 байт. Так что да, упростить и можно и нужно.

BuonanotteMasha
BuonanotteMasha аватар
Offline
Зарегистрирован: 02.01.2018

Подскажите каким способом если не сложно? Полностью отказаться от буфера?

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

Ну, как каким... Возьмите один char[10], а не шесть штук.

BuonanotteMasha
BuonanotteMasha аватар
Offline
Зарегистрирован: 02.01.2018

Что не то вышло... в мониторе ерунда

// TEST
#include <Wire.h>
#include <LiquidCrystal_I2C.h> //закомментировать если дисплей без I2C
LiquidCrystal_I2C lcd(0x27, 16, 2);  //вместо (0x3f) указываем адрес своего дисплея 
#include <avr/pgmspace.h>

//char menu[6][10];
char menu;

enum state_t : byte {REFLOW_STATE_IDLE,
  REFLOW_STATE_MENU_STEPS,
  REFLOW_STATE_MENU_BOTTOM_HEAT,
  REFLOW_STATE_MENU_BOTTOM_PWR,
  REFLOW_STATE_MENU_STEP_RAMP,
  /*REFLOW_STATE_MENU_STEP_TARGET,
  REFLOW_STATE_MENU_TOP_PWR,
  REFLOW_STATE_MENU_STEP_DWELL,
  REFLOW_STATE_STEP_RAMP,
  REFLOW_STATE_STEP,
  REFLOW_STATE_STEP_DWELL,
  REFLOW_STATE_COMPLETE,
  REFLOW_STATE_ERROR,*/
  REFLOW_STATE_MENU_RESET};


const char string_0[] PROGMEM = "IDLE ";
const char string_1[] PROGMEM = "STEPS";
const char string_2[] PROGMEM = "HEAT ";
const char string_3[] PROGMEM = "PWR  ";
const char string_4[] PROGMEM = "RAMP ";
const char string_5[] PROGMEM = "RESET";
  
 const char *const text[] PROGMEM  = { string_0, string_1, string_2, string_3, string_4, string_5 };
/*
const char* menu[] = {
  "Clean plate",
  "Working...",
  "Sensor settings",
  "Effect 1",
  "Effect 2",
  "Effect 3"
};
*/


void setup () {
  Serial.begin(9600); 
  lcd.begin(); // закомментировать если дисплей без I2C
}  // void setup()


void loop() {
  
    static state_t stateID = REFLOW_STATE_IDLE;  
    while (!Serial.available()); 
    byte s = Serial.read()-'0';
    stateID = (state_t)(s);  // Жмем кнопки 0 - 5. 
    //strcpy_P(menu[stateID], (char*)pgm_read_word(&(text[s])));
    Serial.println((const __FlashStringHelper *)  text[s]);
    //Serial.println(menu[stateID]); 
}  // void loop()

 

BuonanotteMasha
BuonanotteMasha аватар
Offline
Зарегистрирован: 02.01.2018

Понял что все усложняю, сделаю теперпь как мне ЕвгенийП подсказал в#24. Ему отдельная благодарность

ПС

// TEST
#include <Wire.h>
#include <LiquidCrystal_I2C.h> //закомментировать если дисплей без I2C
LiquidCrystal_I2C lcd(0x27, 16, 2);  //вместо (0x3f) указываем адрес своего дисплея 
#include <avr/pgmspace.h>

//char menu[6][10];
//char menu;
char buf[6];

/*enum state_t : byte {REFLOW_STATE_IDLE,
  REFLOW_STATE_MENU_STEPS,
  REFLOW_STATE_MENU_BOTTOM_HEAT,
  REFLOW_STATE_MENU_BOTTOM_PWR,
  REFLOW_STATE_MENU_STEP_RAMP,
  REFLOW_STATE_MENU_STEP_TARGET,
  REFLOW_STATE_MENU_TOP_PWR,
  REFLOW_STATE_MENU_STEP_DWELL,
  REFLOW_STATE_STEP_RAMP,
  REFLOW_STATE_STEP,
  REFLOW_STATE_STEP_DWELL,
  REFLOW_STATE_COMPLETE,
  REFLOW_STATE_ERROR,
  REFLOW_STATE_MENU_RESET};*/


const char string_0[] PROGMEM = "IDLE ";
const char string_1[] PROGMEM = "STEPS";
const char string_2[] PROGMEM = "HEAT ";
const char string_3[] PROGMEM = "PWR  ";
const char string_4[] PROGMEM = "RAMP ";
const char string_5[] PROGMEM = "RESET";
  
 const char *const text[] PROGMEM  = { string_0, string_1, string_2, string_3, string_4, string_5 };
/*
const char* menu[] = {
  "Clean plate",
  "Working...",
  "Sensor settings",
  "Effect 1",
  "Effect 2",
  "Effect 3"
};
*/


void setup () {
  Serial.begin(9600); 
  lcd.begin(); // закомментировать если дисплей без I2C
}  // void setup()


void loop() {
  
    //static state_t stateID = REFLOW_STATE_IDLE;  
    while (!Serial.available()); 
    byte s = Serial.read()-'0';
    //stateID = (state_t)(s);  // Жмем кнопки 0 - 5. 
    strcpy_P(buf, (char*)pgm_read_word(&(text[s])));
    //Serial.println((const __FlashStringHelper *)  text[s]);
    Serial.println(buf); 
}  // void loop()

И в итоге

// TEST
#include <Wire.h>
#include <LiquidCrystal_I2C.h> //закомментировать если дисплей без I2C
LiquidCrystal_I2C lcd(0x27, 16, 2);  //вместо (0x3f) указываем адрес своего дисплея 
#include <avr/pgmspace.h>

//char menu[6][10];
//char menu;
char buf[6];

/*enum state_t : byte {REFLOW_STATE_IDLE,
  REFLOW_STATE_MENU_STEPS,
  REFLOW_STATE_MENU_BOTTOM_HEAT,
  REFLOW_STATE_MENU_BOTTOM_PWR,
  REFLOW_STATE_MENU_STEP_RAMP,
  REFLOW_STATE_MENU_STEP_TARGET,
  REFLOW_STATE_MENU_TOP_PWR,
  REFLOW_STATE_MENU_STEP_DWELL,
  REFLOW_STATE_STEP_RAMP,
  REFLOW_STATE_STEP,
  REFLOW_STATE_STEP_DWELL,
  REFLOW_STATE_COMPLETE,
  REFLOW_STATE_ERROR,
  REFLOW_STATE_MENU_RESET};*/


const char string_0[] PROGMEM = "IDLE ";
const char string_1[] PROGMEM = "STEPS";
const char string_2[] PROGMEM = "HEAT ";
const char string_3[] PROGMEM = "PWR  ";
const char string_4[] PROGMEM = "RAMP ";
const char string_5[] PROGMEM = "RESET";
  
 const char *const text[] PROGMEM  = { string_0, string_1, string_2, string_3, string_4, string_5 };
/*
const char* menu[] = {
  "Clean plate",
  "Working...",
  "Sensor settings",
  "Effect 1",
  "Effect 2",
  "Effect 3"
};
*/


void setup () {
  Serial.begin(9600); 
  lcd.begin(); // закомментировать если дисплей без I2C
}  // void setup()


void loop() {
  
    //static state_t stateID = REFLOW_STATE_IDLE;  
    while (!Serial.available()); 
    byte s = Serial.read()-'0';
    //stateID = (state_t)(s);  // Жмем кнопки 0 - 5. 
    function(s);
}  // void loop()

void function (const uint8_t n) {  
        strcpy_P(buf, (char*)pgm_read_word(&(text[n])));
        //Serial.println((const __FlashStringHelper *)  text[s]);
        Serial.println(buf); 
	
}

 

5N62V
Offline
Зарегистрирован: 25.02.2016

Кстати, в тему о прогмем: давече пробовал разместить двумерный массив данных в прогмем, на 2 кбайта. Разместить-то разместил, а вот с вычитывание обычными средствами , я  имею ввиду обычной индексацией двумерных массивов, были проблемы. Решилось размещением одномерного массива, а вычитыванием как двумерного.

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

Странно это. Так бывает когда расположение перепутано (по строкам или по столбцам). Чтобы не путаться, писать и читать нужно всегда, как одномерный, а пользовать как "что угодно". На то и указатели.

5N62V
Offline
Зарегистрирован: 25.02.2016

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

Странно это. Так бывает когда расположение перепутано (по строкам или по столбцам). Чтобы не путаться, писать и читать нужно всегда, как одномерный, а пользовать как "что угодно". На то и указатели.

Первая мысль была "перепутал строки со столбцами". Ан нет, честно перепробовал все варианты. :) 

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

Видать, не все. По-любому надо заводить указатель на байт, который смотрит на начало массива и писать туда всё по sizeof. Читать также. Тогда никуда не денется.

BuonanotteMasha
BuonanotteMasha аватар
Offline
Зарегистрирован: 02.01.2018

Здравствуйте. ЕвгенийП, форумчане может вы мне подскажите, все не было времени но сегодня решился повозиться с кодом. Подскажите, допустим объявляю структуру station в коде

struct station {
  byte steps;
  byte b_heat;
  int b_pwr;
  float ramp;
  byte dwell;
};
 
// TEST
#include <avr/pgmspace.h>

char buf[6];

struct station {
  byte steps;
  byte b_heat;
  int b_pwr;
  float ramp;
  byte dwell;
};

/*enum state_t : byte {REFLOW_STATE_IDLE,
  REFLOW_STATE_MENU_STEPS,
  REFLOW_STATE_MENU_BOTTOM_HEAT,
  REFLOW_STATE_MENU_BOTTOM_PWR,
  REFLOW_STATE_MENU_STEP_RAMP,
  REFLOW_STATE_MENU_STEP_TARGET,
  REFLOW_STATE_MENU_TOP_PWR,
  REFLOW_STATE_MENU_STEP_DWELL,
  REFLOW_STATE_STEP_RAMP,
  REFLOW_STATE_STEP,
  REFLOW_STATE_STEP_DWELL,
  REFLOW_STATE_COMPLETE,
  REFLOW_STATE_ERROR,
  REFLOW_STATE_MENU_RESET};*/


const char string_0[] PROGMEM = "IDLE ";
const char string_1[] PROGMEM = "STEPS";
const char string_2[] PROGMEM = "HEAT ";
const char string_3[] PROGMEM = "PWR  ";
const char string_4[] PROGMEM = "RAMP ";
const char string_5[] PROGMEM = "RESET";
  
 const char *const text[] PROGMEM  = { string_0, string_1, string_2, string_3, string_4, string_5 };

void setup () {
  Serial.begin(9600); 
}  // void setup()


void loop() {    
    while (!Serial.available()); 
    byte s = Serial.read()-'0'; // Жмем кнопки 0 - 5.
    function(s);
}  // void loop()

void function (const uint8_t n) { 
        station reflow; 
        strcpy_P(buf, (char*)pgm_read_word(&(text[n])));
        Serial.print(buf);
        //Serial.print(reflow.steps);  	
}

Работаю с сериалом .В сериал посылаю цифру пускай 0 - соответствие строке string_0. Хотелось бы в функции function сделать вызов не только строки но и элемента структуры. Для цифры 0 этот элемент reflow.steps. Проще говоря каждой отправленной цифре идет в соответствие выводимая строка и элемнт структуры

PS Функции однозначно хочу передавать только один параметр 

 

BuonanotteMasha
BuonanotteMasha аватар
Offline
Зарегистрирован: 02.01.2018

Алгоритм сводится только к тому чтобы 1) вычислить номер текущего элемента структуры, 2) его же как параметр передавать в функцию. 3) А далее в функции по номеру уже вычислять элемент ми отправлять его в порт

Но с 1 и не пойму

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

А чем плохо просто в лоб ?

if (n==0) печатаем то, что при 0
else if (n==1) печатаем то, что при 1
......

 

BuonanotteMasha
BuonanotteMasha аватар
Offline
Зарегистрирован: 02.01.2018

Можно и в лоб НО

признайте это же бесполезный расход памяти и ненужный рост кода. В примере таких вызовов 5, в моем коде 10, а если будет 20

if (n==0) Serial.print(0); //печатаем то, что при 0
else if (n==1) Serial.print(1);//печатаем то, что при 1
......
else Serial.print(20);// да уж

Да и учиться языку Си тоже нужно...

 

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

Во первых, не признаю, расход памяти там настолько копеечный, что говорить неочем. А во-вторых, если Вы хотите учиться, так Вы не с того начали. Начните не с языка, а со стуктур данных и построения алгоритмов - это общие вещи, которые от языка не зависят. Та структура. которую Вы сделали - неоднородна, а потому систематически её обрабатывать трудно, если не сказать вообще невозможно. Если хотите регулярную обрабтку, делайте однородную структуру. Вот как массив - Вы легко работаете с ним по индексу. Но ведь в массиве все элементы имеют один и тот же тип. А в Вашей структуре - разные.

BuonanotteMasha
BuonanotteMasha аватар
Offline
Зарегистрирован: 02.01.2018

Может быть предложите другой способ (не структурой но и не ваш) . Буду вам очень благодарен, хочется доделать 

BuonanotteMasha
BuonanotteMasha аватар
Offline
Зарегистрирован: 02.01.2018

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

Та структура. которую Вы сделали - неоднородна, а потому систематически её обрабатывать трудно, если не сказать вообще невозможно. Если хотите регулярную обрабтку, делайте однородную структуру. Вот как массив - Вы легко работаете с ним по индексу. Но ведь в массиве все элементы имеют один и тот же тип. А в Вашей структуре - разные.

Да вы правы. Но может я опять все усложняю. Такое ТЗ сделал поскольку часть меню решил сделать так, что нажатием одной кнопки можно было перелистывать страницы(обновлять экран) и на них бы отображалась текущая строкка и переменная. Со строками благодаря вам разобрался а с перемнными пускай даже целыми но разного размера int, byte не могу. Хотя нецелые все же нужны. Если не додумаюсь сделаю все int в ущерб себе, создам массив и закрою тему

BuonanotteMasha
BuonanotteMasha аватар
Offline
Зарегистрирован: 02.01.2018

Прикинул что достаточно одной переменной double точно - отдельно будет выводиться, другие сделаю int массив. Спасибо, вопрос закрыт 

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

Я правда не понимаю, что Вам нужно. Все нормальные люди в такой ситуации пишут пачку ифов. Если Вы хотите работать как с массивом, Вам нужно ментяь организацию данных. Если Вы хотите спрятать печать и писать её в одном месте, Вам надо что структура сама себя печатала через интерфейс Printable. Чего Вам надо то?

qwone
qwone аватар
Offline
Зарегистрирован: 03.07.2016

BuonanotteMash. Задачу организации примитивной консоли на Ардуине вы не потяните.