PIClist RUS
микроконтроллеры PIC и интерфейсы
техническая документация
статьи и разработки на русском языке

Уроки PIC24 - Глава 4. Числовые типы данных

« назад на главную страницу

Оригинал: "Programming 16-Bit PIC Microcontrollers in C. Learning to Fly the PIC24", Lucio Di Jasio, 2007

Перевод с английского © piclist.ru


План урока

В этом уроке мы рассмотрим все основные числовые типы данных, предоставляемые компилятором MPLAB C30. Мы узнаем, сколько памяти компилятор выделяет для числовых переменных, а также с помощью инструмента MPLAB SIM Stopwatch ("Секундомер") исследуем относительную эффективность подпрограмм, выполняющих над ними арифметические операции. Этот опыт поможет вам выбирать для вашего встраиваемого приложения "правильные" типы переменных, понимания, когда и как балансировать между производительностью и ресурсами памяти, жёсткими ограничениями выполнения в реальном времени и сложностью.

Перечень необходимого

В этом уроке мы будем работать исключительно с программными инструментами среды разработки MPLAB, а именно компилятором MPLAB C30 и симулятором MPLAB SIM.

Создайте новый проект, дайте ему имя "Numbers" и назовите файл исходного кода "numbers.c".

Урок

Чтобы познакомиться со всеми доступными типами данных, рекомендуем вам заглянуть в "Руководство пользователя по компилятору MPLAB C30" (MPLAB C30 User Guide). Начните с Главы 5, в которой приведён список всех целочисленных типов данных:

Таблица 4-1. Целочисленные типы данных

ТипКоличество битовМин. значениеМакс. значение
char, signed char8-128127
unsigned char80255
short, signed short16-3276832767
unsigned short16065535
int, signed int16-3276832767
unsigned int16065535
long, signed long32-231231- 1
unsigned long320232- 1
long long**, signed long long**64-263263- 1
unsigned long long**640264- 1

** расширение стандарта ANSI-89

Из Таблицы 4-1 видно, что стандартом ANSI C определено десять различных целочисленных типов: char, int, short, long, long long в знаковом (по умолчанию) и в беззнаковом вариантах. Также здесь указано количество битов, отводимых компилятором для переменных каждого типа, и минимальные и максимальные значения. Подразумевается, что если тип знаковый, то один бит выделяется для хранения знака, из-за чего числовой диапазон доступных значений делится пополам.

Ещё один интересный момент - компилятор C30 трактует int и short одинаково, в обоих случаях выделяя 16 битов памяти. Поскольку 8- и 16-разрядные величины обрабатываются арифметико-логическим устройством (АЛУ) PIC24 с одинаковой эффективностью, компилятор может закодировать большинство арифметических операций с помощью нескольких кратких команд.

Целочисленный тип long трактуется как 32-разрядная величина, для которой требуется четыре байта, а для типа long long (описан в дополнениях к ANSI C в 1989г.) требуется восемь байтов. Операции над "длинными" целыми числами выполняются компилятором с помощью короткой последовательности команд, встраиваемых в код. Поэтому при использовании чисел типа long приходится платить небольшой "штраф" в виде временных потерь, а для чисел типа long long этот "штраф" (временные потери) будет ещё больше, так что имейте это в виду.

Давайте в качестве первого примера работы с целыми числами напечатаем этот код:

unsigned int i, j, k;
main ()
{
  i = 0x1234;  // присваиваем начальное значение переменной i
  j = 0x5678;  // присваиваем начальное значение переменной j
  k = i * j;   // выполняем их умножение и сохраняем результат в переменную k
}

