Как вместо digitalRead() использовать регистр ?

Igoreck
Offline
Зарегистрирован: 01.03.2017

Здавствуйте уважаемые форумчане.

Помогите подставить PORTD вместо "!digitalRead(4)".

/*
 
 */
  #include <avr/io.h>
  #include <util/delay.h>  
   #include <OLED_I2C.h>
   OLED  myOLED(SDA, SCL, 1);
   extern uint8_t SmallFont[];
   volatile int encoderPin1 = 2;      
   volatile int encoderPin2 = 3;      
   volatile int MSB ;
   volatile int LSB ;
   volatile int encoded = 0 ;
   volatile int lastEncoded = 0;
   volatile int sum  ;
   volatile int a = 0;
   volatile int b = 0;
   volatile int c = 0;
   volatile int d = 0;
   volatile int encoderValue = 0;
    byte menuPos = 1;
    long val;
    long val_old = 0; 
    int i = 0;
void setup(){
    Serial.begin (9600);
    myOLED.begin();
    myOLED.clrScr();
    myOLED.setFont(SmallFont);
     
    DDRD &= ~(1<<2); 
    DDRD &= ~(1<<3); 
    PORTD |= 1<<2;   
    PORTD |= 1<<3;    
    PORTD |= 1<<4;   //  pinMode(4, INPUT);// Kнопка "Mеню"                 
    DDRD &= ~(1<<4); //  digitalWrite(4, HIGH);
    
    attachInterrupt(0, updateEncoder, CHANGE);
    attachInterrupt(1, updateEncoder, CHANGE);  

}
void loop(){
     MainScreen();
    if(!digitalRead(4)){ // Заменить на порт прямого доступа "pin 4 Arduino Nano" 
      
      delay(200);
      Menu();
      }
} 
void updateEncoder(){
  MSB = digitalRead(encoderPin1);
  LSB = digitalRead(encoderPin2);
  encoded = (MSB << 1) |LSB; 
  sum  = (lastEncoded << 2) | encoded;
    if(encoded==0b0000){
      if(a==0){
        encoderValue = encoderValue+d; c=1; a=1; b=0;
        }
      }
    if(encoded==0b0011){
      if(b==0){
        encoderValue = encoderValue+d; c=1; b=1; a=0;
        }
      }
    if(sum == 0b1101 || sum == 0b0100 || sum == 0b0010 || sum == 0b1011){
        d=+1;
      }
    if(sum == 0b1110 || sum == 0b0111 || sum == 0b0001 || sum == 0b1000){
        d=-1;
      } 
  lastEncoded = encoded;
}
void MainScreen() {
  myOLED.clrScr();
  //myOLED.invertText(true);
  myOLED.print("MainScreen", CENTER, 20);
  //myOLED.invertText(false);
  myOLED.printNumI(encoderValue, CENTER, 40);
  myOLED.update();
}
void Menu(){  
  for (i=0; i<=500; i++){
    val = encoderValue;
   //***************************** Выбор позиции в меню ***********************          
                if(val > val_old){                
                       menuPos = menuPos + 1;
                       val_old = val;
                        }
                       if(menuPos > 6){
                          menuPos = 1;
                        }                       
                if(val < val_old){
                       menuPos = menuPos - 1;
                       val_old = val;
                        }                     
                      if(menuPos < 1){
                      menuPos = 6;
                        }
        switch(menuPos){
  //***************  устанавливаем курсор согласно позиции в меню  ***************
            case 1:
              myOLED.clrScr();             
              //myOLED.print(">", 0, 0);
              myOLED.invertText(true);              
              myOLED.print("Alarm", 10, 0);
              myOLED.invertText(false);         
              myOLED.print("Time", 10, 10);         
              myOLED.print("Setting", 10, 20);
              myOLED.print("Setting2", 10, 30);
              myOLED.print("Setting3", 10, 40);        
              myOLED.print("back", 10, 50);  
            if (menuPos == 1 && !digitalRead(4)) MenuSetAlarm();    
               break;                              
            case 2:
              myOLED.clrScr();                             
              //myOLED.print(">", 0, 10);
              myOLED.print("Alarm", 10, 0);
              myOLED.invertText(true);        
              myOLED.print("Time", 10, 10);
              myOLED.invertText(false); 
              myOLED.print("Setting", 10, 20);
              myOLED.print("Setting2", 10, 30);
              myOLED.print("Setting3", 10, 40);       
              myOLED.print("back", 10, 50);   
              if (menuPos == 2 && !digitalRead(4)) MenuSetTime();
               break;
            case 3:
              myOLED.clrScr();                      
              //myOLED.print(">", 0, 20);             
              myOLED.print("Alarm", 10, 0);         
              myOLED.print("Time", 10, 10);
              myOLED.invertText(true);         
              myOLED.print("Setting", 10, 20);
              myOLED.invertText(false);
              myOLED.print("Setting2", 10, 30);
              myOLED.print("Setting3", 10, 40);          
              myOLED.print("back", 10, 50);   
              if (menuPos == 3 && !digitalRead(4)) MenuSetting();
               break; 
             case 4:
              myOLED.clrScr();                      
              //myOLED.print(">", 0, 30);             
              myOLED.print("Alarm", 10, 0);         
              myOLED.print("Time", 10, 10);         
              myOLED.print("Setting", 10, 20);
              myOLED.invertText(true); 
              myOLED.print("Setting2", 10, 30);
              myOLED.invertText(false);
              myOLED.print("Setting3", 10, 40);         
              myOLED.print("back", 10, 50);   
              if (menuPos == 4 && !digitalRead(4)) MenuSetting();
               break; 
             case 5:
              myOLED.clrScr();                      
              //myOLED.print(">", 0, 40);             
              myOLED.print("Alarm", 10, 0);         
              myOLED.print("Time", 10, 10);         
              myOLED.print("Setting", 10, 20);
              myOLED.print("Setting2", 10, 30);
              myOLED.invertText(true); 
              myOLED.print("Setting3", 10, 40);
              myOLED.invertText(false);          
              myOLED.print("back", 10, 50);   
              if (menuPos == 5 && !digitalRead(4)) MenuSetting();
               break;                     
            case 6:
              myOLED.clrScr();             
              //myOLED.print(">", 0, 50);     
              myOLED.print("Alarm", 10, 0);   
              myOLED.print("Time", 10, 10);  
              myOLED.print("Setting", 10, 20);
              myOLED.print("Setting2", 10, 30);
              myOLED.print("Setting3", 10, 40);
              myOLED.invertText(true);        
              myOLED.print("back", 10, 50);
              myOLED.invertText(false);   
            }    
          myOLED.update();     
      if (!digitalRead(4) && menuPos == 6){break;}
         } 
  delay (150);     
}
void MenuSetTime(){  
    for (i=0; i<=500; i++){
    myOLED.clrScr();            
    myOLED.print("MenuSetTime", CENTER, 20);
    myOLED.update();
    if(!digitalRead(4)){break;}
  }
}
void MenuSetAlarm(){
  for (i=0; i<=500; i++){
  myOLED.clrScr();            
  myOLED.print("MenuSetAlarm", CENTER, 20);
  myOLED.update();
  if(!digitalRead(4)){break;}
  }
}
void MenuSetting(){
  
}

 

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

