Добавил:
ivanov666
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз:
Предмет:
Файл:Микропроцессоры и микроЭВМ. Учебное пособие
.pdf
Рис. 2.6. Окно утилиты для генерации makefile
В меню «Пуск» → Все программы → WinAVR → MFile (иконка в
виде кипящего чайника). Открывается окно, показанное на рис. 2.6. В меню «Mikefile» начинаем вводить параметры своего проекта.
В основном там нужно будет поменять название главного файла проекта (по умолчанию «main»), выбрать тип микроконтроллера MCU type,
остальное можно не трогать; все параметры, в том числе и указанные выше, возможно поменять будет потом в папке проекта. После заполнения
всех необходимых пунктов созданный файл командой «File» → «Save As»
сохраняем под имененем mikefile (по умолчанию) в папку Вашего проекта. Если открыть созданный makefile в Programmer Notepad (рис. 2.7), то в
нём можно обнаружить текст обычного конфигурационного файла. Комментарии начинаются с символа «#».
В переменной «MCU» указывается тип микроконтроллера. F_CPU
содержит тактовую частоту работы и используется в некоторых функциях
для формирования задержек. TARGET – строка, задающая имя главного
файла проекта (того, где есть функция main), имя указывается без расширения С. SRC = $(TARGET). c – строка, указывающая на источники Сикода, которые необходимо подключить к проекту. Если подключаются
ещё какие-то файлы, то они также записываются новой строкой под существующими.
61

Рис. 2.7. Содержимое makefile
В строке AVRDUDE_PROGRAMMER = stk500 можно выбрать лю-
бой тип программатора, который есть в выпадающем списке программы
MFile. Ниже строка выбора порта связи с программатором
AVRDUDE_PORT = com1. Список портов также указан в выпадающем
списке MFile. С программаторами есть небольшой ньюанс. Дело в том,
что в выпадающем списке программаторов нет USBasp, а он достаточно
распространён среди радиолюбителей. Его можно прописать вручную
следующим образом: AVRDUDE_PROGRAMMER = usbasp и указать
порт: AVRDUDE_PORT = usb.
Теперь проект нужно откомпилировать и прошить в кристалл. Для
этого в Programmer Notepage открывается файл, который необходимо откомпилировать, и выбирается команда «Tools» → «Make Clean» — это
очистит папку от предыдущих результатов компиляции. После этого
«Tools» → «Make All» — это откомпилирует проект. В случае успешной
компиляции будет показано окно (рис. 2.8), в котором приведены результирующие размеры файлов для прошивки. Найти эти файлы можно в папке с проектом. Файл с расширением *.hex содержит прошивку flashпамяти программ, а с расширением *.eep – прошивку для встроенной
энергонезависимой памяти eeprom.
62

Рис. 2.8. Окно результата компиляции
Рис. 2.9. Окно результата программирования микросхемы
63

Для прошивки микросхемы выбирается команда «Tools» ->
«Program». Если всё соединено правильно, результат будет таким, как
приведено на рис. 2.9. Теперь, создавая свой проект, можно не выходя
после очередной компиляции в USBASP_AVRDUDE_PROG, Pony Prog
или другую программу, зашивать кристалл и видеть результаты изменения исходного кода. Биты конфигурации микроконтроллера предварительно нужно зашить любой из вышеуказанных программ, так как
WinAVR этого не делает.
По сути, язык Cи стал стандартом в области языков программирования. На него наложены жёсткие ограничения, поэтому структура любой
программы будет иметь примерно такой вид:
Команды препроцессора – команды для автоматического редактирования текста программы перед компиляцией
Описание переменных, видных во всех подпрограммах
Подпрограмма 1
{
Описание переменных, видных только внутри подпрограммы
Тело подпрограммы
}
...
Подпрограмма N
{
Описание переменных, видных только внутри подпрограммы
Тело подпрограммы
}
Главная программа (запускается при включении или перезапуске ЗУ)
{
Описание переменных, видных только внутри подпрограммы
Тело подпрограммы
}
Программа может сопровождаться комментариями – некоторым поясняющим текстом, который при компиляции не учитывается. Многострочные комментарии начинаются с символов «/*», а заканчиваются «*/»
(пример, /* Текст комментария */). Однострочные комментарии начинаются после символов «//» и распространяются до конца строки.
Команды препроцессора начинаются с символа «#» и служат для
указаний компилятору особенностей процесса компиляции. Например,
выражение #include <avr/io.h> – подключает к проекту (вставляет весь
файл io.h вместо этой строки) необходимые описания и функции из ком-
2.4. СТРУКТУРА ПРОГРАММЫ НА ЯЗЫКЕ Cи
64

