структуры и PROGMEM
- Войдите на сайт для отправки комментариев
Чт, 19/04/2012 - 08:31
Здравствуйте.
Заранее извиняюсь за вопрос на который уже возможно были ответы.Но я ,к сожалению,не нашел.
Есть ли возможность записывать во флэш и работать со структурами в ардуино при помощи PROGMEM например?
arduino.cc/en/Reference/PROGMEM
http://playground.arduino.cc/Learning/Memory
Я читал этот референс.Там сказано не очень понятно про пользовательцкие типы переменных.Можно ли их закатывать во флэш
Напрямую - нет. Нельзя даже обычные типы использовать. И там довольно четко написанно:
it is important to use the datatypes outlined in pgmspace.h. Some cryptic bugs are generated by using ordinary datatypes for program memory calls
Что в вольном переводе "важно использовать только типы, описанные в pgmspace.h. Некоторые критические ошибки происходят именно из-за использования обычных типов". И ниже даны типы которые "можно".
То есть "в итоге", думаю можно. Но нужно будет приводить типы, и писать-читать их самому побайтово. Используя типы из pgmspace.
Возможно поможет в этом чтение http://easyelectronics.ru/avr-uchebnyj-kurs-programmirovanie-na-si-rabota-s-pamyatyu-adresa-i-ukazateli.html
Там пол статьи объяснения "указателей", а потом и про PROGMEM есть рассказ и как с ним обращатся.
Спасибо.Прояснилось немного :)
Для практики хочу написать свое меню для LCD. И сталкнулся с небольшой непоняткой.
char buf[30] = {}; typedef const struct PROGMEM MENU1 { const struct MENU1 *Next; const struct MENU1 *Previous; const struct MENU1 *Parent; const struct MENU1 *Child; void (*handler)(void); const char Name[21]; } MENU; MENU *curr = NULL; MENU Menu[]={ {&Menu[1],&Menu[2],&Menu[0],&Menu[3],0,"MANUAL"}, // {&Menu[2],&Menu[0],&Menu[1],&Menu[1],0,"AUTOMATIC"}, // ГЛАВНОЕ МЕНЮ {&Menu[0],&Menu[1],&Menu[2],&Menu[2],0,"SETTINGS"}, // {&Menu[4],&Menu[5],&Menu[0],&Menu[6],0,"SUB_MANUAL_ONE "}, // {&Menu[5],&Menu[3],&Menu[1],&Menu[4],0,"SUB_MANUAL_TWO "}, // СУБМЕНЮ MANUAL {&Menu[3],&Menu[4],&Menu[2],&Menu[5],0,"SUB_MANUAL_THREE "} }; void setup() { Serial.begin(115200); curr = (MENU*)pgm_read_byte(&Menu[0]); sprintf_P(buf, PSTR(">%S"), (char*)curr->Name); Serial.println(buf); curr = (MENU*)pgm_read_byte(&curr->Next); sprintf_P(buf, PSTR(">%S"), (char*)curr->Name); Serial.println(buf); curr = (MENU*)pgm_read_byte(&curr->Next); sprintf_P(buf, PSTR(">%S"), (char*)curr->Name); Serial.println(buf); curr = (MENU*)pgm_read_byte(&curr->Next); sprintf_P(buf, PSTR(">%S"), (char*)curr->Name); Serial.println(buf); } void loop() { // put your main code here, to run repeatedly: }По идее должен вывод начаться с MANUAL, но он выводит AUTOMATIC. Почему так происходит, я же указал адрес &Menu[0], а он начинает выводить с Menu[1], если после 29 строки указать curr-- то работает как надо! Почему так происходит?
Доброго времени суток. Имею скетч, пока занимает 774 (37%) оперативы и 16 794 байт (52%) флэш. Но скетч постоянно дополняется и улучшается и предстоит еще немало доработать. Поэтому если не составит труда подскажите как элементы структуры ниже с помощью PROGMЕM перенести во флэш чтобы потом не возникало желания перейти на другую плату помощнее, uno вполне устраивает
//массив элементов меню struct PODMENU_1{ // char name1[6]; double *k1; }; struct PODMENU_2{ // char name2[6]; double *k2; }; //инициализация меню //строковое имя, адрес переменной которую надо менять PODMENU_1 mMenu[CountMenu] = { "KP1 =", &kp1, "KI1 =", &ki1, "KD1 =", &kd1, "PW1 =", &pwr_BOTTOM }; //инициализация меню //строковое имя, адрес переменной которую надо менять PODMENU_2 nMenu[CountMenu] = { "KP2 =", &kp2, "KI2 =", &ki2, "KD2 =", &kd2, "PW2 =", &pwr_TOP }; void setup() {} void loop() {} //функция выполнения меню в loop void selectParam(void){ // добавить сохранение текущих параметров в eeprom void SaveOption() //вывод использую буфер static char charbuf1[8]; static char charbuf2[8]; dtostrf(*mMenu[curMenu].k1, sizeof(charbuf1)-1, 2, charbuf1); dtostrf(*nMenu[curMenu].k2, sizeof(charbuf2)-1, 2, charbuf2); lcd.setCursor(5,0); lcd.print(mMenu[curMenu].name1); lcd.print(charbuf1); lcd.setCursor(5,2); lcd.print(nMenu[curMenu].name2); lcd.print(charbuf2); }Полный листинг кода
#include <EEPROM.h> // подключаем библиотеку EEPROM #include <Wire.h> // библиотека для управления устройствами по I2C #include <LiquidCrystal_I2C.h> // подключаем библиотеку для LCD 1602 #include "max6675.h" // подключаем библиотеку для работы с чипом max6675 #include <PID_v1.h> //библиотека ПИД-регулятора #include <avr/io.h> // библиотека в которой находятся определения констант, имен регистров avr #include <avr/pgmspace.h> //#define serialenabled // раскомментировать для выдачи в порт отладочной инфы //#define MAX_PROFILES 30 //#define OFFSET 10 //Назначаем пины кнопок управления #define BTN_DOWN 13 // кнопка #define BTN_UP 12 // кнопка #define BTN_OK 11 // кнопка #define BTN_PREV 10 // кнопка #define BTN_NEXT 9 // кнопка #define BTN_CANSEL 8 // кнопка #define RELAYPIN_H PC0 //назначаем пин "НИЖНЕГО" нагревателя #define RELAYPIN_B PC1 //назначаем пин "ВЕРХНЕГО" нагревателя #define MIN_TEMP 100 #define MAX_TEMP 350 #define buzzerPin 3 // назначаем пин для пьезодинамика LiquidCrystal_I2C lcd(0x3f, 20, 4); // присваиваем имя lcd для дисплея 20х4 boolean updateScreen = true; //Update whole screen boolean byte ButtonModecount = 0; // счетчик нажатий кнопки boolean MenuEnter = 0; // текущий статус входа в меню boolean modeOfOperation = true; // профиль выдержан boolean TopStart = 0; // флаг установки работы "ВЕРХНЕГО" нагревателя boolean stOKState = 0; // флаг окончания текущей операции boolean exOKState = 0; // флаг возврата в главное меню boolean runProfile = 0; // флаг начала пайки по профилю byte btnupclick, btndnclick; byte btn1, btn2; const uint8_t mass_byte[4] = {100, 1, 10, 50}; // сохраняем несколько беззнаковых целых чисел const char str[] PROGMEM = "STOP"; const char str1[] PROGMEM = "PEAK"; const char str_[] PROGMEM = " "; uint8_t symbol[8] = {0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f}; // полностью закрашенный символ uint8_t forward_arrow[8] = {0x04, 0x0e, 0x15, 0x04, 0x04, 0x04, 0x04}; // символ стрелка вверх uint8_t backward_arrow[8] = {0x04, 0x04, 0x04, 0x04, 0x15, 0x0e, 0x04}; // символ стрелка вниз uint8_t main_arrow[8] = {0x08, 0x0C, 0x0E, 0x0F, 0x0E, 0x0C, 0x08}; uint8_t clock[8] = {0x0, 0xe, 0x15, 0x17, 0x11, 0xe, 0x0}; // символ часы byte sek = 0; //значение секунд byte minu = 0; //значение минут boolean counter = false; // счетчик для полусекунд #define PERIOD_CLOCK 500000UL #define PERIOD_PWM 1000UL // периодичность вывода температуры (1 cекунда) > 750 #define PERIOD_DURATION 1000UL // периодичность #define PERIOD_BLINK 200UL // периодичность #define TIME_ALARM 3000UL // время в режиме ТРЕВОГА #define TIME_WAIT 1500UL // ожидание для подтверждения возврата в меню double kp1, kd1, ki1; double kp2, kd2, ki2; boolean flag = 0; //флаг для фиксации стартовой температуры double startTemp; // стартовая температура // можно сделать локальной? double setTemp1 = 0.0; // заданная температура "НИЖНЕГО" нагревателя double setTemp2 = 0.0; // заданная температура "ВЕРХНЕГО" нагревателя double *pSetTemp1, *pSetTemp2; // указатель на setTemp1, setTemp2 double temp1Status, temp2Status; double curTemp; boolean sel = 0; double tc1 = 0.0; // текущая температура "НИЖНЕГО" нагревателя double tc2 = 0.0; // текущая температура "ВЕРХНЕГО" нагревателя // переменные для фильтра Калмана "НИЖНЕГО" нагревателя //#define varTerm1 0.25 // среднее отклонение (ищем в excel) //#define varProcess1 0.0125 // скорость реакции на изменение (подбирается вручную) // переменные фильтра Калмана "ВЕРХНЕГО" нагревателя //#define varTerm2 0.25 // среднее отклонение (ищем в excel) //#define varProcess2 0.0125 // скорость реакции на изменение (подбирается вручную) double outputPower1, outputPower2; // по умолчанию диапазон выходных значений для ШИМ устанавливается от 0 до 100 double pwr_BOTTOM = 100.0; //максимальная мощность нижнего нагревателя double pwr_TOP = 100.0; //максимальная мощность верхнего нагревателя boolean state_top = 0; boolean state_bottom = 0; PID myPID1(&tc1, &outputPower1, &setTemp1, kp1, ki1, kd1, DIRECT); PID myPID2(&tc2, &outputPower2, &setTemp2, kp2, ki2, kd2, DIRECT); #define thermoDO 7 #define thermoCLK 5 #define thermoCS_b 6 #define thermoCS_t 4 MAX6675 thermocouple_b(thermoCLK, thermoCS_b, thermoDO); //термопара "НИЖНЕГО" нагревателя MAX6675 thermocouple_t(thermoCLK, thermoCS_t, thermoDO); //термопара "ВЕРХНЕГО" нагревателя enum switchVariants : byte { // Определения для переключателя пунктов меню; MAIN_MENU, MENU_MANUAL, MENU_AUTO, MENU_SETUP, MENU_START, EXITSAVE }; switchVariants switchPointer = MAIN_MENU; // С чего начнем цикл; enum manuState : byte { // Определения для переключателя ручного режима; EDIT_PARAM, HEAT_ZONE, PEAK_ZONE, COOL_ZONE, STOP_PROCESS, EXIT_PROCESS }; manuState mode = EDIT_PARAM; // С чего начнем цикл; typedef enum REFLOW_STATE : byte { // Определения для переключателя режимов пайки по профилю; REFLOW_STATE_IDLE, REFLOW_STATE_COMPLETE, REFLOW_STATE_ERROR } reflowState_t; reflowState_t reflowState; byte curMenu = 0; //текущий пункт меню, bool b_ShowmMenu = 0; // флаг отображения меню const byte CountMenu = 4; //количество пунктов меню //массив элементов меню struct PODMENU_1{ // char name1[6]; double *k1; }; struct PODMENU_2{ // char name2[6]; double *k2; }; //инициализация меню //строковое имя, адрес переменной которую надо менять PODMENU_1 mMenu[CountMenu] = { "KP1 =", &kp1, "KI1 =", &ki1, "KD1 =", &kd1, "PW1 =", &pwr_BOTTOM }; //инициализация меню //строковое имя, адрес переменной которую надо менять PODMENU_2 nMenu[CountMenu] = { "KP2 =", &kp2, "KI2 =", &ki2, "KD2 =", &kd2, "PW2 =", &pwr_TOP }; typedef void (*pDo)() ;// тип -функция обработчик //------Cl_BtnLong----------------------// class Cl_BtnLong { // класс кнопка protected: const byte pin; // номер ноги на кнопке pDo press, longPress; //обработчик короткий,длиный bool bounce = 0; // // антидребезговый флаг bool btn = 1, oldBtn; unsigned long time_Pressed; // Переменные для работы со временем; bool button_state; uint32_t last_state = 0; public: /*конструктор*/ Cl_BtnLong(byte pin_, pDo press_, pDo longPress_) : pin(pin_), press(press_), longPress(longPress_) {} /*инициализация-вставить в setup() void init() { } */ /*работа-вставить в loop()*/ void run() { if (!bounce && digitalRead(pin) != btn) { // если прошел фронт изменения на выводе bounce = 1; // выставить флаг time_Pressed = millis(); } if (bounce && millis() - time_Pressed >= 10) { // если прошло антидребезговое время bounce = 0; // то снять флаг oldBtn = btn; btn = digitalRead(pin); // прочитать реальное значение на выводе if (!btn && oldBtn) { // если нажата кнопка button_state = 1; last_state = millis(); } if (!oldBtn && btn && button_state && millis() - last_state < 500 ) { button_state = 0; press();// короткое нажатие } } if (button_state && millis() - last_state >= 500 ) { button_state = 0; longPress();//длиное нажатие } } }; //-----Компоновка---------------------- // кнопка 1 void press_down() { updown(false); } void longPress_down() { long_updown(false); } Cl_BtnLong Btn_down(/*пин*/BTN_DOWN,/*обработчик короткого*/press_down,/*длинного*/longPress_down); // кнопка 2 void press_up() { updown(true); } void longPress_up() { long_updown(true); } Cl_BtnLong Btn_up(/*пин*/BTN_UP,/*обработчик короткого*/press_up,/*длинного*/longPress_up); // кнопка 3 void press_ok() { ok(); } void longPress_ok() { mode = HEAT_ZONE; } Cl_BtnLong Btn_ok(/*пин*/BTN_OK,/*обработчик короткого*/press_ok,/*длинного*/longPress_ok); // кнопка 4 void press_prev() { } void longPress_prev() { } Cl_BtnLong Btn_prev(/*пин*/BTN_PREV,/*обработчик короткого*/press_prev,/*длиного*/longPress_prev); // кнопка 5 void press_next() { next(); } void longPress_next() { } Cl_BtnLong Btn_next(/*пин*/BTN_NEXT,/*обработчик короткого*/press_next,/*длиного*/longPress_next); // кнопка 6 void press_cansel() { back(); } void longPress_cansel() { stOKState = 1; // халтура } Cl_BtnLong Btn_cansel(/*пин*/BTN_CANSEL,/*обработчик короткого*/press_cansel,/*длиного*/longPress_cansel); //-----main----------------------// void setup() { #ifdef serialenabled Serial.begin(9600); #endif lcd.begin(); // инициализация LCD дисплея lcd.backlight(); // включение подсветки дисплея lcd.setCursor(1, 1); lcd.print(F("ARDUINO REWORK v05")); lcd.createChar(1, symbol); // создает символ lcd.createChar(2, forward_arrow); // создает символ lcd.createChar(3, backward_arrow); // создает символ lcd.createChar(4, main_arrow); // создает символ lcd.createChar(5, clock); // создает символ DDRB = 0x00; // назначить пины кнопок на вход PORTB = 0b00111111; // подключить внутренние подтягивающие pull-up резисторы DDRC |= 1 << RELAYPIN_H; // настраиваем 0-й пин порта C (реле) на вывод DDRC |= 1 << RELAYPIN_B; // настраиваем 1-й пин порта C (реле) на вывод DDRD |= 1 << PD3; // назначить пин для пьезодинамика на выход //Мелодия приветствия tone(buzzerPin, 523); delay(200); tone(buzzerPin, 659); delay(200); tone(buzzerPin, 784); delay(200); tone(buzzerPin, 1046); delay(200); noTone(buzzerPin); delay(1000); // wait for MAX chip to stabilize lcd.clear(); //long magic; //EEPROM.get(MAGIC_ADDR, magic); //if(magic == MAGIC_VAL){ // EEPROM.get(SETPOINT_ADDR, setPoint); // EEPROM.get(BACKLIGHT_ADDR, backlight); //} myPID1.SetSampleTime(1000); // Время расчета выходного сигнала в милисекундах myPID1.SetOutputLimits(0, 100); // Устанавливает границы выходного сигнала myPID1.SetMode(AUTOMATIC); // ПИД-регулятор включен myPID2.SetSampleTime(1000); // Время расчета выходного сигнала в милисекундах myPID2.SetOutputLimits(0, 100); // Устанавливает границы выходного сигнала myPID2.SetMode(AUTOMATIC); // ПИД-регулятор включен } void loop() { Btn_down.run(); Btn_up.run(); Btn_ok.run(); Btn_prev.run(); Btn_next.run(); Btn_cansel.run(); static uint32_t previousMillis = 0; // время последнего изменения состояния stopProcess(); exitMenu(); // дебаг-инфо - в терминал #ifdef serialenabled #endif switch (switchPointer) // Все делаем в одном операторе и одной функции; { case MAIN_MENU: /***************** Главное меню ***************/ setString(ButtonModecount); // включаем отображение стрелки выбора меню lcd.setCursor(1, 0); // 1 строка lcd.print(F("MANUAL")); // настройка lcd.setCursor(1, 1); // 2 строка lcd.print(F("AUTOMATIC")); // настройка lcd.setCursor(1, 2); // 3 строка lcd.print(F("OPTIONS")); // ручной режим lcd.setCursor(1, 3); // 4 строка lcd.print(F("START")); // автоматический режим break; case MENU_MANUAL: /***************** Ручной режим ***************/ { SetTuning(); // настройки регулятора tempToPID(PERIOD_PWM); // передача температуры для расчета ПИД регулятору uint8_t Out1 = OutPWR_TOP(); uint8_t Out2 = OutPWR_BOTTOM(); lcd.setCursor(1, 1); // Печатаем на ЖК; lcd.print(F("TH:")); lcd.print(tc1, 0); lcd.print(F("\x99""C")); lcd.setCursor(12, 1); // Печатаем на ЖК; lcd.print(F("TY:")); lcd.print(setTemp1, 0); lcd.print(F("\x99""C")); lcd.setCursor(1, 3); // Печатаем на ЖК; lcd.print(F("TB:")); lcd.print(tc2, 0); lcd.print(F("\x99""C")); lcd.setCursor(12, 3); // Печатаем на ЖК; lcd.print(F("TY:")); lcd.print(setTemp2, 0); lcd.print(F("\x99""C")); lcd.setCursor(14, 0); // Печатаем на ЖК; lcd.print(Out1, 0); lcd.print(F("\x99""%")); lcd.setCursor(14, 2); // Печатаем на ЖК; lcd.print(Out2, 0); lcd.print(F("\x99""%")); //фиксируем стартовую температуру if (flag == 0) { startTemp = tc1; flag = 1; } switch (mode) { // Выбор режима работы ручного режима case EDIT_PARAM: TopStart = 0; lcd.setCursor(8, 0); // Печатаем на ЖК; lcd.print(F("EDIT")); // Режим редактирования температуры break; case HEAT_ZONE: lcd.setCursor(8, 0); // Печатаем на ЖК; lcd.print(F("HEAT")); // Режим редактирования температуры timing(); //stopProcess(); mode_PWM1(Out1); mode_PWM2(Out2); // запуск работы "ВЕРХА" сразу после догрева "НИЗОМ" //верхний нагреватель включится еcли температура "низа" достигнет уснановленной if (tc1 >= setTemp1) { //if (abs(setTemp1-tc1) <= 2.5) tone(buzzerPin, 1045, 50); //звуковой сигнал TopStart = 1; // нижний нагреватель вышел на необходимую температуру } // if (tc2 >= setTemp2) { previousMillis = millis(); mode = PEAK_ZONE; } break; case PEAK_ZONE: lcd.setCursor(8, 0); // Печатаем на ЖК; //blinking(PERIOD_BLINK, str[0]); blinking(PERIOD_BLINK,0); timing(); mode_PWM1(Out1); mode_PWM2(Out2); peak_Temp(); if (previousMillis) { tone(buzzerPin, 550); // play 550 Hz tone in background for 'onDuration' } if (previousMillis && (millis() - previousMillis >= 1000)) { previousMillis = 0; noTone(buzzerPin); } if (!modeOfOperation) { mode = COOL_ZONE; modeOfOperation = true; // Подъем флага - профиль выдержан! } break; case COOL_ZONE: timing(); turnoff(); // откл реле lcd.setCursor(8, 0); // Печатаем на ЖК; lcd.print(F("COOL")); // Режим редактирования температуры break; case STOP_PROCESS: sek = 0; minu = 0; turnoff(); lcd.setCursor(8, 0); // Печатаем на ЖК; //blinking(PERIOD_BLINK, str[1]); blinking(PERIOD_BLINK,1); break; } } break; case MENU_AUTO: /***************** Автоматический режим ***************/ //currentProfile = currentProfile + 1; //if (currentProfile >= 5)//if currentProfile = 4 and up is pressed go back to profile 1 // { // currentProfile = 1; // } //currentProfile = currentProfile - 1; // if (currentProfile <= 0) // { // currentProfile = 4; //loadProfile();//вызываем функцию loadProfile для выбора профиля из eeprom // устанавливаем количество шагов профиля //profileSteps = profileSteps + 1; // if (profileSteps >= 10) { // profileSteps = 1; //profileSteps = profileSteps - 1; // if (profileSteps <= 0) // { // profileSteps = 9; // } break; case MENU_SETUP: /***************** Настройки ***************/ selectParam(); // save eeprom break; case MENU_START: /*********** Пуск по заданному термопрофилю *********/ if(!runProfile) { prep_Profile(); } else { if (updateScreen) { //Настройка экрана в режиме ожидания lcd.clear(); updateScreen = 0; } } /* static byte i = 0; if (reflowState == REFLOW_STATE_COMPLETE) { //|| alarmOn if (i < 5) { //alarmOn = true; tone(buzzerPin, 1046); delay(100); noTone(buzzerPin); delay(100); i++; } else { //alarmOn = false; } } switch (reflowState) { case REFLOW_STATE_IDLE: if (updateScreen) { //Настройка экрана в режиме ожидания lcd.clear(); } break; case REFLOW_STATE_MENU_RESET: lcd.clear(); lcd.setCursor(0,0); lcd.print("EEPROM formatted"); for (int i = 0; i < 512; i++){ EEPROM.write(i, 0); } softReset(); break; }*/ break; case EXITSAVE: /*********** Полная очистка экрана *********/ lcd.setCursor(3, 1); lcd.print(F("SAVE and EXIT")); // согласие на сохранение результата/изменений break; } } // void tempToPID(unsigned long INTERVAL ) { static unsigned long prevTime = 0; // время когда последний раз переключали диод if (millis() - prevTime > INTERVAL) { prevTime = millis(); // //tc1 = thermocouple_b.readCelsius(); //tc2 = thermocouple_t.readCelsius(); tc1 = 30; tc2 = 30; //tc1 = kalmanFilter1 (thermocouple_b.readCelsius()); //tc2 = kalmanFilter2 (thermocouple_t.readCelsius()); //double gap1 = abs(setTemp1 - tc1); //double gap2 = abs(setTemp2 - tc2); myPID1.Compute(); // Расчет выходного сигнала myPID2.Compute(); // Расчет выходного сигнала } } /*double kalmanFilter1(double val_1) { //функция фильтрации показений "Верхней" термопары double Pc1 = 0.0; double G1 = 0.0; double P1 = 1.0; double Xp1 = 0.0; double Zp1 = 0.0; double Xe1 = 0.0; Pc1 = P1 + varProcess1; G1 = Pc1/(Pc1 + varTerm1); P1 = (1-G1)*Pc1; Xp1 = Xe1; Zp1 = Xp1; Xe1 = G1*(val_1-Zp1)+Xp1; // "фильтрованное" значение return(Xe1); }*/ /*double kalmanFilter2(double val_2) { //функция фильтрации показений "Нижней" термопары double Pc2 = 0.0; double G2 = 0.0; double P2 = 1.0; double Xp2 = 0.0; double Zp2 = 0.0; double Xe2 = 0.0; Pc2 = P2 + varProcess2; G2 = Pc2/(Pc2 + varTerm2); P2 = (1-G2)*Pc2; Xp2 = Xe2; Zp2 = Xp2; Xe2 = G2*(val_2-Zp2)+Xp2; // "фильтрованное" значение return(Xe2); }*/ uint8_t OutPWR_BOTTOM() { static uint8_t er1=0; // ошибка округления uint8_t reg1 = round(outputPower1*(pwr_BOTTOM*0.01)) + er1; //pwr- задание выходной мощности в %, er- ошибка округления if (reg1 < 50) er1 = reg1; // reg- переменная для расчетов else er1 = reg1 - 100; return reg1; } uint8_t OutPWR_TOP() { static uint8_t er2=0; // ошибка округления uint8_t reg2 = round(outputPower2*(pwr_TOP*0.01)) + er2; //pwr- задание выходной мощности в %, er- ошибка округления if (reg2 < 50) er2 = reg2; // reg- переменная для расчетов else er2=reg2-100; return reg2; } // алгоритм ШИМ с частотой 5 Гц void mode_PWM1(uint8_t Output1) { static unsigned long prevTime = 0; if ((state_bottom == 0) && ((millis() - prevTime) >= 200 * (100 - Output1) / 100)) { state_bottom = 1; PORTC |= 1 << RELAYPIN_H; prevTime = millis(); } if ((state_bottom == 1) && ((millis() - prevTime) >= 200 * Output1 / 100)) { state_bottom = 0; PORTC &= ~(1 << RELAYPIN_H); prevTime = millis(); } } // алгоритм ШИМ с частотой 5 Гц void mode_PWM2(uint8_t Output2) { static unsigned long prevTime = 0; if (TopStart && (state_top == 0) && ((millis() - prevTime) >= 200 * (100 - Output2) / 100)) { state_top = 1; PORTC |= 1 << RELAYPIN_B; prevTime = millis(); } if (TopStart && (state_top == 1) && ((millis() - prevTime) >= 200 * Output2 / 100)) { state_top = 0; PORTC &= ~(1 << RELAYPIN_B); prevTime = millis(); } } // void blinking(unsigned long intervalTime, bool number) { static unsigned long prevTime = 0; static boolean i=0; if(millis() - prevTime >= intervalTime) { //Если счетчик превысил интервал, i = !i; prevTime = millis(); } //меняем значение переменной i, добавляем к переменной время интервала. if (number) lcd.print(i ? (const __FlashStringHelper*)str : (const __FlashStringHelper*)str_); else lcd.print(i ? (const __FlashStringHelper*)str1 : (const __FlashStringHelper*)str_); } void peak_Temp(void) { static unsigned long prevTime = 0; static byte count = 0; if (millis() - prevTime >= PERIOD_DURATION) { prevTime = millis(); if (count >= 10) { count = 0; modeOfOperation = false; } else { count++; modeOfOperation = true; } lcd.setCursor(9, 2); // Печатаем на ЖК; lcd.write(count > 0 ? 5 : 32); if (count) lcd.print(count); else lcd.print(F(" ")); } } // отключение реле void turnoff(void) { PORTC &= ~(1 << RELAYPIN_H); PORTC &= ~(1 << RELAYPIN_B); } void setString(uint8_t bcount) { lcd.setCursor(0, bcount); lcd.write(62); if (bcount - 1 < 0) lcd.setCursor(0, bcount+3); else lcd.setCursor(0, bcount-1); lcd.write(32); } void ButtonClick(byte ButtonId) { if (MenuEnter) { // Если мы в меню if (ButtonId == 0) switchPointer = MENU_MANUAL; // Клик [Menu] Выход из меню else if (ButtonId == 1) switchPointer = MENU_AUTO; //MenuCurent--; // Клик [Prev] Позицию ниже else if (ButtonId == 2) switchPointer = MENU_SETUP; //MenuCurent++; // Клик [Next] Позиция выше else if (ButtonId == 3) switchPointer = MENU_START;//MenuItems[MenuCurent].on_click(1); // Клик [+] Увеличиваем значение выбранного параметра } else { switchPointer = MAIN_MENU; // Возврат в главное меню } } void prep_Profile(void) { static uint8_t j = 0; if (j > 19) runProfile = 1; else j++; lcd.setCursor(0,1); for (uint8_t i = 0; i<20; i++) { lcd.write(j>i ? 1 : 32); } lcd.setCursor(5,2); lcd.print(F("Loading...")); delay(500); } // остановка профиля void stopProcess(void) { static uint32_t timer = 0; //static bool timerenabled = 0; if(stOKState) { // нажали на кнопку CANSEL для остановки режима timer = millis(); //нулим таймер stOKState = 0; mode = STOP_PROCESS; } if (timer && millis() - timer > TIME_ALARM){ timer = 0; lcd.clear(); mode = EDIT_PARAM; } } void exitMenu(void) { static uint32_t timer = 0; static uint8_t accum = 0; if(exOKState) { // нажали на кнопку ОК для сохранения и выхода if(!timer) { timer = millis(); //перезапускаем таймер accum = ButtonModecount; // хранится текущий пункт меню lcd.clear(); // очистим заранее экран switchPointer = EXITSAVE; // выход из подменю } } if (timer && millis() - timer > TIME_WAIT) { timer = 0; // не забываем выключить таймер if (exOKState) { MenuEnter = 0; exOKState = 0; }// переходим в главное меню else { MenuEnter = 1; } // возвращаемся обратно в топодменю откуда пришли lcd.clear(); // очистим заранее экран ButtonClick(accum); // Вызывается функция обработки нажатия на кнопку } } //функция выполнения меню void selectParam(void){ // BTN_CANSEL выход из меню // выход из меню // переход к начальному меню // BTN_UP +1 значение //(*mMenu[curMenu].k1)++; или //(*nMenu[curMenu].k2)++; // BTN_DOWN -1 значение // (*mMenu[curMenu].k1)--; или // (*nMenu[curMenu].k2)--; //настройка "ПИД" нижнего нагревателя //настройка "ПИД" верхнего нагревателя //сохраняем текущие параметры в eeprom void SaveOption() //вывод использую буфер static char charbuf1[8]; static char charbuf2[8]; dtostrf(*mMenu[curMenu].k1, sizeof(charbuf1)-1, 2, charbuf1); dtostrf(*nMenu[curMenu].k2, sizeof(charbuf2)-1, 2, charbuf2); lcd.setCursor(5,0); lcd.print(mMenu[curMenu].name1); lcd.print(charbuf1); lcd.setCursor(5,2); lcd.print(nMenu[curMenu].name2); lcd.print(charbuf2); } //void saveState() { // EEPROM.put(MAGIC_ADDR, MAGIC_VAL); // EEPROM.put(SETPOINT_ADDR, setPoint); // EEPROM.put(BACKLIGHT_ADDR, backlight); //} // void timing(void) { static unsigned long prevmicros = 0; // время когда последний раз делали if (micros() - prevmicros > PERIOD_CLOCK) { prevmicros = micros(); //принимает значение каждые полсекунды counter = !counter; if (counter == false) { sek++; lcd.setCursor(3, 0); lcd.print(F(":")); //выводим символ ":"между минутами и секундами } if (sek > 59) { //если переменная секунда больше 59 ... sek = 0; //сбрасываем ее на 0 minu++;//пишем +1 в переменную минута } if (minu > 59) { //если переменная минута больше 59 ... minu = 0; //сбрасываем ее на 0 } lcd.setCursor(1, 0); //выводим значение минут if (minu >= 0 && minu < 10) { lcd.print(F("0")); lcd.print(minu); } else lcd.print(minu); //количество минут lcd.setCursor(4, 0); //выводим значение секунд if (sek >= 0 && sek < 10) { lcd.print(F("0")); lcd.print(sek); } else lcd.print(sek); //количество секунд } } void chsPos(void) { // функция отвечющая за выбор числа sel=!sel; temp1Status = *pSetTemp1; temp2Status = *pSetTemp2; curTemp = 0; } void supCursor(void){ if (sel) { *pSetTemp2 = curTemp + temp2Status; lcd.setCursor(11, 1); lcd.write(32); lcd.setCursor(11, 3); lcd.write(4); } else { *pSetTemp1 = curTemp + temp1Status; lcd.setCursor(11, 1); lcd.write(4); lcd.setCursor(11, 3); lcd.write(32); } } /*void popCursor() { if (sel) { *pSetTemp2 = curTemp + temp2Status; lcd.setCursor(11, 1); lcd.write(32); lcd.setCursor(11, 3); lcd.write(4); } else { *pSetTemp1 = curTemp + temp1Status; lcd.setCursor(11, 1); lcd.write(4); lcd.setCursor(11, 3); lcd.write(32); } }*/ void SetTuning(void) { supCursor(); pSetTemp1 = &setTemp1; pSetTemp2 = &setTemp2; if (setTemp1 <= MIN_TEMP) { setTemp1 = MIN_TEMP; } if (setTemp2 <= MIN_TEMP) { setTemp2 = MIN_TEMP; } if (setTemp1 >= MAX_TEMP) { setTemp1 = MAX_TEMP; } if (setTemp2 >= MAX_TEMP) { setTemp2 = MAX_TEMP; } myPID1.SetTunings(kp1, ki1, kd1); // Настройка ПИД-регулятора во время работы myPID2.SetTunings(kp2, ki2, kd2); // Настройка ПИД-регулятора во время работы /* Тут настраиваются коэффициенты ПИД*/ /*kp1 = 50; ki1 = 0; kd1 = 0; kp2 = 50; ki2 = 0; kd2 = 0;*/ } // делать при длином нажатии void DoLong(byte button){ lcd.print(button); if (button < 10) lcd.print(F(" ")); else if (button < 100) lcd.print(F(" ")); } void loadProfile(uint8_t profile) { /*for (int i=0; i<10; i++){ //byte readByte = EEPROM.read(i+OFFSET); readByte = EEPROM.read(i+OFFSET); Serial.println(readByte); }*/ } void updown(bool up) { if (MenuEnter) { // если находимся в подменю, то if (switchPointer == MENU_MANUAL) (up == 1 ? (curTemp += btn1) : (curTemp -= btn2)); else if (switchPointer == MENU_SETUP) { if(up) { if ((*mMenu[curMenu].k1) >= 500) (*mMenu[curMenu].k1) = 0; else (*mMenu[curMenu].k1)+=10; //0.05; 0.5 5 50 } else { if ((*mMenu[curMenu].k1) <= 0) (*mMenu[curMenu].k1) = 0; else (*mMenu[curMenu].k1)-=10;//--; //0.05; 0.5 5 50 } } else if (switchPointer == MENU_AUTO) { } } } void long_updown(bool lg) { if (MenuEnter) { // если находимся в подменю, то if (switchPointer == MENU_MANUAL) { if (lg) { if(btnupclick<3) btnupclick++; //увеличивает счетчик кнопки на 1 else btnupclick = 0; //если счетчик достиг предела положений то его надо обнулить. lcd.setCursor(1, 2); lcd.write(2); btn1 = mass_byte[btnupclick]; DoLong(btn1); } else { if(btndnclick<3) btndnclick++; //увеличивает счетчик кнопки на 1 else btndnclick = 0; //если счетчик достиг предела положений то его надо обнулить. lcd.setCursor(1, 2); lcd.write(3); btn2 = mass_byte[btndnclick]; DoLong(btn2); } } else if (switchPointer == MENU_SETUP) { } } } void ok() { if (MenuEnter) { // если находимся в подменю, то exOKState = 1; } else { // если находимся в главном меню, то lcd.clear(); MenuEnter = 1; ButtonClick(ButtonModecount); // Вызывается функция обработки нажатия на кнопку } } void next() { if (MenuEnter) { // если находимся в подменю, то if (switchPointer == MENU_MANUAL) { chsPos(); } else if (switchPointer == MENU_AUTO) { } else if (switchPointer == MENU_SETUP) { if (curMenu == CountMenu - 1) curMenu = 0; //следующий пункт меню по кругу else curMenu++; } } else { // если находимся в главном меню, то ButtonModecount++; // с каждым нажатием ButtonModecount++ пока ButtonModecount<4 if (ButtonModecount > 3) ButtonModecount = 0; } } void back() { exOKState = 0; // если что то поменял -> не сохр } void softReset() { asm volatile ("jmp 0"); } /* static int profile1[]={ // профиль 1: шаги, температура низа, 9 скоростей роста температуры шага умноженое на 4 4,150,4,2,4,4,4,4,4,4,4, // 9 продолжительностей шага 120,60,9,15,1,1,1,1,1, // температуры верхнего нагревателя по 9 шагам 150,180,189,190,205,210,215,220}; for (int i = 0; i < 29; i++) EEPROM.write(i, profile1[i]); static int profile2[]={ // профиль 2: шаги, температура низа, 9 скоростей роста температуры шага умноженое на 4 4,150,4,2,4,4,4,4,4,4,4, // 9 продолжительностей шага 120,60,14,15,0,0,0,0,0, // температуры верхнего нагревателя по 9 шагам 150,180,194,195,0,0,0,0}; //for (int i = 29; i < 58; i++) EEPROM.write(i, profile2[i-29]); for (int i = 0; i < 29; i++) EEPROM.write(i+OFFSET, profile2[i]); //OFFSET = 29 static int profile3[]={ // профиль 3: шаги, температура низа, 9 скоростей роста температуры шага умноженое на 4 4,150,4,3,4,4,4,4,4,4,4, // 9 продолжительностей шага 120,93,4,15,0,0,0,0,0, // температуры верхнего нагревателя по 9 шагам 150,220,224,225,0,0,0,0,0}; //for (int i = 58; i < 87; i++)EEPROM.write(i, profile3[i-58]); for (int i = 0; i < 29; i++) EEPROM.write(i+OFFSET, profile2[i]); //OFFSET = 58 static int profile4[]={ // профиль 4: шаги, температура низа, 9 скоростей роста температуры шага умноженое на 4 4,150,4,12,4,4,4,4,4,4,4, // 9 продолжительностей шага 120,93,9,15,1,1,1,1,1, // температуры верхнего нагревателя по 9 шагам 150,220,229,230,220,220,220,220,220}; //for (int i = 87; i < 116; i++) EEPROM.write(i, profile4[i-87]); for (int i = 0; i < 29; i++) EEPROM.write(i+OFFSET, profile2[i]); //OFFSET = 87 }*/А зачем вам рассказывать, если вы не слушаете.
А зачем вам рассказывать, если вы не слушаете.
qwone в чем вы со мной не согласны. В том что хочу строки во флэш записать или вы о другом?
Где это Вы указали адрес &Menu[0]?
В 29-ой строке Вы прочитали то, что находится по адресу &Menu[0]. А там как раз находится адрес &Menu[1]. Вот он и попал в Ваш curr.
BuonanotteMasha, как Вы себе это видите? У Вас вторым элементом структуры идёт адрес некоего плавающего числа. Если Вы загоните адрес в progmem, то он будет константой - его нельзя будет менять в процессе работы. Это нормально? А на что он будет указывать? Где будет находиться само это число?
ЕвгенийП, признал ошибку. Спасибо
Вас и других не затруднит указать мне (знаю что не по теме, но не хочется сразу покидать эту ветку) как возможно сделать вместо многочисленных вызовов в ветках case функции установки курсора тольк один ее вызов. Под рукой нет платы, поэтому буду думать
void setup() { } void loop() { // объявлен оператор switch case doA : lcd.setCursor(1, 1); lcd.print(F("A")); break; case doB: lcd.setCursor(1, 1); lcd.print(F("B")); break; case doC: lcd.setCursor(1, 1); lcd.print(F("B")); break; }Думаю либо завести отдельный буфер под строки, либо через указатели на эти строки
А что такое doA? doB и doC? Есть ограничения на их значения или можно любые задать?
и ты это, инвариантные строки, типа
lcd.setCursor(1, 1);за switch вынеси, не раздувай код.
и ты это, инвариантные строки, типа
lcd.setCursor(1, 1);за switch вынеси, не раздувай код.
Это не всегда возможно. Часто бывает, что эта строка нужна только в том случае, если хоть что-нибудь из свитча совпало, а если ничего не совпало, то она не нужна. Если же вынести, то она будет выполняться всегда.
Здесь, наверное, можно вообще убрать к чертям свитч, но для этого я жду ответа от ТС.
Полный листинг кода #7
Тут привел фрагмент для лучшего понимания сути моего вопроса.
enum switchVariants : byte { // Определения для переключателя пунктов меню; MAIN_MENU, MENU_MANUAL, MENU_AUTO, MENU_SETUP, MENU_START, EXITSAVE }; switchVariants switchPointer = MAIN_MENU; // С чего начнем цикл; enum manuState : byte { // Определения для переключателя ручного режима; EDIT_PARAM, HEAT_ZONE, PEAK_ZONE, COOL_ZONE, STOP_PROCESS, EXIT_PROCESS }; manuState mode = EDIT_PARAM; // С чего начнем цикл; typedef enum REFLOW_STATE : byte { // Определения для переключателя режимов пайки по профилю; REFLOW_STATE_IDLE, REFLOW_STATE_COMPLETE, REFLOW_STATE_ERROR } reflowState_t; reflowState_t reflowState; //-----main----------------------// void setup() { } void loop() { switch (switchPointer) // Все делаем в одном операторе и одной функции; { case MAIN_MENU: /***************** Главное меню ***************/ setString(ButtonModecount); // включаем отображение стрелки выбора меню lcd.setCursor(1, 0); // 1 строка lcd.print(F("MANUAL")); // настройка lcd.setCursor(1, 1); // 2 строка lcd.print(F("AUTOMATIC")); // настройка lcd.setCursor(1, 2); // 3 строка lcd.print(F("OPTIONS")); // ручной режим lcd.setCursor(1, 3); // 4 строка lcd.print(F("START")); // автоматический режим break; case MENU_MANUAL: /***************** Ручной режим ***************/ SetTuning(); // настройки регулятора tempToPID(PERIOD_PWM); // передача температуры для расчета ПИД регулятору //uint8_t Out1 = OutPWR_TOP(); //uint8_t Out2 = OutPWR_BOTTOM(); lcd.setCursor(1, 1); // Печатаем на ЖК; lcd.print(F("TH:")); lcd.print(tc1, 0); lcd.print(F("\x99""C")); lcd.setCursor(12, 1); // Печатаем на ЖК; lcd.print(F("TY:")); lcd.print(setTemp1, 0); lcd.print(F("\x99""C")); lcd.setCursor(1, 3); // Печатаем на ЖК; lcd.print(F("TB:")); lcd.print(tc2, 0); lcd.print(F("\x99""C")); lcd.setCursor(12, 3); // Печатаем на ЖК; lcd.print(F("TY:")); lcd.print(setTemp2, 0); lcd.print(F("\x99""C")); lcd.setCursor(14, 0); // Печатаем на ЖК; lcd.print(outputPower1, 0); lcd.print(F("\x99""%")); lcd.setCursor(14, 2); // Печатаем на ЖК; lcd.print(outputPower2, 0); lcd.print(F("\x99""%")); //фиксируем стартовую температуру if (flag == 0) { startTemp = tc1; flag = 1; } switch (mode) { // Выбор режима работы ручного режима case EDIT_PARAM: TopStart = 0; lcd.setCursor(8, 0); // Печатаем на ЖК; lcd.print(F("EDIT")); // Режим редактирования температуры break; case HEAT_ZONE: lcd.setCursor(8, 0); // Печатаем на ЖК; lcd.print(F("HEAT")); // Режим редактирования температуры timing(); //stopProcess(); mode_PWM1(outputPower1); mode_PWM2(outputPower2); // запуск работы "ВЕРХА" сразу после догрева "НИЗОМ" //верхний нагреватель включится еcли температура "низа" достигнет уснановленной if (tc1 >= setTemp1) { //if (abs(setTemp1-tc1) <= 2.5) tone(buzzerPin, 1045, 50); //звуковой сигнал TopStart = 1; // нижний нагреватель вышел на необходимую температуру } // if (tc2 >= setTemp2) { previousMillis = millis(); mode = PEAK_ZONE; } break; case PEAK_ZONE: lcd.setCursor(8, 0); // Печатаем на ЖК; //blinking(PERIOD_BLINK, str[0]); blinking(PERIOD_BLINK,0); timing(); mode_PWM1(outputPower1); mode_PWM2(outputPower2); peak_Temp(); if (previousMillis) { tone(buzzerPin, 550); // play 550 Hz tone in background for 'onDuration' } if (previousMillis && (millis() - previousMillis >= 1000)) { previousMillis = 0; noTone(buzzerPin); } if (!modeOfOperation) { mode = COOL_ZONE; modeOfOperation = true; // Подъем флага - профиль выдержан! } break; case COOL_ZONE: timing(); turnoff(); // откл реле lcd.setCursor(8, 0); // Печатаем на ЖК; lcd.print(F("COOL")); // Режим редактирования температуры break; case STOP_PROCESS: sek = 0; minu = 0; turnoff(); lcd.setCursor(8, 0); // Печатаем на ЖК; //blinking(PERIOD_BLINK, str[1]); blinking(PERIOD_BLINK,1); break; } break; case MENU_AUTO: /***************** Автоматический режим ***************/ break; case MENU_SETUP: /***************** Настройки ***************/ break; case MENU_START: /*********** Пуск по заданному термопрофилю *********/ break; case EXITSAVE: /*********** Полная очистка экрана *********/ lcd.setCursor(3, 1); lcd.print(F("SAVE and EXIT")); // согласие на сохранение результата/изменений break; } }А что такое doA? doB и doC? Есть ограничения на их значения или можно любые задать?
Про это забудьте, это я как мог хотел упростить ТЗ. Видимо не донес никакой информации
Про это забудьте, это я как мог хотел упростить ТЗ. Видимо не донес никакой информации
Блин, ну тогда, что такое EDIT_PARAM, HEAT_ZONE и т.п.?
Вопрос-то простой. Я могу все эти выборы свести к 0, 1, 2, и т.п.?
Если могу, то задача элементарно решается.
Замените на здоровье, тип byte. Насчет элементарности до меня не дошло решение
enum manuState : byte { // Определения для переключателя ручного режима; EDIT_PARAM, HEAT_ZONE, PEAK_ZONE, COOL_ZONE, STOP_PROCESS, EXIT_PROCESS }; manuState mode = EDIT_PARAM; // С чего начнем цикл;Да и функция blinking. Используется для вывода мигающего текста
const char str[] PROGMEM = "STOP"; const char str1[] PROGMEM = "PEAK"; const char str_[] PROGMEM = " "; // // void blinking(unsigned long intervalTime, bool number) { static unsigned long prevTime = 0; static boolean i=0; if(millis() - prevTime >= intervalTime) { //Если счетчик превысил интервал, i = !i; prevTime = millis(); } //меняем значение переменной i, добавляем к переменной время интервала. if (number) lcd.print(i ? (const __FlashStringHelper*)str : (const __FlashStringHelper*)str_); else lcd.print(i ? (const __FlashStringHelper*)str1 : (const __FlashStringHelper*)str_); }Вот в этом фрагменте допустим
case STOP_PROCESS: sek = 0; minu = 0; turnoff(); lcd.setCursor(8, 0); // Печатаем на ЖК; //blinking(PERIOD_BLINK, str[1]); blinking(PERIOD_BLINK,1); break; } break;Тогда делаете так. Заводите функции для каждой ветви. Скорее всего столько функций, сколько ветвей, Вам не понадобится, т.к. часть действий одинаковая с точностью до параметров.
Все функции должны иметь одинаковый прототип (т.е. возвращаемое значение и параметры). Даже, если какой-то функции параметры не нужны, они должны быть, что у всех функций было одинаково.
Сейчас я сделаю пример, только Вы тут уже столько разных кодов наплодили, что давайте я сделаю абстрактный пример, чтобы Вы идею поняли а Вы уж для своего кода по образу и подобию там сделаете что надо.
Вот, смотрите, допустим, есть у Вас вот такой switch
switch(kaka) { case 0: Serial.println("0 detected"); break; case 1: Serial.println("1 detected"); break; case 2: Serial.println("2 detected"); break; case 3: Serial.println("3 detected"); break; case 4: digitalWrite(LED_BUILTIN, LOW); break; case 5: digitalWrite(LED_BUILTIN, HIGH); break; }Что мы здесь видим? Ветви 0-3 - одинаковые с точностью до параметра - строки. Но реально достаточно передавать целое число. А ветки 4-5 одинаковые с точностью до параметра - логического выражение (0 или не 0).
Значит, чтобы заменить всё, нам достаточно двух функций. У обеих параметр будет int. Для веток 0-3 - пердавать будем собственно индекс. А для веток 4-5 - тоже индекс, но внутри вычтем из него 4, чтобы получить 0 или 1.
Пишем эти две функции.
void f_0_3 (const int n) { Serial.print(n); Serial.println(" detected"); } void f_4_5 (const int n) { digitalWrite(LED_BUILTIN, n - 4); }Теперь запишем массив функций, которые надо вызывать при том или ином значении kaka. очевидно, что при 0-3 надо вызывать f_0_3, а при 4-5 надо вызывать f_4_5. Так и напишем.
typedef void (* TFunc) (const int); static const TFunc functions[] = { f_0_3, f_0_3, f_0_3, f_0_3, f_4_5, f_4_5 };Ну, собственно, теперь вместо того длинного switch можно использовать вот такую строку
Соберём всё вместе.
// // Где-то выше определяем две функции и массив // void f_0_3 (const int n) { Serial.print(n); Serial.println(" detected"); } void f_4_5 (const int n) { digitalWrite(LED_BUILTIN, n - 4); } typedef void (* TFunc) (const int); static const TFunc functions[] = { f_0_3, f_0_3, f_0_3, f_0_3, f_4_5, f_4_5 }; // // Вместо всего switch просто пишем одну строку // functions[kaka] (kaka);Ну, вот, как-то так, если нигде не ляпнул чего-нибудь.
Спасибо, освобожусь, обязательно попробую вашу идею
Пока сделал так, еще доработаю
#include <avr/pgmspace.h> const char string_0[] PROGMEM = "EDIT"; const char string_1[] PROGMEM = "HEAT"; const char string_2[] PROGMEM = "COOL"; char buf[6]; // создаем таблицу с отсылками к этим строкам: const char *const string_table[] PROGMEM = { string_0, string_1, string_2 }; void f1 (const uint8_t n) { strcpy_P(buf, (char*)pgm_read_word(&(string_table[n]))); Serial.println(buf); } void f2 (const uint8_t n) { //digitalWrite(LED_BUILTIN, n - 4); } typedef void (* TFunc) (const uint8_t); static const TFunc functions[] = { f1, f2 }; void setup() { Serial.begin(9600); } void loop() { functions[0] (1); }Строки 13 и 14 ... зачем? print и сам умеет из прогмема брать.
Всмысле
Serial.println(string_table[n]) верно?Ну, вроде работало так. Работает?
Нет Serial.println(string_table[n]); не работает
Евгений спасибо вам за активную помощь, но возможно ли вызывать функцию blinking #23 из f1. Или лучше сделать ее отдельный вызов. Все строки string_0 ... string_5 у меня наэкране занимают одинаковые знакоместа. С первыми тремя я разобрался, но две другие понадобилось чтобы мигали как бы сигнализировали, поэтому и спрашиваю
// строка мигает в течение intervalTime void blinking(unsigned long intervalTime, bool number) { static unsigned long prevTime = 0; static boolean i=0; if(millis() - prevTime >= intervalTime) { //Если счетчик превысил интервал, i = !i; prevTime = millis(); } //меняем значение переменной i, добавляем к переменной время интервала. // if (number) Serial.println(i ? (const __FlashStringHelper*)string_3 : (const __FlashStringHelper*)string_5); // else Serial.println(i ? (const __FlashStringHelper*)string_4 : (const __FlashStringHelper*)string_5); // тут хотелось бы выводить нужную строку в зависимости от состояния i }#include <avr/pgmspace.h> const char string_0[] PROGMEM = "EDIT"; const char string_1[] PROGMEM = "HEAT"; const char string_2[] PROGMEM = "COOL"; const char string_3[] PROGMEM = "STOP"; const char string_4[] PROGMEM = "PEAK"; const char string_5[] PROGMEM = " "; char buf[6]; // создаем таблицу с отсылками к этим строкам: const char *const string_table[] PROGMEM = { string_0, string_1, string_2,string_3, string_4, string_5 }; void f1 (const uint8_t n) { strcpy_P(buf, (char*)pgm_read_word(&(string_table[n]))); Serial.println(buf); } void f2 (const uint8_t n) { //digitalWrite(LED_BUILTIN, n - 4); } typedef void (* TFunc) (const uint8_t); static const TFunc functions[] = { f1, f2 }; void setup() { Serial.begin(9600); } void loop() { functions[0] (1); }А почему нельзя-то? Неужели Он уж и это запретил? :)
Тогда буду пробывать, если разрешено :)