Помогите оптимизировать код для уменьшения занимаемой памяти

Тема в разделе "Микроконтроллеры AVR", создана пользователем ELITE, 19 дек 2018.

  1. ELITE

    ELITE Гик

    Делаю игрушку ребенку
    собственно апппаратная часть уже готова, осталось код...
    Суть устройства - РЖБ диод и 4 кнопки
    одна кнопка переключать "ручной" или "авто" режим работы
    3 кнопки в "ручном" режиме включают соответствующие цвета
    в "авто" выполняют программы
    Всего в плане было запихнуть 16 вариантов работы, но влезло только 10...

    в качестве "мозга" тини 13, с 1кб памяти
    //Просьба не предлагать перейти на другой МК, знаю, что можно 25/45 или даже 85ю купить и не парится! но 13я уже есть, а за другими надо ехать... да и плата уже спаяна и корпус уже собран .
    ---------
    есть код, который у меня уже 1008 байт скушал :( но мне бы еще байт 250-300 выжать бы...

    Помогите, что в нем можно сократить без потери функциональности
    Код (C++):
    #include <avr/io.h>
    #include <stdlib.h>
    #include <avr/interrupt.h>
    #include <util/delay.h>

    #define LED1_OFF PORTB &= ~_BV(PB0)
    #define LED2_OFF PORTB &= ~_BV(PB1)
    #define LED3_OFF PORTB &= ~_BV(PB2)
    #define LED1_ON PORTB |= _BV(PB0)
    #define LED2_ON PORTB |= _BV(PB1)
    #define LED3_ON PORTB |= _BV(PB2)

    #define ANL_IN _BV(PB3)
    #define DIG_IN _BV(PB4)

    uint8_t counter=0,  j=0,  ci=0;
    uint8_t lev_ch[]={0,0,0};
    uint8_t ch[3];
    uint8_t mods = 0; // выбор режима
    uint8_t vol = 0;

    uint8_t levels[] = {0, 1, 3, 5, 7, 10, 13, 17, 21, 26, 32, 40, 48, 58, 68, 80, 92, 106, 120, 140, 164, 192, 224, 255};  //коррекция уровней видимой яркости

    ISR(TIM0_OVF_vect){
    /*программный ШИМ*/
    counter>=lev_ch[0]?LED1_OFF:LED1_ON;
    counter>=lev_ch[1]?LED2_OFF:LED2_ON;
    counter>=lev_ch[2]?LED3_OFF:LED3_ON;
    counter++;

    }
    ISR(ADC_vect){ //ЦАП для определения какая кнопка нажата (кнопки на резисторном делителе)
    vol = ADCH;
    if (             vol <= 95 ) mods=1;
    if (vol > 95  && vol <= 105 ) mods=2;
    if (vol > 105 && vol <= 120 ) mods=3;
    if (vol > 120 && vol <= 135 ) mods=4;
    if (vol > 135 && vol <= 155 ) mods=5;
    if (vol > 155 && vol <= 180 ) mods=6;
    if (vol > 180 && vol <= 225 ) mods=7;
    if (vol > 225 && vol <= 255 ) mods=8;
    }

    int main(void)
    {
    DDRB=0b00000111; // установка PortB пины 0 1 2 выходы
    PORTB |= DIG_IN; // Включаем подтягивающий (Pull-UP) резистор для пина
    TCCR0B = _BV(CS00);  // таймер работает без предделителя
    TIMSK0 = _BV(TOIE0); // прерывания по переполнению таймера разрешены

    // Настройка АЦП:
    ADMUX = 0b00100011;
    ADCSRA = 0b11101111;
    ADCSRB = 0b00000000;
    DIDR0 |= ANL_IN;  

    sei();
    /*******************************************************************************************************/
    while (1)   //бесконечная шарманка
    {
     
      if( !(PINB & DIG_IN) ) {     mods = mods+10;     }
     
      /****************************************/  
      // Ручные режимы    //   синий             зелены            красный
          if (mods==1) {  lev_ch[0]=60;      lev_ch[1]=60;     lev_ch[2]=60;    }         //белый 30%
          if (mods==2) {  lev_ch[0]=0;       lev_ch[1]=0;      lev_ch[2]=255;   }     //красный
          if (mods==3) {  lev_ch[0]=0;       lev_ch[1]=255;    lev_ch[2]=0;     }     //зеленый
          if (mods==4) {  lev_ch[0]=0;       lev_ch[1]=255;    lev_ch[2]=255;   }       //желтый
          if (mods==5) {  lev_ch[0]=255;     lev_ch[1]=0;      lev_ch[2]=0;     }     //синий
          if (mods==6) {  lev_ch[0]=255;     lev_ch[1]=0;      lev_ch[2]=255;   }       //сиреневый
          if (mods==7) {  lev_ch[0]=255;     lev_ch[1]=255;    lev_ch[2]=0;     }       //бирюзовый
          if (mods==8) {  lev_ch[0]=255;     lev_ch[1]=255;    lev_ch[2]=255;   }         //белый
     
      // Авторежимы
        if (mods==11) {     //переливы цветов с полными переходами        
             if( ci==0) {      lev_ch[0]=levels[j];      lev_ch[1]=0;              lev_ch[2]=levels[23];        }
             if( ci==1) {      lev_ch[0]=levels[23];     lev_ch[1]=0;              lev_ch[2]=levels[23-j];      }
             if( ci==2) {      lev_ch[0]=levels[23];     lev_ch[1]=levels[j];      lev_ch[2]=0;                 }
             if( ci==3) {      lev_ch[0]=levels[23-j];   lev_ch[1]=levels[23];     lev_ch[2]=0;                 }
             if( ci==4) {      lev_ch[0]=0;              lev_ch[1]=levels[23];     lev_ch[2]=levels[j];         }
             if( ci==5) {      lev_ch[0]=0;              lev_ch[1]=levels[23-j];   lev_ch[2]=levels[23];        }
           
             if (++j>23) {j=0; ci++; if(ci==6) ci=0;}
           _delay_ms (250);    
          }
          if (mods==12) {     //  радуга
            lev_ch[0]=0;  lev_ch[1]=0;  lev_ch[2]=0;  _delay_ms (500);      //-
            lev_ch[0]=0;  lev_ch[1]=0;  lev_ch[2]=255;  _delay_ms (2500);   //К
            lev_ch[0]=0;  lev_ch[1]=64;  lev_ch[2]=255;  _delay_ms (2500);  //О
            lev_ch[0]=0;  lev_ch[1]=127;  lev_ch[2]=255;  _delay_ms (2500); //Ж
            lev_ch[0]=0;  lev_ch[1]=255;  lev_ch[2]=0;  _delay_ms (2500);   //З
            lev_ch[0]=127;  lev_ch[1]=32;  lev_ch[2]=32;  _delay_ms (2500); //Г
            lev_ch[0]=255;  lev_ch[1]=0;  lev_ch[2]=0;  _delay_ms (2500);   //С
            lev_ch[0]=255;  lev_ch[1]=0;  lev_ch[2]=127;  _delay_ms (2500); //Ф
            lev_ch[0]=0;  lev_ch[1]=0;  lev_ch[2]=0;  _delay_ms (500);      //-
         
            }
          if (mods==13) {     //  а на это мне уже не хватило памяти :(
            }
          if (mods==14) {     //  
            }
          if (mods==15) {     //  
            }
          if (mods==16) {     //  
            }
          if (mods==17) {     //  
            }
          if (mods==18) {     //  
            }
       
    }
    }
     
  2. b707

    b707 Гуру

    1. резисторы в кнопочном ЦАПе подобрать так, чтоб их отношение составляло степени двойки. Тогда нажатие каждой кнопки можно будет свести к включению в ADC соответсвующего бита и сложный IF для чтения кнопок можно будет переписать битовыми операциями в одну-две строчки.
    2. В ручных режимах закодировать цвета в формате 222 - по три цвета в один байт - в итоге восемь IF для первых режимов можно бужет записать в одну строку.
     
    vasdor, ELITE и Airbus нравится это.
  3. ELITE

    ELITE Гик

    резисторы 1, 2 ,4 ком / 4к7

    а как в ЦАПе в АДС на биты перейти?
     

    Вложения:

    • 124214.png
      124214.png
      Размер файла:
      1,4 КБ
      Просмотров:
      74
  4. AlexU

    AlexU Гуру

    Могу ошибаться, но это вроде как 'inline' функция. Если переписать, то можно сэкономить несколько десятков байт.
    Так же уверенности полной нет, но версия компилятора может помочь сэкономить память. Есть проект для ATtiny45, который avr-gcc компилятором версии < 7.0, давал код на две сотни байт больше, чем версии >= 7.0. При этом функциональность вся сохранялась.
     
  5. parovoZZ

    parovoZZ Гуру

    Это в eeprom.
    Попробовать if заменить на switch.

    это нельзя простой арифметикой заменить?

    int main на void main. Хотя это только ОЗУ прибавляет либо в современных компиляторах вообще ничего не дает.

    На сайте микрочипа лежит апноут по эффективному программированию. На микросине есть отличный перевод.
     
  6. Asper Daffy

    Asper Daffy Гуру

    О, спец по двадцати языкам проявился. Привет! Как жизнь?
     
    DIYMan нравится это.
  7. DetSimen

    DetSimen Спамовредитель Модератор

    попробуй заменить на:
    Код (C++):
    const uint8_t keyValues[] PROGMEM = { 95,105,120,135,155,180,225,255 };

    uint8_t getKey(uint8_t avalue) {
        uint8_t valsize = sizeof(keyValues) / sizeof(keyValues[0]);
        for (uint8_t i = 0; i < valsize; i++)
            if (avalue <= keyValues[i]) return i;
        return 0xFF;   // заглушка от варнингов, такого никогда не случится
    }

    ISR(ADC_vect) { //ЦАП для определения какая кнопка нажата (кнопки на резисторном делителе)
        mods=getKey(ADCH);
    }
     
    скока байт, интересно, выграеца.

    Я к тому, что твой код чуть более, чем полностью, состоит из таких рутинных операций, которые можно заменить простым выбором из таблиц.
     
    Последнее редактирование: 20 дек 2018
    Andrey12 и Пушной звер нравится это.
  8. DetSimen

    DetSimen Спамовредитель Модератор

    Да чота как-то вяленько жизнь, для таких-то знаний. :)
     
  9. AlexU

    AlexU Гуру

    Борьба идёт за каждый байт:
    Код (C++):
    const uint8_t keyValues[] PROGMEM = { 95,105,120,135,155,180,225 };
    #define VALSIZE 7

    uint8_t getKey(uint8_t avalue) {
        for (uint8_t i = 0; i < VALSIZE; i++)
            if (avalue <= keyValues[i]) return i;
        return 0xFF;
    }

    ISR(ADC_vect) { //ЦАП для определения какая кнопка нажата (кнопки на резисторном делителе)
        mods=getKey(ADCH);
    }
    И по поводу
    Степень двойки лишнее и не уверен, что будет работать стабильно. Скорее нужно подобрать резисторы так, что бы они гарантированно выдавали определённые старшие три бита регистра ADCH. Тогда простым сдвигом вправо на пять бит получим код режима без всяких 'if'.
     
    DetSimen нравится это.
  10. DetSimen

    DetSimen Спамовредитель Модератор

    ну тогда и return 0xFF выкинуть, он только место занимает, а на warnings - забить
     
    Andrey12 нравится это.
  11. AlexU

    AlexU Гуру

    Работать перестанет, из массива-то выкинул последнее значение...
     
  12. DetSimen

    DetSimen Спамовредитель Модератор

    аа. ну, не заметил спьяну. проститя.
     
  13. DetSimen

    DetSimen Спамовредитель Модератор

    Код (C++):
    const uint8_t keyValues[] PROGMEM = { 95,105,120,135,155,180,225 };
    #define VALSIZE 7

    uint8_t getKey(uint8_t avalue) {
        for (uint8_t i = 0; i < VALSIZE; i++)
            if (avalue <= keyValues[i]) return i;
        return 0x07;
    }

    ISR(ADC_vect) { //ЦАП для определения какая кнопка нажата (кнопки на резисторном делителе)
        mods=getKey(ADCH);
    }
    тогда, так правильнее
     
    Andrey12 нравится это.
  14. ELITE

    ELITE Гик

    спасибо, -80 байт есть

    итого 102 байта свободно
     
  15. ELITE

    ELITE Гик

    замена IF на switch дала еще 24 байта
     
  16. ELITE

    ELITE Гик

    удалил часть повторных действий и привел обнуление переменных в вид Х=Y=Z=0 вместо обнуления каждой по отдельности
    еще сократился код на 2 десятка байт
     
  17. DetSimen

    DetSimen Спамовредитель Модератор

    так, глядишь, еще место на интерпретатор Бэйсика останеца.

    Нальёшь, при случае.
     
    Последнее редактирование: 20 дек 2018
    Airbus и parovoZZ нравится это.
  18. parovoZZ

    parovoZZ Гуру

    delay_ms тоже надо на что-то заменить, пусть и с понижением опорной частоты. На каждом вызове delay_ms тратим 2 байта вместо одного.