После компиляции проекта ("Project->Build All" или [Ctrl + F10]) можно открыть окно дизассемблера ("View->Disassembly Listing") и взглянуть на код, сгенерированный компилятором. Даже без глубокого знания набора команд PIC24 мы можем распознать две операции присваивания. Они выполняются загрузкой константных значений в регистр w0, а затем оттуда - в ячейку памяти, зарезервированную для переменных i и j соответственно.

            i = 1234;  
 204D20     mov.w #0x4d2,0x0000  		 // поместить константное значение в регистр W0
 884290     mov.w 0x0000,0x0852  		 // поместить данные из W0 в i
            j = 5678; 
 2162E0     mov.w #0x162e,0x0000 		 // поместить константное значение в регистр W0
 8842A0     mov.w 0x0000,0x0854  		 // поместить данные из W0 в j
            k = i * j; 
 804291     mov.w 0x0852,0x0002  		 // поместить данные из i в W1
 8042A0     mov.w 0x0854,0x0000  		 // поместить данные из j в W0
 B98800     mul.ss 0x0002,0x0000,0x0000
 8842B0     mov.w 0x0000,0x0856  		 // поместить результат в k

Умножение выполняется путём передачи значений из ячеек памяти, выделенных для двух переменных, i и j, обратно в регистры-аккумуляторы w0 и w1, а затем выполняется команда mul (отмечена жирным шрифтом). Полученный результат выгружается из w0 в ячейку памяти, выделенную под переменную k. Всё просто.

Оптимизация (или её отсутствие)

Думаю, вы заметили, что наша скомпилированная программа получилась немного избыточной. Например, значение переменной j всё ещё находится в регистре w0, когда оно повторно загружается туда для умножения. Неужели компилятор не видит, что в этой операции нет необходимости?

В самом деле, компилятор, в отличие от нас, не видит всего этого, ведь его задача - создать "надёжный" код, избегая (по крайней мере, изначально) принятия на себя какой-либо ответственности и используя стандартные последовательности команд. Если же включены соответствующие опции оптимизации, будет выполняться дополнительный проход (проходы) для удаления избыточного кода. Но вот во время разработки и отладки проекта лучше всё-таки полностью отключать оптимизацию, поскольку она приводит к изменению структуры кода, а это вызывает трудности его анализа и пошагового прохождения, а также проблемы с размещением точек останова. Далее в наших уроках мы не будем пользоваться какими-либо возможностями оптимизации, предоставляемыми компилятором; мы убедимся, что необходимый уровень производительности достигается независимо от этого. Как следствие, вы сможете выполнить все приведённые в наших уроках примеры даже в бесплатной версии компилятора C30 Compiler Student Edition.

Тестирование

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

1) Установите курсор на первой строке, содержащей инициализацию первой переменной, и выполните "Run To Cursor". Программа выполнит инициализацию и остановится прямо перед первой командой, которую мы будем наблюдать.

2) Откройте окно наблюдения ("View->Watch") и выберите WREG0 в списке SFR, затем нажмите кнопку "Add SFR".

3) Проделайте то же самое для WREG1.

4) Выберите i в списке переменных, и нажмите кнопку "Add Symbol".

5) Таким же образом добавьте j и k.

6) С помощью функции "Step Over" выполните несколько следующих строк программы, наблюдая за изменениями регистров и переменных в окне наблюдения (меняющиеся значения подсвечиваются красным цветом).

Если вам нужно повторить тест, выполните сброс ("Debugger->Reset->Processor Reset") и затем снова поместите курсор на первой строке анализируемого кода и выполните команду "Run To Cursor".

"Длинные" числа

Стоит нам поменять только первую строку кода, и поменяется вся программа, - теперь операции будут производиться над переменными типа long.

unsigned long i, j, k;
main ()
{
  i = 0x1234;  // присваиваем начальное значение переменной i
  j = 0x5678;  // присваиваем начальное значение переменной j
  k = i * j;   // выполняем их умножение и сохраняем результат в переменную k
}