Для разных контроллеров по-разному, но, если верить остальным фрагментам кода, то должно подойти !(PIND & (1<<4)).

Но в совокупности с delay(200) это не имеет никакого смысла.

 

PS. Поправлено: оказывается, "остальным фрагментам" верить нельзя.

Green
Offline
Зарегистрирован: 01.10.2015

!(PIND & 1<<4)

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

Igoreck, для чего вам это? На фоне delay(200) никакого смысла в этом нет.

Вы для начала научитесь просто писать нормальный код, без делеев и без жутких операторв кэйса, где в пяти вариантах выбора на 80% один и тот же код.

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

А чё, RTFМ прямо на этом сайте ... с религией что-то?

Igoreck
Offline
Зарегистрирован: 01.03.2017

Спасибо за "!(PIND & 1<<4)" работает четко.

Но поповоду "жутких операторв кэйса" , посоветуйте пожалуйста правильную альтернативу!

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

Igoreck пишет:

Но поповоду "жутких операторв кэйса" , посоветуйте пожалуйста правильную альтернативу!

ну вот смотрите - у вас все кейсы выводят одно и то же - меняется только то, какая строка инвертирована, верно? Так сделайте  процедуру вывода меню, которая будет принимать в качестве параметра номер строки, которую надо подсветить.

По поводу использования регистров вместо digitalRead - вам наверно кажется, что это признак крутого программиста? ^) На самом деле неуклюжий код кейса опускает вас куда сильнее, чем использование регистров - поднимает. По коду кейса сразу видно, что писал чайник. И на фоне этого кажется, что код с регистрами вы просто где-то списали.

Igoreck
Offline
Зарегистрирован: 01.03.2017

Признаюсь - да я в этом пока чайник.

Собрал этот код по кускам и хочу зделать задуманное устройство, но места уже не хватает.

Пришлось укорачивать код посредством перехода из digitalRead()  на тот кошмар который Вы видите.

Говорите "принимать в качестве параметра номер строки, которую надо подсветить", подскажите как привильно это реализовать.

Не кричите на меня, ведь я только учусь.

Прошу Вас поправте в меня в коде.

Igoreck
Offline
Зарегистрирован: 01.03.2017

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

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

Igoreck пишет:

Собрал этот код по кускам и хочу зделать задуманное устройство, но места уже не хватает.

Какого места не хватает? - флеша или оперативки? скорее оперативки...

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

Igoreck
Offline
Зарегистрирован: 01.03.2017

Какие именно строки?

Igoreck
Offline
Зарегистрирован: 01.03.2017

b707 пишет:

Igoreck пишет:

Собрал этот код по кускам и хочу зделать задуманное устройство, но места уже не хватает.

Какого места не хватает? - флеша или оперативки? скорее оперативки...

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

Прошу Вас помогите с оптимизацией кода!

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

В начале скетча добавить

#include <avr/pgmspace.h>
const char string_1[] PROGMEM = "Alarm";
const char string_2[] PROGMEM = "Time";
const char string_3[] PROGMEM = "Settings";
const char string_4[] PROGMEM = "Settings2";
const char string_5[] PROGMEM = "Settings3";
const char string_6[] PROGMEM = "back";
char * menu_table[] ={string_1,string_2,string_3,string_4,string_5,string_6};


void printMenu (int invertLine) {
  int i;
  myOLED.clrScr();     
  for (i = 0; i < 6; i++ )
   {if (i == (invertLine - 1)) myOLED.invertText(true);
    myOLED.print((const __FlashStringHelper *) menu_table[i] , 10, i*10); 
    if (i == (invertLine - 1)) myOLED.invertText(false);   } 
  myOLED.update();   
}

