Оптимизация кода для ATtiny10

Я пытаюсь втиснуть код в ATtiny10, но у меня ничего не получается. Я использую 1060 байт, и у меня есть место только для 1024 байт.

Код достаточно прост; мне нужно прочитать вывод кнопки. Если он высокий, то нужно выбрать случайное значение, и это приводит к тому, что один из двух случайных светодиодов на печатной плате включается на две секунды. Есть ли способ оптимизировать этот код для работы на этой ИС?

int buttonState = 0;

void setup() {
    pinMode(3, INPUT);
}

void loop() {
    buttonState = digitalRead(3);
    if (buttonState == HIGH) {
        bool ranNum=random(0,1);
        if(ranNum == 0) {
            digitalWrite(0, HIGH);
        }
        else {
            digitalWrite(1, HIGH);
        }
        delay(2000);
        digitalWrite(1, LOW);
        digitalWrite(2, LOW);
    }
}

, 👍7

Обсуждение

Я не уверен, что компилятор оптимизирует в этой ситуации, но пробовали ли вы использовать прямое манипулирование портом вместо использования digitalWrite (), digitalRead() и pinMode()? Когда это не используется, компилятор может оптимизировать эти функции., @chrisl

Не уверен, что это помогает по байтам, но вы можете изменить if(ranNum == 0) { digitalWrite(0, ВЫСОКИЙ); } еще { digitalWrite(1, ВЫСОКИЙ); } into digitalWrite(ranNum == 0 ? 0 : 1, ВЫСОКИЙ);, @Michel Keijzers

Я не уверен в том, как работает прямая переадресация портов, я посмотрю некоторые учебные пособия спасибо за предложение. Тернарный оператор, похоже, не сохраняет никаких байтов хорошее предложение, @Earthbound Ruben

digitalWrite(ranNum & 1, HIGH) - также используйте uint8_t вместо int., @Majenko

кроме того, "random ()" - это оболочка вокруг " rand ()", которая, вероятно, тратит байты впустую. Используйте rand() & 1, чтобы получить случайный 1 или 0., @Majenko