плекта поставки WinAVR. Это очень удобно, так как позволяют пользоваться всеми подпрограммами включаемого файла. Угловые скобки говорят о том, что файл надо искать в том месте, куда установился WinAVR в
подкаталоге avr. Двойные кавычки указывают компилятору, что файлы
надо искать в той же папке где находится главный файл программы
main.cpp. Расширение «c» говорит о том, что внутри файла лежат подпрограммы на языке Си. Расширение «h» говорит, что там лежат описания
переменных и функций.
Другая удобная команда препроцессора: #define aaa bbb. Эта команда перед компиляцией обыскивает весь файл программы ниже этой команды и меняет aaa на bbb. Причём в качестве bbb может быть использовано достаточно сложное выражение (макрос), которое заменит aaa в тексте программы, что очень сильно облегчает читаемость исходного текста.
Все команды препроцессора, включая #include и #define, напрямую
никак не отражаются в конечной программе на машинных кодах, они просто меняют текст программы перед компиляцией. Сами слова #include и
#define – это жёстко зафиксированные слова в Си. Их никак иначе нельзя
использовать, как и множество других слов, которые считаются зарезервированными.
Далее идёт описание констант.
Константа – это лексема языка, представляющая какое-либо фиксированное числовое или иное значение. Константы делятся на несколько
групп: целочисленные, действительные, символьные и строковые. В зависимости от значения константы, компилятор относит её к одному из известных типов данных, выделяя для неё соответствующее количество
байтов памяти.
Целочисленные константы представляют собой запись чисел в од-
ной из допустимых систем счисления: десятичной, двоичной, восьмеричной и шестнадцатеричной. Система счисления определяется компилятором следующим образом: если лексема, определённая как константа, начинается с символов «0b» – принимается, что это запись числа в двоичной
системе; если лексема начинается с символов «0x» – принимается шестнадцатеричное представление числа. Восьмеричная система подразумевает, что число всегда начинается с нуля. Если же ни один из рассмотренных
признаков не обнаружен – принимается десятичная система.
По значению константы компилятор определяет наименьший подходящий для неё тип данных. Однако часто необходимо задавать константы
так, чтобы они заранее соответствовали конкретному типу, например,
нужно определить константу long, равную 2. Если просто написать 2, то
компилятор посчитает, что для константы достаточно типа char, и будет
его и использовать, что может привести к серьёзным ошибкам вычисления. Чтобы принудительно указать размер константы, используются специальные суффиксы, приписываемые к константе справа:
65

L – для обозначения long (можно и прописную букву «l» использо-
вать);
U – для обозначения unsigned (можно и прописную букву «u» использовать). Суффиксы могут использоваться по отдельности или вместе
в любых комбинациях. Константы действительного типа определяются
компилятором по наличию точки в записи лексемы или по наличию
символа «e» (или «E»), отделяющего мантиссу 7 от показателя степени
числа 10.
Вот пример констант этого типа:
66.2 .1 0. 2.14e-7
Количество байтов, выделенное компилятором для хранения константы, в этом случае определяется формой представления чисел с плавающей точкой, т.е. может быть разным для разных компиляторов. При
помощи служебного слова sizeof можно получить константу, равную количеству байтов для хранения константы:
sizeof(3) будет равно 1 (1 байт);
sizeof(3L) будет равно 4 (4 байта),
и т.д.
Символьная константа – это разновидность целочисленной константы, служащей для определения кода алфавитно-цифрового символа. Для
обозначения такой константы используется запись символа, окружённого
апострофами: ′F′ ′5′′.′Очевидно, что далеко не любой символ можно ввести таким образом. Снять проблему позволяет особый символ «обратная
черта» (или back-slash): этот символ (называется escape-символ) позволяет ввести неотображаемые символы, символы «апостроф», «кавычки»,
«вопрос» и «обратная черта», а так же любой другой символ, указав его
код.
После приводится описание всех глобальных переменных проекта по
шаблону: тип_переменной имя_переменной1, имя_переменной2,…;.
В качестве типа переменной может быть использован стандартный или
собственный тип данных (собственный тип данных базируется на стандартных). В таблице 2.1 представлены основные типы данных в С++. Вся
таблица делится на три столбца. В первом столбце указывается зарезервированное слово в С++, которое будет определять, каждый свой, тип данных. Во втором столбце указывается количество байт, которое отводится
под переменную с соответствующим типом данных. В третьем столбце
показан диапазон допустимых значений для каждого типа данных. Следует обратить внимание на то, что в таблице все типы данных расположены
от меньшего к большему.
66

