Оптимизация кода для 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);
}
}
4 ответа
Вы уверены, что выводы правильные?
Может быть, использовать меньший генератор случайных чисел на 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
Как я уже говорил в комментарии, это устройство было бы слишком маленьким, чтобы я мог запрограммировать его с помощью ядра 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
Если кнопка нажата человеком и ваши часы достаточно высоки (диапазон МГц), вы можете использовать трюк и избавиться от ГСЧ.
Вы можете заменить его счетчиком свободного хода следующим образом:
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
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
- Как повторить кусок кода
- Использование std::list в программировании Arduino
- Управление сервоприводом с помощью ATtiny13A
- Оптимизация скорости с использованием const, static, constexpr и т. д. в функции
- Как уменьшить использование глобальных переменных? Attiny85
- C++ против языка Arduino?
- Как использовать SPI на Arduino?
- Как объявить массив переменного размера (глобально)
Я не уверен, что компилятор оптимизирует в этой ситуации, но пробовали ли вы использовать прямое манипулирование портом вместо использования
digitalWrite ()
,digitalRead()
иpinMode()
? Когда это не используется, компилятор может оптимизировать эти функции., @chrislНе уверен, что это помогает по байтам, но вы можете изменить
if(ranNum == 0) { digitalWrite(0, ВЫСОКИЙ); } еще { digitalWrite(1, ВЫСОКИЙ); }
intodigitalWrite(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