а вместо всего твоего switch - вот это

printMenu(menuPos); 
if (digitalRead(4)) {
  case (menuPos) {
  case 1: MenuSetAlarm();break;
  case 2: MenuSetTime();;break;
  case 3: MenuSettings();;break;
  case 4: MenuSettings();;break;
  case 5: MenuSettings();;break;
}

и не забудь померить, сколько занимал код до и после

Igoreck
Offline
Зарегистрирован: 01.03.2017

b707 пишет:

В начале скетча добавить

#include <avr/pgmspace.h>
const char string_1[] PROGMEM = "Alarm";
const char string_2[] PROGMEM = "Time";
const char string_3[] PROGMEM = "Settings";
const char string_4[] PROGMEM = "Settings2";
const char string_5[] PROGMEM = "Settings3";
const char string_6[] PROGMEM = "back";
char * menu_table[] ={string_1,string_2,string_3,string_4,string_5,string_6};


void printMenu (int invertLine) {
  int i;
  myOLED.clrScr();     
  for (i = 0; i < 6; i++ )
   {if (i == (invertLine - 1)) myOLED.invertText(true);
    myOLED.print((const __FlashStringHelper *) menu_table[i] , 10, i*10); 
    if (i == (invertLine - 1)) myOLED.invertText(false);   } 
  myOLED.update();   
}

а вместо всего твоего switch - вот это

printMenu(menuPos); 
if (digitalRead(4)) {
  case (menuPos) {
  case 1: MenuSetAlarm();break;
  case 2: MenuSetTime();;break;
  case 3: MenuSettings();;break;
  case 4: MenuSettings();;break;
  case 5: MenuSettings();;break;
}

и не забудь померить, сколько занимал код до и после

Выражаю особеную благодарность и дай Вам Бог здоровья.

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

Igoreck пишет:

Выражаю особеную благодарность и дай Вам Бог здоровья.

напишите лучше - помогло или нет? что-то у меня ваш код и мой дают примерно одинаквую загрузку памяти, что удивительно, с учетом переноса строк в Пргмем и того, насколько мой код короче

Igoreck
Offline
Зарегистрирован: 01.03.2017

b707 пишет:

Igoreck пишет:

Выражаю особеную благодарность и дай Вам Бог здоровья.

напишите лучше - помогло или нет? что-то у меня ваш код и мой дают примерно одинаквую загрузку памяти, что удивительно, с учетом переноса строк в Пргмем и того, насколько мой код короче

 

 

Помойму 28%. Точнее сравнить смогу завтра. 

 

Igoreck
Offline
Зарегистрирован: 01.03.2017

К сожалению Ваша оптимизация занимает 8 514 bytes (27%) памяти, а до этого была 7 954 bytes (25%).

Может мой быдлокод не такой плохой как кажется?

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

Igoreck пишет:

К сожалению Ваша оптимизация занимает 8 514 bytes (27%) памяти, а до этого была 7 954 bytes (25%).

Может мой быдлокод не такой плохой как кажется?

Свертывание шести одинаковых кейсов в одну компактную функцию - это однозначно более правильно, чем было.  А почему более компактный код занял больше места - у меня есть некие мысли, вечером вам напишу. Я, видимо, где-то не совсем оптимально построил код, размер скетча явно должен быть менше.

Green
Offline
Зарегистрирован: 01.10.2015

Igoreck пишет:

К сожалению Ваша оптимизация занимает 8 514 bytes (27%) памяти, а до этого была 7 954 bytes (25%).

Может мой быдлокод не такой плохой как кажется?


Это не ваша заслуга. Это заслуга компилятора.)

Igoreck
Offline
Зарегистрирован: 01.03.2017

Не хочу показаться назойливым, но Вы обещали  помочь с оптимизацией !

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

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

Вам нужно что-то ещё?

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

Igoreck пишет:

Не хочу показаться назойливым, но Вы обещали  помочь с оптимизацией !

Не совсем так. Я обещал посмотреть, почему уменьшение скетча не привелу к уменьшению кода. Я мельком глянул - явных ошибок нет, на большее времени пока не хватило. Сегодня попытаюсь покопаться подробнее - мне самому этот вопрос интересен.

qwone
qwone аватар
Offline
Зарегистрирован: 03.07.2016
#include <avr/pgmspace.h>
const char string_1[] PROGMEM = "Alarm";
const char string_2[] PROGMEM = "Time";
const char string_3[] PROGMEM = "Settings";
const char string_4[] PROGMEM = "Settings2";
const char string_5[] PROGMEM = "Settings3";
const char string_6[] PROGMEM = "back";
char * menu_table[] = {string_1, string_2, string_3, string_4, string_5, string_6};


void printMenu (int invertLine) {
  int i;
  myOLED.clrScr();
  for (i = 0; i < 6; i++ )
  { if (i == (invertLine - 1)) myOLED.invertText(true);
    char buffer[15];
    strcpy_P(buffer, (char*)pgm_read_word(&(menu_table[i])));
    myOLED.print( buffer, 10, i * 10);
    if (i == (invertLine - 1)) myOLED.invertText(false);
  }
  myOLED.update();
}

 

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

qwone?  ты меня потроллить решил? :)

Строки 16 и 17 не обязательны, возьми ИДЕ и проверь :)

 