Выполните сборку проекта и снова переключитесь на окно дизассемблера (быстро переключаться между окнами можно клавишами [Ctrl+Tab]). Вы увидите, насколько вновь сгенерированный код стал больше предыдущего. Хотя инициализация осталась достаточно прозрачной, операция умножения выполняется уже с помощью нескольких дополнительных команд.

            k = i * j;
 8042C1     mov.w 0x0858,0x0002
 8042E0     mov.w 0x085c,0x0000
 B80A00     mul.uu 0x0002,0x0000,0x0008
 8042C1     mov.w 0x0858,0x0002
 8042F0     mov.w 0x085e,0x0000
 B98800     mul.ss 0x0002,0x0000,0x0000
 780105     mov.w 0x000a,0x0004
 410100     add.w 0x0004,0x0000,0x0004
 8042E1     mov.w 0x085c,0x0002
 8042D0     mov.w 0x085a,0x0000
 B98800     mul.ss 0x0002,0x0000,0x0000
 410100     add.w 0x0004,0x0000,0x0004
 780282     mov.w 0x0004,0x000a
 884304     mov.w 0x0008,0x0860
 884315     mov.w 0x000a,0x0862

Поскольку АЛУ PIC24 способно обрабатывать за раз только 16-битные величины, 32-разрядное умножение на самом деле выполняется как последовательность 16-разрядных умножений и сложений. Последовательность, используемая компилятором, почти аналогична "умножению столбиком", только выполняется не по цифре, а по слову.

Замечания по умножению длинных целых

На практике, чтобы выполнить 32-разрядное умножение с помощью 16-разрядных команд, нам потребуется четыре умножения и два сложения. Но в действительности компилятор вставил только три команды умножения. В чём же дело?

Дело в том, что умножение двух длинных целых (каждое по 32 бита) даст 64-битный результат. Но в примере выше мы указали, что результат будет сохранён в переменную типа long, а это ограничивает его 32-мя битами. Поступая так, мы допустили возможность переполнения, но также дали компилятору разрешение игнорировать старшие биты результата. Зная, что эти биты не считаются потерянными, компилятор полностью исключил четвёртый шаг умножения, в некоторой степени оптимизировав код.

Умножение чисел long long

Изменить объявление переменных на тип long long (64 бита) также просто:

unsigned long long i, j, k;
main ()
{
  i = 0x1234;  // присваиваем начальное значение переменной i
  j = 0x5678;  // присваиваем начальное значение переменной j
  k = i * j;   // выполняем их умножение и сохраняем результат в переменную k
}

Перекомпиляция и изучение нового кода в окне дизассемблера показывает, что на этот раз компилятор избрал другой метод. Вместо вставки более длинной последовательности здесь теперь всего лишь несколько команд передачи данных в предопределённые регистры, за которыми следует вызов подпрограммы. Эта подпрограмма разместилась в окне дизассемблера после кода функции main. Она чётко отделена строкой комментария, сообщающей, что эта часть кода является частью специальной библиотеки, модуля с названием "muldi3.c". Исходный код этой подпрограммы поставляется вместе с полной документацией на компилятор C30 и располагается в каталоге с установленным компилятором в подкаталоге "src/libm/src".

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

Плавающая точка

Кроме целочисленных типов компилятор C30 предлагает поддержку нескольких дополнительных типов, охватывающих дробные значения, - типы данных с плавающей точкой. Вы можете выбирать из трёх типов данных, соответствующих двум уровням точности: float, double и long double.

Обратите внимание, что по умолчанию компилятор C30 для типов float и double выделяет одинаковое количество битов, используя формат с плавающей точкой одинарной точности, определённый стандартом IEEE754. Только тип long double трактуется как настоящий тип данных с плавающей точкой двойной точности, соответствующий стандарту IEEE754.

ТипБитыМин. EМакс. EМин. NМакс. N
float32-1261272-1262128
double*32-1261272-1262128
long double64-102210232-102221024

E = экспонента
N = нормализованная запись (приблизительное значение)
*double эквивалентен long double при использовании опции -fno-short-double

Замечания для экспертов по Си