На таком маленьком устройстве я бы избавился от ядра Arduino и запрограммировал его на [avr-libc](https://www.nongnu.org/avr-libc/user-manual/index.html) уровень., @Edgar Bonet

@EdgarBonet На таком маленьком устройстве я бы избавился от avr-libc и запрограммировал его в сборке..., @Majenko

Пожалуйста, взгляните на статью, которую я написал некоторое время назад относительно [оптимизации кода AVR](https://jpralves.net/post/2015/06/16/avr-optimizations.html)., @João Alves

Правильны ли ваши контакты и pinMode ()? pinMode(3,INPUT) кажется стандартным, а случайные максимумы на 0 и 1 кажутся несовместимыми с LOW на 1 и 2, @Dave X

Насколько велик код, если вы замените random константой?, @copper.hat

По теме: *Оптимизация кода для использования меньшего количества флэш-памяти и SRAM* и *[Что я могу сделать, если у меня закончится флэш-память или SRAM?](https://arduinoprosto.ru/q/221/)*, @Peter Mortensen

Разве светодиоды не очень слабые? Разве это не вход ввода-вывода по умолчанию? Код, представленный здесь, по-видимому, включает и выключает внутренние подтягивающие резисторы (поскольку ***ни один из выводов ввода-вывода не настроен как выход***). Включение и выключение внутренних подтягивающих резисторов ***приведет к некоторому освещению светодиодов, но не очень сильно. Ток через них будет только порядка 0,1 мА (100 мкА). Или есть какая-то схема драйвера, которая компенсирует это?, @Peter Mortensen


4 ответа


3

Вы уверены, что выводы правильные?

Может быть, использовать меньший генератор случайных чисел на https://arduino.stackexchange.com/a/18092/6628 ?

static uint8_t lfsr = 0x01;
const byte LFSR_MASK = 0x8e;

void setup() {
  pinMode(0, OUTPUT);
  pinMode(1, OUTPUT);
  pinMode(3, INPUT);
}
void loop() {
  //      int buttonState = digitalRead(3);
  if (digitalRead(3)) {
    if (generateNoise()) {
      digitalWrite(0, HIGH);
    }
    else {
      digitalWrite(1, HIGH);
    }
    //delay(2000)
    //for (int i = 31; i; i--)_delay_us(64516);
    _delay_ms(2000);
    digitalWrite(0, LOW);
    digitalWrite(1, LOW);
  }
}


uint8_t generateNoise() {
  // Return 1 bit of noise using a Galois Linear Feedback Shift Register
  // See https://en.wikipedia.org/wiki/Linear_feedback_shift_register#Galois_LFSRs

  if (lfsr & 1) {
    lfsr =  (lfsr >> 1) ^ LFSR_MASK ;
    return (1);
  }
  else         {
    lfsr >>= 1;
    return (0);
  }
}

ГСЧ помог не так сильно, как изменение столь пренебрежительной задержки.

,

Ваш линейный регистр обратной связи действительно кажется довольно эффективным в пространстве. Подключив его к моей реализации программы, я уменьшил ее размер с 660 до 142 байт!, @Edgar Bonet

Отлично. Вероятно, вы могли бы позволить себе перейти к 16-или 32-битной переменной случайного состояния для более богатой случайности., @Dave X


12

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

#include <stdlib.h>
#include <avr/io.h>
#include <util/delay.h>

int main(void)
{
    DDRB = _BV(PB0)   // PB0 as output
         | _BV(PB1);  // PB1 as output
    for (;;) {
        loop_until_bit_is_set(PINB, PB3);  // wait for PB3 high
        if (rand() & 1)
            PORTB |= _BV(PB1);  // PB1 high
        else
            PORTB |= _BV(PB0);  // PB0 high
        _delay_ms(2000);
        PORTB &= ~_BV(PB0);     // PB0 low
        PORTB &= ~_BV(PB1);     // PB1 low
    }
}

Это обычный C, но я ожидаю, что он будет принят Arduino IDE как допустимый файл .ino. Я попытался скомпилировать его следующим образом:

avr-gcc -mmcu=attiny10 -Os -Wall -Wextra -DF_CPU=8000000 prog.c -o prog.elf

И это привело к созданию программы, которая использует 660 байт флэш-памяти. Большая часть этого заключается в реализации rand() и его зависимостей (процедуры умножения и деления).

,

Если "rand ()" действительно занимает так много места, вы, вероятно, могли бы сэкономить его, используя более компактный ГСЧ. [Вот один из моих любимых](https://www.pcg-random.org/posts/bob-jenkins-small-prng-passes-practrand.html), который должен отлично работать на ATtiny и, вероятно, генерировать лучшую случайность., @Ilmari Karonen

Вы даже можете использовать шум LSB АЦП для генерации случайного числа. По моему опыту, работает довольно хорошо., @Sim Son


7

Если кнопка нажата человеком и ваши часы достаточно высоки (диапазон МГц), вы можете использовать трюк и избавиться от ГСЧ.

Вы можете заменить его счетчиком свободного хода следующим образом:

unsigned char count = 0;

void loop() {

    count += 1;

    buttonState = digitalRead(3);

    if (buttonState == HIGH) {
        if( (count & 1) == 0) {
            digitalWrite(0, HIGH);
        }
        else {
            digitalWrite(1, HIGH);
        }
        delay(2000);
        digitalWrite(1, LOW);
        digitalWrite(2, LOW);
    }
}

Вы увеличиваете счетчик каждые ⪝10 us (в зависимости от вашей тактовой частоты attiny выполняет одну инструкцию за цикл), поэтому, когда пользователь нажимает кнопку, счетчик будет отбираться.

Примечание: я предположил, что loop () - это все, что должен делать микроконтроллер, и он постоянно вызывается.

,

Это скорее случайность, чем ГСЧ. Поскольку задержка и его часы работают, вы можете использовать его TCNT0 или TCNT0L вместо "count" и получить более быстрый подсчет при нулевых затратах., @Dave X

Ни loop (), ни digitalWrite() даже не могут быть вызваны. Базовый компилятор GCC довольно хорошо встраивается, оптимизируя их вплоть до отдельных инструкций по сборке., @Peter Mortensen

Какая разница, была ли функция встроена или нет в этом контексте? В конце код функции выполняется, независимо от того, встроен он компилятором или нет., @next-hack

@next-hack: вызовы функций-это инструкции, которые используют ценное флэш-пространство (и требуют времени для выполнения)., @Michael

@Michael, мне кажется, я это прекрасно знаю... Тем не менее, какова связь между ответом Redy000 и тем фактом, что loop() или digitalWrite() могут быть встроены или реализованы как фактическая ветвь ?, @next-hack


1

8-битный чип требует нескольких инструкций для обработки 16 бит. Изменение ints на uint8_t или int8_t сохранит код.

В зависимости от оптимизации компилятора, замена

if ( (count & 1) == 0) {
    digitalWrite(0, HIGH);
}
else {
    digitalWrite(1, HIGH);
}

с

digitalWrite((count & 1), HIGH);

можно было бы сэкономить байты.

,

Я попробовал оптимизацию, которую вы предлагаете, на Uno. Он сохранил два байта., @Edgar Bonet