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

Например, математическая функция вычисления факториала находит произведение всех целых чисел, предшествующих указанному числу, включая его. Факториал числа 6 равен 6 х 5 х 4 х 3 х 2 х 1 = 720.

Рекурсивный алгоритм вычисления факториала определяется так.

• Если n = 0, факториал числа n равен 1.

• Иначе факториал числа n равен произведению n на факториал (n – 1).

Далее показана реализация этого алгоритма на языке Arduino C:

long factorial(long n)

{

  if (n == 0)

  {

    return 1;

  }

  else

  {

    return n* factorial(n — 1);

  }

}

Полную версию кода, который вычисляет факториалы чисел и выводит результаты, вы найдете в скетче sketch_06_02_factorial. Люди с математическим складом ума находят такую реализацию весьма искусной. Но обратите внимание на то, что глубина стека в вызове такой функции равна числу, факториал которого требуется найти. Совсем нетрудно догадаться, как реализовать нерекурсивную версию функции factorial:

long factorial(long n)

{

  long result = 1;

  while (n > 0)

  {

    result = result * n;

    n--;

  }

  return result;

}

С точки зрения удобочитаемости этот код, возможно, выглядит понятнее, а кроме того, он расходует меньше памяти и работает быстрее. Вообще старайтесь избегать рекурсии или хотя бы ограничивайтесь высокоэффективными рекурсивными алгоритмами, такими как Quicksort (http://ru.wikipedia.org/wiki/Быстрая_сортировка), который очень эффективно упорядочивает массив чисел.

Сохраняйте строковые константы во флеш-памяти

По умолчанию строковые константы, как в следующем примере, сохраняются в ОЗУ и во флеш-памяти — один экземпляр хранится в коде программы, а второй экземпляр создается в ОЗУ во время выполнения скетча:

Serial.println("Program Started");

Но если использовать код, как показано далее, строковая константа будет храниться только во флеш-памяти:

Serial.println(F("Program Started"));

В разделе «Использование флеш-памяти» далее в этой главе вы познакомитесь с другими способами использования флеш-памяти.

Типичные заблуждения

Многие заблуждаются, полагая, что использование более коротких имен переменных позволяет экономить память. В действительности это не так. Компилятор сам заботится об этом и не включает имена переменных в скомпилированный скетч. Другое распространенное заблуждение: комментарии увеличивают размер программы или объем потребляемой ею оперативной памяти. Это не так.

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

Измерение объема свободной памяти

Узнать, какой объем ОЗУ занимает скетч во время выполнения, можно с помощью библиотеки MemoryFree, доступной по адресу http://playground.arduino.cc/Code/AvailableMemory.

Пользоваться этой библиотекой совсем не сложно: в ней имеется функция free­Me­mory, возвращающая число доступных байтов. Следующий скетч иллюстрирует ее использование:

#include <MemoryFree.h>

void setup()

{

  Serial.begin(115200);

}

void loop()

{

  Serial.print("freeMemory()=");

  Serial.println(freeMemory());

  delay(1000);

}

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

Уменьшение используемого объема флеш-памяти

По окончании процедуры компиляции скетча в нижней части окна Arduino IDE появится примерно такое сообщение:

Скетч использует 1344 байт (4%) памяти устройства. Всего доступно 32 256 байт.

Эта строка сообщает точный объем флеш-памяти в Arduino, который будет занят скетчем, благодаря чему вы всегда будете знать, насколько близко подошли к пределу в 32 Кбайт. Оказавшись близко к предельному значению, нужно позаботиться об оптимизации использования флеш-памяти. В этом вам помогут рассматриваемые далее рекомендации.

Используйте константы

Многие, стараясь дать имена контактам, определяют для этого переменные, как показано ниже:

int ledPin = 13;

Если вы не собираетесь изменять номер контакта с именем ledPin в процессе выполнения скетча, то вместо переменной можно использовать константу. Просто добавьте слово const в начало объявления:

const int ledPin = 13;

Это поможет сэкономить 2 байта ОЗУ плюс 2 байта флеш-памяти при каждом использовании константы. Для часто используемых переменных экономия может достигать нескольких десятков байтов.

Удалите ненужные трассировочные вызовы

В процессе отладки скетчей для Arduino принято вставлять в код команды Serial.println, помогающие увидеть значения переменных в разных точках программы и определить источники ошибок. Эти команды потреб­ляют значительный объем флеш-памяти. Любое использование Serial.println требует включения в скетч примерно 500 байт библиотечного кода. Поэтому, убедившись в безупречной работе скетча, удалите или закомментируйте все такие команды.

Откажитесь от использования загрузчика

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

Статическое и динамическое размещение в памяти

Если вы, подобно автору книги, имеете опыт разработки крупномасштабных систем на таких языках, как Java или C#, вам наверняка приходилось создавать объекты во время выполнения и позволять сборщику мусора освобождать занимаемую ими память без вашего участия. Этот подход к программированию в принципе непригоден для программ, выполняющихся на микропроцессорах, которые имеют всего 2 Кбайт памяти. Ведь в Arduino просто нет никакого сборщика мусора, и, что более важно, в программах, которые пишутся для Arduino, выделение и освобождение памяти во время выполнения редко бывают необходимы.

Ниже приводится пример объявления статического массива, как это обычно делается в скетчах:

// sketch_06_04_static

int array[100];

void setup()

{

  array[0] = 1;

  array[50] = 2;

  Serial.begin(9600);

  Serial.println(array[50]);

}

void loop()

{

}

Объем памяти, занимаемой массивом, известен уже на этапе компиляции скетча, поэтому компилятор может зарезервировать для массива необходимый объем памяти. Второй пример, приведенный ниже, также создает массив того же размера, но выделяет память для него во время выполнения из пула доступной памяти. Обратите внимание на то, что версии Arduino IDE ниже 1.0.4 не поддерживают malloc.

// sketch_06_03_dynamic

int *array;

void setup()

{

  array = (int *)malloc(sizeof(int) * 100);

  array[0] = 1;

  array[50] = 2;

  Serial.begin(9600);

  Serial.println(array[50]);

}

void loop()

{

}

В начале скетча

Добавить отзыв
ВСЕ ОТЗЫВЫ О КНИГЕ В ИЗБРАННОЕ

0

Вы можете отметить интересные вам фрагменты текста, которые будут доступны по уникальной ссылке в адресной строке браузера.

Отметить Добавить цитату