Я убеждён, что такие установки для чисел с плавающей точкой использованы разработчиками MPLAB C30 намеренно, чтобы упростить и сделать более эффективным перенос сложных математических алгоритмов во встраиваемые приложения. Большинство алгоритмов и библиотек, доступных в литературе, разработаны для производительности и ресурсов персональных компьютеров, и поэтому в целях получения максимальной точности вычисления с плавающей точкой двойной точности применяют везде, где это возможно. Что же касается встраиваемых приложений, то чаще всего приходится жертвовать некоторой точностью ради достижения уровня производительности, необходимого для осуществления отклика в реальном времени.

Если нужно, такое поведение можно изменить либо локально, поменяв double на long double в отдельных случаях, либо глобально, с помощью специальной опции компилятора (откройте диалоговое окно "Project->Build Options->Project", установите флажок "Use alternate Setting" и добавьте строку "-fno-short-double" в окно редактирования, расположенное ниже).

Поскольку у PIC24 нет аппаратного блока вычислений с плавающей точкой (floating point unit - FPU), все операции с плавающей точкой кодируются компилятором с помощью специальных арифметических библиотек, чей размер и сложность значительно больше, чем у любой целочисленной библиотеки. Если вы выберите эти типы данных, то вам придётся заплатить за это сильным снижением производительности. Но, с другой стороны, если задача требует использования дробных чисел, то компилятор C30 делает работу с ними весьма лёгкой.

Давайте изменим наш предыдущий пример для работы с переменными с плавающей точкой.

float i, j, k;
main ()
{
  i = 12.34;  // присваиваем начальное значение переменной i
  j = 56.78;  // присваиваем начальное значение переменной j
  k = i * j;  // выполняем их умножение и сохраняем результат в переменную k
}

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

Изменение программы на использование чисел с плавающей точкой двойной точности, long double, даст аналогичный результат. Изменится только сегмент с начальным присвоением.

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

Измерение производительности

Давайте теперь воспользуемся недавно приобретёнными знаниями об инструментах симуляции для измерения фактической относительной производительности арифметических библиотек (целочисленных и с плавающей точкой), используемых компилятором C30. Мы можем начать с использования встроенного в симулятор программного инструмента "Stopwatch" ("Секундомер"). Будем исследовать следующий код:

// 
//   Числа 
//

int   i1, i2, i3;
long  l1, l2, l3;
long long ll1, ll2, ll3;
float f1,f2, f3;
long double d1, d2, d3;

main ()
{
  i1 = 1234;		// тестирование чисел типа int (16-битные)
  i2 = 5678; 
  i3= i1 * i2;		// 1. Умножение чисел типа int

  l1 = 1234;		// тестирование чисел типа long (32-битные)
  l2 = 5678; 
  l3= l1 * l2;		// 2. Умножение чисел типа long

  ll1 = 1234;		// тестирование чисел типа long long (64-битные)
  ll2 = 5678; 
  ll3= ll1 * ll2;	// 3. Умножение чисел типа long long

  f1 = 12.34;	// тестирование чисел с плавающей точкой одинарной точности (32-битные)
  f2 = 56.78; 
  f3= f1 * f2;	// 4. Умножение чисел с плавающей точкой одинарной точности

  d1 = 12.34;	// тестирование чисел с плавающей точкой двойной точности (64-битные)
  d2 = 56.78; 
  d3= d1 * d2;	// 5. Умножение чисел с плавающей точкой двойной точности
}

