Меню

Atmel studio настройка оптимизации

AVR Урок 2. Создание проекта в Atmel Studio

Урок 2

Создание проекта в Atmel Studio

Сегодня мы научимся создавать проекты в среде программирования Atmel Studio. Делается это не очень сложно.

Запускаем Atmel Studio.

У нас откроется диалог, в котором будет 5 вариантов проектов на C/C++, один вид проекта на ассемблере, а также есть вариант создание пустого решения, в которое уже потом добавлять проекты. Мы будем писать именно на языке C. С помощью кнопки «browse» в данном диалоге мы выбираем папку, в которой будем создавать свои проекты, выберем вариант проекта «GCC C Executable Project», дадим проекту имя, например «Test01». Галочка слева от надписи «Create directory for solution» означает, что будет создаваться папка для проекта, то есть данную папку заранее создавать не нужно. Нажмем «OK»

В следующем диалоге выберем наш контроллер Atmega8A, впечатав это в соответствующем окошке. Мы видим здесь справа также несколько интересных вещей, в том числе Datasheet на наш контроллер (ссылка на техническую документацию), который мы отсюда спокойно можем скачать.

Здесь мы также нажимаем «OK»

Проект создан. Мы должны увидеть окно с проектом приблизительно следующего вида

Теперь немного настроим наш проект.

Затем в открывшемся диалоге перейдём во вкладку Tool и выберем там в качестве отладчика Simulator, а если у кого поддерживатеся программатор в качестве отладчика, то свой программатор. Мой программатор не может выступать в качестве отладчика, так как нет драйвера для Atmel Studio. В свойствах проекта есть огромный ряд настроек (настройки оптимизации, включение операций с плавающей точкой и т.д.), которые мы рассмотрим в более поздних занятиях. А пока нажмём кнопку «сохранить всё» в панели управления в виде нескольких дискет (на картинку можно нажать для увеличения)

После сохранения закроем вкладку с настройками и немного поиграемся с текстом главного файла Test01.c. Прежде чем удалить оттуда ненужный комментарий сверху, мы заодно и поучимся, но а если кто знает, тот повторит, как пишутся комментарии в языке C. Чтобы написать многострочный комментарий, необязательно обозначать значком комментария каждую строчку. Достаточно вначале поставить обычный слеш (черточку, наклоненную вправо) и звездочку, а в конце блока с текстом комментария – наоборот – сначала звездочку, а затем обычный слеш и блок наш в данной среде программирования сразу отметится зелёным цветом, то есть станет комментарием и компилятор при сборке проекта обрабатывать данный текст не будет. Данный вид обозначения комментария ещё может пригодиться для обзначения комментарием не всей сторки, а её части, если мы хотим отметить комментарием часть строки не до её конца. Комментарии удобны в практике программирования тем, что мы указываем, что именно мы хотим добиться кодом, который обозначен комментарием, что, во-первых, не даёт нам забыть, что мы именно хотели сделать кодом, а также служит объяснением тому, кто будет наш код затем читать и изучать. Вот как раз пример комментария в нашем файле проекта

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

Теперь другой вид комментария. Если мы хотим написать комментарий в виде одной строки, то целесообразно отметить этот комментарий специальным значком в виде двух обычных слешей, предназначенных именно для обозначения однострочного комментария. Данный значек ставится вначале строки и действует до конца строки. У нас также есть с вами для этого уже пример в коде. Здесь среда нам подсказывает, где именно следует начинать писать свой код

Данный комментарий можно будет также удалить.

Теперь давайте попробуем откомпилировать наш проект (или как ещё говорят в народе «соберём его», так как проект данным действием не только компилируется, но ещё и линкуется). Делается это следующим образом. Либо нажимаем соответствующую кнопку в панели инструментов, либо нажимаем функциональную клавишу «F7». После этого действия, если сборщик проекта не встретит никаких ошибок, то мы получим в нижней части нашей среды программирования определённое сообщение

Читайте также:  В контакте в меню нет мои настройки

Также в папке «\Test01\Debug» нашего проекта у нас будет сгенерирован исполняемый файл, или как в народе говорят – прошивка – файл «Test01.hex». Данный файл мы и будем «заливать» в наш контроллер впоследствии. Для этого нам конечно надо будет научиться писать какой-то полезный код. Этим мы займёмся уже на следующем занятии.

Источник

Особенности