И вообще - вопрос в другом - вставь этот код в код ТС из заголовка темы и убедись - число строк скетча сокращается с 200 до 150, а размер прошивки при этом растет с 6700 до 7700 байт. почему?

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

b707 пишет:
И вообще - вопрос в другом - вставь этот код в код ТС из заголовка темы и убедись - число строк скетча сокращается с 200 до 150, а размер прошивки при этом растет с 6700 до 7700 байт. почему?
Я давно не лезу в чужие [идеологически неправильно спроектированые тексты ] коды . И все потому, что чужие коды заминированы кучей [не мин] скрытых ошибок. А ответ на почему, так исходник и получаемый код это совершенно разные вещи. Можно написать короткую программу , занимающую очень много места [привет #include] и обратно.

ПС: У меня этого железа нет, а значит и программу под это железо писать не могу. Потому что успешное компилирование и успешная работа это тоже разные вещи.

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

qwone пишет:

ПС: У меня этого железа нет, а значит и программу под это железо писать не могу. Потому что успешное компилирование и успешная работа это тоже разные вещи.

Ссылки на неунжное железо можно выкинуть, суть вопроса не в этом. Я, собственно, собирался сегодня об этом даже новую тему открыть (что у меня случается крайне редко) - но потом решил, что еще не готов, не все варианты кода попробовал. Я не совсем рад, что эта тема выплыла - рано еще - но если у кого есть свежие мысли, то велкам.

Igoreck
Offline
Зарегистрирован: 01.03.2017

Добрый вечер уважаемые колеги.

  
#include <OLED_I2C.h>
  const char string_1[] PROGMEM = "Alarm";
  const char string_2[] PROGMEM = "Time";
  const char string_3[] PROGMEM = "Setting";
  const char string_4[] PROGMEM = "Setting2";
  const char string_5[] PROGMEM = "Setting3";
  const char string_6[] PROGMEM = "back";
  const char * menu_table[] ={string_1,string_2,string_3,string_4,string_5,string_6};

  OLED  myOLED(SDA, SCL, 1);
  extern uint8_t SmallFont[];
  #define encoderPin1  2
  #define encoderPin2  3
  byte MSB ;
  byte LSB ;
  byte encoded = 0 ;
  byte lastEncoded = 0;
  byte sum;  
  byte a = 0;
  byte b = 0;
  byte c = 0;
  volatile int d = 0;
  volatile int encoderValue = 0;
  byte menuPos = 1;
  byte val;
  byte val_old = 0; 
  int i = 0;
void setup(){
    Serial.begin (9600);
    myOLED.begin();
    myOLED.clrScr();
    myOLED.setFont(SmallFont);
     
    DDRD &= ~(1<<2); 
    DDRD &= ~(1<<3); 
    PORTD |= 1<<2;   
    PORTD |= 1<<3;    
    PORTD |= 1<<4;   //    pinMode(4, INPUT);    // Kнопка "Mеню"                 
    DDRD &= ~(1<<4); //    digitalWrite(4, HIGH);
    
    attachInterrupt(0, updateEncoder, CHANGE);
    attachInterrupt(1, updateEncoder, CHANGE);  

}
void loop(){
     MainScreen();
    if(!(PIND & 1<<4)){
      
      delay(200);
      Menu();
      }

      
}
void printMenu (byte invertLine) {
  byte i;
  myOLED.clrScr();     
  for (i = 0; i < 6; i++ )
   {if (i == (invertLine - 1)) myOLED.invertText(true);
    myOLED.print((const __FlashStringHelper *) menu_table[i] , 10, i*10); 
    if (i == (invertLine - 1)) myOLED.invertText(false);   } 
  myOLED.update();   
} 
void updateEncoder(){
  MSB = digitalRead(encoderPin1);
  LSB = digitalRead(encoderPin2);
  encoded = (MSB << 1) |LSB; 
  sum  = (lastEncoded << 2) | encoded;
    if(encoded==0b0000){
      if(a==0){
        encoderValue = encoderValue+d; c=1; a=1; b=0;
        }
      }
    if(encoded==0b0011){
      if(b==0){
        encoderValue = encoderValue+d; c=1; b=1; a=0;
        }
      }
    if(sum == 0b1101 || sum == 0b0100 || sum == 0b0010 || sum == 0b1011){
        d=+1;
      }
    if(sum == 0b1110 || sum == 0b0111 || sum == 0b0001 || sum == 0b1000){
        d=-1;
      } 
  lastEncoded = encoded;
}
void MainScreen() {
  myOLED.clrScr();  
  myOLED.print("MainScreen", CENTER, 20);  
  myOLED.printNumI(encoderValue, CENTER, 40);
  myOLED.update();
}
void Menu(){  
  for (i=0; i<=500; i++){
    val = encoderValue;
   //***************************** Выбор позиции в меню ***********************          
                if(val > val_old){                
                       menuPos = menuPos + 1;
                       val_old = val;
                        }
                       if(menuPos > 6){
                          menuPos = 1;
                        }                       
                if(val < val_old){
                       menuPos = menuPos - 1;
                       val_old = val;
                        }                     
                      if(menuPos < 1){
                      menuPos = 6;
                        }
      printMenu(menuPos); 
      if (!(PIND & 1<<4)) {
          switch  (menuPos) {
          case 1: MenuSetAlarm();break;
          case 2: MenuSetTime();break;
          case 3: MenuSetting();break;
          case 4: MenuSetting2();break;
          case 5: MenuSetting3();break;
          case 6: MainScreen();break;
        }
      }
  }
}
void MenuSetTime(){  
    for (i=0; i<=500; i++){
    myOLED.clrScr();            
    myOLED.print("MenuSetTime", CENTER, 20);
    myOLED.update();
    if(!(PIND & 1<<4)){break;}
  }
}
void MenuSetAlarm(){
  for (i=0; i<=500; i++){
  myOLED.clrScr();            
  myOLED.print("MenuSetAlarm", CENTER, 20);
  myOLED.update();
  if(!(PIND & 1<<4)){break;}
  }
}
void MenuSetting(){
  
}
void MenuSetting2(){
  
}
void MenuSetting3(){
  
}

 

 
Sketch uses 8 514 bytes (27%) of program storage space. Maximum is 30 720 bytes.
Global variables use 1 297 bytes (63%) of dynamic memory, leaving 751 bytes for local variables. Maximum is 2 048 bytes.

Может библиотеку заменить на U8glib, она меньше весит, но сам я не справлюсь ?

Помогите мне пожалуйста.

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

Вообще-то скетч на Ардуине, если в ней должно быть меню надо начинать так :)

