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

   AVR ядро основано на продвинутой RISC архитектуре оптимизированной для Си кода. Это позволяет разрабатывать хорошие и дешевые продукты с широкой функциональностью.
   Когда речь идет об оптимизации, мы обычно имеем в виду две вещи: размер кода и скорость его выполнения. В настоящее время Си компиляторы имеют различные варианты оптимизации, позволяющие разработчикам получать эффективный код по одному из этих критериев. 
   Хороший Си код дает компилятору больше возможности по его оптимизации. Однако, в некоторых случаях оптимизация кода по одному из критериев ухудшает другой, поэтому разработчик должен искать баланс между ними для удовлетворения своих требований. Понимание некоторых нюансов программирования на Си для AVR позволяет разработчикам фокусировать свои усилия в нужном направлении для достижения эффективного кода. 
   В этой статье мы рассмотрим рекомендации по программированию на Си для компилятора avr-gcc. Однако эти советы могут быть использованы и с другими компиляторами.

 Майкл Барр

 Когда я разговариваю с Си программистами об аппаратных интерфейсах или стандарте программирования, я часто вижу, что они не обладают необходимыми навыками и информацией об этом языке программирования. Как правило, это потому, что все мы в основном инженеры-электронщики, которые изучали Си (или какой-нибудь другой язык) самостоятельно. 

   Один из таких навыков относится к созданию заголовочных файлов. Что нужно (или не нужно) размещать в заголовочном Си файле .h? Когда нужно создавать заголовочный файл? И почему?
   На перечисленные вопросы у меня есть свой список ответов.

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

   Существует интересный макрос, который используется для определения количества элементов в объявленном массиве. Его обычное определение выглядит так:

#define N_ELEMENTS(X) (sizeof(X)/sizeof(*(X)))

   Данный макрос удобно применять для перебора всех элементов массива в циклах.

void foo(void)
{
   uint8_t bar[] = {0, 1, 2, 3, 4};
   uint8_t i;

   /* передать каждый элемент массива bar[] */
   for (i = 0; i < N_ELEMENTS(bar); ++i){
      txc(bar[i]);
   }
}

   Выводы микроконтроллера в проекте обычно задают с помощью макроопределений - define`ов. Мы получаем некую "отвязку" от железа и в дальнейшем это позволяет нам переназначать выводы на другие порты. Неудобство такого подхода состоит в том, что для каждого вывода нужно определять три регистра. Бывает, что два (только PORTx и DDRx), но это тоже неудобно, если выводов много. Существует другой подход, позволяющий сократить число макроопределений. Разберемся в чем он заключается.

Майкл Барр

   Недавно я наткнулся на пост в блоге одного разработчика, в котором он представил десять Си правил для лучшего программирования встраиваемых систем. На половину из этих правил у меня возникла жестко негативная реакция и я хочу описать, что мне так не понравилось. Далее по тексту я буду называть этого автора "Плохой Советчик". Я надеюсь, что если вы следовали пяти описанным ниже правилам, то мои комментарии убедят вас отойти от них. Если вы не согласны, начинайте конструктивное обсуждение в комментариях. 

   Иногда при программировании микроконтроллеров требуется выводить на дисплей или терминал какие-то числовые данные. Это могут быть показания АЦП, значение внутренних часов, коды ошибок, состояния автомата и т.п. Процедура вывода числа состоит из следующих шагов, нужно преобразовать двоичное число в двоично-десятичное, перевести двоично-десятичное число в символьное представление и после этого передать полученный результат какой-то функции вывода. Для выполнения этой задачи я написал небольшую библиотеку.

   При программировании микроконтроллеров разработчики иногда сталкиваются с проблемой вычисления квадратного корня. Например, данная операция требуется при выполнении быстрого преобразования Фурье или вычислении среднеквадратического значения сигнала. 
   В стандартной библиотеке Си – math.h, есть функция для вычисления квадратного корня sqrt(), которой при желании можно воспользоваться. Она работает с числами типа float, обеспечивает высокую точность результата, но требует для своей работы длительного времени. Для микроконтроллера AVR это порядка 3000 циклов тактовой частоты (проверено в компиляторе IAR на разных уровнях оптимизации). 
   Если к точности вычисления корня не предъявляются высокие требования, можно воспользоваться упрощенным алгоритмом, занимающим меньше места в памяти и выполняющим вычисления в несколько раз быстрее.

Дэн Сакс
 
   Использование символов для представления постоянных числовых величин — это одна  из основных рекомендаций, которую узнают большинство программистов в первую очередь.  Например, вместо того чтобы писать:
 
char buffer[256];
...
fgets(buffer, 256, stdin);
 
лучше описать символ, скажем buffer_size, представляющий размерность буфера, и использовать его вместо литеральной константы. 
 
char buffer[buffer_size];
...
fgets(buffer, buffer_size, stdin);
 
   Си и Си++ предлагают несколько способов описания таких символов. Итак, давайте их рассмотрим. 
Страница 1 из 2