— Введение в ядро Atmel AVR и Atmel AVR GCC
— Советы и трюки по уменьшению размера кода
— Советы и трюки по уменьшению времени выполнения кода
— Примеры применения

1. Введение

AVR ядро основано на продвинутой RISC архитектуре оптимизированной для Си кода. Это позволяет разрабатывать хорошие и дешевые продукты с широкой функциональностью.

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

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

В этой статье мы рассмотрим рекомендации по программированию на Си для компилятора avr-gcc. Однако эти советы могут быть использованы и с другими компиляторами.

2. Atmel AVR ядро и Atmel AVR GCC

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

2.1 Архитектура 8-и разрядного AVR

AVR использует Гарвардскую архитектуру с раздельной памятью и шиной для программы и данных. Он имеет регистровый файл из 32 8-и разрядных рабочих регистров общего назначения с временем доступа один тактовый цикл. 32 рабочих регистра – один из ключей к эффективному Си программированию. Эти регистры имеют такие же функции, как и традиционные аккумуляторы, только их 32 штуки. За один такт AVR может передать два произвольных регистра арифметическому логическому устройству, выполнить операцию и записать результат обратно в регистровый файл.

Инструкции в памяти программ выполняются на одном конвейерном уровне. Пока одна команда выполняется, следующая извлекается из памяти. Эта концепция позволяет выполнять команды за один такт. Большинство инструкций AVR имеют 16-и разрядный формат. Каждый адрес памяти программ содержит 16 или 32 разрядные инструкции.

Для более детальной информации почитайте раздел “AVR CPU Core” в документации на микроконтроллеры.

2.2 AVR GCC

GCC расшифровывается как коллекция GNU компиляторов (GNU Compiler Collection). GCC используемый для AVR микроконтроллеров называется AVR GCC.

Для ознакомления с полным списком параметров и уровней оптимизации обратитесь к руководству на GNU компиляторы. Руководство можно найти по ссылке ниже.

Кроме компилятора, AVR GCC включает в себя другие инструменты, которые работают вместе для получения конечного исполняемого приложения для AVR микроконтроллера. Эта группа инструментов называется “тулчейном “ (toolchain). Важную функцию в этом AVR тулчейне выполняет AVR-Libс, которая обеспечивает функции стандартной Си библиотеки, а также множество дополнительных специфичных для AVR функций и базовым стартовым кодом (startup code). Руководство на AVR Libc вы можете найти по ссылке ниже.

Читайте также:  Заказ настроек интернета водафон

2.3 Платформа разработки

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

1. Интегрированная среда разработки: Atmel AVR Studio 5 (версия 5.0.1119)
2. AVR GCC тулчейн: AVR_8_bit_GNU_Toolchain_3.2.1_292 (gcc версии 4.5.1)
3. Микроконтроллер: Atmel ATmega88PA.

3. Советы и трюки по уменьшению размера кода

В этом разделы мы перечислим некоторые рекомендации по уменьшению размера кода. Для каждого совета будет дано описание и пример.

Используйте наименьший тип данных, способный представить вашу переменную. Для чтения 8-и разрядного регистра, например, не нужно использовать 2-х байтную переменную, достаточно однобайтной.

Размеры типов данных для AVR можно посмотреть в заголовочном файле stdint.h и в таблице 3-1.

Таблица 3-1. Типы данных для AVR, описанные в stdint.h

Имейте в виду, что некоторые опции компилятора могут повлиять на типы данных (например, AVR-GCC имеет опцию –mint8, которая делает тип int 8-и разрядным).

Два примера кода в таблице 3-2 показывают результат использования разных типов данных. Также в таблице показаны размеры кода, получаемого при построении проекта с опцией оптимизации –Os (оптимизация для размера).

Таблица 3-2. Пример использования различных типов данных.

В левом примере мы пользуемся 2-х байтным типом данных для временной переменной и возвращаемого значения. В правом примере вместо этого используется однобайтный тип char. Считывание данных происходит с 8-и разрядного регистра ADCH, а значит такого размера переменной вполне достаточно. Это позволяет нам сэкономить 2 байта памяти.

Заметьте, что до запуска функции main() есть начальный код инициализации (startup code), поэтому эти простые примеры Си кода занимают 90 байт.

В большинстве случаев не рекомендуется использовать глобальные переменные. Применяйте локальные переменные везде, где возможно. Если переменная используется только в функции, ее следует объявлять внутри функции как локальную переменную.

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