Зарезервированное
слово
2.1. Типы данных языка C++
Размер занимаемой
памяти (байт)
Диапазон принимаемых
значений
Целочисленный (логический) тип данных
bool 1 0 / 255
Целочисленный (символьный) тип данных
char 1 0 / 255
Целочисленные типы данных
short int 2 – 32 768 / 32 767
unsigned short int 2 0 / 65 535
int 4 – 2 147 483 648 / 2 147 483 647
unsigned int 4 0 / 4 294 967 295
long int 4 – 2 147 483 648 / 2 147 483 647
unsigned long int 4 0 / 4 294 967 295
Типы данных с плавающей точкой
float 4
– 2 147 483 648.0 / 2 147 483
647.0
long float 8
– 9 223 372 036 854 775 808 .0 /
9 223 372 036 854 775 807.0
double 8
–9 223 372 036 854 775 808 .0 /
9 223 372 036 854 775 807.0
Переменная – это именованная величина определённого типа, которая может изменять своё значение в ходе выполнения программы. После
объявления переменной для доступа к ней используется соответствующий
идентификатор (он эквивалентен адресу переменной в ОЗУ), а присваиваемые значения должны соответствовать её типу данных.
Порядок байтов, составляющих переменную, определяется платформой, для которой компилируется программа. Для микроконтроллеров
AVR принято, что многобайтные переменные хранятся в памяти «от
младшего байта к старшему»; т.е. первым размещается младший байт
(наименее значащие биты числа), а затем все более старшие, вплоть
до последнего (наиболее значащие биты числа). При описании переменных могут дополнительно использоваться ключевые слова static, register,
extern и volatile. Эти слова могут располагаться как до определения типа
переменной, так и после него, однако обязательно до указания идентификатора. Если переменная объявлена с использованием слова register, это
означает, что компилятор поместит её в один из доступных регистров
67

микроконтроллера. Не стоит надеяться, что регистровые переменные каким-то образом помогут создать более быстродействующую программу:
для большинства случаев результат будет скорее обратным, хотя в некоторых случаях положительный эффект может быть.
Ключевое слово volatile – очень важное. Дословно оно означает «изменяемая», что на первый взгляд излишне для переменной, само наименование которой означает изменение значения. Однако, дело тут в другом.
Компилятор в процессе своей работы анализирует написанный программистом текст программы и старается убрать из него те части, которые
явно не несут никакого смысла (оптимизатор программ). В частности,
компилятор может исключить все участки программы, которые обрабатывают переменные, не изменяющие (с точки зрения компилятора) значения. Разумеется, далеко не всегда это соответствует замыслам программиста. Ключевое слово volatile, использованное при объявлении переменной,
укажет компилятору, что эта переменная все-таки изменяется, пусть даже
и неизвестным компилятору способом. Можно считать, что volatile есть
синоним слова «неприкосновенный», т.е. объявленную таким образом
переменную нельзя трогать при оптимизации.
Далее идут описания подпрограмм, которые можно вызывать из любого участка исполняемого кода. Вообще, в языке Си отсутствуют такие
понятия, свойственные языку Pascal, как подпрограммы и процедуры,
вместо которых используется функция. Так, если функция не возвращает
значения, то она эквивалентна процедуре, а если возвращает – то подпрограмме. Благодаря специальным адресным переменным – указателям,
функции могут быть превращены в очень мощный программный инструмент. Однажды написанная функция может быть использована многократно и в других программах. При написании функции стремятся, чтобы
её алгоритм был функционально достаточен и не зависел от других функций. Разбивая алгоритм программы на функции, которые в совокупности
выполняют поставленную задачу, общая структура алгоритма приобретает модульное строение. Имея несколько независимых модулей, открываются широкие перспективы по их разработке независимыми участниками
проекта (появляется возможность просто добавлять готовые функции к
проекту с помощью директивы include).
Формат объявления функции имеет вид: тип_возвращае-
мого_значения имя_функции (тип_аргумента1 имя_аргумента1,
тип_аргумента2 имя_аргумента2, …). В качестве связки
тип_аргумента имя_аргумента может быть использовано ключевое сло-
во void, которое указывает на то, что этот аргумент является пустым. Так,
если объявление функции имеет вид void func (void), то это означает, что
функция ничего не возвращает, и не передаются аргументы при обращении к ней.
68