/**/
//-------------------------------
typedef struct {
  uint16_t Name;/*выводимый текст*/
  uint8_t  PgUp;
  uint8_t  PgDn;
  uint16_t Left;
  uint16_t Right;
} MenuNote;
//--------------------------------------
void Do0minus() {} /*обработчик 0 экрана*/
void Do0plus() {} /*обработчик 0 экрана*/
void Do1minus() {} /*обработчик 1 экрана*/
void Do1plus() {} /*обработчик 1 экрана*/
void Do2minus() {} /*обработчик 2 экрана*/
void Do2plus() {} /*обработчик 2 экрана*/
void Do3minus() {} /*обработчик 3 экрана*/
void Do3plus(){}/*обработчик 3 экрана*/
const char PROGMEM txt0[] = "0-punkt";
const char PROGMEM txt1[] = "1-punkt";
const char PROGMEM txt2[] = "2-punkt";
const char PROGMEM txt3[] = "3-punkt";
const MenuNote PROGMEM Menu[]  = {
  /*имя,PgUp,PgDn,Left,Right*/
  txt0, 0, 0, (uint16_t)Do0minus, (uint16_t)Do0plus, /*0-экран*/
  txt1, 0, 0, (uint16_t)Do1minus, (uint16_t)Do1plus, /*1-экран*/
  txt2, 0, 0, (uint16_t)Do2minus, (uint16_t)Do2plus, /*2-экран*/
  txt3, 0, 0, (uint16_t)Do3minus, (uint16_t)Do3plus  /*3-экран*/
};
//----------------------------------------
void setup() {


}

void loop() {

}

 

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

Разбирался, почему у меня после сокращения исходника на четверть размер прошивки вдруг вырос. Провел тесты, упростил скетч до предела - и НАШЕЛ.

Правда найти - не значит понять. Вот примеры, чтобы ВЫСВЕТИТЬ ЭФФЕКТ.

Эффект, надо сказать, классный.

Скетч номер 1

#include <OLED_I2C.h>
OLED  myOLED(SDA, SCL, 1);

void setup() {
  myOLED.begin();
}

void loop() {
      myOLED.print("Alarm", 10, 0);   
     }

Flash: 2786 bytes, RAM: 1050

 

Скетч номер 2

#include <OLED_I2C.h>
OLED  myOLED(SDA, SCL, 1);

const char string_1[] = "Alarm";



void setup() {
  myOLED.begin();
}

void loop() {
    myOLED.print( string_1, 10, 10);
      }

Flash: 4338 bytes, RAM: 1060

 

Непостижимо. Откуда разница в 1500 байт? Можно было бы подумать, что для разных параметров вызываются разные функции myOLED.print(). Но параметр-то вроде одного типа - const char*

 

 

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

А что говорит дизассемблер?

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

Если хотите, чтобы я тоже попытался поломать голову, то дайте пожалуйста ссылку где Вы брали OLED_I2C.h(ну и вообще библиотеку), чтобы эксперимент чистым был.

Igoreck
Offline
Зарегистрирован: 01.03.2017

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

Если хотите, чтобы я тоже попытался поломать голову, то дайте пожалуйста ссылку где Вы брали OLED_I2C.h(ну и вообще библиотеку), чтобы эксперимент чистым был.

Пожалуйста.

http://www.rinkydinkelectronics.com/library.php?id=79

 

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

b707, ну, понятно.

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

Уберите const в строке 4 и скомпилируйте. Полюбуйтесь.

А потом (тоже интересно) ещё попробуйте ту же строку в виде char * string_1 = "Alarm";)

Проблема же в том, что разработчик библиотеки не предусмотрел метода print с константным параметром. У него параметр не объявлен константой (и, кстати, совершенно необоснованно). Ну, а коль так, Ваш компилятор тащит "службу защиты констант", с копированием, дублированием и восстановлением после вызова. 

Почему он не сделал параметр константой - хрен его знает. Как говорил Мюллер в исполнении Броневого: "Невозможно понять логику непрофессионала".

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

ЕвгенийП, спасибо за ответ. Но яснее не стало. Назвать меня "грамотным" вы явно поспешили :)