После компиляции и сборки проекта нужно установить курсор на строке, содержащей первое целочисленное умножение (// 1) и выполнить команду "Run To Cursor". Затем откройте окно секундомера ("Debugger->Stopwatch") и разместите его в любом удобном месте.

Обнулите таймер секундомера и выполните команду "StepOver" ("Debug->StepOver" или [F8]). Как только симулятор завершит обновление окна секундомера, вы можете записать время, необходимое для выполнения операции с типом int. Время в окне секундомера указывается в виде количества циклов и в миллисекундах, которые получаются умножением количества циклов на симулируемую тактовую частоту. Эта частота задаётся в окне настроек отладчика "Debugger Settings" (вкладка "Debugger->Settings->Osc/Trace").

Теперь установите курсор на следующем умножении (// 2) и выполните "Run To Cursor", либо дойдите до этого места с помощью пошаговой операции. Снова обнулите секундомер, выполните "StepOver" и запишите новый результат. Продолжайте, пока не протестируете все типы данных.

Таблица 4-3. Результаты теста относительной производительности с помощью MPLAB C30 1.30 (оптимизация отключена)

Тест на умножениеКол-во цикловПроизводительность по отношению к
intlongfloat
int41--
long int 153.751-
long long int 9924.756.6-
float, double (одинарная точность)1213081
long double (двойная точность)31779212.6

В Таблице 4-3 в первой колонке записаны результаты (в циклах), далее идёт ещё несколько дополнительных колонок, в которых показана относительная производительность, полученная делением количества циклов в каждой строке на количество циклов для указанного типа. Не волнуйтесь, если у вас получились другие значения, - на измерение могут повлиять несколько факторов: в будущих версиях компилятора, возможно, используются более эффективные библиотеки, и/или во время тестирования могут быть включены функции оптимизации.

Имейте в виду, что этот тест лишён какой-либо строгости, необходимой для настоящей проверки производительности. То, что мы можем из него извлечь, - это просто основное понимание того, какое влияние на производительность может оказывать выбор того или иного типа данных. И только что полученная нами таблица уже даёт нам интересные сведения.

Как и ожидалось, самыми быстрыми являются 16-разрядные операции. Умножение длинных целых (32 бита) выполняется примерно в четыре раза медленнее, а умножение длинных-длинных целых (64 бита) на порядок медленнее. А вот умножение 32-битных чисел с плавающей точкой более чем в 30 раз медленнее, чем умножение 16-битных целых. Это означает, что оно в восемь раз медленнее умножения 32-битных целых, или почти на порядок. Тем не менее, применение чисел с плавающей точкой двойной точности (64-битные) лишь удваивает количество циклов. Это говорит о том, что, по всей видимости, библиотеки для работы с числами с плавающей точкой двойной сложности, используемые компилятором, более эффективны, нежели соответствующие библиотеки для 64-битных целых чисел.

Так когда же использовать для наших вычислений плавающую точку, а когда целые числа?

Из того, что мы к настоящему времени узнали, можно извлечь следующие правила:

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

2) Используйте наименьший целочисленный тип, который не приведёт к переполнению или недобору.

3) Если вы вынуждены применить тип с плавающей точкой (нужны дроби), приготовьтесь к снижению производительности вашей программы на порядок.

4) Числа с плавающей точкой двойной точности (long double) уменьшают производительность ещё в два раза.

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

Подводим итоги

В этом уроке мы узнали не только о том, какие типы данных доступны и сколько памяти под них отводится, но и то, как они влияют на скомпилированную программу и в плане размера кода, и в плане скорости выполнения. Чтобы измерить количество циклов (и, соответственно, время), необходимое для выполнения последовательных сегментов кода, мы воспользовались функцией "Секундомер" симулятора MPLAB SIM. Надеюсь, собранная здесь информация будет вам полезна в будущем, когда придётся балансировать между точностью и производительностью во встраиваемых приложениях.

Замечания для экспертов по ассемблеру

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

При использовании целых чисел, однако, иногда возникает ощущение потери контроля, поскольку компилятор прячет детали реализации, и некоторые операции могут стать неясными или менее интуитивно понятными/читаемыми. Вот несколько примеров операций преобразования и работы с байтами, которые могут вызвать некоторое беспокойство:

1) преобразование целых типов в целый тип меньшей/большей разрядности;

2) извлечение или установка самого старшего или самого младшего значащего байта 16-битных данных;

3) извлечение или установка одного бита из целочисленной переменной.

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

int  i;  // 16-битное
long  l; // 32-битное
l = i;   // значение i помещается в два младших байта l
         // два старших байта l обнуляются