Так как функция может быть вызвана из любого участка исполняемого кода, то вызываться она может из другой функции или она может
вызывать саму себя (рекурсия). Для вызова функции из другой функции
необходимо, чтобы она была объявлена до вызывающей функции (была в
области видимости), что порой трудно обеспечить при сложной взаимосвязи между компонентами программы. Поэтому используют прототипы
функций (заголовки функций), которые размещаются сразу после объявления глобальных переменных. Структура объявления прототипа очень
схожа со структурой объявления функции, но даёт информацию лишь о
названии и типах возвращаемого и передаваемых значений. Формат объявления прототипа функции имеет вид: тип_возвращаемого_значения
имя_функции, (тип_аргумента1, тип_аргумента_2 и т.д.).
Существует множество стандартных функций, а также функции,
подключаемые к проекту из других файлов. Среди стандартных функций
наиболее важной является функция main(), с которой начинается выполнение программы сразу после запуска микроконтроллера. Именно на её
начало указывает команда перехода Reset, и по сути она является обработчиком прерывания «сброс».
Сброс может быть вызван и с помощью сторожевого таймера, который имеется практически у каждого микроконтроллера AVR. В зависимости от настроек, он постоянно уменьшает своё значение с определённым
периодом, дойдя до нулевого значения, он вызовет сброс микроконтроллера. Чтобы этого не произошло, программа должна постоянно сбрасывать значения этого счётчика в процессе своей работы с помощью вызова
функции wdt_reset(). Этот механизм предусмотрен разработчиками
ATMEL для предотвращения зависания программы. Если программа зависла, то она не проходит основной цикл и не сбрасывает сторожевой
таймер, и тогда процессор перезапустится. Все процессы в ЗУ инициализируются как будто вы только что включили ЗУ. Конечно, надо искать
ошибку и не допускать произвольных перезагрузок. Функция wdt_reset();
не получает ни каких параметров (поэтому внутри скобок ничего нет) и
ничего не возвращает, поэтому результат работы функции не присваивается никакой переменной. Чтобы иметь право вызвать на выполнение эту
функцию, надо чтобы её текст был где-то описан раньше вызова. Эту
функцию можно найти внутри библиотек функций компилятора WinAVR,
и, чтобы её вызвать, необходимо указать компилятору, что это за функция. Для этого надо в начале текста программы подключить заголовочный
файл, включающий в себя описание функций сторожевого таймера
#include <avr/wdt.h>.
Познакомимся с особой переменной – указателем. Указатель – это
переменная, которая содержит не само значение переменной, а адрес той
области памяти, где искомое значение хранится. Указатель – это одна из
наиболее мощных возможностей языка Си и, как любое мощное средство,
69

указатель может быть как чрезвычайно удобным инструментом, так и источником больших проблем, если используется неумело. Для объявления
указателя достаточно добавить к типу переменной символ «*»:
unsigned int * iptr;
char * arr[];
long * lptr;
Объявленные таким образом переменные iptr, arr[] и lptr будут указателями: iptr – указатель на число типа int, arr – массив указателей на
символ, lptr – указатель на число типа long.
Чтобы обратиться к тем данным, на которые указывает переменнаяуказатель, следует предварить её имя символом «*» (такая операция получила название «разыменование указателя»):
iptr – адрес области памяти, хранящей число типа int;
*iptr – значение этого числа.
Очевидно, что использование указателей позволит получить доступ к
любой произвольной области памяти. Именно в этом и кроется источник
возможных проблем: стоит ошибиться в значении переменной-указателя,
как последствия обращения к данным, на которые она указывает, становятся непредсказуемыми.
Выражение – это запись алгоритма вычислений над операндами при
помощи знаков математических, логических и прочих действий. Другими
словами: выражение – есть формула вычислений, записанная средствами
языка программирования. В определении выражения использован новый
термин: операнд.
Операнд – это выражение, над которым выполняется какое-либо действие. Как видно, операнд и выражение оказываются определёнными друг
через друга, что сопоставимо с математическим выражением. В языке Си
определены два типа операндов (выражений): математические и логические. Математическое выражение состоит из операндов, эквивалентных
числам, и знаков математических операций. Логическое выражение – это
выражение отношений, т.е. ответов на вопросы «больше?», «меньше?»,
«равно?» и т.д. Аналогично математическим, существуют логические действия и соответствующие им знаки логических операций.
Действия могут выполняться над двумя операндами или над одним
операндом – унарные операции. В Си определены следующие унарные
математические операции:
Знак Операция
– Унарный минус, признак изменения знака числа
на противоположный.
+ Унарный плюс, означает сохранение знака числа
без изменения (принципиального смысла не имеет).
++ Знак инкремента, увеличение операнда на 1.
70
Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]