Мне непонятно, почему код компилируется по разному. Ведь если автор библиотеки поленился обьявить параметр функции константой - это должно сказываться в обоих случаях, рахве нет?

Разве константная строка "Alarm" и константа const char string_1[] = "Alarm"; - это не один тип данных для компилятора?

Если ответ лежит в примерах, которые вы предлагаете проверить - тогда подождем до вечера, мне сейчас нечем компилировать.

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

b707 пишет:

это должно сказываться в обоих случаях, рахве нет?

Нет. Вот смотрите.

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

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

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

понял, огромное спасибо.

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

Igoreck - практический вывод из этой дискуссии - надо чуть подправить библиотеку OLED_I2C.h. Я вечером посмотрю и напишу. что вам сделать. Думаю, получится выиграть порядка 1500 - 2000 байт в размере кода.

Но это касается флеш. Занятость оперативной памяти от этого почти не изменится - а у вас, насколько я вижу - загвоздка в этом. Чтобы решить эту проблему кардинально - надо выбирать другую библиотеку ОЛЕД или вообще писать свою.

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

Кстати, попробовал поменять в библиотеке тип параметра на const (ну, и всё, что для этого надо аккуратно сделал). Ваш "плохой пример" сразу стал нормально компилироваться с нормальным размером.

P.S. Заодно глянул код библиотеки - действительно очень по-любительски написано.

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

Так накидал примерный скетч Меню, работает. Если ТС разберется, то сможет применить.

/**/
unsigned long mill;// переменная для millis()
typedef void (*pDo)() ;// тип -функция обработчик
//-------------------------------
typedef struct {
  uint16_t Name;/*текст страницы*/
  uint8_t  ThisLine;/*текущая строка*/
  uint8_t  TopLine;/*верхняя строка*/
  uint8_t  LowerLine;/*нижняя строка*/
  uint8_t  UpPg;/*верхняя страница*/
  uint8_t  DnPg;/*нижняя страница*/
  uint16_t DoExe;/*обработчик привязанный к странице*/
} MenuNote;
//------Cl_Display----------------------
// класс регулярный вывод на дисплей
class Cl_Display {
  protected:
    pDo Do;//обработчик
    bool refreshON = 1; //сигнал обновить
  public:
    /*конструктор*/
    Cl_Display(pDo D): Do(D) {}
    /*инициализация-вставить в setup()*/
    void init() {
    }
    /*работа-вставить в loop()*/
    void run() {
      if (refreshON) {
        refreshON = 0;
        Do();
      }
    }
    void refresh() {
      refreshON = 1;
    }
    /*записать новый обработчик*/
    void write( pDo D) {
      Do = D;
    }
};
//------Cl_Btn----------------------
// класс кнопка
class Cl_Btn {
  protected:
    const byte pin;
    pDo Do;//обработчик
    bool bounce = 0;
    bool btn = 1, oldBtn;
    unsigned long past;
  public:
    /*конструктор*/
    Cl_Btn(byte p, pDo D): pin(p), Do(D) {}
    /*инициализация-вставить в setup()*/
    void init() {
      pinMode(pin, INPUT_PULLUP);
    }
    /*работа-вставить в loop()*/
    void run() {
      bool newBtn = digitalRead(pin);
      if (!bounce && newBtn != btn) {
        bounce = 1;
        past = mill;
      }
      if (bounce && mill - past >= 10) {
        bounce = 0 ;
        oldBtn = btn;
        btn = newBtn;
        past = mill;
        if (!btn && oldBtn) Do();
      }
    }
    /*записать новый обработчик*/
    void write( pDo D) {
      Do = D;
    }
};
//--------------------------------------
const byte maxScreen = 6;
byte screen = 0;// текущий экран
void DoExe0() { /*обработчик 0 экрана*/
  Serial.println();
  Serial.print("DoExe0");
}
void DoExe1() { /*обработчик 1 экрана*/
  Serial.println();
  Serial.print("DoExe1");
}
void DoExe2() { /*обработчик 2 экрана*/
  Serial.println();
  Serial.print("DoExe2");
}
void DoExe3() { /*обработчик 3 экрана*/
  Serial.println();
  Serial.print("DoExe3");
}
void DoExe4() {/*обработчик 4 экрана*/
  Serial.println();
  Serial.print("DoExe4");
}
void DoExe5() { /*обработчик 5 экрана*/
  Serial.println();
  Serial.print("DoExe5");
}
const char txt0[] PROGMEM = "Alarm";
const char txt1[] PROGMEM = "Time";
const char txt2[] PROGMEM = "Setting";
const char txt3[] PROGMEM = "Setting2";
const char txt4[] PROGMEM = "Setting3";
const char txt5[] PROGMEM = "Back";
const MenuNote PROGMEM Menu[maxScreen]  = {
  /*Name,ThisLine,TopLine,LowerLine,UpPg,DnPg,DoExe*/
  {txt0  , 0     , 5     , 0     , 0  , 1 , (uint16_t)DoExe0}, /*0-экран*/
  {txt1  , 1     , 5     , 0     , 0  , 2 , (uint16_t)DoExe1}, /*1-экран*/
  {txt2  , 2     , 5     , 0     , 1  , 3 , (uint16_t)DoExe2}, /*2-экран*/
  {txt3  , 3     , 5     , 0     , 2  , 4 , (uint16_t)DoExe3}, /*3-экран*/
  {txt4  , 4     , 5     , 0     , 3  , 5 , (uint16_t)DoExe4}, /*4-экран*/
  {txt5  , 5     , 5     , 0     , 4  , 5 , (uint16_t)DoExe5}  /*5-экран*/
};
// дисплей
/*вывеcти страницу на дисплей*/
void vievPg() {
  byte ThisLine = (uint8_t)pgm_read_byte_near(&Menu[screen].ThisLine);
  byte TopLine = (uint8_t)pgm_read_byte_near(&Menu[screen].TopLine);
  byte LowerLine = (uint8_t)pgm_read_byte_near(&Menu[screen].LowerLine);
  Serial.println();
  for (byte i = LowerLine; i <= TopLine; ++i) {
    char buffer[15];
    strcpy_P(buffer, (char*)pgm_read_word(&Menu[i].Name));
    Serial.println();
    if (i == ThisLine) Serial.print(">");
    else Serial.print(" ");
    Serial.print(buffer);
  }
}
Cl_Display Display(/*обработчик*/vievPg);
/*перейти на верхнюю строчку*/
void GoTopLine() {
  screen = (uint8_t)pgm_read_byte_near(&Menu[screen].UpPg);
  Display.refresh();
}
/*перейти на нижнюю строчку*/
void GoLowerLine() {
  screen = (uint8_t)pgm_read_byte_near(&Menu[screen].DnPg);
  Display.refresh();
}
Cl_Btn BtnUp  (/*пин*/2,/*обработчик*/GoTopLine);/*Кнопка строка вверх*/
Cl_Btn BtnDown(/*пин*/3,/*обработчик*/GoLowerLine);/*Кнопка строка вниз*/
/*Выполнить обработчик строки*/
void DoExe() {
  pDo Do = (pDo)pgm_read_word_near(&Menu[screen].DoExe);
  Do();
  //Display.refresh();
}
Cl_Btn BtnExe(/*пин*/4,/*обработчик*/DoExe);/*Кнопка Выполнить*/
//---main()-------------------------------------
void setup() {
  Serial.begin(9600);
  Display.init();
  BtnUp.init();
  BtnDown.init();
  BtnExe.init();
}
void loop() {
  mill = millis();
  Display.run();
  BtnUp.run();
  BtnDown.run();
  BtnExe.run();
}
/*Скетч использует 2662 байт (8%) памяти устройства. Всего доступно 30720 байт.
  Глобальные переменные используют 270 байт (13%) динамической памяти, оставляя 1778 байт для локальных переменных. Максимум: 2048 байт.
*/

 