Если переменная объявлена как глобальная, в оперативной памяти для нее выделяется уникальный адрес. Также для доступа к глобальной переменной, как правило, используются дополнительные байты (по 2 на 16-и разрядный адрес), чтобы получить ее адрес.

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

Два примера в таблице 3-3 показывают эффект применения глобальных и локальных переменных.

Таблица 3-3. Пример глобальных и локальных переменных.

В левом примере мы объявляем однобайтовую глобальную переменную. Avr-size утилита показывает, что мы используем 104 байта памяти программ и один байт памяти данных при оптимизации –Os.

В правом примере, мы объявляем локальную переменную внутри main() функции и код уменьшается до 84 байтов, а оперативная память не используется совсем.

Циклы широко используются при программировании микроконтроллеров. В Си существуют три типа циклов – “while()<>”, “for()<>“ и “do<>while()”. Если включена опция оптимизации –Os, компилятор будет оптимизировать циклы автоматически, чтобы уменьшить размер кода. Однако мы можем сами немного уменьшить его.

Если используется цикл “do<>while()”, то инкремент и декремент индексной переменной цикла будет давать код разного размера. Обычно мы используем инкремент, чтобы подсчитывать количество циклов от нуля до максимального значения. Однако более эффективно делать наоборот – считать от максимального значения до нуля, то есть использовать декремент.

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

В таблице 3-4 приведены примеры кода, использующего цикл “do <>while()” с инкрементом и декрементом индексной переменной.

Таблица 3-4. Пример do<>while() циклов с инкрементом и декрементом индексной переменной

Читайте также:  Настройка claymore для etherium dwarfpool

Чтобы иметь более прозрачный Си код, мы записали этот пример как “dowhile(count);”, а не “do<>while(count—);”, как обычно пишут в книгах по Си. Однако в обоих случаях размер кода будет одинаковым.

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

Таблица 3-5. Пример объединения циклов.

3.5 Совет #5 – константы в программной памяти

Глобальные переменные, таблицы и массивы, которые никогда не меняются, должны размещаться во флэш памяти микроконтролера. Это позволяет экономить оперативную память.

В этом примере мы не используем ключевое слово “const”. Потому что объявление переменной с “const” сообщает компилятору, что ее значение не будет меняться, но не определяет место хранения.

Чтобы разместить данные в программной памяти и считывать их оттуда, AVR-Libs предоставляет макросы “PROGMEM” и “pgm_read_byte”. Эти макросы определены в заголовочном файле pgmspace.h.

Пример в таблице 3-6 показывает, как можно сохранить ОЗУ, переместив строку в программную память.

Таблица 3-6. Пример констант в программной памяти.

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

Если данные, хранящиеся во флэш памяти, используются в коде несколько раз, мы можем получить меньший код, используя дополнительную временную переменную вместо многократного вызова макроса “pgm_read_byte”.

В заголовочном файле pgmspace.h есть несколько макросов и функций для сохранения и чтения различных типов данных в/из памяти программ. Для большей информации обратитесь к руководству на avr-libc.

3.6 Совет #6 – типы доступа: static

Для глобальных данных использование ключевого слова static не всегда возможно. Если глобальные переменные объявлены с ключевым словом static, они могут быть доступны только в том файле, внутри которого они определены. Это предотвращает случайное использование переменной в других файлов.

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

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

Таблица 3-7. Пример использования типов доступа – статическая функция.

Заметьте, если функция вызывается несколько раз, она не будет встраиваться, потому что это увеличит код больше, чем прямой вызов функции.

3.7 Совет #7 – низко уровневые ассемблерные инструкции

Ассемблерные команды всегда лучше оптимизированного кода. Единственный недостаток ассемблерного кода – это непереносимый синтаксис, так что это в большинстве случаев его не рекомендуется использовать.

Чтобы улучшить читаемость и портируемость кода, можно использовать ассемблерные макросы. Такими макросами можно заменять функции, которые генерируются в 2-3 ассемблерные строки. В таблице 3-8 показан пример использования ассемблерного макроса вместо функции.

Таблица 3-8. Пример использования низкоуровневых ассемблерных инструкций.

Для более подробной информации относительно использования ассемблера с языком Си на AVR, ознакомьтесь с разделом “Inline Assembler Cookbook” в руководстве на avr-libc.

Источник