В некоторых случаях может потребоваться явное преобразование (называемое "приведением типов"), иначе компилятор предположил бы ошибку:

int  i;  	// 16-битное
long  l; 	// 32-битное
i = (int) l;	// (int) - это приведение типа, в результате которого два старших бита l
 		// будут отброшены, поскольку здесь l трактуется как 16-битное значение

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

Вот пример, извлечённый из включаемого файла нашего проекта, в котором определяется регистр управления таймером Timer1 T1CON, а каждый отдельный управляющий бит представляется в виде структуры, объявленной как T1CONbits:

extern unsigned int  T1CON;
extern  union {
  struct {
    unsigned :1;
    unsigned TCS:1;
    unsigned TSYNC:1;
    unsigned :1;
    unsigned TCKPS0:1;
    unsigned TCKPS1:1;
    unsigned TGATE:1;
    unsigned :6;
    unsigned TSIDL:1;
    unsigned :1;
    unsigned TON:1;
  };
  struct {
    unsigned :4;
    unsigned TCKPS:2;
  };
} T1CONbits;

Замечания для экспертов по микроконтроллерам PIC

Пользователи микроконтроллеров PIC, знакомые с 8-разрядными PIC'ами и соответствующими им компиляторами, заметят существенное улучшение производительности и в случае целочисленной арифметики, и в случае арифметики чисел с плавающей точкой. Несомненно, 16-разрядное АЛУ в архитектуре PIC24 даёт огромное преимущество, обрабатывая за один цикл удвоенное количество битов, но улучшение производительности ещё больше подчёркивается наличием до восьми рабочих регистров-аккумуляторов, которые делают программирование критических арифметических процедур и численных алгоритмов более эффективным.

Советы и трюки

Математические библиотеки

MPLAB C30 поддерживает несколько библиотек стандарта ANSI C, включающих:

- "limits.h" - содержит много полезных макросов, задающих определяемые реализацией пределы, такие как, например, число битов, составляющих тип char (CHAR_BIT) или наибольшее целое значение (INT_MAX).

- "float.h" - содержит подобные определяемые реализацией пределы для типов данных с плавающей точкой, например, наибольшее значение экспоненты для переменной с плавающей точкой одинарной точности (FLT_MAX_EXP).

- "math.h" - содержит тригонометрические функции, функции округления, логарифмические и экспоненциальные функции.

Комплексные типы данных

Компилятор MPLAB C30 поддерживает комплексные типы данных complex в качестве расширения для целочисленных типов и типов с плавающей точкой. Вот пример объявления для типа с плавающей точкой одинарной точности:

__complex__ float z;

Обратите внимание на двойное подчёркивание до и после ключевого слова complex.

Объявленная таким образом переменная z теперь имеет действительную и мнимую части, которые адресуются по отдельности с помощью синтаксиса __real__ z и __imag__ z соответственно.

Подобным образом следующее объявление создаёт комплексную переменную 16-разрядного целого типа:

__complex__ int x;

Комплексные константы легко создаются простым добавлением суффиксов "i" или "j", как это показано в следующем примере:

x = 2 + 3j;
z = 2.0f + 3.0fj;

Все стандартные арифметические операции (+, -, *, /) на комплексных типах данных выполняются корректно.

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

Упражнения

1) Напишите программу, которая использует Timer2 как секундомер для измерения производительности в реальном времени. Если размер Timer2 недостаточен:

- используйте предделитель (в этом случае вы потеряете некоторые младшие биты) или

- используйте объединение таймеров Timer 2 и Timer 3 в режим 32-разрядного таймера.

2) Проверьте относительную производительность деления для различных типов данных.

3) Проверьте производительность тригонометрических функций по отношению к стандартным арифметическим операциям.

4) Проверьте относительную производительность умножения комплексных типов данных.


© PIClist-RUS (piclist.ru), 2009 г.

PIClist RUS (piclist.ru) © 2009
все права сохранены. перепечатка статей и переводов с данного сайта запрещена.