Igoreck
Offline
Зарегистрирован: 01.03.2017

Ценю Ваш труд колега, но есть вопросы.

 

 

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

Мои вам соболезнования и вашему компилятору.  Как вариант могу этот вариант скинуть . Но у меня все в норме

/**/
unsigned long mill;// переменная для millis()
typedef void (*pDo)() ;// тип -функция обработчик
//-------------------------------
typedef struct {
  const char *Name;/*текст страницы*/
  const uint8_t  ThisLine;/*текущая строка*/
  const uint8_t  TopLine;/*верхняя строка*/
  const uint8_t  LowerLine;/*нижняя строка*/
  const uint8_t  UpPg;/*верхняя страница*/
  const uint8_t  DnPg;/*нижняя страница*/
  const pDo DoExe;/*обработчик привязанный к странице*/
} MenuNote;
//------Cl_Display----------------------
// класс регулярный вывод на дисплей
class Cl_Display {
  protected:
    pDo Do;//обработчик
    bool refreshON = 1; //сигнал обновить
  public:
    /*конструктор*/
    Cl_Display(pDo D): Do(D) {}
    /*инициализация-вставить в setup()*/
    void init() {
    }
    /*работа-вставить в loop()*/
    void run() {
      if (refreshON) {
        refreshON = 0;
        Do();
      }
    }
    void refresh() {
      refreshON = 1;
    }
    /*записать новый обработчик*/
    void write( pDo D) {
      Do = D;
    }
};
//------Cl_Btn----------------------
// класс кнопка
class Cl_Btn {
  protected:
    const byte pin;
    pDo Do;//обработчик
    bool bounce = 0;
    bool btn = 1, oldBtn;
    unsigned long past;
  public:
    /*конструктор*/
    Cl_Btn(byte p, pDo D): pin(p), Do(D) {}
    /*инициализация-вставить в setup()*/
    void init() {
      pinMode(pin, INPUT_PULLUP);
    }
    /*работа-вставить в loop()*/
    void run() {
      bool newBtn = digitalRead(pin);
      if (!bounce && newBtn != btn) {
        bounce = 1;
        past = mill;
      }
      if (bounce && mill - past >= 10) {
        bounce = 0 ;
        oldBtn = btn;
        btn = newBtn;
        past = mill;
        if (!btn && oldBtn) Do();
      }
    }
    /*записать новый обработчик*/
    void write( pDo D) {
      Do = D;
    }
};
//--------------------------------------
const byte maxScreen = 6;
byte screen = 0;// текущий экран
void DoExe0() { /*обработчик 0 экрана*/
  Serial.println();
  Serial.print("DoExe0");
}
void DoExe1() { /*обработчик 1 экрана*/
  Serial.println();
  Serial.print("DoExe1");
}
void DoExe2() { /*обработчик 2 экрана*/
  Serial.println();
  Serial.print("DoExe2");
}
void DoExe3() { /*обработчик 3 экрана*/
  Serial.println();
  Serial.print("DoExe3");
}
void DoExe4() {/*обработчик 4 экрана*/
  Serial.println();
  Serial.print("DoExe4");
}
void DoExe5() { /*обработчик 5 экрана*/
  Serial.println();
  Serial.print("DoExe5");
}
const char txt0[] PROGMEM = "Alarm";
const char txt1[] PROGMEM = "Time";
const char txt2[] PROGMEM = "Setting";
const char txt3[] PROGMEM = "Setting2";
const char txt4[] PROGMEM = "Setting3";
const char txt5[] PROGMEM = "Back";
const MenuNote PROGMEM Menu[maxScreen]  = {
  /*Name,ThisLine,TopLine,LowerLine,UpPg,DnPg,DoExe*/
  {txt0  , 0     , 5     , 0     , 0  , 1 , &DoExe0}, /*0-экран*/
  {txt1  , 1     , 5     , 0     , 0  , 2 , &DoExe1}, /*1-экран*/
  {txt2  , 2     , 5     , 0     , 1  , 3 , &DoExe2}, /*2-экран*/
  {txt3  , 3     , 5     , 0     , 2  , 4 , &DoExe3}, /*3-экран*/
  {txt4  , 4     , 5     , 0     , 3  , 5 , &DoExe4}, /*4-экран*/
  {txt5  , 5     , 5     , 0     , 4  , 5 , &DoExe5}  /*5-экран*/
};
// дисплей
/*вывеcти страницу на дисплей*/
void vievPg() {
  byte ThisLine = (uint8_t)pgm_read_byte_near(&Menu[screen].ThisLine);
  byte TopLine = (uint8_t)pgm_read_byte_near(&Menu[screen].TopLine);
  byte LowerLine = (uint8_t)pgm_read_byte_near(&Menu[screen].LowerLine);
  Serial.println();
  for (byte i = LowerLine; i <= TopLine; ++i) {
    char buffer[15];
    strcpy_P(buffer, (char*)pgm_read_word(&Menu[i].Name));
    Serial.println();
    if (i == ThisLine) Serial.print(">");
    else Serial.print(" ");
    Serial.print(buffer);
  }
}
Cl_Display Display(/*обработчик*/vievPg);
/*перейти на верхнюю строчку*/
void GoTopLine() {
  screen = (uint8_t)pgm_read_byte_near(&Menu[screen].UpPg);
  Display.refresh();
}
/*перейти на нижнюю строчку*/
void GoLowerLine() {
  screen = (uint8_t)pgm_read_byte_near(&Menu[screen].DnPg);
  Display.refresh();
}
Cl_Btn BtnUp  (/*пин*/2,/*обработчик*/GoTopLine);/*Кнопка строка вверх*/
Cl_Btn BtnDown(/*пин*/3,/*обработчик*/GoLowerLine);/*Кнопка строка вниз*/
/*Выполнить обработчик строки*/
void DoExe() {
  pDo Do = (pDo)pgm_read_word_near(&Menu[screen].DoExe);
  Do();
  //Display.refresh();
}
Cl_Btn BtnExe(/*пин*/4,/*обработчик*/DoExe);/*Кнопка Выполнить*/
//---main()-------------------------------------
void setup() {
  Serial.begin(9600);
  Display.init();
  BtnUp.init();
  BtnDown.init();
  BtnExe.init();
}
void loop() {
  mill = millis();
  Display.run();
  BtnUp.run();
  BtnDown.run();
  BtnExe.run();
}
/*Скетч использует 2662 байт (8%) памяти устройства. Всего доступно 30720 байт.
  Глобальные переменные используют 270 байт (13%) динамической памяти, оставляя 1778 байт для локальных переменных. Максимум: 2048 байт.
*/

 

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

