Оптимизация кода. Что еще можно?

Тема в разделе "Микроконтроллеры AVR", создана пользователем issida, 17 фев 2017.

  1. issida

    issida Нерд

    Каким образом можно уменьшить размер скетча?
    Код (C++):
    #include <OneWire.h>


    #define led_max 5
    #define led_min 3
    #define ds18_b20 4
    #define tranzistor 1
    #define battery 2
    #define out_chip 0

    OneWire  ds(ds18_b20);

    uint8_t start = 1; //1-заряжается, 0-разряжется
    volatile long ttime = 0;

    void led_off(void) {
      PORTB &= ~((1 << led_max) | (1 << led_min)); // выключвкм диоды
    }
    void blin_k(uint8_t pin) {
      led_off();
      delay(1000);//ждем
      PORTB |= (1 << pin);
      delay(1000);//ждем
    }

    void B_blink(uint8_t pin) {
      led_off();
      delay(200);//ждем
      PORTB |= (1 << pin); //включам
      delay(1000);//ждем
    }

    uint16_t analog_Read(void) {


      ADCSRA |= 1 << ADSC; //запуск преобразования ацп
      while (!(ADCSRA & (1 << ADIF))); //ждем окончания преобразования
      ADCSRA |= 1 << ADIF; //сбрасывание флага (ADLAR=0)
      byte low  = ADCL;//чтение из регистров результата
      byte high = ADCH;
      // ADCSRA &= ~(1 << ADEN);  // отключаем АЦП, для уменьшения энергопотребления
      return (high << 8) | low;// перобразуем

    }



    ISR(TIMER0_OVF_vect)//прерывание по переполнению
    {
      if (start == 1) {
        ttime++;
      }
    }


    void setup() {
      //----устанавливаем входы-выходы 0-вход, 1-выход
      DDRB = 0x3A;//0-вход от схемы , 1-транзистор, 2-батарея, 3-диод низкий уровень, 4-18b20, 5-диодвысокий уровень
      PORTB = 0; //устанавливаем логические уровни в 0
      //------настройка Таймера0---------
      TCCR0A = 0;
      TCCR0B = 0b00000101; //делитель 1024
      TCNT0  = 0;
      TIMSK0 = 0b00000010; // Разрешение прерываний по переполнению
      //---------настройка АЦП
      ADMUX = battery; // выбор входного канала
      ADCSRA |= 1 << ADEN; //разрешение работы ацп
      sei();
    }

    void loop() {

      noInterrupts();//запрещаем прерывания

      //-------работа с датчиком температуры
      uint8_t data[2];
      ds.reset();
      ds.write(0xcc);
      ds.write(0x44);
      delay(70);// точность 0,5 записана в EEPROMе
      ds.reset();
      ds.write(0xcc);
      ds.write(0xBE);
      data[0] = ds.read();
      data[1] = ds.read();
      uint16_t tempr = ((data[1] << 8) | data[0]) >> 4;
      //-----конец вычислений
      uint16_t vol = analog_Read();//читаем значение аккумулятора
      interrupts();//разрешаем прерывани

      if (ttime == 164000) { // прошло 10 часов, и зарядка не закончилась
        ttime = 0;
        PORTB &= ~(1 << tranzistor);//отключаем транзистор зпрядки
        PORTB |= (1 << led_max) | (1 << led_min); //включам все диоды
        start = 10;
      }

      if (tempr > 40) {//если температура больше 40
        PORTB &= ~(1 << tranzistor);//отключаем транзистор зпрядки
        start = 11;
      }
      else if (tempr < 21 && start == 11) {// когда температура упала до 20
        PORTB |= 1 << tranzistor;//включаем транзистор зпрядки
        start = 1;
      }



      if (start == 1 && (PINB >> out_chip) & 0x01 == 0) {//идет зарядка. ждем пока батарейка зарядится до максимального  уровня
        PORTB &= ~(1 << tranzistor);//отключаем транзистор зпрядки
        start = 0; //теперь будет процесс разрядки
        ttime = 0;
      }

      else if ( vol <= 540 && start == 0) { // ждем, когда на аккумуляторе меньше 3,4
        PORTB |= (1 << tranzistor);// вкл транзистор зарядки
        start = 1;//запускаем процесс зарядки
      }

      //---------Аварийные ситуации-----------

      if (vol > 1000 && start < 11) { //если аккумулятор был вынут либо он выщел из строя
        PORTB &= ~(1 << tranzistor);//отключаем транзистор зпрядки
        start = 10; //неиспраность, напряжение превысло порог
        B_blink(led_max);
      }

      else if (vol < 100 && start < 11) { // слишком низкое напряжение, либо короткое замыкание
        PORTB &= ~(1 << tranzistor);//отключаем транзистор зпрядки
        start = 10; //неиспраность, напряжение превысло порог
        B_blink(led_min);
      }

      //---------------отображение уровня текущей зарядки-------------
      else if (start <= 1) {
        if (vol > 600 ) { //аккумулятор выше 4,2В
          led_off();
          PORTB |= (1 << led_max);//включем высокой зарядки
          if (start == 1) {//если идет зарядка, мигаем светодиодом//60
            blin_k(led_max);// функция мигания светодиодомм
          }
        }

        else if (vol < 550) { //если меньше 3,6в , вкл диод низной зарядки
          led_off();
          PORTB |= (1 << led_min);
          if (start == 1) {//если идет зарядка, мигаем светодиодом
            blin_k(led_min);// функция мигания светодиодомм
          }
        }
      }
      //--------------------------------------------------------------------

    }

     
    Attiny13a, ядро https://raw.githubusercontent.com/sleemanj/optiboot/master/dists/package_gogo_diy_attiny_index.json
     
  2. Unixon

    Unixon Оракул Модератор

    А что сейчас сколько места занимает? Что внутри OneWire? Что еще линкуется?
     
  3. Airbus

    Airbus Радиохулиган Модератор

    Можно написать на асме.Что этот код делает?
     
  4. Unixon

    Unixon Оракул Модератор

    Сначала неплохо бы глянуть результат работы компилятора и найти хотя бы одно место, где руками получится переписать оптимальнее.
     
  5. serg_admin

    serg_admin Гик

    1. Учитывая, что ты используешь только одну библиотеку - надо подумать об чистом С
    2. Мигалки вынеси на прерывание таймера - вырежется delay.
    3. return (high << 8) | low; -- реально цыкличесткого сдвига у него нет поэтому двигай в другую сторону
    4. PORTB &= ~((1 << led_max) | (1 << led_min)); -- Как не парадоксально, но две отдельные команды сгенерируют более оптимальный код.
    5. else if -- Если предыдущий IF имеет исключающее условие то такая конструкция не нужна

    Сколько сэкономить надо?

    На сколько я могу судить, ТВОЙ код весит не много. Большая часть ядро arduino
     
    issida нравится это.
  6. issida

    issida Нерд

    Это зарядное для литиевого аккумулятора. Заряжает - ждет пока разрядится- опять заряжает. Будет стоять в гараже на сигналке. Еще планирую прицепить нагреватель, чтоб холод не уробил аккумулятор, вместо одного светодиода.
    Скетч использует 946 байт (92%) памяти устройства. Всего доступно 1024 байт.
    Глобальные переменные используют 20 байт (31%) динамической памяти, оставляя 44 байт для локальных переменных. Максимум: 64 байт.
    4 пункт сэкономил 2 байта. Завязать мигалки от прерывания была такая мысль, но слабо думал об этом и так и осталось.
     
    Последнее редактирование: 17 фев 2017
  7. ostrov

    ostrov Гуру

    Вместо Attiny13 взять Attiny85 не вариант? Или важен спортивный интерес?
     
  8. serg_admin

    serg_admin Гик

    Здесь все просто. Прерывание таймера ты уже сделал -я так понял 4 срабатывания в секунду.
    Мигалка имеет два параметра:
    - Длительность цикла (частота)
    - Длительность горения (скважность)

    Соответственно делаешь счетчик от 0 до "длительность цикла" - все задается в тиках таймера
    Соответственно два условия:
    при "счетчик = 0" ставиш HIGH при "счетчик=длительность горения" ставишь LOW

    В основном цикле меняешь эти два параметра и сбрасываешь счетчик в 0

    На "С" delay() функция inline т.е. при ее вызове вставляется не код вызова функция а вся функция целиком. Поэтому фактически у тебя в коде 4 реализации delay(). Правда для Arduino IDE это надо бы проверить.
     
  9. Unixon

    Unixon Оракул Модератор

    А в чем тут оптимизация? Имхо одно только неудобство от невозможности использовать более богатый синтаксис.

    В правой части только константы, выражение от них вычисляется на этапе компиляции. Итого должна получиться одна команда с постоянным аргументом.
     
  10. serg_admin

    serg_admin Гик

    Вопрос был - экономия места.
    ООП подразумевает удобство, но за это расплачивается понижением производительности и расход памяти. А в указанном примере от C++ очень мало.

    Особенности компилятора:
    Если устанавливает один бит то используется всего одна команда ассемблера
    Код (Text):
    SBI port, bit
    Если устанавливаются несколько битов, то это эквивалентно всему байту
    Код (Text):
    in reg, port
    andi reg, const
    out port, reg
    Соответственно небольшая экономия. В масштабах одно килобайта заметно. Дополнительно, не понадобится освобождать регистр.
     
  11. Unixon

    Unixon Оракул Модератор

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

    Ну так то да, я на первый вариант не рассчитывал. Кстати, если важна одновременность установки битов, то оптимизация с несколькими командами sbi не подойдет.
     
  12. mcureenab

    mcureenab Гуру

    OneWire много весит. В ней одна только таблица CRC чего стоит...

    С аналоговым датчиком температуры можно много сэкономить. Точность большая не нужна как я понимаю. Даже диод в качестве датчика сойдет.
     
  13. save.l

    save.l Гик

    Добрый день.
    Есть несколько подобных функций:
    Код (C++):
     
    char buff[20];
      for (byte i = 1; i <= 9; i ++){
        switch (i) {
           case 1:   sprintf(buff, "[V,%d,%d]", i, EE.u_show_data + 1);    break;
           case 2:   sprintf(buff, "[V,%d,%d]", i, EE.u_wiegand + 1);      break;
           case 3:   sprintf(buff, "[V,%d,%d]", i, EE.u_port_in[0] + 1);   break;
           case 4:   sprintf(buff, "[V,%d,%d]", i, EE.u_port_in[1] + 1);   break;
           case 5:   sprintf(buff, "[V,%d,%d]", i, EE.u_port_out[0] + 1);  break;
           case 6:   sprintf(buff, "[V,%d,%d]", i, EE.u_port_out[1] + 1);  break;
           case 7:   sprintf(buff, "[V,%d,%d]", i, EE.u_port_out[2] + 1);  break;
           case 8:   sprintf(buff, "[V,%d,%d]", i, EE.u_port_out[3] + 1);  break;
           case 9:   sprintf(buff, "[V,%d,%d]", i, EE.u_voice_hint + 1);   break;
        }
        Serial.println(buff);
      }
    Возможна ли оптимизация данного куска с целью экономии места во flash ? Может есть более оптимизированный аналог sprintf ?
     
  14. Asper Daffy

    Asper Daffy Иксперд

    Разумеется, 100500 способами. Например, напишите вот так, думаю кило - кило триста как с куста:
    Код (C++):
      char buff[20] = "[V,";
      const int arr[9] = {
            EE.u_show_data + 1,
            EE.u_wiegand + 1,
            EE.u_port_in[0] + 1,
            EE.u_port_in[1] + 1,
            EE.u_port_out[0] + 1,
            EE.u_port_out[1] + 1,
            EE.u_port_out[2] + 1,
            EE.u_port_out[3] + 1,
            EE.u_voice_hint + 1
      };
      for (byte i = 1; i <= 9; i++) {
            itoa(i, buff + 3, 10);
            uint8_t index = strlen(buff);
            buff[index] = ',';
            itoa(arr[i-1], buff + index + 1, 10);
            Serial.println(strcat(buff, "]"));
      }
     
     
    save.l нравится это.
  15. save.l

    save.l Гик

    Спасибо, погнал проверять)
     
    Последнее редактирование: 18 ноя 2024 в 00:07
  16. Asper Daffy

    Asper Daffy Иксперд

    Ну, разумеется, это если в других местах нету printf - экономия ведь за счёт отказа от него. Если он есть в другом месте, то мало что изменится.
     
  17. AlexU

    AlexU Гуру

    Опасный код. Чреват выходом за границы буфера.
    Да и зачем накапливать данные в буфере, если можно сразу в Serial отправлять?
    Типа такого (пишу по памяти, могут быть опечатки):
    Код (C++):

      for (byte i = 1; i <= 9; i ++){
        Serial.print(F("[V,"));
        switch (i) {
           case 1:   Serial.print(i, 10); Serial.print(F(",")); Serial.print(EE.u_show_data + 1, 10);    break;
           case 2:  ... и т.д.
        }
        Serial.println(F("]"));
      }
    Строку:
    Код (C++):
    Serial.print(i, 10); Serial.print(F(",")); Serial.print(EE.u_show_data + 1, 10);
    лучше в отдельную функцию вынести с двумя параметрами -- 'i' и что там из EE.
     
  18. Asper Daffy

    Asper Daffy Иксперд

    С какого бодуна? int занимает максимум 6 байтов, там их два. Ещё пять байтов на всякие скобочки + один для хвостового нуля, итого 2*6 + 5 + 1 = 18. Куда он нахрен выйдет?

    Да и задача стояла - оптимизация по размеру кода. Ну, сравните что у меня и что у Вас.
     
  19. save.l

    save.l Гик

    реально кило триста.
    как вы это делаете? :rolleyes::D
     
  20. Asper Daffy

    Asper Daffy Иксперд

    Давно тут сижу.
     
    save.l нравится это.