Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

Микропроцессоры и микроЭВМ. Учебное пособие

.pdf
Скачиваний:
0
Добавлен:
07.09.2026
Размер:
2 Мб
Скачать
☆
Рис. 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
Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]