Igoreck.

Как обещал - пишу что надо поправить в библиотеке OLED_I2C.h.

=== В файле OLED_I2C.h  ===

---- строка 113

void    print(char *st, int x, int y);

исправить на

void    print(const char *st, int x, int y);

 

=== В файле OLED_I2C.cpp  ===

---- строка 181

void OLED::print(char *st, int x, int y)

исправить на

void OLED::print(const char *st, int x, int y)

===========================

У меня исправление двух этих строк уменьшают ваш код на 1450 байт.

 

 

 

 

Igoreck
Offline
Зарегистрирован: 01.03.2017

Спасибо огромное, мне очень приятно что Вы находите  на это время, и помогаете. 

Igoreck
Offline
Зарегистрирован: 01.03.2017

На чем Вы работаете?

Какая у Вас среда и версия разработки?

Igoreck
Offline
Зарегистрирован: 01.03.2017

Можно ли в Visual Studio, но заливать потом как?

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

Igoreck пишет:

На чем Вы работаете?

Какая у Вас среда и версия разработки?

Ваш код я проверял на Ардуино ИДЕ 1.8.3,   Вин7 х64

 

Igoreck
Offline
Зарегистрирован: 01.03.2017

Тогда все ясно- 1.6.5!

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

Igoreck пишет:

Тогда все ясно- 1.6.5!

А что, что-то не работает?

Igoreck
Offline
Зарегистрирован: 01.03.2017

Да тут Qwone пишет что соболезнует моему компилятору, поэтому я разпереживался.

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

Igoreck пишет:

Можно ли в Visual Studio, но заливать потом как?

при уже установленной Arduino IDE добавить в Visual Studio плагин Visual Micro